AI-For-Beginners/translations/ur/lessons/5-NLP/16-RNN/RNNPyTorch.ipynb

479 lines
30 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# بار بار نیورل نیٹ ورکس\n",
"\n",
"پچھلے ماڈیول میں، ہم نے متن کی بھرپور معنوی نمائندگیوں کا استعمال کیا اور ایمبیڈنگز کے اوپر ایک سادہ لکیری درجہ بندی کی۔ اس فن تعمیر کا کام جملے میں الفاظ کے مجموعی معنی کو پکڑنا ہے، لیکن یہ الفاظ کی **ترتیب** کو مدنظر نہیں رکھتا، کیونکہ ایمبیڈنگز کے اوپر جمع کرنے کا عمل اصل متن سے یہ معلومات ہٹا دیتا ہے۔ چونکہ یہ ماڈلز الفاظ کی ترتیب کو ماڈل کرنے سے قاصر ہیں، وہ زیادہ پیچیدہ یا مبہم کام جیسے کہ متن کی تخلیق یا سوالات کے جوابات دینے کو حل نہیں کر سکتے۔\n",
"\n",
"متن کی ترتیب کے معنی کو پکڑنے کے لیے، ہمیں ایک اور نیورل نیٹ ورک فن تعمیر استعمال کرنے کی ضرورت ہے، جسے **بار بار نیورل نیٹ ورک** یا RNN کہا جاتا ہے۔ RNN میں، ہم اپنا جملہ نیٹ ورک کے ذریعے ایک وقت میں ایک علامت پاس کرتے ہیں، اور نیٹ ورک کچھ **حالت** پیدا کرتا ہے، جسے ہم اگلی علامت کے ساتھ دوبارہ نیٹ ورک میں پاس کرتے ہیں۔\n",
"\n",
"دیے گئے ٹوکنز کی ترتیب $X_0,\\dots,X_n$ کے ساتھ، RNN نیورل نیٹ ورک بلاکس کی ایک ترتیب بناتا ہے، اور اس ترتیب کو آخر تک بیک پروپیگیشن کے ذریعے تربیت دیتا ہے۔ ہر نیٹ ورک بلاک ایک جوڑی $(X_i,S_i)$ کو ان پٹ کے طور پر لیتا ہے، اور نتیجے کے طور پر $S_{i+1}$ پیدا کرتا ہے۔ آخری حالت $S_n$ یا آؤٹ پٹ $X_n$ ایک لکیری درجہ بندی میں جاتا ہے تاکہ نتیجہ پیدا کیا جا سکے۔ تمام نیٹ ورک بلاکس ایک جیسے وزن کا اشتراک کرتے ہیں، اور ایک بیک پروپیگیشن پاس کے ذریعے آخر تک تربیت دیے جاتے ہیں۔\n",
"\n",
"چونکہ حالت ویکٹرز $S_0,\\dots,S_n$ نیٹ ورک کے ذریعے پاس کیے جاتے ہیں، یہ الفاظ کے درمیان ترتیب وار انحصار سیکھنے کے قابل ہوتا ہے۔ مثال کے طور پر، جب لفظ *نہیں* ترتیب میں کہیں ظاہر ہوتا ہے، تو یہ حالت ویکٹر کے اندر کچھ عناصر کو منفی کرنے کے لیے سیکھ سکتا ہے، جس کے نتیجے میں نفی پیدا ہوتی ہے۔\n",
"\n",
"> چونکہ تصویر میں تمام RNN بلاکس کے وزن مشترک ہیں، اسی تصویر کو ایک بلاک (دائیں طرف) کے طور پر ظاہر کیا جا سکتا ہے جس میں ایک بار بار آنے والا فیڈ بیک لوپ ہوتا ہے، جو نیٹ ورک کی آؤٹ پٹ حالت کو دوبارہ ان پٹ میں پاس کرتا ہے۔\n",
"\n",
"آئیے دیکھتے ہیں کہ بار بار نیورل نیٹ ورکس ہمارے نیوز ڈیٹا سیٹ کو درجہ بندی کرنے میں کس طرح مدد کر سکتے ہیں۔\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Loading dataset...\n",
"Building vocab...\n"
]
}
],
"source": [
"import torch\n",
"import torchtext\n",
"from torchnlp import *\n",
"train_dataset, test_dataset, classes, vocab = load_dataset()\n",
"vocab_size = len(vocab)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## سادہ آر این این کلاسیفائر\n",
"\n",
"سادہ آر این این کے معاملے میں، ہر ری کرنٹ یونٹ ایک سادہ لکیری نیٹ ورک ہوتا ہے، جو ان پٹ ویکٹر اور اسٹیٹ ویکٹر کو جوڑ کر ایک نیا اسٹیٹ ویکٹر تیار کرتا ہے۔ PyTorch اس یونٹ کو `RNNCell` کلاس کے ذریعے ظاہر کرتا ہے، اور ایسے سیلز کے نیٹ ورک کو `RNN` لیئر کے طور پر پیش کرتا ہے۔\n",
"\n",
"آر این این کلاسیفائر کو ڈیفائن کرنے کے لیے، ہم سب سے پہلے ایک ایمبیڈنگ لیئر کا استعمال کریں گے تاکہ ان پٹ وکیبلری کی ڈائمینشن کو کم کیا جا سکے، اور پھر اس کے اوپر ایک آر این این لیئر لگائیں گے:\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"class RNNClassifier(torch.nn.Module):\n",
" def __init__(self, vocab_size, embed_dim, hidden_dim, num_class):\n",
" super().__init__()\n",
" self.hidden_dim = hidden_dim\n",
" self.embedding = torch.nn.Embedding(vocab_size, embed_dim)\n",
" self.rnn = torch.nn.RNN(embed_dim,hidden_dim,batch_first=True)\n",
" self.fc = torch.nn.Linear(hidden_dim, num_class)\n",
"\n",
" def forward(self, x):\n",
" batch_size = x.size(0)\n",
" x = self.embedding(x)\n",
" x,h = self.rnn(x)\n",
" return self.fc(x.mean(dim=1))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"> **نوٹ:** یہاں ہم سادگی کے لیے غیر تربیت یافتہ ایمبیڈنگ لیئر استعمال کر رہے ہیں، لیکن بہتر نتائج کے لیے ہم پہلے سے تربیت یافتہ ایمبیڈنگ لیئر جیسے Word2Vec یا GloVe ایمبیڈنگز استعمال کر سکتے ہیں، جیسا کہ پچھلے یونٹ میں بیان کیا گیا ہے۔ بہتر سمجھ کے لیے، آپ اس کوڈ کو پہلے سے تربیت یافتہ ایمبیڈنگز کے ساتھ کام کرنے کے لیے ڈھال سکتے ہیں۔\n",
"\n",
"ہمارے کیس میں، ہم پیڈڈ ڈیٹا لوڈر استعمال کریں گے، تاکہ ہر بیچ میں ایک ہی لمبائی کے پیڈڈ سیکوئنسز ہوں۔ RNN لیئر ایمبیڈنگ ٹینسرز کے سیکوئنس کو لے گی اور دو آؤٹ پٹس پیدا کرے گی:\n",
"* $x$ ہر قدم پر RNN سیل آؤٹ پٹس کا سیکوئنس ہے\n",
"* $h$ سیکوئنس کے آخری عنصر کے لیے آخری چھپی ہوئی حالت ہے\n",
"\n",
"اس کے بعد ہم ایک مکمل طور پر جڑا ہوا لینیئر کلاسیفائر لگاتے ہیں تاکہ کلاس کی تعداد حاصل کی جا سکے۔\n",
"\n",
"> **نوٹ:** RNNs کو تربیت دینا کافی مشکل ہے، کیونکہ جب RNN سیلز کو سیکوئنس کی لمبائی کے ساتھ ان رول کیا جاتا ہے، تو بیک پروپیگیشن میں شامل لیئرز کی تعداد کافی زیادہ ہو جاتی ہے۔ اس لیے ہمیں چھوٹا لرننگ ریٹ منتخب کرنا ہوگا اور نیٹ ورک کو بڑے ڈیٹا سیٹ پر تربیت دینا ہوگا تاکہ اچھے نتائج حاصل کیے جا سکیں۔ یہ کافی وقت لے سکتا ہے، اس لیے GPU کا استعمال ترجیح دی جاتی ہے۔\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"3200: acc=0.3090625\n",
"6400: acc=0.38921875\n",
"9600: acc=0.4590625\n",
"12800: acc=0.511953125\n",
"16000: acc=0.5506875\n",
"19200: acc=0.57921875\n",
"22400: acc=0.6070089285714285\n",
"25600: acc=0.6304296875\n",
"28800: acc=0.6484027777777778\n",
"32000: acc=0.66509375\n",
"35200: acc=0.6790056818181818\n",
"38400: acc=0.6929166666666666\n",
"41600: acc=0.7035817307692308\n",
"44800: acc=0.7137276785714286\n",
"48000: acc=0.72225\n",
"51200: acc=0.73001953125\n",
"54400: acc=0.7372794117647059\n",
"57600: acc=0.7436631944444444\n",
"60800: acc=0.7503947368421052\n",
"64000: acc=0.75634375\n",
"67200: acc=0.7615773809523809\n",
"70400: acc=0.7662642045454545\n",
"73600: acc=0.7708423913043478\n",
"76800: acc=0.7751822916666666\n",
"80000: acc=0.7790625\n",
"83200: acc=0.7825\n",
"86400: acc=0.7858564814814815\n",
"89600: acc=0.7890513392857142\n",
"92800: acc=0.7920474137931034\n",
"96000: acc=0.7952708333333334\n",
"99200: acc=0.7982258064516129\n",
"102400: acc=0.80099609375\n",
"105600: acc=0.8037594696969697\n",
"108800: acc=0.8060569852941176\n"
]
}
],
"source": [
"train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=16, collate_fn=padify, shuffle=True)\n",
"net = RNNClassifier(vocab_size,64,32,len(classes)).to(device)\n",
"train_epoch(net,train_loader, lr=0.001)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## لمبی مختصر مدتی یادداشت (LSTM)\n",
"\n",
"روایتی RNNs کا ایک بڑا مسئلہ **vanishing gradients** کا مسئلہ ہے۔ چونکہ RNNs کو ایک ہی بیک پروپیگیشن پاس میں اختتام سے اختتام تک تربیت دی جاتی ہے، اس لیے یہ نیٹ ورک کی ابتدائی تہوں تک غلطی کو منتقل کرنے میں مشکل کا سامنا کرتے ہیں، اور اس طرح نیٹ ورک دور دراز ٹوکنز کے درمیان تعلقات سیکھنے سے قاصر رہتا ہے۔ اس مسئلے سے بچنے کے ایک طریقے کے طور پر **واضح حالت کا انتظام** متعارف کرایا جاتا ہے، جس میں **gates** کا استعمال کیا جاتا ہے۔ اس قسم کی دو مشہور آرکیٹیکچرز ہیں: **لمبی مختصر مدتی یادداشت** (LSTM) اور **Gated Relay Unit** (GRU)۔\n",
"\n",
"![لمبی مختصر مدتی یادداشت سیل کی ایک مثال دکھانے والی تصویر](../../../../../lessons/5-NLP/16-RNN/images/long-short-term-memory-cell.svg)\n",
"\n",
"LSTM نیٹ ورک RNN کی طرح ترتیب دیا گیا ہے، لیکن یہاں دو حالتیں ہیں جو ایک تہہ سے دوسری تہہ تک منتقل ہوتی ہیں: اصل حالت $c$، اور چھپی ہوئی ویکٹر $h$۔ ہر یونٹ پر، چھپی ہوئی ویکٹر $h_i$ کو ان پٹ $x_i$ کے ساتھ جوڑا جاتا ہے، اور یہ **gates** کے ذریعے حالت $c$ پر اثر انداز ہوتے ہیں۔ ہر گیٹ ایک نیورل نیٹ ورک ہے جس میں سگموئڈ ایکٹیویشن (آؤٹ پٹ $[0,1]$ کی حد میں) ہوتا ہے، جسے حالت ویکٹر کے ساتھ ضرب دے کر بٹ وائز ماسک کے طور پر سمجھا جا سکتا ہے۔ درج ذیل گیٹس موجود ہیں (اوپر دی گئی تصویر میں بائیں سے دائیں تک):\n",
"* **forget gate** چھپی ہوئی ویکٹر لیتا ہے اور طے کرتا ہے کہ ویکٹر $c$ کے کون سے اجزاء کو بھولنا ہے اور کون سے گزرنے دینا ہے۔\n",
"* **input gate** ان پٹ اور چھپی ہوئی ویکٹر سے کچھ معلومات لیتا ہے، اور انہیں حالت میں شامل کرتا ہے۔\n",
"* **output gate** حالت کو $\\tanh$ ایکٹیویشن کے ساتھ کسی لکیری تہہ کے ذریعے تبدیل کرتا ہے، پھر اس کے کچھ اجزاء کو چھپی ہوئی ویکٹر $h_i$ کا استعمال کرتے ہوئے منتخب کرتا ہے تاکہ نئی حالت $c_{i+1}$ پیدا کی جا سکے۔\n",
"\n",
"حالت $c$ کے اجزاء کو کچھ جھنڈوں کے طور پر سمجھا جا سکتا ہے جنہیں آن اور آف کیا جا سکتا ہے۔ مثال کے طور پر، جب ہم سلسلے میں *Alice* نام کا سامنا کرتے ہیں، تو ہم یہ فرض کر سکتے ہیں کہ یہ کسی خاتون کردار کی طرف اشارہ کرتا ہے، اور حالت میں جھنڈا بلند کرتے ہیں کہ جملے میں ایک خاتون اسم موجود ہے۔ جب ہم مزید جملے *and Tom* کا سامنا کرتے ہیں، تو ہم جھنڈا بلند کرتے ہیں کہ ہمارے پاس جمع اسم ہے۔ اس طرح، حالت کو جوڑ کر ہم جملے کے حصوں کی گرامر خصوصیات کا سراغ رکھ سکتے ہیں۔\n",
"\n",
"> **نوٹ**: LSTM کی اندرونی ساخت کو سمجھنے کے لیے ایک بہترین وسیلہ کرسٹوفر اولاہ کا یہ شاندار مضمون [Understanding LSTM Networks](https://colah.github.io/posts/2015-08-Understanding-LSTMs/) ہے۔\n",
"\n",
"اگرچہ LSTM سیل کی اندرونی ساخت پیچیدہ نظر آ سکتی ہے، PyTorch اس عمل درآمد کو `LSTMCell` کلاس کے اندر چھپاتا ہے، اور پورے LSTM تہہ کی نمائندگی کے لیے `LSTM` آبجیکٹ فراہم کرتا ہے۔ اس طرح، LSTM کلاسیفائر کا نفاذ اوپر دیکھے گئے سادہ RNN کی طرح ہوگا:\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"class LSTMClassifier(torch.nn.Module):\n",
" def __init__(self, vocab_size, embed_dim, hidden_dim, num_class):\n",
" super().__init__()\n",
" self.hidden_dim = hidden_dim\n",
" self.embedding = torch.nn.Embedding(vocab_size, embed_dim)\n",
" self.embedding.weight.data = torch.randn_like(self.embedding.weight.data)-0.5\n",
" self.rnn = torch.nn.LSTM(embed_dim,hidden_dim,batch_first=True)\n",
" self.fc = torch.nn.Linear(hidden_dim, num_class)\n",
"\n",
" def forward(self, x):\n",
" batch_size = x.size(0)\n",
" x = self.embedding(x)\n",
" x,(h,c) = self.rnn(x)\n",
" return self.fc(h[-1])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": []
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"3200: acc=0.259375\n",
"6400: acc=0.25859375\n",
"9600: acc=0.26177083333333334\n",
"12800: acc=0.2784375\n",
"16000: acc=0.313\n",
"19200: acc=0.3528645833333333\n",
"22400: acc=0.3965625\n",
"25600: acc=0.4385546875\n",
"28800: acc=0.4752777777777778\n",
"32000: acc=0.505375\n",
"35200: acc=0.5326704545454546\n",
"38400: acc=0.5557552083333334\n",
"41600: acc=0.5760817307692307\n",
"44800: acc=0.5954910714285714\n",
"48000: acc=0.6118333333333333\n",
"51200: acc=0.62681640625\n",
"54400: acc=0.6404779411764706\n",
"57600: acc=0.6520138888888889\n",
"60800: acc=0.662828947368421\n",
"64000: acc=0.673546875\n",
"67200: acc=0.6831547619047619\n",
"70400: acc=0.6917897727272727\n",
"73600: acc=0.6997146739130434\n",
"76800: acc=0.707109375\n",
"80000: acc=0.714075\n",
"83200: acc=0.7209134615384616\n",
"86400: acc=0.727037037037037\n",
"89600: acc=0.7326674107142858\n",
"92800: acc=0.7379633620689655\n",
"96000: acc=0.7433645833333333\n",
"99200: acc=0.7479032258064516\n",
"102400: acc=0.752119140625\n",
"105600: acc=0.7562405303030303\n",
"108800: acc=0.76015625\n",
"112000: acc=0.7641339285714286\n",
"115200: acc=0.7677777777777778\n",
"118400: acc=0.7711233108108108\n"
]
},
{
"data": {
"text/plain": [
"(0.03487814127604167, 0.7728)"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"net = LSTMClassifier(vocab_size,64,32,len(classes)).to(device)\n",
"train_epoch(net,train_loader, lr=0.001)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## پیکڈ سیکوئنسز\n",
"\n",
"ہمارے مثال میں، ہمیں منی بیچ میں تمام سیکوئنسز کو صفر ویکٹرز کے ساتھ پیڈ کرنا پڑا۔ اگرچہ اس سے کچھ میموری ضائع ہوتی ہے، لیکن RNNs کے ساتھ یہ زیادہ اہم ہے کہ اضافی RNN سیلز پیڈڈ ان پٹ آئٹمز کے لیے بنائے جاتے ہیں، جو تربیت میں حصہ لیتے ہیں لیکن کوئی اہم ان پٹ معلومات نہیں رکھتے۔ بہتر ہوگا کہ RNN کو صرف اصل سیکوئنس سائز پر تربیت دی جائے۔\n",
"\n",
"اس مقصد کے لیے، PyTorch میں پیڈڈ سیکوئنس اسٹوریج کا ایک خاص فارمیٹ متعارف کرایا گیا ہے۔ فرض کریں کہ ہمارے پاس پیڈڈ منی بیچ ان پٹ ہے جو اس طرح دکھائی دیتا ہے:\n",
"```\n",
"[[1,2,3,4,5],\n",
" [6,7,8,0,0],\n",
" [9,0,0,0,0]]\n",
"```\n",
"یہاں 0 پیڈڈ ویلیوز کو ظاہر کرتا ہے، اور ان پٹ سیکوئنسز کی اصل لمبائی کا ویکٹر `[5,3,1]` ہے۔\n",
"\n",
"پیڈڈ سیکوئنس کے ساتھ مؤثر طریقے سے RNN کو تربیت دینے کے لیے، ہم چاہتے ہیں کہ پہلے گروپ کے RNN سیلز کی تربیت بڑے منی بیچ (`[1,6,9]`) کے ساتھ شروع کریں، لیکن پھر تیسرے سیکوئنس کی پروسیسنگ ختم کریں، اور چھوٹے منی بیچز (`[2,7]`, `[3,8]`) کے ساتھ تربیت جاری رکھیں، اور اسی طرح۔ اس طرح، پیکڈ سیکوئنس کو ایک ویکٹر کے طور پر ظاہر کیا جاتا ہے - ہمارے کیس میں `[1,6,9,2,7,3,8,4,5]`، اور لمبائی کا ویکٹر (`[5,3,1]`)، جس سے ہم آسانی سے اصل پیڈڈ منی بیچ کو دوبارہ تشکیل دے سکتے ہیں۔\n",
"\n",
"پیکڈ سیکوئنس بنانے کے لیے، ہم `torch.nn.utils.rnn.pack_padded_sequence` فنکشن استعمال کر سکتے ہیں۔ تمام ری کرنٹ لیئرز، بشمول RNN، LSTM اور GRU، پیکڈ سیکوئنسز کو ان پٹ کے طور پر سپورٹ کرتے ہیں، اور پیکڈ آؤٹ پٹ پیدا کرتے ہیں، جسے `torch.nn.utils.rnn.pad_packed_sequence` کے ذریعے ڈی کوڈ کیا جا سکتا ہے۔\n",
"\n",
"پیکڈ سیکوئنس پیدا کرنے کے قابل ہونے کے لیے، ہمیں نیٹ ورک کو لمبائی کا ویکٹر پاس کرنا ہوگا، اور اس طرح ہمیں منی بیچز تیار کرنے کے لیے ایک مختلف فنکشن کی ضرورت ہوگی:\n"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"def pad_length(b):\n",
" # build vectorized sequence\n",
" v = [encode(x[1]) for x in b]\n",
" # compute max length of a sequence in this minibatch and length sequence itself\n",
" len_seq = list(map(len,v))\n",
" l = max(len_seq)\n",
" return ( # tuple of three tensors - labels, padded features, length sequence\n",
" torch.LongTensor([t[0]-1 for t in b]),\n",
" torch.stack([torch.nn.functional.pad(torch.tensor(t),(0,l-len(t)),mode='constant',value=0) for t in v]),\n",
" torch.tensor(len_seq)\n",
" )\n",
"\n",
"train_loader_len = torch.utils.data.DataLoader(train_dataset, batch_size=16, collate_fn=pad_length, shuffle=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"حقیقی نیٹ ورک `LSTMClassifier` کی طرح ہوگا، لیکن `forward` پاس کو دونوں پیڈڈ منی بیچ اور سیکوئنس لمبائیوں کے ویکٹر موصول ہوں گے۔ ایمبیڈنگ کا حساب لگانے کے بعد، ہم پیکڈ سیکوئنس کا حساب لگاتے ہیں، اسے LSTM لیئر میں پاس کرتے ہیں، اور پھر نتیجہ کو دوبارہ ان پیک کرتے ہیں۔\n",
"\n",
"> **نوٹ**: ہم درحقیقت ان پیکڈ نتیجہ `x` استعمال نہیں کرتے، کیونکہ ہم اگلے حسابات میں چھپے ہوئے لیئرز کے آؤٹ پٹ کو استعمال کرتے ہیں۔ لہذا، ہم اس کوڈ سے ان پیکنگ کو مکمل طور پر ہٹا سکتے ہیں۔ ہم نے اسے یہاں اس لیے رکھا ہے تاکہ آپ اس کوڈ کو آسانی سے تبدیل کر سکیں، اگر آپ کو نیٹ ورک آؤٹ پٹ کو مزید حسابات میں استعمال کرنے کی ضرورت ہو۔\n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"class LSTMPackClassifier(torch.nn.Module):\n",
" def __init__(self, vocab_size, embed_dim, hidden_dim, num_class):\n",
" super().__init__()\n",
" self.hidden_dim = hidden_dim\n",
" self.embedding = torch.nn.Embedding(vocab_size, embed_dim)\n",
" self.embedding.weight.data = torch.randn_like(self.embedding.weight.data)-0.5\n",
" self.rnn = torch.nn.LSTM(embed_dim,hidden_dim,batch_first=True)\n",
" self.fc = torch.nn.Linear(hidden_dim, num_class)\n",
"\n",
" def forward(self, x, lengths):\n",
" batch_size = x.size(0)\n",
" x = self.embedding(x)\n",
" pad_x = torch.nn.utils.rnn.pack_padded_sequence(x,lengths,batch_first=True,enforce_sorted=False)\n",
" pad_x,(h,c) = self.rnn(pad_x)\n",
" x, _ = torch.nn.utils.rnn.pad_packed_sequence(pad_x,batch_first=True)\n",
" return self.fc(h[-1])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": []
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"3200: acc=0.285625\n",
"6400: acc=0.33359375\n",
"9600: acc=0.3876041666666667\n",
"12800: acc=0.44078125\n",
"16000: acc=0.4825\n",
"19200: acc=0.5235416666666667\n",
"22400: acc=0.5559821428571429\n",
"25600: acc=0.58609375\n",
"28800: acc=0.6116666666666667\n",
"32000: acc=0.63340625\n",
"35200: acc=0.6525284090909091\n",
"38400: acc=0.668515625\n",
"41600: acc=0.6822596153846154\n",
"44800: acc=0.6948214285714286\n",
"48000: acc=0.7052708333333333\n",
"51200: acc=0.71521484375\n",
"54400: acc=0.7239889705882353\n",
"57600: acc=0.7315277777777778\n",
"60800: acc=0.7388486842105263\n",
"64000: acc=0.74571875\n",
"67200: acc=0.7518303571428572\n",
"70400: acc=0.7576988636363636\n",
"73600: acc=0.7628940217391305\n",
"76800: acc=0.7681510416666667\n",
"80000: acc=0.7728125\n",
"83200: acc=0.7772235576923077\n",
"86400: acc=0.7815393518518519\n",
"89600: acc=0.7857700892857142\n",
"92800: acc=0.7895043103448276\n",
"96000: acc=0.7930520833333333\n",
"99200: acc=0.7959072580645161\n",
"102400: acc=0.798994140625\n",
"105600: acc=0.802064393939394\n",
"108800: acc=0.8051378676470589\n",
"112000: acc=0.8077857142857143\n",
"115200: acc=0.8104600694444445\n",
"118400: acc=0.8128293918918919\n"
]
},
{
"data": {
"text/plain": [
"(0.029785829671223958, 0.8138166666666666)"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"net = LSTMPackClassifier(vocab_size,64,32,len(classes)).to(device)\n",
"train_epoch_emb(net,train_loader_len, lr=0.001,use_pack_sequence=True)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"> **نوٹ:** آپ نے شاید `use_pack_sequence` پیرامیٹر دیکھا ہو جو ہم تربیتی فنکشن کو پاس کرتے ہیں۔ فی الحال، `pack_padded_sequence` فنکشن کو لمبائی ترتیب ٹینسر کو CPU ڈیوائس پر رکھنے کی ضرورت ہوتی ہے، اور اس طرح تربیتی فنکشن کو تربیت کے دوران لمبائی ترتیب ڈیٹا کو GPU پر منتقل کرنے سے گریز کرنا پڑتا ہے۔ آپ [`torchnlp.py`](../../../../../lessons/5-NLP/16-RNN/torchnlp.py) فائل میں `train_emb` فنکشن کی عملدرآمد کو دیکھ سکتے ہیں۔\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## دو طرفہ اور کثیر پرت RNNs\n",
"\n",
"ہمارے مثالوں میں، تمام recurrent نیٹ ورکس ایک ہی سمت میں کام کرتے تھے، یعنی ترتیب کے آغاز سے اختتام تک۔ یہ فطری لگتا ہے کیونکہ یہ اس طریقے سے مشابہت رکھتا ہے جس طرح ہم پڑھتے ہیں یا تقریر سنتے ہیں۔ تاہم، چونکہ بہت سے عملی معاملات میں ہمیں ان پٹ ترتیب تک بے ترتیب رسائی حاصل ہوتی ہے، اس لیے دونوں سمتوں میں recurrent حساب کتاب چلانا سمجھ میں آ سکتا ہے۔ ایسے نیٹ ورکس کو **دو طرفہ** RNNs کہا جاتا ہے، اور انہیں RNN/LSTM/GRU کنسٹرکٹر میں `bidirectional=True` پیرامیٹر دے کر بنایا جا سکتا ہے۔\n",
"\n",
"دو طرفہ نیٹ ورک کے ساتھ کام کرتے وقت، ہمیں دو hidden state ویکٹرز کی ضرورت ہوگی، ہر سمت کے لیے ایک۔ PyTorch ان ویکٹرز کو ایک ویکٹر کے طور پر انکوڈ کرتا ہے جس کا سائز دوگنا ہوتا ہے، جو کافی آسان ہے، کیونکہ عام طور پر آپ نتیجے میں بننے والے hidden state کو مکمل طور پر جڑے ہوئے linear layer میں پاس کرتے ہیں، اور آپ کو صرف اس سائز میں اضافے کو مدنظر رکھنا ہوگا جب آپ layer بنا رہے ہوں۔\n",
"\n",
"Recurrent نیٹ ورک، چاہے وہ ایک طرفہ ہو یا دو طرفہ، ترتیب کے اندر کچھ خاص patterns کو پکڑتا ہے اور انہیں state ویکٹر میں محفوظ کر سکتا ہے یا آؤٹ پٹ میں منتقل کر سکتا ہے۔ جیسے convolutional نیٹ ورکس کے ساتھ، ہم پہلے layer کے اوپر ایک اور recurrent layer بنا سکتے ہیں تاکہ اعلیٰ سطح کے patterns کو پکڑا جا سکے، جو پہلے layer کے ذریعے نکالے گئے نچلی سطح کے patterns سے بنے ہوں۔ یہ ہمیں **کثیر پرت RNN** کے تصور تک لے جاتا ہے، جو دو یا زیادہ recurrent نیٹ ورکس پر مشتمل ہوتا ہے، جہاں پچھلے layer کا آؤٹ پٹ اگلے layer کو ان پٹ کے طور پر دیا جاتا ہے۔\n",
"\n",
"![کثیر پرت long-short-term-memory- RNN کی تصویر](../../../../../translated_images/multi-layer-lstm.dd975e29bb2a59fe58b429db833932d734c81f211cad2783797a9608984acb8c.ur.jpg)\n",
"\n",
"*یہ تصویر [اس شاندار پوسٹ](https://towardsdatascience.com/from-a-lstm-cell-to-a-multilayer-lstm-network-with-pytorch-2899eb5696f3) سے لی گئی ہے، جو فرنینڈو لوپیز نے لکھی ہے۔*\n",
"\n",
"PyTorch ایسے نیٹ ورکس کی تعمیر کو آسان بناتا ہے، کیونکہ آپ کو صرف RNN/LSTM/GRU کنسٹرکٹر میں `num_layers` پیرامیٹر پاس کرنا ہوتا ہے تاکہ recurrence کی کئی layers خود بخود بن سکیں۔ اس کا مطلب یہ بھی ہوگا کہ hidden/state ویکٹر کا سائز تناسب سے بڑھے گا، اور آپ کو recurrent layers کے آؤٹ پٹ کو ہینڈل کرتے وقت اس کو مدنظر رکھنا ہوگا۔\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## دیگر کاموں کے لیے RNNs\n",
"\n",
"اس یونٹ میں، ہم نے دیکھا کہ RNNs کو سلسلہ بندی کی درجہ بندی کے لیے استعمال کیا جا سکتا ہے، لیکن حقیقت میں، یہ بہت سے دوسرے کاموں کو بھی سنبھال سکتے ہیں، جیسے کہ متن کی تخلیق، مشین ترجمہ، اور مزید۔ ہم ان کاموں پر اگلے یونٹ میں غور کریں گے۔\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**ڈسکلیمر**: \nیہ دستاویز AI ترجمہ سروس [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": "522ee52ae3d5ae933e283286254e9a55",
"translation_date": "2025-08-28T04:27:05+00:00",
"source_file": "lessons/5-NLP/16-RNN/RNNPyTorch.ipynb",
"language_code": "ur"
}
},
"nbformat": 4,
"nbformat_minor": 2
}