462 lines
31 KiB
Plaintext
462 lines
31 KiB
Plaintext
{
|
||
"cells": [
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"# شبکههای عصبی بازگشتی\n",
|
||
"\n",
|
||
"در ماژول قبلی، ما به بررسی نمایشهای معنایی غنی از متن پرداختیم. معماریای که تاکنون استفاده کردهایم، معنای کلی کلمات در یک جمله را استخراج میکند، اما **ترتیب** کلمات را در نظر نمیگیرد، زیرا عملیات تجمیعی که پس از تعبیهها (embeddings) انجام میشود، این اطلاعات را از متن اصلی حذف میکند. از آنجا که این مدلها قادر به نمایش ترتیب کلمات نیستند، نمیتوانند وظایف پیچیدهتر یا مبهمتری مانند تولید متن یا پاسخ به سوالات را حل کنند.\n",
|
||
"\n",
|
||
"برای درک معنای یک توالی متنی، از معماری شبکه عصبیای به نام **شبکه عصبی بازگشتی** یا RNN استفاده خواهیم کرد. هنگام استفاده از RNN، جمله خود را به صورت یک توکن در هر مرحله از شبکه عبور میدهیم و شبکه یک **وضعیت (state)** تولید میکند که آن را همراه با توکن بعدی به شبکه بازمیگردانیم.\n",
|
||
"\n",
|
||
"\n",
|
||
"\n",
|
||
"با توجه به توالی ورودی توکنها $X_0,\\dots,X_n$، RNN یک توالی از بلوکهای شبکه عصبی ایجاد میکند و این توالی را به صورت انتها به انتها با استفاده از پسانتشار (backpropagation) آموزش میدهد. هر بلوک شبکه یک جفت $(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",
|
||
"> برای محیط آزمایشی (sandbox)، باید سلول زیر را اجرا کنیم تا مطمئن شویم کتابخانه مورد نیاز نصب شده و دادهها پیشبارگذاری شدهاند. اگر به صورت محلی اجرا میکنید، میتوانید از اجرای سلول زیر صرفنظر کنید.\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": [
|
||
"هنگام آموزش مدلهای بزرگ، تخصیص حافظه GPU ممکن است به یک مشکل تبدیل شود. همچنین ممکن است نیاز داشته باشیم اندازههای مختلف minibatch را امتحان کنیم تا دادهها در حافظه GPU جا شوند و در عین حال آموزش به اندازه کافی سریع باشد. اگر این کد را روی ماشین GPU خود اجرا میکنید، میتوانید با تنظیم اندازه minibatch برای تسریع آموزش آزمایش کنید.\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 ارسال کرد، این کار به دلیل ابعاد بالای آنها ایده خوبی نیست. بنابراین، از یک لایه embedding برای کاهش ابعاد بردارهای کلمه استفاده میکنیم، سپس یک لایه RNN و در نهایت یک طبقهبند `Dense`.\n",
|
||
"\n",
|
||
"> **توجه**: در مواردی که ابعاد خیلی بالا نیست، مثلاً هنگام استفاده از توکنسازی در سطح کاراکتر، ممکن است منطقی باشد که توکنهای کدگذاریشده بهصورت یکداغ را مستقیماً به سلول 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` به طور خودکار توالیهای با طول متغیر را در یک دسته کوچک با توکنهای پرکننده (pad tokens) پر میکند. مشخص شده است که این توکنها نیز در فرآیند آموزش شرکت میکنند و میتوانند باعث پیچیدگی در همگرایی مدل شوند.\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**: این آموزش حدود ۵ دقیقه طول میکشد تا یک دوره کامل روی کل مجموعه داده انجام شود. اگر صبرتان تمام شد، میتوانید آموزش را در هر زمان متوقف کنید. همچنین میتوانید مقدار دادهای که برای آموزش استفاده میشود را محدود کنید، با اضافه کردن عبارت `.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",
|
||
"> **توجه**: آیا متوجه شدهاید که ما از وکتورایزر آموزشدیده بر روی عناوین اخبار استفاده کردهایم، نه کل متن مقاله؟ این موضوع ممکن است باعث شود برخی از توکنها نادیده گرفته شوند، بنابراین بهتر است وکتورایزر را دوباره آموزش دهیم. با این حال، ممکن است تأثیر بسیار کمی داشته باشد، بنابراین برای سادهتر شدن کار، از وکتورایزر از پیش آموزشدیده قبلی استفاده میکنیم.\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## حافظه طولانی-کوتاه مدت (LSTM)\n",
|
||
"\n",
|
||
"یکی از مشکلات اصلی شبکههای عصبی بازگشتی (RNNs) **ناپدید شدن گرادیانها** است. شبکههای RNN میتوانند بسیار طولانی باشند و ممکن است در هنگام پسانتشار، در انتقال گرادیانها به لایه اول شبکه دچار مشکل شوند. وقتی این اتفاق میافتد، شبکه نمیتواند روابط بین توکنهای دور از هم را یاد بگیرد. یکی از راههای جلوگیری از این مشکل، معرفی **مدیریت صریح حالت** با استفاده از **دروازهها** است. دو معماری رایج که دروازهها را معرفی میکنند، **حافظه طولانی-کوتاه مدت** (LSTM) و **واحد بازگشتی دروازهدار** (GRU) هستند. در اینجا به LSTMها میپردازیم.\n",
|
||
"\n",
|
||
"\n",
|
||
"\n",
|
||
"یک شبکه LSTM به شکلی مشابه RNN سازماندهی شده است، اما دو حالت وجود دارد که از لایهای به لایه دیگر منتقل میشوند: حالت واقعی $c$ و بردار مخفی $h$. در هر واحد، بردار مخفی $h_{t-1}$ با ورودی $x_t$ ترکیب میشود و با هم کنترل میکنند که چه اتفاقی برای حالت $c_t$ و خروجی $h_{t}$ از طریق **دروازهها** بیفتد. هر دروازه دارای فعالسازی سیگموئید (خروجی در محدوده $[0,1]$) است که میتوان آن را به عنوان یک ماسک بیتی در نظر گرفت که هنگام ضرب در بردار حالت اعمال میشود. LSTMها دارای دروازههای زیر هستند (از چپ به راست در تصویر بالا):\n",
|
||
"* **دروازه فراموشی** که تعیین میکند کدام اجزای بردار $c_{t-1}$ باید فراموش شوند و کدام باید عبور کنند.\n",
|
||
"* **دروازه ورودی** که تعیین میکند چه مقدار اطلاعات از بردار ورودی و بردار مخفی قبلی باید در بردار حالت گنجانده شود.\n",
|
||
"* **دروازه خروجی** که بردار حالت جدید را میگیرد و تصمیم میگیرد کدام یک از اجزای آن برای تولید بردار مخفی جدید $h_t$ استفاده شود.\n",
|
||
"\n",
|
||
"اجزای حالت $c$ را میتوان به عنوان پرچمهایی در نظر گرفت که میتوانند روشن و خاموش شوند. برای مثال، وقتی در دنبالهای با نام *آلیس* مواجه میشویم، حدس میزنیم که به یک زن اشاره دارد و پرچمی را در حالت بالا میبریم که نشان میدهد یک اسم مؤنث در جمله داریم. وقتی در ادامه با کلمات *و تام* مواجه میشویم، پرچمی را بالا میبریم که نشان میدهد یک اسم جمع داریم. بنابراین با دستکاری حالت میتوانیم ویژگیهای دستوری جمله را دنبال کنیم.\n",
|
||
"\n",
|
||
"> **Note**: اینجا یک منبع عالی برای درک ساختار داخلی LSTMها وجود دارد: [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": [
|
||
"**توجه** داشته باشید که آموزش LSTMها نیز نسبتاً کند است و ممکن است در ابتدای آموزش افزایش زیادی در دقت مشاهده نکنید. ممکن است نیاز باشد برای دستیابی به دقت خوب، آموزش را برای مدتی ادامه دهید.\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## شبکههای عصبی بازگشتی دوطرفه و چندلایه\n",
|
||
"\n",
|
||
"در مثالهایی که تاکنون بررسی کردیم، شبکههای بازگشتی از ابتدای یک دنباله تا انتهای آن عمل میکنند. این روش برای ما طبیعی به نظر میرسد، زیرا همان جهتی را دنبال میکند که در آن میخوانیم یا به گفتار گوش میدهیم. با این حال، در سناریوهایی که نیاز به دسترسی تصادفی به دنباله ورودی دارند، منطقیتر است که محاسبات بازگشتی در هر دو جهت انجام شود. شبکههای بازگشتی که امکان محاسبات در هر دو جهت را فراهم میکنند، **شبکههای بازگشتی دوطرفه** نامیده میشوند و میتوان آنها را با استفاده از لایه خاصی به نام `Bidirectional` ایجاد کرد.\n",
|
||
"\n",
|
||
"> **Note**: لایه `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",
|
||
"> **Note** این کد دوباره زمان زیادی برای اجرا نیاز دارد، اما بالاترین دقتی که تاکنون دیدهایم را به ما میدهد. بنابراین شاید ارزش صبر کردن و دیدن نتیجه را داشته باشد.\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": [
|
||
"## شبکههای عصبی بازگشتی (RNN) برای وظایف دیگر\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-31T17:07:05+00:00",
|
||
"source_file": "lessons/5-NLP/16-RNN/RNNTF.ipynb",
|
||
"language_code": "fa"
|
||
}
|
||
},
|
||
"nbformat": 4,
|
||
"nbformat_minor": 4
|
||
} |