AI-For-Beginners/translations/ar/lessons/5-NLP/13-TextRep/TextRepresentationPyTorch.i...

577 lines
24 KiB
Plaintext

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# مهمة تصنيف النصوص\n",
"\n",
"كما ذكرنا، سنركز على مهمة تصنيف النصوص البسيطة باستخدام مجموعة بيانات **AG_NEWS**، والتي تهدف إلى تصنيف عناوين الأخبار إلى واحدة من 4 فئات: العالم، الرياضة، الأعمال، والعلوم/التكنولوجيا.\n",
"\n",
"## مجموعة البيانات\n",
"\n",
"تم تضمين هذه المجموعة في وحدة [`torchtext`](https://github.com/pytorch/text)، مما يجعل الوصول إليها سهلاً.\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import torch\n",
"import torchtext\n",
"import os\n",
"import collections\n",
"os.makedirs('./data',exist_ok=True)\n",
"train_dataset, test_dataset = torchtext.datasets.AG_NEWS(root='./data')\n",
"classes = ['World', 'Sports', 'Business', 'Sci/Tech']"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"هنا، يحتوي `train_dataset` و `test_dataset` على مجموعات تُرجع أزواجًا من التصنيف (رقم الفئة) والنص على التوالي، على سبيل المثال:\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(3,\n",
" \"Wall St. Bears Claw Back Into the Black (Reuters) Reuters - Short-sellers, Wall Street's dwindling\\\\band of ultra-cynics, are seeing green again.\")"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"list(train_dataset)[0]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"لذا، دعونا نطبع أول 10 عناوين جديدة من مجموعتنا البيانية:\n"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"**Sci/Tech** -> Wall St. Bears Claw Back Into the Black (Reuters) Reuters - Short-sellers, Wall Street's dwindling\\band of ultra-cynics, are seeing green again.\n",
"**Sci/Tech** -> Carlyle Looks Toward Commercial Aerospace (Reuters) Reuters - Private investment firm Carlyle Group,\\which has a reputation for making well-timed and occasionally\\controversial plays in the defense industry, has quietly placed\\its bets on another part of the market.\n",
"**Sci/Tech** -> Oil and Economy Cloud Stocks' Outlook (Reuters) Reuters - Soaring crude prices plus worries\\about the economy and the outlook for earnings are expected to\\hang over the stock market next week during the depth of the\\summer doldrums.\n",
"**Sci/Tech** -> Iraq Halts Oil Exports from Main Southern Pipeline (Reuters) Reuters - Authorities have halted oil export\\flows from the main pipeline in southern Iraq after\\intelligence showed a rebel militia could strike\\infrastructure, an oil official said on Saturday.\n",
"**Sci/Tech** -> Oil prices soar to all-time record, posing new menace to US economy (AFP) AFP - Tearaway world oil prices, toppling records and straining wallets, present a new economic menace barely three months before the US presidential elections.\n"
]
}
],
"source": [
"for i,x in zip(range(5),train_dataset):\n",
" print(f\"**{classes[x[0]]}** -> {x[1]}\")\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"لأن مجموعات البيانات هي مكررات، إذا أردنا استخدام البيانات عدة مرات، نحتاج إلى تحويلها إلى قائمة:\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"train_dataset, test_dataset = torchtext.datasets.AG_NEWS(root='./data')\n",
"train_dataset = list(train_dataset)\n",
"test_dataset = list(test_dataset)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## تقسيم النص إلى وحدات\n",
"\n",
"الآن نحتاج إلى تحويل النص إلى **أرقام** يمكن تمثيلها كـ **موترات**. إذا أردنا تمثيل النص على مستوى الكلمات، يجب علينا القيام بأمرين:\n",
"* استخدام **المقسم النصي** لتقسيم النص إلى **وحدات نصية**\n",
"* بناء **قاموس** لهذه الوحدات النصية.\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"['he', 'said', 'hello']"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"tokenizer = torchtext.data.utils.get_tokenizer('basic_english')\n",
"tokenizer('He said: hello')"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"counter = collections.Counter()\n",
"for (label, line) in train_dataset:\n",
" counter.update(tokenizer(line))\n",
"vocab = torchtext.vocab.vocab(counter, min_freq=1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"باستخدام المفردات، يمكننا بسهولة ترميز السلسلة المرمزة إلى مجموعة من الأرقام:\n"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Vocab size if 95810\n"
]
},
{
"data": {
"text/plain": [
"[599, 3279, 97, 1220, 329, 225, 7368]"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"vocab_size = len(vocab)\n",
"print(f\"Vocab size if {vocab_size}\")\n",
"\n",
"stoi = vocab.get_stoi() # dict to convert tokens to indices\n",
"\n",
"def encode(x):\n",
" return [stoi[s] for s in tokenizer(x)]\n",
"\n",
"encode('I love to play with my words')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## تمثيل النص باستخدام حقيبة الكلمات\n",
"\n",
"لأن الكلمات تمثل المعاني، يمكننا أحيانًا فهم معنى النص بمجرد النظر إلى الكلمات الفردية، بغض النظر عن ترتيبها في الجملة. على سبيل المثال، عند تصنيف الأخبار، كلمات مثل *الطقس* و*الثلج* من المحتمل أن تشير إلى *توقعات الطقس*، بينما كلمات مثل *الأسهم* و*الدولار* قد تشير إلى *الأخبار المالية*.\n",
"\n",
"**حقيبة الكلمات** (BoW) هي الطريقة الأكثر استخدامًا لتمثيل النصوص بشكل تقليدي باستخدام المتجهات. يتم ربط كل كلمة بمؤشر في المتجه، ويحتوي عنصر المتجه على عدد مرات ظهور الكلمة في مستند معين.\n",
"\n",
"![صورة توضح كيفية تمثيل حقيبة الكلمات باستخدام المتجهات في الذاكرة.](../../../../../translated_images/ar/bag-of-words-example.606fc1738f1d7ba9.webp)\n",
"\n",
"> **ملاحظة**: يمكنك أيضًا التفكير في حقيبة الكلمات كجمع لجميع المتجهات المشفرة بطريقة \"واحد-ساخن\" للكلمات الفردية في النص.\n",
"\n",
"فيما يلي مثال على كيفية إنشاء تمثيل حقيبة الكلمات باستخدام مكتبة Scikit Learn بلغة بايثون:\n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[1, 1, 0, 2, 0, 0, 0, 0, 0]], dtype=int64)"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from sklearn.feature_extraction.text import CountVectorizer\n",
"vectorizer = CountVectorizer()\n",
"corpus = [\n",
" 'I like hot dogs.',\n",
" 'The dog ran fast.',\n",
" 'Its hot outside.',\n",
" ]\n",
"vectorizer.fit_transform(corpus)\n",
"vectorizer.transform(['My dog likes hot dogs on a hot day.']).toarray()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"لحساب متجه حقيبة الكلمات من تمثيل المتجه لمجموعة بيانات AG_NEWS الخاصة بنا، يمكننا استخدام الدالة التالية:\n"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"tensor([2., 1., 2., ..., 0., 0., 0.])\n"
]
}
],
"source": [
"vocab_size = len(vocab)\n",
"\n",
"def to_bow(text,bow_vocab_size=vocab_size):\n",
" res = torch.zeros(bow_vocab_size,dtype=torch.float32)\n",
" for i in encode(text):\n",
" if i<bow_vocab_size:\n",
" res[i] += 1\n",
" return res\n",
"\n",
"print(to_bow(train_dataset[0][1]))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"> **ملاحظة:** هنا نستخدم المتغير العالمي `vocab_size` لتحديد الحجم الافتراضي للمفردات. نظرًا لأن حجم المفردات غالبًا ما يكون كبيرًا، يمكننا تحديد حجم المفردات بالكلمات الأكثر شيوعًا. حاول تقليل قيمة `vocab_size` وتشغيل الكود أدناه، وشاهد كيف يؤثر ذلك على الدقة. يجب أن تتوقع انخفاضًا في الدقة، ولكن ليس بشكل كبير، مقابل أداء أعلى.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## تدريب مصنف BoW\n",
"\n",
"الآن بعد أن تعلمنا كيفية بناء تمثيل حقيبة الكلمات (Bag-of-Words) لنصوصنا، دعونا ندرب مصنفًا يعتمد عليه. أولاً، نحتاج إلى تحويل مجموعة البيانات الخاصة بنا للتدريب بطريقة يتم فيها تحويل جميع تمثيلات المتجهات الموضعية إلى تمثيل حقيبة الكلمات. يمكن تحقيق ذلك عن طريق تمرير وظيفة `bowify` كمعامل `collate_fn` إلى `DataLoader` القياسي في مكتبة torch:\n"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {},
"outputs": [],
"source": [
"from torch.utils.data import DataLoader\n",
"import numpy as np \n",
"\n",
"# this collate function gets list of batch_size tuples, and needs to \n",
"# return a pair of label-feature tensors for the whole minibatch\n",
"def bowify(b):\n",
" return (\n",
" torch.LongTensor([t[0]-1 for t in b]),\n",
" torch.stack([to_bow(t[1]) for t in b])\n",
" )\n",
"\n",
"train_loader = DataLoader(train_dataset, batch_size=16, collate_fn=bowify, shuffle=True)\n",
"test_loader = DataLoader(test_dataset, batch_size=16, collate_fn=bowify, shuffle=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"الآن دعونا نعرّف شبكة عصبية تصنيفية بسيطة تحتوي على طبقة خطية واحدة. حجم متجه الإدخال يساوي `vocab_size`، وحجم الإخراج يتوافق مع عدد الفئات (4). نظرًا لأننا نقوم بحل مهمة التصنيف، فإن وظيفة التنشيط النهائية هي `LogSoftmax()`.\n"
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {},
"outputs": [],
"source": [
"net = torch.nn.Sequential(torch.nn.Linear(vocab_size,4),torch.nn.LogSoftmax(dim=1))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"الآن سنقوم بتعريف حلقة التدريب القياسية في PyTorch. نظرًا لأن مجموعة البيانات لدينا كبيرة جدًا، ولأغراض التعليم، سنقوم بالتدريب فقط لمدة حقبة واحدة، وأحيانًا حتى أقل من حقبة واحدة (يسمح لنا تحديد معامل `epoch_size` بتقييد التدريب). سنقوم أيضًا بالإبلاغ عن دقة التدريب المتراكمة أثناء التدريب؛ يتم تحديد تكرار الإبلاغ باستخدام معامل `report_freq`.\n"
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {},
"outputs": [],
"source": [
"def train_epoch(net,dataloader,lr=0.01,optimizer=None,loss_fn = torch.nn.NLLLoss(),epoch_size=None, report_freq=200):\n",
" optimizer = optimizer or torch.optim.Adam(net.parameters(),lr=lr)\n",
" net.train()\n",
" total_loss,acc,count,i = 0,0,0,0\n",
" for labels,features in dataloader:\n",
" optimizer.zero_grad()\n",
" out = net(features)\n",
" loss = loss_fn(out,labels) #cross_entropy(out,labels)\n",
" loss.backward()\n",
" optimizer.step()\n",
" total_loss+=loss\n",
" _,predicted = torch.max(out,1)\n",
" acc+=(predicted==labels).sum()\n",
" count+=len(labels)\n",
" i+=1\n",
" if i%report_freq==0:\n",
" print(f\"{count}: acc={acc.item()/count}\")\n",
" if epoch_size and count>epoch_size:\n",
" break\n",
" return total_loss.item()/count, acc.item()/count"
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"3200: acc=0.8028125\n",
"6400: acc=0.8371875\n",
"9600: acc=0.8534375\n",
"12800: acc=0.85765625\n"
]
},
{
"data": {
"text/plain": [
"(0.026090790722161722, 0.8620069296375267)"
]
},
"execution_count": 25,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"train_epoch(net,train_loader,epoch_size=15000)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## ثنائيات الكلمات، ثلاثيات الكلمات، و N-Grams\n",
"\n",
"أحد قيود طريقة حقيبة الكلمات هو أن بعض الكلمات تكون جزءًا من تعبيرات متعددة الكلمات. على سبيل المثال، الكلمة \"hot dog\" لها معنى مختلف تمامًا عن الكلمتين \"hot\" و\"dog\" في سياقات أخرى. إذا قمنا دائمًا بتمثيل الكلمات \"hot\" و\"dog\" بنفس المتجهات، فقد يؤدي ذلك إلى إرباك النموذج الخاص بنا.\n",
"\n",
"لمعالجة هذا الأمر، يتم استخدام **تمثيلات N-gram** غالبًا في طرق تصنيف المستندات، حيث يكون تكرار كل كلمة، أو ثنائية الكلمات، أو ثلاثية الكلمات ميزة مفيدة لتدريب المصنفات. في تمثيل ثنائيات الكلمات، على سبيل المثال، سنضيف جميع أزواج الكلمات إلى المفردات، بالإضافة إلى الكلمات الأصلية.\n",
"\n",
"فيما يلي مثال على كيفية إنشاء تمثيل حقيبة كلمات لثنائيات الكلمات باستخدام مكتبة Scikit Learn:\n"
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Vocabulary:\n",
" {'i': 7, 'like': 11, 'hot': 4, 'dogs': 2, 'i like': 8, 'like hot': 12, 'hot dogs': 5, 'the': 16, 'dog': 0, 'ran': 14, 'fast': 3, 'the dog': 17, 'dog ran': 1, 'ran fast': 15, 'its': 9, 'outside': 13, 'its hot': 10, 'hot outside': 6}\n"
]
},
{
"data": {
"text/plain": [
"array([[1, 0, 1, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],\n",
" dtype=int64)"
]
},
"execution_count": 26,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"bigram_vectorizer = CountVectorizer(ngram_range=(1, 2), token_pattern=r'\\b\\w+\\b', min_df=1)\n",
"corpus = [\n",
" 'I like hot dogs.',\n",
" 'The dog ran fast.',\n",
" 'Its hot outside.',\n",
" ]\n",
"bigram_vectorizer.fit_transform(corpus)\n",
"print(\"Vocabulary:\\n\",bigram_vectorizer.vocabulary_)\n",
"bigram_vectorizer.transform(['My dog likes hot dogs on a hot day.']).toarray()\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"العيب الرئيسي في نهج N-gram هو أن حجم المفردات يبدأ في النمو بسرعة كبيرة. في الواقع، نحتاج إلى دمج تمثيل N-gram مع بعض تقنيات تقليل الأبعاد، مثل *التضمينات*، والتي سنناقشها في الوحدة التالية.\n",
"\n",
"لاستخدام تمثيل N-gram في مجموعة بيانات **AG News** الخاصة بنا، نحتاج إلى بناء مفردات ngram خاصة:\n"
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Bigram vocabulary length = 1308842\n"
]
}
],
"source": [
"counter = collections.Counter()\n",
"for (label, line) in train_dataset:\n",
" l = tokenizer(line)\n",
" counter.update(torchtext.data.utils.ngrams_iterator(l,ngrams=2))\n",
" \n",
"bi_vocab = torchtext.vocab.vocab(counter, min_freq=1)\n",
"\n",
"print(\"Bigram vocabulary length = \",len(bi_vocab))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"يمكننا استخدام نفس الكود أعلاه لتدريب المصنف، ولكن ذلك سيكون غير فعال من حيث استهلاك الذاكرة. في الوحدة التالية، سنقوم بتدريب مصنف ثنائي باستخدام التضمينات.\n",
"\n",
"> **ملاحظة:** يمكنك فقط الاحتفاظ بتلك ngrams التي تظهر في النص أكثر من عدد مرات محدد. هذا سيضمن استبعاد الثنائيات النادرة، وسيقلل بشكل كبير من الأبعاد. للقيام بذلك، قم بتعيين قيمة أعلى للمعامل `min_freq`، وراقب كيف يتغير طول المفردات.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## تكرار المصطلح وتكرار الوثيقة العكسي TF-IDF\n",
"\n",
"في تمثيل BoW، يتم وزن ظهور الكلمات بالتساوي بغض النظر عن الكلمة نفسها. ومع ذلك، من الواضح أن الكلمات الشائعة مثل *a* و*in* أقل أهمية بكثير للتصنيف مقارنة بالمصطلحات المتخصصة. في الواقع، في معظم مهام معالجة اللغة الطبيعية، تكون بعض الكلمات أكثر أهمية من غيرها.\n",
"\n",
"**TF-IDF** تعني **تكرار المصطلح–تكرار الوثيقة العكسي**. وهي عبارة عن تعديل لطريقة حقيبة الكلمات، حيث يتم استخدام قيمة عائمة بدلاً من القيمة الثنائية 0/1 التي تشير إلى ظهور كلمة في وثيقة، وهذه القيمة ترتبط بتكرار ظهور الكلمة في النصوص.\n",
"\n",
"بشكل أكثر رسمية، يتم تعريف الوزن $w_{ij}$ لكلمة $i$ في الوثيقة $j$ على النحو التالي:\n",
"$$\n",
"w_{ij} = tf_{ij}\\times\\log({N\\over df_i})\n",
"$$\n",
"حيث:\n",
"* $tf_{ij}$ هو عدد مرات ظهور الكلمة $i$ في الوثيقة $j$، أي القيمة التي رأيناها في طريقة BoW\n",
"* $N$ هو عدد الوثائق في المجموعة\n",
"* $df_i$ هو عدد الوثائق التي تحتوي على الكلمة $i$ في المجموعة بأكملها\n",
"\n",
"قيمة TF-IDF $w_{ij}$ تزداد بشكل يتناسب مع عدد مرات ظهور الكلمة في الوثيقة، ويتم تعديلها بناءً على عدد الوثائق في النصوص التي تحتوي على الكلمة، مما يساعد على التكيف مع حقيقة أن بعض الكلمات تظهر بشكل أكثر تكرارًا من غيرها. على سبيل المثال، إذا ظهرت الكلمة في *كل* الوثائق في المجموعة، فإن $df_i=N$، و$w_{ij}=0$، وسيتم تجاهل تلك المصطلحات تمامًا.\n",
"\n",
"يمكنك بسهولة إنشاء تمثيل TF-IDF للنص باستخدام مكتبة Scikit Learn:\n"
]
},
{
"cell_type": "code",
"execution_count": 28,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[0.43381609, 0. , 0.43381609, 0. , 0.65985664,\n",
" 0.43381609, 0. , 0. , 0. , 0. ,\n",
" 0. , 0. , 0. , 0. , 0. ,\n",
" 0. ]])"
]
},
"execution_count": 28,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from sklearn.feature_extraction.text import TfidfVectorizer\n",
"vectorizer = TfidfVectorizer(ngram_range=(1,2))\n",
"vectorizer.fit_transform(corpus)\n",
"vectorizer.transform(['My dog likes hot dogs on a hot day.']).toarray()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## الخاتمة\n",
"\n",
"على الرغم من أن تمثيلات TF-IDF توفر وزنًا تكراريًا للكلمات المختلفة، إلا أنها غير قادرة على تمثيل المعنى أو الترتيب. كما قال اللغوي الشهير ج. ر. فيرث في عام 1935: \"المعنى الكامل للكلمة دائمًا ما يكون سياقيًا، ولا يمكن أخذ أي دراسة للمعنى بعيدًا عن السياق على محمل الجد.\" سنتعلم لاحقًا في الدورة كيفية التقاط المعلومات السياقية من النص باستخدام نمذجة اللغة.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**إخلاء المسؤولية**: \nتم ترجمة هذا المستند باستخدام خدمة الترجمة بالذكاء الاصطناعي [Co-op Translator](https://github.com/Azure/co-op-translator). بينما نسعى لتحقيق الدقة، يرجى العلم أن الترجمات الآلية قد تحتوي على أخطاء أو معلومات غير دقيقة. يجب اعتبار المستند الأصلي بلغته الأصلية المصدر الرسمي. للحصول على معلومات حاسمة، يُوصى بالاستعانة بترجمة بشرية احترافية. نحن غير مسؤولين عن أي سوء فهم أو تفسيرات خاطئة تنشأ عن استخدام هذه الترجمة.\n"
]
}
],
"metadata": {
"interpreter": {
"hash": "16af2a8bbb083ea23e5e41c7f5787656b2ce26968575d8763f2c4b17f9cd711f"
},
"kernelspec": {
"display_name": "Python 3.8.12 ('py38')",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.12"
},
"coopTranslator": {
"original_hash": "7b9040985e748e4e2d4c689892456ad7",
"translation_date": "2025-08-28T04:35:40+00:00",
"source_file": "lessons/5-NLP/13-TextRep/TextRepresentationPyTorch.ipynb",
"language_code": "ar"
}
},
"nbformat": 4,
"nbformat_minor": 2
}