477 lines
38 KiB
Plaintext
477 lines
38 KiB
Plaintext
{
|
|
"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$ नेटवर्कमधून पास केले जातात, त्यामुळे नेटवर्कला शब्दांमधील अनुक्रमिक संबंध शिकता येतात. उदाहरणार्थ, जेव्हा *not* हा शब्द अनुक्रमात कुठेतरी दिसतो, तेव्हा नेटवर्क स्थिती वेक्टरमधील विशिष्ट घटकांना नकारात्मक करण्यासाठी शिकू शकते, ज्यामुळे नकार तयार होतो.\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": [
|
|
"> **टीप:** येथे सोपेपणासाठी आम्ही अनट्रेंड एम्बेडिंग लेयर वापरतो, परंतु अधिक चांगल्या परिणामांसाठी आपण Word2Vec किंवा GloVe एम्बेडिंगसह प्री-ट्रेंड एम्बेडिंग लेयर वापरू शकतो, जसे की मागील युनिटमध्ये वर्णन केले आहे. अधिक चांगल्या समजासाठी, तुम्ही हा कोड प्री-ट्रेंड एम्बेडिंगसह काम करण्यासाठी अनुकूलित करू शकता.\n",
|
|
"\n",
|
|
"आपल्या बाबतीत, आम्ही पॅडेड डेटा लोडर वापरणार आहोत, त्यामुळे प्रत्येक बॅचमध्ये समान लांबीच्या पॅडेड सिक्वेन्सेसचा समावेश असेल. RNN लेयर एम्बेडिंग टेन्सर्सच्या सिक्वेन्सला घेईल आणि दोन आउटपुट तयार करेल:\n",
|
|
"* $x$ म्हणजे प्रत्येक स्टेपवर RNN सेल आउटपुट्सची सिक्वेन्स\n",
|
|
"* $h$ म्हणजे सिक्वेन्सच्या शेवटच्या घटकासाठी अंतिम हिडन स्टेट\n",
|
|
"\n",
|
|
"यानंतर आम्ही पूर्णपणे कनेक्टेड लीनियर क्लासिफायर लागू करतो, ज्यामुळे वर्गांची संख्या मिळते.\n",
|
|
"\n",
|
|
"> **टीप:** 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",
|
|
"पारंपरिक 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",
|
|
"आपल्या उदाहरणात, आम्हाला मिनीबॅचमधील सर्व अनुक्रम शून्य वेक्टरसह पॅड करावे लागले. यामुळे काही प्रमाणात मेमरी वाया जाते, परंतु RNN मध्ये अधिक महत्त्वाचे म्हणजे पॅड केलेल्या इनपुट आयटमसाठी अतिरिक्त 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": []
|
|
},
|
|
{
|
|
"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-28T09:37:56+00:00",
|
|
"source_file": "lessons/5-NLP/16-RNN/RNNPyTorch.ipynb",
|
|
"language_code": "mr"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 2
|
|
} |