{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# రికరెంట్ న్యూరల్ నెట్‌వర్క్స్\n", "\n", "మునుపటి మాడ్యూల్‌లో, మేము టెక్స్ట్ యొక్క సమృద్ధి సారాంశ ప్రతినిధులను ఉపయోగించి, ఎంబెడ్డింగ్స్ పై ఒక సాదా లీనియర్ క్లాసిఫయర్‌ను ఉపయోగించాము. ఈ ఆర్కిటెక్చర్ వాక్యంలో ఉన్న పదాల సమగ్ర అర్థాన్ని పట్టుకోవడమే చేస్తుంది, కానీ పదాల **క్రమం**ని పరిగణలోకి తీసుకోదు, ఎందుకంటే ఎంబెడ్డింగ్స్ పై సమగ్రత ఆపరేషన్ మూల టెక్స్ట్ నుండి ఈ సమాచారాన్ని తీసివేస్తుంది. ఈ మోడల్స్ పదాల క్రమాన్ని మోడల్ చేయలేకపోవడం వలన, అవి టెక్స్ట్ జనరేషన్ లేదా ప్రశ్నోత్తరాల వంటి క్లిష్టమైన లేదా అనిశ్చిత పనులను పరిష్కరించలేవు.\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* అనే పదం ఎక్కడైనా వస్తే, అది స్థితి వెక్టర్‌లోని కొన్ని అంశాలను నిరాకరించడానికి నేర్చుకోవచ్చు, దీని ఫలితంగా నిరాకరణ (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 లేయర్‌ను ఉపయోగిస్తున్నాము, కానీ మరింత మెరుగైన ఫలితాల కోసం మునుపటి యూనిట్‌లో వివరించినట్లుగా 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", "![Image showing an example long short term memory cell](../../../../../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": [ "ఇప్పుడు మన నెట్‌వర్క్‌ను శిక్షణ ఇవ్వడం ప్రారంభిద్దాం. 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", "![బహుళస్థాయి లాంగ్-షార్ట్-టర్మ్-మెమరీ RNNని చూపించే చిత్రం](../../../../../translated_images/te/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:00:03+00:00", "source_file": "lessons/5-NLP/16-RNN/RNNPyTorch.ipynb", "language_code": "te" } }, "nbformat": 4, "nbformat_minor": 2 }