479 lines
39 KiB
Plaintext
479 lines
39 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"# पुनरावर्ती न्यूरल नेटवर्क\n",
|
|
"\n",
|
|
"पिछले मॉड्यूल में, हमने टेक्स्ट के समृद्ध सेमांटिक प्रतिनिधित्व और एम्बेडिंग के ऊपर एक साधारण लीनियर क्लासिफायर का उपयोग किया। यह आर्किटेक्चर वाक्य में शब्दों के समग्र अर्थ को कैप्चर करता है, लेकिन यह शब्दों के **क्रम** को ध्यान में नहीं रखता, क्योंकि एम्बेडिंग पर किया गया समग्र ऑपरेशन मूल टेक्स्ट से इस जानकारी को हटा देता है। चूंकि ये मॉडल शब्दों के क्रम को मॉडल नहीं कर सकते, वे टेक्स्ट जनरेशन या प्रश्नोत्तर जैसे अधिक जटिल या अस्पष्ट कार्यों को हल नहीं कर सकते।\n",
|
|
"\n",
|
|
"टेक्स्ट अनुक्रम के अर्थ को कैप्चर करने के लिए, हमें एक अन्य न्यूरल नेटवर्क आर्किटेक्चर का उपयोग करना होगा, जिसे **पुनरावर्ती न्यूरल नेटवर्क** या RNN कहा जाता है। RNN में, हम अपने वाक्य को नेटवर्क के माध्यम से एक बार में एक प्रतीक पास करते हैं, और नेटवर्क कुछ **स्थिति** (state) उत्पन्न करता है, जिसे हम अगले प्रतीक के साथ नेटवर्क में फिर से पास करते हैं।\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$ नेटवर्क के माध्यम से पास किए जाते हैं, यह शब्दों के बीच अनुक्रमिक निर्भरताओं को सीखने में सक्षम होता है। उदाहरण के लिए, जब अनुक्रम में कहीं *not* शब्द आता है, तो यह स्थिति वेक्टर के कुछ तत्वों को नकारने (negate) के लिए सीख सकता है, जिससे नकारात्मकता (negation) उत्पन्न होती है।\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": [
|
|
"## सरल RNN वर्गीकरणकर्ता\n",
|
|
"\n",
|
|
"सरल RNN के मामले में, प्रत्येक पुनरावर्ती यूनिट एक साधारण रैखिक नेटवर्क होता है, जो संयोजित इनपुट वेक्टर और स्टेट वेक्टर लेता है, और एक नया स्टेट वेक्टर उत्पन्न करता है। PyTorch इस यूनिट को `RNNCell` क्लास के साथ दर्शाता है, और ऐसे सेल्स के नेटवर्क को `RNN` लेयर के रूप में।\n",
|
|
"\n",
|
|
"एक RNN वर्गीकरणकर्ता को परिभाषित करने के लिए, हम पहले एक एम्बेडिंग लेयर लागू करेंगे ताकि इनपुट शब्दावली की आयामीयता को कम किया जा सके, और फिर इसके ऊपर 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": [
|
|
"> **नोट:** यहां हम सरलता के लिए बिना प्रशिक्षित embedding layer का उपयोग कर रहे हैं, लेकिन और बेहतर परिणामों के लिए हम Word2Vec या GloVe embeddings के साथ प्री-ट्रेंड embedding layer का उपयोग कर सकते हैं, जैसा कि पिछले यूनिट में बताया गया है। बेहतर समझ के लिए, आप इस कोड को प्री-ट्रेंड embeddings के साथ काम करने के लिए अनुकूलित कर सकते हैं।\n",
|
|
"\n",
|
|
"हमारे मामले में, हम padded data loader का उपयोग करेंगे, जिससे प्रत्येक बैच में समान लंबाई वाले padded sequences होंगे। RNN layer embedding tensors के sequence को लेगी और दो आउटपुट उत्पन्न करेगी:\n",
|
|
"* $x$ प्रत्येक चरण पर RNN सेल आउटपुट का sequence है\n",
|
|
"* $h$ sequence के अंतिम तत्व के लिए अंतिम hidden state है\n",
|
|
"\n",
|
|
"इसके बाद हम एक fully-connected linear classifier लागू करेंगे ताकि वर्गों (classes) की संख्या प्राप्त की जा सके।\n",
|
|
"\n",
|
|
"> **नोट:** RNNs को प्रशिक्षित करना काफी कठिन होता है, क्योंकि जब RNN सेल्स को sequence की लंबाई के साथ unroll किया जाता है, तो back propagation में शामिल लेयर्स की संख्या काफी बड़ी हो जाती है। इसलिए हमें छोटा learning rate चुनने की आवश्यकता होती है और अच्छे परिणाम प्राप्त करने के लिए नेटवर्क को बड़े dataset पर प्रशिक्षित करना पड़ता है। इसमें काफी समय लग सकता है, इसलिए 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 को एक ही बैक-प्रोपेगेशन पास में एंड-टू-एंड ट्रेन किया जाता है, यह नेटवर्क की शुरुआती लेयर्स तक एरर को प्रोपेगेट करने में कठिनाई महसूस करता है, और इस कारण नेटवर्क दूरस्थ टोकन के बीच संबंधों को सीख नहीं पाता। इस समस्या से बचने के तरीकों में से एक है **explicit state management** को लागू करना, जिसे **gates** के उपयोग से किया जाता है। इस प्रकार की दो सबसे प्रसिद्ध आर्किटेक्चर हैं: **लॉन्ग शॉर्ट टर्म मेमोरी** (LSTM) और **गेटेड रिले यूनिट** (GRU)।\n",
|
|
"\n",
|
|
"\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",
|
|
"> **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",
|
|
"\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-31T15:24:12+00:00",
|
|
"source_file": "lessons/5-NLP/16-RNN/RNNPyTorch.ipynb",
|
|
"language_code": "hi"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 2
|
|
} |