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

479 lines
40 KiB
Plaintext

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# पुनरावर्ती न्यूरल नेटवर्कहरू\n",
"\n",
"अघिल्लो मोड्युलमा, हामीले पाठको धनी अर्थपूर्ण प्रतिनिधित्वहरू प्रयोग गरिरहेका थियौं, र इम्बेडिङ्सको माथि एउटा साधारण रेखीय वर्गीकरणकर्ता प्रयोग गरिरहेका थियौं। यो आर्किटेक्चरले वाक्यमा शब्दहरूको समग्र अर्थलाई समेट्छ, तर यसले शब्दहरूको **क्रम**लाई ध्यानमा राख्दैन, किनभने इम्बेडिङ्सको माथि गरिएको समग्र अपरेशनले मूल पाठबाट यो जानकारी हटाइदिन्छ। यी मोडेलहरूले शब्दहरूको क्रमलाई मोडेल गर्न नसक्ने भएकाले, तिनीहरूले पाठ उत्पादन (text generation) वा प्रश्न उत्तरजस्ता जटिल वा अस्पष्ट कार्यहरू समाधान गर्न सक्दैनन्।\n",
"\n",
"पाठ अनुक्रमको अर्थ समेट्नका लागि, हामीले अर्को न्यूरल नेटवर्क आर्किटेक्चर प्रयोग गर्नुपर्छ, जसलाई **पुनरावर्ती न्यूरल नेटवर्क** (recurrent neural network) वा RNN भनिन्छ। RNN मा, हामी हाम्रो वाक्यलाई नेटवर्कमा एक पटकमा एउटा प्रतीक (symbol) मार्फत पठाउँछौं, र नेटवर्कले केही **स्थिति** (state) उत्पादन गर्छ, जसलाई हामी अर्को प्रतीकसँग फेरि नेटवर्कमा पठाउँछौं।\n",
"\n",
"दिइएको टोकनहरूको अनुक्रम $X_0,\\dots,X_n$ को इनपुटको रूपमा, RNN ले न्यूरल नेटवर्क ब्लकहरूको अनुक्रम सिर्जना गर्छ, र यो अनुक्रमलाई अन्त्यसम्म ब्याक प्रोपोगेसन (back propagation) प्रयोग गरेर प्रशिक्षण दिन्छ। प्रत्येक नेटवर्क ब्लकले $(X_i,S_i)$ को जोडीलाई इनपुटको रूपमा लिन्छ, र परिणामस्वरूप $S_{i+1}$ उत्पादन गर्छ। अन्तिम स्थिति $S_n$ वा आउटपुट $X_n$ लाई रेखीय वर्गीकरणकर्तामा पठाइन्छ, जसले नतिजा उत्पादन गर्छ। सबै नेटवर्क ब्लकहरूले एउटै तौल (weights) साझा गर्छन्, र एक पटकको ब्याक प्रोपोगेसन पास प्रयोग गरेर अन्त्यसम्म प्रशिक्षण गरिन्छ।\n",
"\n",
"किनभने स्थिति भेक्टरहरू $S_0,\\dots,S_n$ नेटवर्कमार्फत पास गरिन्छन्, यसले शब्दहरू बीचको अनुक्रमिक निर्भरता सिक्न सक्षम हुन्छ। उदाहरणका लागि, जब *not* शब्द कतै अनुक्रममा देखा पर्छ, यसले स्थिति भेक्टरभित्रका केही तत्वहरूलाई नकार्न (negate) सिक्न सक्छ, जसले नकारात्मकता (negation) उत्पन्न गर्छ।\n",
"\n",
"> चित्रमा देखाइएका सबै RNN ब्लकहरूको तौल साझा गरिएकोले, एउटै चित्रलाई एउटा ब्लक (दायाँतर्फ) को रूपमा प्रतिनिधित्व गर्न सकिन्छ, जसमा पुनरावर्ती फिडब्याक लूप (recurrent feedback loop) हुन्छ, जसले नेटवर्कको आउटपुट स्थितिलाई इनपुटमा फिर्ता पठाउँछ।\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": [
"## साधारण RNN वर्गीकरणकर्ता\n",
"\n",
"साधारण RNN को अवस्थामा, प्रत्येक पुनरावर्ती इकाई एक साधारण रेखीय नेटवर्क हो, जसले इनपुट भेक्टर र अवस्था भेक्टरलाई जोडेर लिन्छ, र नयाँ अवस्था भेक्टर उत्पादन गर्छ। PyTorch ले यस इकाईलाई `RNNCell` वर्गको रूपमा प्रतिनिधित्व गर्छ, र यस्ता कोषहरूको नेटवर्कलाई `RNN` तहको रूपमा।\n",
"\n",
"RNN वर्गीकरणकर्ता परिभाषित गर्नका लागि, हामी पहिले इनपुट शब्दावलीको आयाम घटाउनको लागि एक embedding तह लागू गर्नेछौं, र त्यसपछि यसको माथि RNN तह राख्नेछौं:\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": [
"> **Note:** यहाँ हामी सरलताको लागि अनट्रेन गरिएको एम्बेडिङ लेयर प्रयोग गर्छौं, तर अझ राम्रो नतिजाका लागि हामी प्रि-ट्रेन गरिएको एम्बेडिङ लेयर जस्तै Word2Vec वा GloVe एम्बेडिङ प्रयोग गर्न सक्छौं, जसको वर्णन अघिल्लो युनिटमा गरिएको छ। राम्रो बुझाइको लागि, तपाईंले यो कोडलाई प्रि-ट्रेन गरिएको एम्बेडिङसँग काम गर्न अनुकूल बनाउन चाहन सक्नुहुन्छ।\n",
"\n",
"हाम्रो केसमा, हामी प्याड गरिएको डाटा लोडर प्रयोग गर्नेछौं, जसले गर्दा प्रत्येक ब्याचमा समान लम्बाइको प्याड गरिएको सिक्वेन्सहरूको संख्या हुनेछ। RNN लेयरले एम्बेडिङ टेन्सरहरूको सिक्वेन्स लिन्छ, र दुई आउटपुट उत्पादन गर्छ:\n",
"* $x$ भनेको प्रत्येक चरणमा RNN सेलको आउटपुटहरूको सिक्वेन्स हो\n",
"* $h$ भनेको सिक्वेन्सको अन्तिम तत्वको लागि अन्तिम हिडन स्टेट हो\n",
"\n",
"त्यसपछि हामी पूर्ण रूपमा जडित लीनियर क्लासिफायर लागू गर्छौं ताकि वर्गहरूको संख्या प्राप्त गर्न सकियोस्।\n",
"\n",
"> **Note:** RNN हरूलाई ट्रेन गर्न निकै गाह्रो हुन्छ, किनभने 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",
"परम्परागत RNN को मुख्य समस्याहरू मध्ये एकलाई **vanishing gradients** समस्या भनिन्छ। किनभने RNN हरू एकै पटकको ब्याक-प्रोपागेसन पासमा अन्त-देखि-अन्तसम्म प्रशिक्षित गरिन्छन्, यसले नेटवर्कको पहिलो तहहरूमा त्रुटि फैलाउन कठिनाइ अनुभव गर्छ, जसका कारण नेटवर्कले टाढा टोकनहरू बीचको सम्बन्ध सिक्न सक्दैन। यस समस्यालाई समाधान गर्नका लागि **स्पष्ट अवस्था व्यवस्थापन**को परिचय गराउने एउटा तरिका भनेको **गेटहरू** प्रयोग गर्नु हो। यस प्रकारका दुई सबैभन्दा प्रख्यात आर्किटेक्चरहरू हुन्: **लामो छोटो समय स्मृति** (LSTM) र **गेटेड रिलेको इकाई** (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$सँग जोडिन्छ, र तिनीहरूले **गेटहरू** मार्फत अवस्था $c$मा के हुन्छ भन्ने नियन्त्रण गर्छन्। प्रत्येक गेट एक न्युरल नेटवर्क हो जसमा सिग्मोइड सक्रियता हुन्छ (आउटपुट $[0,1]$को दायरामा), जसलाई अवस्था भेक्टरसँग गुणा गर्दा बिटवाइज मास्कको रूपमा सोच्न सकिन्छ। माथिको चित्रमा (बायाँदेखि दायाँ) निम्न गेटहरू छन्:\n",
"* **भुल्ने गेट** लुकेको भेक्टर लिन्छ र भेक्टर $c$का कुन-कुन कम्पोनेन्टहरू भुल्नुपर्छ र कुन-कुन पास गर्नुपर्छ भन्ने निर्धारण गर्छ। \n",
"* **इनपुट गेट** इनपुट र लुकेको भेक्टरबाट केही जानकारी लिन्छ, र यसलाई अवस्थामा समावेश गर्छ।\n",
"* **आउटपुट गेट** अवस्थालाई केही रेखीय तह मार्फत $\\tanh$ सक्रियता प्रयोग गरेर रूपान्तरण गर्छ, त्यसपछि लुकेको भेक्टर $h_i$ प्रयोग गरेर यसको केही कम्पोनेन्टहरू चयन गर्छ ताकि नयाँ अवस्था $c_{i+1}$ उत्पादन गर्न सकियोस्।\n",
"\n",
"अवस्था $c$का कम्पोनेन्टहरूलाई केही झण्डाहरूको रूपमा सोच्न सकिन्छ जसलाई अन र अफ गर्न सकिन्छ। उदाहरणका लागि, जब हामी अनुक्रममा *Alice* नाम भेट्छौं, हामीले यसलाई महिला पात्रलाई जनाउँछ भन्ने मान्न सक्छौं, र अवस्थामा झण्डा उठाउन सक्छौं कि वाक्यमा महिला संज्ञा छ। जब हामी थप *and Tom* वाक्यांश भेट्छौं, हामीले बहुवचन संज्ञा भएको झण्डा उठाउँछौं। यसरी अवस्थालाई हेरफेर गरेर हामीले वाक्यका भागहरूको व्याकरणिक गुणहरू ट्र्याक गर्न सक्ने सम्भावना हुन्छ।\n",
"\n",
"> **Note**: 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",
"> **Note**: हामी वास्तवमा अनप्याक गरिएको परिणाम `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",
"हाम्रा उदाहरणहरूमा, सबै पुनरावर्ती नेटवर्कहरू एक दिशामा काम गर्थे, अनुक्रमको सुरुबाट अन्त्यसम्म। यो स्वाभाविक देखिन्छ, किनकि यो हामीले पढ्ने र बोल्ने तरिकासँग मेल खान्छ। तर, धेरै व्यावहारिक अवस्थामा हामीसँग इनपुट अनुक्रममा जताततै पहुँच हुने सम्भावना हुन्छ, त्यसैले पुनरावर्ती गणना दुवै दिशामा चलाउनु उपयुक्त हुन सक्छ। यस्ता नेटवर्कहरूलाई **द्विदिशात्मक** RNNs भनिन्छ, र तिनीहरूलाई RNN/LSTM/GRU कन्स्ट्रक्टरमा `bidirectional=True` प्यारामिटर पास गरेर सिर्जना गर्न सकिन्छ।\n",
"\n",
"द्विदिशात्मक नेटवर्कसँग काम गर्दा, हामीलाई दुईवटा लुकेको अवस्था भेक्टरहरू चाहिन्छ, प्रत्येक दिशाका लागि एक। PyTorch ले ती भेक्टरहरूलाई दुई गुणा ठूलो आकारको एक भेक्टरको रूपमा इनकोड गर्छ, जुन धेरै सुविधाजनक छ, किनकि सामान्यत: तपाईंले प्राप्त लुकेको अवस्थालाई पूर्ण-सम्पर्कित रेखीय तहमा पास गर्नुहुन्छ, र तह सिर्जना गर्दा आकारको यो वृद्धि ध्यानमा राख्न मात्र आवश्यक हुन्छ।\n",
"\n",
"पुनरावर्ती नेटवर्क, एक-दिशात्मक होस् वा द्विदिशात्मक, अनुक्रमभित्रका केही ढाँचाहरू समात्छ, र तिनीहरूलाई अवस्था भेक्टरमा भण्डारण गर्न वा आउटपुटमा पास गर्न सक्छ। जस्तै कनभोल्युसनल नेटवर्कहरूमा, हामी पहिलो तहले निकालेका तल्लो-स्तरीय ढाँचाहरूबाट उच्च-स्तरीय ढाँचाहरू समात्न अर्को पुनरावर्ती तह माथि निर्माण गर्न सक्छौं। यसले हामीलाई **बहु-स्तरीय RNN** को अवधारणामा पुर्‍याउँछ, जसमा दुई वा बढी पुनरावर्ती नेटवर्कहरू हुन्छन्, जहाँ अघिल्लो तहको आउटपुटलाई अर्को तहमा इनपुटको रूपमा पास गरिन्छ।\n",
"\n",
"![बहु-स्तरीय लामो-छोटो-अवधि-स्मृति RNN देखाउने चित्र](../../../../../translated_images/ne/multi-layer-lstm.dd975e29bb2a59fe.webp)\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` प्यारामिटर पास गर्न मात्र आवश्यक हुन्छ, जसले स्वचालित रूपमा पुनरावृत्त तहहरूको निर्माण गर्छ। यसले लुकेको/अवस्था भेक्टरको आकार समानुपातिक रूपमा बढ्ने अर्थ पनि राख्छ, र पुनरावर्ती तहहरूको आउटपुट ह्यान्डल गर्दा तपाईंले यसलाई ध्यानमा राख्न आवश्यक हुन्छ।\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-28T09:38:55+00:00",
"source_file": "lessons/5-NLP/16-RNN/RNNPyTorch.ipynb",
"language_code": "ne"
}
},
"nbformat": 4,
"nbformat_minor": 2
}