{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# പുനരാവർത്തന ന്യൂറൽ നെറ്റ്‌വർക്കുകൾ\n", "\n", "മുൻ മോഡ്യൂളിൽ, നാം ടെക്സ്റ്റിന്റെ സമ്പന്നമായ സാംവേദനാത്മക പ്രതിനിധാനങ്ങൾ ഉപയോഗിച്ച്, എംബെഡിംഗുകളുടെ മുകളിൽ ഒരു ലളിതമായ ലീനിയർ ക്ലാസിഫയർ ഉപയോഗിച്ചിരുന്നു. ഈ ആർക്കിടെക്ചർ ചെയ്യുന്നത് ഒരു വാക്യത്തിലെ വാക്കുകളുടെ സംയുക്ത അർത്ഥം പിടിച്ചുപറ്റുകയാണ്, പക്ഷേ വാക്കുകളുടെ **ക്രമം** പരിഗണിക്കുന്നില്ല, കാരണം എംബെഡിംഗുകളുടെ മുകളിൽAggregation പ്രവർത്തനം ഈ വിവരങ്ങൾ യഥാർത്ഥ ടെക്സ്റ്റിൽ നിന്ന് നീക്കം ചെയ്യുന്നു. ഈ മോഡലുകൾ വാക്കുകളുടെ ക്രമീകരണം മോഡൽ ചെയ്യാൻ കഴിയാത്തതിനാൽ, ടെക്സ്റ്റ് ജനറേഷൻ അല്ലെങ്കിൽ ചോദ്യോത്തരങ്ങൾ പോലുള്ള കൂടുതൽ സങ്കീർണ്ണമായ അല്ലെങ്കിൽ സംശയാസ്പദമായ പ്രവർത്തനങ്ങൾ പരിഹരിക്കാൻ കഴിയില്ല.\n", "\n", "ടെക്സ്റ്റ് സീക്വൻസിന്റെ അർത്ഥം പിടിച്ചുപറ്റാൻ, നാം മറ്റൊരു ന്യൂറൽ നെറ്റ്‌വർക്ക് ആർക്കിടെക്ചർ ഉപയോഗിക്കേണ്ടതുണ്ട്, അതാണ് **പുനരാവർത്തന ന്യൂറൽ നെറ്റ്‌വർക്ക്** അല്ലെങ്കിൽ RNN. RNN-ൽ, നാം നമ്മുടെ വാക്യം ഒരു സിംബോളായി ഓരോ തവണയും നെറ്റ്‌വർക്കിലൂടെ കടത്തുന്നു, നെറ്റ്‌വർക്ക് ചില **സ്റ്റേറ്റ്** ഉൽപ്പാദിപ്പിക്കുന്നു, അത് പിന്നീട് അടുത്ത സിംബോളിനൊപ്പം വീണ്ടും നെറ്റ്‌വർക്കിലേക്ക് നൽകുന്നു.\n", "\n", "\"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": [ "> **കുറിപ്പ്:** ലളിതത്വത്തിനായി ഇവിടെ ഞങ്ങൾ പരിശീലനമില്ലാത്ത embedding ലെയർ ഉപയോഗിക്കുന്നു, എന്നാൽ മുൻവശം യൂണിറ്റിൽ വിവരിച്ച പോലെ Word2Vec അല്ലെങ്കിൽ GloVe embeddings ഉപയോഗിച്ച് മുൻപരിചയമുള്ള embedding ലെയർ ഉപയോഗിച്ചാൽ കൂടുതൽ മികച്ച ഫലങ്ങൾ ലഭിക്കും. കൂടുതൽ മനസ്സിലാക്കാൻ, നിങ്ങൾക്ക് ഈ കോഡ് മുൻപരിചയമുള്ള embeddings ഉപയോഗിച്ച് പ്രവർത്തിക്കാൻ മാറ്റാം.\n", "\n", "നമ്മുടെ കേസിൽ, നാം പാഡഡ് ഡാറ്റ ലോഡർ ഉപയോഗിക്കും, അതിനാൽ ഓരോ ബാച്ചിലും ഒരേ നീളമുള്ള പാഡഡ് സീക്വൻസുകളുടെ എണ്ണം ഉണ്ടാകും. RNN ലെയർ embedding ടെൻസറുകളുടെ സീക്വൻസ് സ്വീകരിച്ച് രണ്ട് ഔട്ട്പുട്ടുകൾ നൽകും: \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", "ക്ലാസിക്കൽ RNN-കളുടെ പ്രധാന പ്രശ്നങ്ങളിൽ ഒന്നാണ്所谓 **വാനിഷിംഗ് ഗ്രേഡിയന്റ്സ്** പ്രശ്നം. 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-യുടെ ആന്തരിക ഘടന മനസ്സിലാക്കാൻ മികച്ച ഒരു വിഭവം Christopher Olah എഴുതിയ [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": [ "ഇപ്പോൾ നമുക്ക് നമ്മുടെ നെറ്റ്‌വർക്ക് പരിശീലിപ്പിക്കാം. LSTM പരിശീലിപ്പിക്കുന്നത് വളരെ മന്ദഗതിയിലാണ് എന്നതും ശ്രദ്ധിക്കുക, പരിശീലനത്തിന്റെ തുടക്കത്തിൽ കൃത്യതയിൽ വലിയ വർദ്ധനവ് കാണാനാകില്ല. കൂടാതെ, യുക്തിസഹമായ പരിശീലന വേഗത ലഭിക്കുന്നതിനും മെമ്മറി വൃത്തിയാക്കൽ ഒഴിവാക്കുന്നതിനും `lr` ലേണിംഗ് റേറ്റ് പാരാമീറ്ററുമായി പരീക്ഷണം നടത്തേണ്ടതുണ്ടാകും.\n" ] }, { "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 പരിശീലിപ്പിക്കാൻ, ആദ്യം വലിയ മിനിബാച്ച് (`[1,6,9]`) ഉപയോഗിച്ച് RNN സെല്ലുകളുടെ ആദ്യ ഗ്രൂപ്പ് പരിശീലിപ്പിക്കാൻ ആഗ്രഹിക്കുന്നു, പിന്നീട് മൂന്നാം സീക്വൻസിന്റെ പ്രോസസ്സിംഗ് അവസാനിപ്പിച്ച്, ചെറുതായ മിനിബാച്ചുകളുമായി (`[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": [ "ഇപ്പോൾ പരിശീലനം നടത്താം:\n" ] }, { "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` ഫംഗ്ഷന്‍ length sequence ടെൻസർ CPU ഡിവൈസിൽ വേണം, അതിനാൽ ട്രെയിനിംഗ് ഫംഗ്ഷൻ length sequence ഡാറ്റ GPU-യിലേക്ക് മാറ്റുന്നത് ഒഴിവാക്കണം. നിങ്ങൾക്ക് [`torchnlp.py`](../../../../../lessons/5-NLP/16-RNN/torchnlp.py) ഫയലിലെ `train_emb` ഫംഗ്ഷന്റെ ഇംപ്ലിമെന്റേഷൻ പരിശോധിക്കാം.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## ദ്വിദിശയും ബഹുസ്തര RNN-കളും\n", "\n", "നമ്മുടെ ഉദാഹരണങ്ങളിൽ, എല്ലാ റികറന്റ് നെറ്റ്വർക്കുകളും ഒരു ദിശയിൽ പ്രവർത്തിച്ചിരുന്നു, ഒരു സീക്വൻസിന്റെ തുടക്കത്തിൽ നിന്ന് അവസാനം വരെ. ഇത് സ്വാഭാവികമാണ്, കാരണം ഇത് നാം വായിക്കുകയും സംസാരശ്രവണം നടത്തുകയും ചെയ്യുന്ന രീതിയെ അനുകരിക്കുന്നു. എന്നാൽ, പല പ്രായോഗിക സാഹചര്യങ്ങളിലും നമുക്ക് ഇൻപുട്ട് സീക്വൻസിലേക്ക് യാദൃച്ഛിക ആക്‌സസ് ലഭിക്കുന്നതിനാൽ, റികറന്റ് കണക്കുകൂട്ടൽ ഇരുവശത്തും നടത്തുന്നത് ബുദ്ധിമുട്ടില്ല. ഇത്തരം നെറ്റ്വർക്കുകൾ **ദ്വിദിശ RNN-കൾ** എന്ന് വിളിക്കപ്പെടുന്നു, അവ RNN/LSTM/GRU കൺസ്ട്രക്ടറിലേക്ക് `bidirectional=True` പാരാമീറ്റർ നൽകി സൃഷ്ടിക്കാം.\n", "\n", "ദ്വിദിശ നെറ്റ്വർക്കുമായി ഇടപഴകുമ്പോൾ, ഓരോ ദിശയ്ക്കും ഒരു ഹിഡൻ സ്റ്റേറ്റ് വെക്ടർ വേണം. PyTorch ആ വെക്ടറുകൾ ഇരട്ട വലുപ്പമുള്ള ഒരു വെക്ടറായി കോഡുചെയ്യുന്നു, ഇത് വളരെ സൗകര്യപ്രദമാണ്, കാരണം സാധാരണയായി നിങ്ങൾ ഫലമായ ഹിഡൻ സ്റ്റേറ്റ് ഫുൾ-കണക്ടഡ് ലിനിയർ ലെയറിലേക്ക് പാസ്സ് ചെയ്യുക, അതിനാൽ ലെയർ സൃഷ്ടിക്കുമ്പോൾ ഈ വലുപ്പ വർദ്ധനവ് പരിഗണിക്കേണ്ടതുണ്ട്.\n", "\n", "റികറന്റ് നെറ്റ്വർക്ക്, ഒരുദിശയിലോ ദ്വിദിശയിലോ, ഒരു സീക്വൻസിനുള്ളിൽ ചില പാറ്റേണുകൾ പിടിച്ചുപറ്റുകയും അവ സ്റ്റേറ്റ് വെക്ടറിലോ ഔട്ട്പുട്ടിലോ സൂക്ഷിക്കുകയും ചെയ്യുന്നു. കോൺവല്യൂഷണൽ നെറ്റ്വർക്കുകളെപ്പോലെ, നാം ആദ്യ ലെയറിന്റെ മുകളിൽ മറ്റൊരു റികറന്റ് ലെയർ നിർമ്മിച്ച് ഉയർന്ന തലത്തിലുള്ള പാറ്റേണുകൾ പിടിച്ചുപറ്റാം, ആദ്യ ലെയർ കണ്ടെത്തിയ താഴ്ന്ന തലത്തിലുള്ള പാറ്റേണുകളിൽ നിന്നാണ് ഇത് നിർമ്മിക്കുന്നത്. ഇതാണ് **ബഹുസ്തര RNN** എന്ന ആശയം, ഇത് രണ്ട് അല്ലെങ്കിൽ അതിലധികം റികറന്റ് നെറ്റ്വർക്കുകൾ ഉൾക്കൊള്ളുന്നു, മുൻ ലെയറിന്റെ ഔട്ട്പുട്ട് അടുത്ത ലെയറിന്റെ ഇൻപുട്ടായി പാസ്സ് ചെയ്യപ്പെടുന്നു.\n", "\n", "![Multilayer long-short-term-memory- RNN കാണിക്കുന്ന ചിത്രം](../../../../../translated_images/ml/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": [ "## മറ്റ് ജോലികൾക്കുള്ള RNNകൾ\n", "\n", "ഈ യൂണിറ്റിൽ, RNNകൾ ക്രമം തിരിച്ചറിയലിനായി ഉപയോഗിക്കാമെന്ന് നാം കണ്ടു, എന്നാൽ യഥാർത്ഥത്തിൽ, അവ ടെക്സ്റ്റ് ജനറേഷൻ, മെഷീൻ വിവർത്തനം തുടങ്ങിയ നിരവധി ജോലികൾ കൈകാര്യം ചെയ്യാൻ കഴിയും. ആ ജോലികൾ അടുത്ത യൂണിറ്റിൽ നാം പരിഗണിക്കും.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n\n\n**അസൂയാ**: \nഈ രേഖ AI വിവർത്തന സേവനം [Co-op Translator](https://github.com/Azure/co-op-translator) ഉപയോഗിച്ച് വിവർത്തനം ചെയ്തതാണ്. നാം കൃത്യതയ്ക്ക് ശ്രമിച്ചിട്ടുണ്ടെങ്കിലും, സ്വയം പ്രവർത്തിക്കുന്ന വിവർത്തനങ്ങളിൽ പിശകുകൾ അല്ലെങ്കിൽ തെറ്റുകൾ ഉണ്ടാകാമെന്ന് ദയവായി ശ്രദ്ധിക്കുക. അതിന്റെ മാതൃഭാഷയിലുള്ള യഥാർത്ഥ രേഖ അധികാരപരമായ ഉറവിടമായി കണക്കാക്കപ്പെടണം. നിർണായക വിവരങ്ങൾക്ക്, പ്രൊഫഷണൽ മനുഷ്യ വിവർത്തനം ശുപാർശ ചെയ്യപ്പെടുന്നു. ഈ വിവർത്തനം ഉപയോഗിക്കുന്നതിൽ നിന്നുണ്ടാകുന്ന ഏതെങ്കിലും തെറ്റിദ്ധാരണകൾക്കോ തെറ്റായ വ്യാഖ്യാനങ്ങൾക്കോ ഞങ്ങൾ ഉത്തരവാദികളല്ല.\n\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-11-26T02:01:47+00:00", "source_file": "lessons/5-NLP/16-RNN/RNNPyTorch.ipynb", "language_code": "ml" } }, "nbformat": 4, "nbformat_minor": 2 }