AI-For-Beginners/translations/pa/lessons/5-NLP/16-RNN/RNNPyTorch.ipynb

479 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 ਦੀਆਂ ਮੁੱਖ ਸਮੱਸਿਆਵਾਂ ਵਿੱਚੋਂ ਇੱਕ ਹੈ **ਵੈਨਿਸ਼ਿੰਗ ਗ੍ਰੇਡੀਐਂਟਸ** ਦੀ ਸਮੱਸਿਆ। ਕਿਉਂਕਿ RNNs ਨੂੰ ਇੱਕ ਬੈਕ-ਪ੍ਰੋਪਾਗੇਸ਼ਨ ਪਾਸ ਵਿੱਚ ਐਂਡ-ਟੂ-ਐਂਡ ਟ੍ਰੇਨ ਕੀਤਾ ਜਾਂਦਾ ਹੈ, ਇਸ ਲਈ ਇਹ ਨੈੱਟਵਰਕ ਦੇ ਪਹਿਲੇ ਲੇਅਰਾਂ ਤੱਕ ਗਲਤੀ ਨੂੰ ਪ੍ਰੋਪਾਗੇਟ ਕਰਨ ਵਿੱਚ ਮੁਸ਼ਕਲ ਮਹਿਸੂਸ ਕਰਦਾ ਹੈ, ਅਤੇ ਇਸ ਤਰ੍ਹਾਂ ਨੈੱਟਵਰਕ ਦੂਰਲੇ ਟੋਕਨਜ਼ ਦੇ ਵਿਚਕਾਰ ਸੰਬੰਧ ਸਿੱਖ ਨਹੀਂ ਸਕਦਾ। ਇਸ ਸਮੱਸਿਆ ਤੋਂ ਬਚਣ ਦੇ ਤਰੀਕਿਆਂ ਵਿੱਚੋਂ ਇੱਕ ਹੈ **ਗੇਟਸ** ਦੀ ਵਰਤੋਂ ਕਰਕੇ **ਸਪਸ਼ਟ ਸਟੇਟ ਮੈਨੇਜਮੈਂਟ** ਲਿਆਉਣਾ। ਇਸ ਕਿਸਮ ਦੀਆਂ ਦੋ ਸਭ ਤੋਂ ਪ੍ਰਸਿੱਧ ਆਰਕੀਟੈਕਚਰ ਹਨ: **ਲਾਂਗ ਸ਼ੌਰਟ ਟਰਮ ਮੈਮੋਰੀ** (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$ ਦੇ ਹਿੱਸਿਆਂ ਨੂੰ ਕੁਝ ਝੰਡਿਆਂ ਵਜੋਂ ਸੋਚਿਆ ਜਾ ਸਕਦਾ ਹੈ ਜੋ ਚਾਲੂ ਜਾਂ ਬੰਦ ਕੀਤੇ ਜਾ ਸਕਦੇ ਹਨ। ਉਦਾਹਰਣ ਵਜੋਂ, ਜਦੋਂ ਅਸੀਂ ਕ੍ਰਮ ਵਿੱਚ ਇੱਕ ਨਾਮ *ਐਲਿਸ* ਨੂੰ ਮਿਲਦੇ ਹਾਂ, ਤਾਂ ਅਸੀਂ ਮੰਨ ਸਕਦੇ ਹਾਂ ਕਿ ਇਹ ਇੱਕ ਮਹਿਲਾ ਪਾਤਰ ਨੂੰ ਦਰਸਾਉਂਦਾ ਹੈ, ਅਤੇ ਸਟੇਟ ਵਿੱਚ ਝੰਡਾ ਚੁੱਕ ਸਕਦੇ ਹਾਂ ਕਿ ਸਜਾ ਵਿੱਚ ਇੱਕ ਮਹਿਲਾ ਸੰਗਿਆ ਹੈ। ਜਦੋਂ ਅੱਗੇ ਅਸੀਂ ਵਾਕਾਂਸ਼ *ਐਂਡ ਟੌਮ* ਨੂੰ ਮਿਲਦੇ ਹਾਂ, ਤਾਂ ਅਸੀਂ ਝੰਡਾ ਚੁੱਕ ਸਕਦੇ ਹਾਂ ਕਿ ਸਜਾ ਵਿੱਚ ਬਹੁਵਚਨ ਸੰਗਿਆ ਹੈ। ਇਸ ਤਰ੍ਹਾਂ ਸਟੇਟ ਨੂੰ ਮੈਨੇਜ ਕਰਕੇ ਅਸੀਂ ਵਾਕ ਦੇ ਵੱਖ-ਵੱਖ ਭਾਗਾਂ ਦੇ ਵਿਆਕਰਣ ਗੁਣਾਂ ਦਾ ਪਤਾ ਲਗਾ ਸਕਦੇ ਹਾਂ।\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",
"![ਬਹੁ-ਪੱਧਰੀ ਲੰਬੇ-ਛੋਟੇ-ਅਵਧੀ-ਮੈਮੋਰੀ RNN ਦਿਖਾਉਣ ਵਾਲੀ ਚਿੱਤਰ](../../../../../translated_images/pa/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": [
"## ਹੋਰ ਕੰਮਾਂ ਲਈ 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:40:26+00:00",
"source_file": "lessons/5-NLP/16-RNN/RNNPyTorch.ipynb",
"language_code": "pa"
}
},
"nbformat": 4,
"nbformat_minor": 2
}