485 lines
42 KiB
Plaintext
485 lines
42 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"# ಪುನರಾವರ್ತಿತ ನ್ಯೂರಲ್ ನೆಟ್ವರ್ಕ್ಗಳು\n",
|
|
"\n",
|
|
"ಹಿಂದಿನ ಘಟಕದಲ್ಲಿ, ನಾವು ಪಠ್ಯದ ಶ್ರೀಮಂತ ಅರ್ಥಾತ್ಮಕ ಪ್ರತಿನಿಧಾನಗಳನ್ನು ಮತ್ತು ಎम्बೆಡ್ಡಿಂಗ್ಗಳ ಮೇಲೆ ಸರಳ ರೇಖೀಯ ವರ್ಗೀಕರಣವನ್ನು ಬಳಸುತ್ತಿದ್ದೇವೆ. ಈ ವಾಸ್ತುಶಿಲ್ಪವು ವಾಕ್ಯದಲ್ಲಿನ ಪದಗಳ ಸಂಗ್ರಹಿತ ಅರ್ಥವನ್ನು ಹಿಡಿಯುತ್ತದೆ, ಆದರೆ ಪದಗಳ **ಕ್ರಮ**ವನ್ನು ಪರಿಗಣಿಸುವುದಿಲ್ಲ, ಏಕೆಂದರೆ ಎम्बೆಡ್ಡಿಂಗ್ಗಳ ಮೇಲಿನ ಸಂಗ್ರಹಣಾ ಕಾರ್ಯಾಚರಣೆ ಮೂಲ ಪಠ್ಯದಿಂದ ಈ ಮಾಹಿತಿಯನ್ನು ತೆಗೆದುಹಾಕುತ್ತದೆ. ಈ ಮಾದರಿಗಳು ಪದಗಳ ಕ್ರಮವನ್ನು ಮಾದರಿಮಾಡಲು ಅಸಮರ್ಥವಾಗಿರುವುದರಿಂದ, ಅವು ಪಠ್ಯ ರಚನೆ ಅಥವಾ ಪ್ರಶ್ನೋತ್ತರದಂತಹ ಹೆಚ್ಚು ಸಂಕೀರ್ಣ ಅಥವಾ ಅಸ್ಪಷ್ಟ ಕಾರ್ಯಗಳನ್ನು ಪರಿಹರಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ.\n",
|
|
"\n",
|
|
"ಪಠ್ಯದ ಕ್ರಮದ ಅರ್ಥವನ್ನು ಹಿಡಿಯಲು, ನಾವು ಮತ್ತೊಂದು ನ್ಯೂರಲ್ ನೆಟ್ವರ್ಕ್ ವಾಸ್ತುಶಿಲ್ಪವನ್ನು ಬಳಸಬೇಕಾಗುತ್ತದೆ, ಇದನ್ನು **ಪುನರಾವರ್ತಿತ ನ್ಯೂರಲ್ ನೆಟ್ವರ್ಕ್** ಅಥವಾ RNN ಎಂದು ಕರೆಯುತ್ತಾರೆ. RNN ನಲ್ಲಿ, ನಾವು ನಮ್ಮ ವಾಕ್ಯವನ್ನು ಒಂದು ಸಂಕೇತವನ್ನು ಪ್ರತಿ ಬಾರಿ ನೆಟ್ವರ್ಕ್ ಮೂಲಕ ಕಳುಹಿಸುತ್ತೇವೆ, ಮತ್ತು ನೆಟ್ವರ್ಕ್ ಕೆಲವು **ಸ್ಥಿತಿ** ಅನ್ನು ಉತ್ಪಾದಿಸುತ್ತದೆ, ಅದನ್ನು ನಂತರ ಮುಂದಿನ ಸಂಕೇತದೊಂದಿಗೆ ಮತ್ತೆ ನೆಟ್ವರ್ಕ್ಗೆ ಕಳುಹಿಸುತ್ತೇವೆ.\n",
|
|
"\n",
|
|
"<img alt=\"RNN\" src=\"../../../../../translated_images/kn/rnn.27f5c29c53d727b5.webp\" width=\"60%\"/>\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 ವರ್ಗೀಕರಣೆಯನ್ನು ವ್ಯಾಖ್ಯಾನಿಸಲು, ಮೊದಲು ಇನ್ಪುಟ್ ಶಬ್ದಕೋಶದ ಆಯಾಮವನ್ನು ಕಡಿಮೆ ಮಾಡಲು 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": [
|
|
"> **ಗಮನಿಸಿ:** ಸರಳತೆಗೆ ನಾವು ಇಲ್ಲಿ ತರಬೇತಿಗೊಳ್ಳದ embedding ಲೇಯರ್ ಅನ್ನು ಬಳಸುತ್ತಿದ್ದೇವೆ, ಆದರೆ ಇನ್ನೂ ಉತ್ತಮ ಫಲಿತಾಂಶಗಳಿಗಾಗಿ ನಾವು ಹಿಂದಿನ ಘಟಕದಲ್ಲಿ ವಿವರಿಸಿದಂತೆ Word2Vec ಅಥವಾ GloVe embeddings ಹೊಂದಿರುವ ಪೂರ್ವ-ತರಬೇತಿಗೊಳ್ಳಲಾದ embedding ಲೇಯರ್ ಅನ್ನು ಬಳಸಬಹುದು. ಉತ್ತಮ ಅರ್ಥಮಾಡಿಕೊಳ್ಳಲು, ನೀವು ಈ ಕೋಡ್ ಅನ್ನು ಪೂರ್ವ-ತರಬೇತಿಗೊಳ್ಳಲಾದ embeddings ಜೊತೆಗೆ ಕೆಲಸ ಮಾಡಲು ಹೊಂದಿಸಬಹುದು.\n",
|
|
"\n",
|
|
"ನಮ್ಮ ಪ್ರಕರಣದಲ್ಲಿ, ನಾವು padded ಡೇಟಾ ಲೋಡರ್ ಅನ್ನು ಬಳಸುತ್ತೇವೆ, ಆದ್ದರಿಂದ ಪ್ರತಿ ಬ್ಯಾಚ್ ಒಂದೇ ಉದ್ದದ padded ಸರಣಿಗಳ ಸಂಖ್ಯೆಯನ್ನು ಹೊಂದಿರುತ್ತದೆ. RNN ಲೇಯರ್ embedding ಟೆನ್ಸರ್ಗಳ ಸರಣಿಯನ್ನು ತೆಗೆದು, ಎರಡು output ಗಳನ್ನು ಉತ್ಪಾದಿಸುತ್ತದೆ: \n",
|
|
"* $x$ ಪ್ರತಿ ಹಂತದಲ್ಲಿ RNN ಸೆಲ್ output ಗಳ ಸರಣಿ \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",
|
|
"\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 ತರಬೇತಿಗೊಳಿಸಲು, ನಾವು ಮೊದಲ 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": [
|
|
"ಈಗ ತರಬೇತಿ ಮಾಡೋಣ:\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",
|
|
"\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<!-- CO-OP TRANSLATOR DISCLAIMER START -->\n**ಅಸ್ವೀಕರಣ**: \nಈ ದಸ್ತಾವೇಜು AI ಅನುವಾದ ಸೇವೆ [Co-op Translator](https://github.com/Azure/co-op-translator) ಬಳಸಿ ಅನುವಾದಿಸಲಾಗಿದೆ. ನಾವು ನಿಖರತೆಯಿಗಾಗಿ ಪ್ರಯತ್ನಿಸುತ್ತಿದ್ದರೂ, ಸ್ವಯಂಚಾಲಿತ ಅನುವಾದಗಳಲ್ಲಿ ತಪ್ಪುಗಳು ಅಥವಾ ಅಸತ್ಯತೆಗಳು ಇರಬಹುದು ಎಂದು ದಯವಿಟ್ಟು ಗಮನಿಸಿ. ಮೂಲ ಭಾಷೆಯಲ್ಲಿರುವ ಮೂಲ ದಸ್ತಾವೇಜನ್ನು ಅಧಿಕೃತ ಮೂಲವಾಗಿ ಪರಿಗಣಿಸಬೇಕು. ಮಹತ್ವದ ಮಾಹಿತಿಗಾಗಿ, ವೃತ್ತಿಪರ ಮಾನವ ಅನುವಾದವನ್ನು ಶಿಫಾರಸು ಮಾಡಲಾಗುತ್ತದೆ. ಈ ಅನುವಾದ ಬಳಕೆಯಿಂದ ಉಂಟಾಗುವ ಯಾವುದೇ ತಪ್ಪು ಅರ್ಥಮಾಡಿಕೊಳ್ಳುವಿಕೆ ಅಥವಾ ತಪ್ಪು ವಿವರಣೆಗಳಿಗೆ ನಾವು ಹೊಣೆಗಾರರಾಗುವುದಿಲ್ಲ.\n<!-- CO-OP TRANSLATOR DISCLAIMER END -->\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:03:34+00:00",
|
|
"source_file": "lessons/5-NLP/16-RNN/RNNPyTorch.ipynb",
|
|
"language_code": "kn"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 2
|
|
} |