462 lines
29 KiB
Plaintext
462 lines
29 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"# الشبكات العصبية التكرارية\n",
|
|
"\n",
|
|
"في الوحدة السابقة، تناولنا التمثيلات الدلالية الغنية للنصوص. الهيكلية التي استخدمناها تلتقط المعنى المجمع للكلمات في الجملة، لكنها لا تأخذ بعين الاعتبار **ترتيب** الكلمات، لأن عملية التجميع التي تلي التضمينات تزيل هذه المعلومات من النص الأصلي. وبسبب عدم قدرة هذه النماذج على تمثيل ترتيب الكلمات، فإنها لا تستطيع حل المهام الأكثر تعقيدًا أو الغامضة مثل توليد النصوص أو الإجابة على الأسئلة.\n",
|
|
"\n",
|
|
"لتمثيل معنى تسلسل النصوص، سنستخدم هيكلية شبكة عصبية تُعرف بـ **الشبكة العصبية التكرارية** أو RNN. عند استخدام RNN، نقوم بتمرير الجملة عبر الشبكة كلمة واحدة في كل مرة، وتنتج الشبكة **حالة** معينة، والتي نمررها مرة أخرى إلى الشبكة مع الكلمة التالية.\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"بالنظر إلى تسلسل الإدخال من الرموز $X_0,\\dots,X_n$، تقوم RNN بإنشاء تسلسل من كتل الشبكة العصبية، وتدرب هذا التسلسل من البداية إلى النهاية باستخدام الانتشار العكسي. كل كتلة شبكة تأخذ زوجًا $(X_i,S_i)$ كمدخل، وتنتج $S_{i+1}$ كنتيجة. الحالة النهائية $S_n$ أو الناتج $Y_n$ يتم تمريرها إلى مصنف خطي لإنتاج النتيجة. جميع كتل الشبكة تشترك في نفس الأوزان، ويتم تدريبها من البداية إلى النهاية باستخدام تمريرة انتشار عكسي واحدة.\n",
|
|
"\n",
|
|
"> الشكل أعلاه يوضح الشبكة العصبية التكرارية في شكلها المفكوك (على اليسار)، وفي تمثيلها التكراري الأكثر اختصارًا (على اليمين). من المهم أن ندرك أن جميع خلايا RNN تشترك في نفس **الأوزان القابلة للمشاركة**.\n",
|
|
"\n",
|
|
"نظرًا لأن متجهات الحالة $S_0,\\dots,S_n$ تمر عبر الشبكة، فإن RNN قادرة على تعلم التبعيات التسلسلية بين الكلمات. على سبيل المثال، عندما تظهر كلمة *not* في مكان ما في التسلسل، يمكنها تعلم نفي عناصر معينة داخل متجه الحالة.\n",
|
|
"\n",
|
|
"في الداخل، تحتوي كل خلية RNN على مصفوفتين من الأوزان: $W_H$ و $W_I$، وانحياز $b$. في كل خطوة من RNN، وبالنظر إلى الإدخال $X_i$ وحالة الإدخال $S_i$، يتم حساب حالة الإخراج كالتالي: $S_{i+1} = f(W_H\\times S_i + W_I\\times X_i+b)$، حيث $f$ هي دالة التنشيط (غالبًا $\\tanh$).\n",
|
|
"\n",
|
|
"> بالنسبة للمشاكل مثل توليد النصوص (التي سنتناولها في الوحدة التالية) أو الترجمة الآلية، نرغب أيضًا في الحصول على قيمة إخراج عند كل خطوة من RNN. في هذه الحالة، هناك أيضًا مصفوفة أخرى $W_O$، ويتم حساب الإخراج كالتالي: $Y_i=f(W_O\\times S_i+b_O)$.\n",
|
|
"\n",
|
|
"دعونا نرى كيف يمكن للشبكات العصبية التكرارية مساعدتنا في تصنيف مجموعة بيانات الأخبار الخاصة بنا.\n",
|
|
"\n",
|
|
"> بالنسبة لبيئة التجربة، نحتاج إلى تشغيل الخلية التالية للتأكد من تثبيت المكتبة المطلوبة وجلب البيانات مسبقًا. إذا كنت تعمل محليًا، يمكنك تخطي الخلية التالية.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 1,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"import sys\n",
|
|
"!{sys.executable} -m pip install --quiet tensorflow_datasets==4.4.0\n",
|
|
"!cd ~ && wget -q -O - https://mslearntensorflowlp.blob.core.windows.net/data/tfds-ag-news.tgz | tar xz"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 2,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"import tensorflow as tf\n",
|
|
"from tensorflow import keras\n",
|
|
"import tensorflow_datasets as tfds\n",
|
|
"import numpy as np\n",
|
|
"\n",
|
|
"# We are going to be training pretty large models. In order not to face errors, we need\n",
|
|
"# to set tensorflow option to grow GPU memory allocation when required\n",
|
|
"physical_devices = tf.config.list_physical_devices('GPU') \n",
|
|
"if len(physical_devices)>0:\n",
|
|
" tf.config.experimental.set_memory_growth(physical_devices[0], True)\n",
|
|
"\n",
|
|
"ds_train, ds_test = tfds.load('ag_news_subset').values()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {
|
|
"nteract": {
|
|
"transient": {
|
|
"deleting": false
|
|
}
|
|
}
|
|
},
|
|
"source": [
|
|
"عند تدريب النماذج الكبيرة، قد تصبح إدارة ذاكرة وحدة معالجة الرسومات مشكلة. قد نحتاج أيضًا إلى تجربة أحجام مختلفة للدفعات الصغيرة، بحيث تتناسب البيانات مع ذاكرة وحدة معالجة الرسومات لدينا، ومع ذلك يكون التدريب سريعًا بما يكفي. إذا كنت تقوم بتشغيل هذا الكود على جهاز وحدة معالجة الرسومات الخاص بك، يمكنك تجربة تعديل حجم الدفعة الصغيرة لتسريع عملية التدريب.\n",
|
|
"\n",
|
|
"> **ملاحظة**: من المعروف أن بعض إصدارات برامج تشغيل NVidia لا تقوم بإعادة تحرير الذاكرة بعد تدريب النموذج. نحن نقوم بتشغيل عدة أمثلة في هذا الدفتر، وقد يؤدي ذلك إلى استنفاد الذاكرة في بعض الإعدادات، خاصة إذا كنت تقوم بتجاربك الخاصة كجزء من نفس الدفتر. إذا واجهت أخطاء غريبة عند بدء تدريب النموذج، قد تحتاج إلى إعادة تشغيل نواة الدفتر.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 3,
|
|
"metadata": {
|
|
"collapsed": true,
|
|
"jupyter": {
|
|
"outputs_hidden": false,
|
|
"source_hidden": false
|
|
},
|
|
"nteract": {
|
|
"transient": {
|
|
"deleting": false
|
|
}
|
|
}
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"batch_size = 16\n",
|
|
"embed_size = 64"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## مصنف RNN بسيط\n",
|
|
"\n",
|
|
"في حالة RNN البسيط، كل وحدة متكررة هي شبكة خطية بسيطة، تأخذ متجه إدخال ومتجه حالة، وتنتج متجه حالة جديد. في Keras، يمكن تمثيل ذلك باستخدام الطبقة `SimpleRNN`.\n",
|
|
"\n",
|
|
"على الرغم من أنه يمكننا تمرير الرموز المشفرة بطريقة one-hot مباشرة إلى طبقة RNN، إلا أن هذه ليست فكرة جيدة بسبب أبعادها العالية. لذلك، سنستخدم طبقة تضمين لتقليل أبعاد متجهات الكلمات، يليها طبقة RNN، وأخيرًا مصنف `Dense`.\n",
|
|
"\n",
|
|
"> **ملاحظة**: في الحالات التي لا تكون فيها الأبعاد عالية جدًا، على سبيل المثال عند استخدام الترميز على مستوى الحروف، قد يكون من المنطقي تمرير الرموز المشفرة بطريقة one-hot مباشرة إلى خلية RNN.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 4,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Model: \"sequential\"\n",
|
|
"_________________________________________________________________\n",
|
|
"Layer (type) Output Shape Param # \n",
|
|
"=================================================================\n",
|
|
"text_vectorization (TextVect (None, None) 0 \n",
|
|
"_________________________________________________________________\n",
|
|
"embedding (Embedding) (None, None, 64) 1280000 \n",
|
|
"_________________________________________________________________\n",
|
|
"simple_rnn (SimpleRNN) (None, 16) 1296 \n",
|
|
"_________________________________________________________________\n",
|
|
"dense (Dense) (None, 4) 68 \n",
|
|
"=================================================================\n",
|
|
"Total params: 1,281,364\n",
|
|
"Trainable params: 1,281,364\n",
|
|
"Non-trainable params: 0\n",
|
|
"_________________________________________________________________\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"vocab_size = 20000\n",
|
|
"\n",
|
|
"vectorizer = keras.layers.experimental.preprocessing.TextVectorization(\n",
|
|
" max_tokens=vocab_size,\n",
|
|
" input_shape=(1,))\n",
|
|
"\n",
|
|
"model = keras.models.Sequential([\n",
|
|
" vectorizer,\n",
|
|
" keras.layers.Embedding(vocab_size, embed_size),\n",
|
|
" keras.layers.SimpleRNN(16),\n",
|
|
" keras.layers.Dense(4,activation='softmax')\n",
|
|
"])\n",
|
|
"\n",
|
|
"model.summary()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"> **ملاحظة:** نستخدم هنا طبقة تضمين غير مدربة للتبسيط، ولكن للحصول على نتائج أفضل يمكننا استخدام طبقة تضمين مدربة مسبقًا باستخدام Word2Vec، كما تم شرحه في الوحدة السابقة. سيكون من الجيد أن تحاول تعديل هذا الكود ليعمل مع تضمينات مدربة مسبقًا.\n",
|
|
"\n",
|
|
"الآن دعونا نقوم بتدريب شبكة RNN. بشكل عام، تعتبر شبكات RNN صعبة التدريب، لأنه بمجرد فك خلايا RNN على طول تسلسل البيانات، يصبح عدد الطبقات الناتجة عن عملية الانتشار العكسي كبيرًا جدًا. لذلك، نحتاج إلى اختيار معدل تعلم أصغر، وتدريب الشبكة على مجموعة بيانات أكبر للحصول على نتائج جيدة. قد يستغرق هذا وقتًا طويلاً، لذا يُفضل استخدام وحدة معالجة الرسومات (GPU).\n",
|
|
"\n",
|
|
"لتسريع العملية، سنقوم بتدريب نموذج RNN فقط على عناوين الأخبار، مع استبعاد الوصف. يمكنك تجربة التدريب باستخدام الوصف لترى ما إذا كان بإمكانك جعل النموذج يتدرب.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 5,
|
|
"metadata": {
|
|
"scrolled": true
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Training vectorizer\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"def extract_title(x):\n",
|
|
" return x['title']\n",
|
|
"\n",
|
|
"def tupelize_title(x):\n",
|
|
" return (extract_title(x),x['label'])\n",
|
|
"\n",
|
|
"print('Training vectorizer')\n",
|
|
"vectorizer.adapt(ds_train.take(2000).map(extract_title))"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 6,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"7500/7500 [==============================] - 82s 11ms/step - loss: 0.6629 - acc: 0.7623 - val_loss: 0.5559 - val_acc: 0.7995\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\n"
|
|
]
|
|
},
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"<tensorflow.python.keras.callbacks.History at 0x7f3e0030d350>"
|
|
]
|
|
},
|
|
"execution_count": 6,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'], optimizer='adam')\n",
|
|
"model.fit(ds_train.map(tupelize_title).batch(batch_size),validation_data=ds_test.map(tupelize_title).batch(batch_size))"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {
|
|
"nteract": {
|
|
"transient": {
|
|
"deleting": false
|
|
}
|
|
}
|
|
},
|
|
"source": [
|
|
"> **ملاحظة** أن الدقة من المحتمل أن تكون أقل هنا، لأننا نقوم بالتدريب فقط على عناوين الأخبار.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## إعادة النظر في تسلسل المتغيرات\n",
|
|
"\n",
|
|
"تذكر أن طبقة `TextVectorization` تقوم تلقائيًا بإضافة رموز تعبئة لتسلسل البيانات ذات الأطوال المتغيرة في كل دفعة صغيرة. ولكن، يتضح أن هذه الرموز تشارك أيضًا في عملية التدريب، مما قد يعقد من عملية تقارب النموذج.\n",
|
|
"\n",
|
|
"هناك عدة طرق يمكننا اتباعها لتقليل كمية التعبئة. إحدى هذه الطرق هي إعادة ترتيب مجموعة البيانات حسب طول التسلسل وتجميع جميع التسلسلات حسب الحجم. يمكن القيام بذلك باستخدام دالة `tf.data.experimental.bucket_by_sequence_length` (راجع [التوثيق](https://www.tensorflow.org/api_docs/python/tf/data/experimental/bucket_by_sequence_length)).\n",
|
|
"\n",
|
|
"طريقة أخرى هي استخدام **الإخفاء**. في Keras، تدعم بعض الطبقات إدخالًا إضافيًا يوضح أي الرموز يجب أخذها في الاعتبار أثناء التدريب. لإدخال الإخفاء في نموذجنا، يمكننا إما تضمين طبقة `Masking` منفصلة ([التوثيق](https://keras.io/api/layers/core_layers/masking/))، أو يمكننا تحديد المعامل `mask_zero=True` في طبقة `Embedding` الخاصة بنا.\n",
|
|
"\n",
|
|
"> **Note**: سيستغرق هذا التدريب حوالي 5 دقائق لإكمال حقبة واحدة على مجموعة البيانات بأكملها. يمكنك إيقاف التدريب في أي وقت إذا نفد صبرك. كما يمكنك تقليل كمية البيانات المستخدمة في التدريب عن طريق إضافة العبارة `.take(...)` بعد مجموعتي البيانات `ds_train` و `ds_test`.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 7,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"7500/7500 [==============================] - 371s 49ms/step - loss: 0.5401 - acc: 0.8079 - val_loss: 0.3780 - val_acc: 0.8822\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\n"
|
|
]
|
|
},
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"<tensorflow.python.keras.callbacks.History at 0x7f3dec118850>"
|
|
]
|
|
},
|
|
"execution_count": 7,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"def extract_text(x):\n",
|
|
" return x['title']+' '+x['description']\n",
|
|
"\n",
|
|
"def tupelize(x):\n",
|
|
" return (extract_text(x),x['label'])\n",
|
|
"\n",
|
|
"model = keras.models.Sequential([\n",
|
|
" vectorizer,\n",
|
|
" keras.layers.Embedding(vocab_size,embed_size,mask_zero=True),\n",
|
|
" keras.layers.SimpleRNN(16),\n",
|
|
" keras.layers.Dense(4,activation='softmax')\n",
|
|
"])\n",
|
|
"\n",
|
|
"model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'], optimizer='adam')\n",
|
|
"model.fit(ds_train.map(tupelize).batch(batch_size),validation_data=ds_test.map(tupelize).batch(batch_size))"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"الآن بعد أن أصبحنا نستخدم الإخفاء، يمكننا تدريب النموذج على مجموعة البيانات الكاملة للعناوين والوصف.\n",
|
|
"\n",
|
|
"> **ملاحظة**: هل لاحظت أننا كنا نستخدم vectorizer تم تدريبه على عناوين الأخبار فقط، وليس على النص الكامل للمقالة؟ من المحتمل أن يؤدي ذلك إلى تجاهل بعض الرموز، لذا من الأفضل إعادة تدريب الـ vectorizer. ومع ذلك، قد يكون التأثير صغيرًا جدًا، لذلك سنلتزم بـ vectorizer المدرب مسبقًا من أجل البساطة.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## الذاكرة طويلة وقصيرة المدى (LSTM)\n",
|
|
"\n",
|
|
"إحدى المشاكل الرئيسية في الشبكات العصبية المتكررة (RNNs) هي **تلاشي التدرجات**. يمكن أن تكون الشبكات العصبية المتكررة طويلة جدًا، وقد تواجه صعوبة في نقل التدرجات إلى الطبقة الأولى من الشبكة أثناء عملية الانتشار العكسي. عندما يحدث ذلك، لا تستطيع الشبكة تعلم العلاقات بين الرموز البعيدة. إحدى الطرق لتجنب هذه المشكلة هي إدخال **إدارة صريحة للحالة** باستخدام **البوابات**. أكثر البنى شيوعًا التي تستخدم البوابات هي **الذاكرة طويلة وقصيرة المدى** (LSTM) و**وحدة التتابع البوابية** (GRU). سنغطي LSTMs هنا.\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"يتم تنظيم شبكة LSTM بطريقة مشابهة للشبكات العصبية المتكررة، ولكن هناك حالتان يتم تمريرهما من طبقة إلى أخرى: الحالة الفعلية $c$، والمتجه المخفي $h$. في كل وحدة، يتم دمج المتجه المخفي $h_{t-1}$ مع المدخل $x_t$، ويتحكمان معًا فيما يحدث للحالة $c_t$ والمخرج $h_{t}$ من خلال **البوابات**. كل بوابة تحتوي على تفعيل سيجمويد (الناتج في النطاق $[0,1]$)، والذي يمكن اعتباره قناعًا ثنائيًا عند ضربه في متجه الحالة. تحتوي LSTMs على البوابات التالية (من اليسار إلى اليمين في الصورة أعلاه):\n",
|
|
"* **بوابة النسيان** التي تحدد أي مكونات من المتجه $c_{t-1}$ نحتاج إلى نسيانها، وأيها نمرره.\n",
|
|
"* **بوابة الإدخال** التي تحدد مقدار المعلومات من متجه المدخل والمتجه المخفي السابق التي يجب دمجها في متجه الحالة.\n",
|
|
"* **بوابة الإخراج** التي تأخذ متجه الحالة الجديد وتقرر أي من مكوناته سيتم استخدامها لإنتاج المتجه المخفي الجديد $h_t$.\n",
|
|
"\n",
|
|
"يمكن اعتبار مكونات الحالة $c$ كأعلام يمكن تشغيلها وإيقافها. على سبيل المثال، عندما نصادف الاسم *أليس* في التسلسل، نخمن أنه يشير إلى امرأة، ونرفع العلم في الحالة الذي يشير إلى وجود اسم مؤنث في الجملة. عندما نصادف لاحقًا الكلمات *وتوم*، سنرفع العلم الذي يشير إلى وجود اسم جمع. وبالتالي، من خلال التلاعب بالحالة، يمكننا تتبع الخصائص النحوية للجملة.\n",
|
|
"\n",
|
|
"> **ملاحظة**: إليك مصدر رائع لفهم التفاصيل الداخلية لـ LSTMs: [Understanding LSTM Networks](https://colah.github.io/posts/2015-08-Understanding-LSTMs/) بقلم كريستوفر أولاه.\n",
|
|
"\n",
|
|
"على الرغم من أن البنية الداخلية لخلية LSTM قد تبدو معقدة، فإن مكتبة Keras تخفي هذا التنفيذ داخل طبقة `LSTM`، لذا فإن الشيء الوحيد الذي نحتاج إلى القيام به في المثال أعلاه هو استبدال الطبقة المتكررة:\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 8,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"15000/15000 [==============================] - 188s 13ms/step - loss: 0.5692 - acc: 0.7916 - val_loss: 0.3441 - val_acc: 0.8870\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\n"
|
|
]
|
|
},
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"<tensorflow.python.keras.callbacks.History at 0x7f3d6af5c350>"
|
|
]
|
|
},
|
|
"execution_count": 8,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"model = keras.models.Sequential([\n",
|
|
" vectorizer,\n",
|
|
" keras.layers.Embedding(vocab_size, embed_size),\n",
|
|
" keras.layers.LSTM(8),\n",
|
|
" keras.layers.Dense(4,activation='softmax')\n",
|
|
"])\n",
|
|
"\n",
|
|
"model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'], optimizer='adam')\n",
|
|
"model.fit(ds_train.map(tupelize).batch(8),validation_data=ds_test.map(tupelize).batch(8))"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"> **ملاحظة** أن تدريب الشبكات العصبية طويلة وقصيرة المدى (LSTMs) بطيء جدًا، وقد لا تلاحظ زيادة كبيرة في الدقة في بداية التدريب. قد تحتاج إلى الاستمرار في التدريب لبعض الوقت لتحقيق دقة جيدة.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## الشبكات العصبية المتكررة ثنائية الاتجاه ومتعددة الطبقات\n",
|
|
"\n",
|
|
"في الأمثلة التي تناولناها حتى الآن، تعمل الشبكات العصبية المتكررة من بداية التسلسل حتى نهايته. هذا يبدو طبيعيًا بالنسبة لنا لأنه يتبع نفس الاتجاه الذي نقرأ أو نستمع فيه إلى الكلام. ومع ذلك، في السيناريوهات التي تتطلب الوصول العشوائي إلى تسلسل الإدخال، يكون من المنطقي أكثر تشغيل الحساب المتكرر في كلا الاتجاهين. الشبكات العصبية المتكررة التي تسمح بالحسابات في كلا الاتجاهين تُسمى **الشبكات المتكررة ثنائية الاتجاه**، ويمكن إنشاؤها عن طريق تغليف الطبقة المتكررة بطبقة خاصة تُسمى `Bidirectional`.\n",
|
|
"\n",
|
|
"> **ملاحظة**: طبقة `Bidirectional` تقوم بإنشاء نسختين من الطبقة الموجودة بداخلها، وتضبط خاصية `go_backwards` لإحدى النسختين على القيمة `True`، مما يجعلها تعمل في الاتجاه المعاكس على طول التسلسل.\n",
|
|
"\n",
|
|
"الشبكات العصبية المتكررة، سواء كانت أحادية الاتجاه أو ثنائية الاتجاه، تلتقط الأنماط داخل التسلسل وتخزنها في متجهات الحالة أو تُرجعها كمخرجات. كما هو الحال مع الشبكات الالتفافية، يمكننا بناء طبقة متكررة أخرى بعد الطبقة الأولى لالتقاط أنماط على مستوى أعلى، يتم بناؤها من الأنماط ذات المستوى الأدنى التي استخرجتها الطبقة الأولى. هذا يقودنا إلى مفهوم **الشبكة المتكررة متعددة الطبقات**، والتي تتكون من شبكتين متكررتين أو أكثر، حيث يتم تمرير مخرجات الطبقة السابقة إلى الطبقة التالية كمدخلات.\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"*الصورة مأخوذة من [هذا المقال الرائع](https://towardsdatascience.com/from-a-lstm-cell-to-a-multilayer-lstm-network-with-pytorch-2899eb5696f3) بقلم فرناندو لوبيز.*\n",
|
|
"\n",
|
|
"تجعل مكتبة Keras بناء هذه الشبكات مهمة سهلة، حيث تحتاج فقط إلى إضافة المزيد من الطبقات المتكررة إلى النموذج. بالنسبة لجميع الطبقات باستثناء الأخيرة، نحتاج إلى تحديد المعامل `return_sequences=True`، لأننا نحتاج إلى أن تُرجع الطبقة جميع الحالات الوسيطة، وليس فقط الحالة النهائية للحساب المتكرر.\n",
|
|
"\n",
|
|
"لنقم ببناء شبكة LSTM ثنائية الاتجاه مكونة من طبقتين لمشكلة التصنيف الخاصة بنا.\n",
|
|
"\n",
|
|
"> **ملاحظة**: هذا الكود يستغرق وقتًا طويلاً للتنفيذ مرة أخرى، ولكنه يحقق أعلى دقة رأيناها حتى الآن. لذا ربما يستحق الانتظار لرؤية النتيجة.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 9,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"5044/7500 [===================>..........] - ETA: 2:33 - loss: 0.3709 - acc: 0.8706\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r5045/7500 [===================>..........] - ETA: 2:33 - loss: 0.3709 - acc: 0.8706"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"model = keras.models.Sequential([\n",
|
|
" vectorizer,\n",
|
|
" keras.layers.Embedding(vocab_size, 128, mask_zero=True),\n",
|
|
" keras.layers.Bidirectional(keras.layers.LSTM(64,return_sequences=True)),\n",
|
|
" keras.layers.Bidirectional(keras.layers.LSTM(64)), \n",
|
|
" keras.layers.Dense(4,activation='softmax')\n",
|
|
"])\n",
|
|
"\n",
|
|
"model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'], optimizer='adam')\n",
|
|
"model.fit(ds_train.map(tupelize).batch(batch_size),\n",
|
|
" validation_data=ds_test.map(tupelize).batch(batch_size))"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## الشبكات العصبية التكرارية (RNNs) لمهام أخرى\n",
|
|
"\n",
|
|
"حتى الآن، ركزنا على استخدام الشبكات العصبية التكرارية لتصنيف تسلسلات النصوص. ولكن يمكنها التعامل مع العديد من المهام الأخرى، مثل توليد النصوص والترجمة الآلية — سنتناول هذه المهام في الوحدة التالية.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"\n---\n\n**إخلاء المسؤولية**: \nتم ترجمة هذا المستند باستخدام خدمة الترجمة بالذكاء الاصطناعي [Co-op Translator](https://github.com/Azure/co-op-translator). بينما نسعى لتحقيق الدقة، يرجى العلم أن الترجمات الآلية قد تحتوي على أخطاء أو عدم دقة. يجب اعتبار المستند الأصلي بلغته الأصلية المصدر الرسمي. للحصول على معلومات حاسمة، يُوصى بالاستعانة بترجمة بشرية احترافية. نحن غير مسؤولين عن أي سوء فهم أو تفسيرات خاطئة تنشأ عن استخدام هذه الترجمة.\n"
|
|
]
|
|
}
|
|
],
|
|
"metadata": {
|
|
"kernel_info": {
|
|
"name": "conda-env-py37_tensorflow-py"
|
|
},
|
|
"kernelspec": {
|
|
"display_name": "py37_tensorflow",
|
|
"language": "python",
|
|
"name": "conda-env-py37_tensorflow-py"
|
|
},
|
|
"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.7.9"
|
|
},
|
|
"nteract": {
|
|
"version": "nteract-front-end@1.0.0"
|
|
},
|
|
"coopTranslator": {
|
|
"original_hash": "81351e61f619b432ff51010a4f993194",
|
|
"translation_date": "2025-08-28T04:23:16+00:00",
|
|
"source_file": "lessons/5-NLP/16-RNN/RNNTF.ipynb",
|
|
"language_code": "ar"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 4
|
|
} |