479 lines
25 KiB
Plaintext
479 lines
25 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Recurrent neural networks\n",
|
|
"\n",
|
|
"Sa nakaraang module, gumamit tayo ng mas mayamang semantic na representasyon ng teksto, at isang simpleng linear classifier sa ibabaw ng embeddings. Ang ginagawa ng arkitekturang ito ay kunin ang pinagsama-samang kahulugan ng mga salita sa isang pangungusap, ngunit hindi nito isinasaalang-alang ang **pagkakasunod-sunod** ng mga salita, dahil ang operasyon ng pagsasama-sama sa ibabaw ng embeddings ay tinanggal ang impormasyong ito mula sa orihinal na teksto. Dahil hindi kayang i-modelo ng mga modelong ito ang pagkakasunod-sunod ng mga salita, hindi nila masosolusyunan ang mas kumplikado o malalabong gawain tulad ng pagbuo ng teksto o pagsagot sa mga tanong.\n",
|
|
"\n",
|
|
"Upang makuha ang kahulugan ng pagkakasunod-sunod ng teksto, kailangan nating gumamit ng ibang arkitektura ng neural network, na tinatawag na **recurrent neural network**, o RNN. Sa RNN, ipinapasa natin ang ating pangungusap sa network nang paisa-isang simbolo, at ang network ay gumagawa ng isang **estado**, na pagkatapos ay ipinapasa natin muli sa network kasama ang susunod na simbolo.\n",
|
|
"\n",
|
|
"Given ang input sequence ng mga token $X_0,\\dots,X_n$, ang RNN ay lumilikha ng isang sequence ng mga neural network blocks, at sinasanay ang sequence na ito end-to-end gamit ang back propagation. Ang bawat network block ay tumatanggap ng pares $(X_i,S_i)$ bilang input, at gumagawa ng $S_{i+1}$ bilang resulta. Ang final state $S_n$ o output $X_n$ ay ipinapasa sa isang linear classifier upang makuha ang resulta. Ang lahat ng network blocks ay may parehong weights, at sinasanay end-to-end gamit ang isang back propagation pass.\n",
|
|
"\n",
|
|
"Dahil ang mga state vectors $S_0,\\dots,S_n$ ay ipinapasa sa network, kaya nitong matutunan ang mga sequential dependencies sa pagitan ng mga salita. Halimbawa, kapag ang salitang *not* ay lumitaw sa isang bahagi ng sequence, maaari nitong matutunan na i-negate ang ilang elemento sa loob ng state vector, na nagreresulta sa negation.\n",
|
|
"\n",
|
|
"> Dahil ang weights ng lahat ng RNN blocks sa larawan ay shared, ang parehong larawan ay maaaring i-representa bilang isang block (sa kanan) na may recurrent feedback loop, na ipinapasa ang output state ng network pabalik sa input.\n",
|
|
"\n",
|
|
"Tingnan natin kung paano makakatulong ang recurrent neural networks sa pag-classify ng ating news dataset.\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": [
|
|
"## Simpleng RNN na tagapag-uri\n",
|
|
"\n",
|
|
"Sa kaso ng simpleng RNN, ang bawat recurrent unit ay isang simpleng linear na network, na tumatanggap ng pinagsamang input vector at state vector, at gumagawa ng bagong state vector. Kinakatawan ng PyTorch ang unit na ito gamit ang klase na `RNNCell`, at ang mga network ng ganitong mga cell - bilang `RNN` layer.\n",
|
|
"\n",
|
|
"Para magtakda ng isang RNN na tagapag-uri, una nating gagamitin ang embedding layer upang mabawasan ang dimensionality ng input na bokabularyo, at pagkatapos ay maglalagay ng RNN layer sa ibabaw nito:\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": [
|
|
"> **Tandaan:** Gumagamit tayo ng untrained embedding layer dito para sa kasimplehan, ngunit para sa mas magagandang resulta, maaari tayong gumamit ng pre-trained embedding layer gamit ang Word2Vec o GloVe embeddings, tulad ng ipinaliwanag sa nakaraang unit. Para mas maintindihan, maaaring i-adapt ang code na ito upang gumana gamit ang pre-trained embeddings.\n",
|
|
"\n",
|
|
"Sa ating kaso, gagamit tayo ng padded data loader, kaya ang bawat batch ay magkakaroon ng bilang ng mga padded sequence na may parehong haba. Ang RNN layer ay tatanggap ng sequence ng embedding tensors, at magbibigay ng dalawang output:\n",
|
|
"* $x$ ay isang sequence ng mga output ng RNN cell sa bawat hakbang\n",
|
|
"* $h$ ay ang huling hidden state para sa pinakahuling elemento ng sequence\n",
|
|
"\n",
|
|
"Pagkatapos, mag-a-apply tayo ng fully-connected linear classifier upang makuha ang bilang ng klase.\n",
|
|
"\n",
|
|
"> **Tandaan:** Ang mga RNN ay medyo mahirap i-train, dahil kapag ang mga RNN cell ay na-unroll sa haba ng sequence, ang bilang ng mga layer na kasangkot sa back propagation ay nagiging napakarami. Dahil dito, kailangan nating pumili ng maliit na learning rate, at i-train ang network sa mas malaking dataset upang makakuha ng magagandang resulta. Maaari itong tumagal ng mahabang oras, kaya mas mainam na gumamit ng 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": [
|
|
"## Long Short Term Memory (LSTM)\n",
|
|
"\n",
|
|
"Isa sa mga pangunahing problema ng klasikong RNNs ay ang tinatawag na **vanishing gradients** na problema. Dahil ang RNNs ay sinasanay mula simula hanggang dulo sa isang back-propagation pass, nahihirapan itong ipasa ang error sa mga unang layer ng network, kaya't hindi natututo ang network ng mga relasyon sa pagitan ng mga malalayong token. Isa sa mga paraan upang maiwasan ang problemang ito ay ang pagpapakilala ng **explicit state management** gamit ang tinatawag na **gates**. Dalawa sa mga pinakakilalang arkitektura ng ganitong uri ay ang **Long Short Term Memory** (LSTM) at **Gated Relay Unit** (GRU).\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"Ang LSTM Network ay nakaayos sa paraang katulad ng RNN, ngunit may dalawang estado na ipinapasa mula layer patungo sa layer: ang aktwal na estado $c$, at ang nakatagong vector $h$. Sa bawat unit, ang nakatagong vector $h_i$ ay pinagsasama sa input $x_i`, at sila ang nagkokontrol kung ano ang mangyayari sa estado $c` sa pamamagitan ng **gates**. Ang bawat gate ay isang neural network na may sigmoid activation (output sa saklaw na $[0,1]$), na maaaring isipin bilang bitwise mask kapag pinarami sa state vector. Narito ang mga sumusunod na gates (mula kaliwa hanggang kanan sa larawan sa itaas):\n",
|
|
"* **forget gate** - kinukuha ang nakatagong vector at tinutukoy kung aling mga bahagi ng vector $c` ang kailangan nating kalimutan, at alin ang ipapasa.\n",
|
|
"* **input gate** - kumukuha ng ilang impormasyon mula sa input at nakatagong vector, at isinasama ito sa estado.\n",
|
|
"* **output gate** - binabago ang estado sa pamamagitan ng ilang linear layer na may $\\tanh$ activation, pagkatapos ay pinipili ang ilang bahagi nito gamit ang nakatagong vector $h_i` upang makabuo ng bagong estado $c_{i+1}`.\n",
|
|
"\n",
|
|
"Ang mga bahagi ng estado $c` ay maaaring isipin bilang mga flag na maaaring i-on o i-off. Halimbawa, kapag nakatagpo tayo ng pangalang *Alice* sa sequence, maaaring ipalagay na tumutukoy ito sa isang babaeng karakter, at itataas ang flag sa estado na mayroon tayong pangngalang pambabae sa pangungusap. Kapag nakatagpo pa tayo ng mga pariralang *and Tom*, itataas natin ang flag na mayroon tayong pangngalang maramihan. Kaya't sa pamamagitan ng pagmamanipula ng estado, maaari nating subaybayan ang mga gramatikal na katangian ng mga bahagi ng pangungusap.\n",
|
|
"\n",
|
|
"> **Note**: Isang mahusay na mapagkukunan para sa pag-unawa sa mga detalye ng LSTM ay ang artikulong [Understanding LSTM Networks](https://colah.github.io/posts/2015-08-Understanding-LSTMs/) ni Christopher Olah.\n",
|
|
"\n",
|
|
"Bagama't maaaring mukhang kumplikado ang panloob na istruktura ng LSTM cell, itinatago ng PyTorch ang implementasyong ito sa loob ng `LSTMCell` class, at nagbibigay ng `LSTM` object upang kumatawan sa buong LSTM layer. Kaya't ang implementasyon ng LSTM classifier ay magiging halos katulad ng simpleng RNN na nakita natin sa itaas:\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": [
|
|
"## Mga Naka-pack na Sequence\n",
|
|
"\n",
|
|
"Sa ating halimbawa, kinailangan nating magdagdag ng zero vectors sa lahat ng sequence sa minibatch. Bagamat nagdudulot ito ng kaunting pag-aaksaya ng memorya, mas kritikal sa RNNs na ang karagdagang RNN cells ay nalilikha para sa mga padded na input, na kasali sa training ngunit walang mahalagang impormasyon na dala. Mas mainam kung ang RNN ay ituturo lamang sa aktwal na haba ng sequence.\n",
|
|
"\n",
|
|
"Upang magawa ito, isang espesyal na format ng pag-iimbak ng padded sequence ang ipinakilala sa PyTorch. Halimbawa, mayroon tayong input na padded minibatch na ganito ang hitsura:\n",
|
|
"```\n",
|
|
"[[1,2,3,4,5],\n",
|
|
" [6,7,8,0,0],\n",
|
|
" [9,0,0,0,0]]\n",
|
|
"```\n",
|
|
"Dito, ang 0 ay kumakatawan sa mga padded na halaga, at ang aktwal na haba ng vector ng mga input sequence ay `[5,3,1]`.\n",
|
|
"\n",
|
|
"Upang epektibong mag-train ng RNN gamit ang padded sequence, nais nating simulan ang training ng unang grupo ng RNN cells gamit ang malaking minibatch (`[1,6,9]`), ngunit pagkatapos ay tapusin ang pagproseso ng ikatlong sequence, at ipagpatuloy ang training gamit ang mas maiikling minibatch (`[2,7]`, `[3,8]`), at iba pa. Kaya, ang packed sequence ay kinakatawan bilang isang vector - sa ating halimbawa `[1,6,9,2,7,3,8,4,5]`, at isang length vector (`[5,3,1]`), mula rito madali nating maibabalik ang orihinal na padded minibatch.\n",
|
|
"\n",
|
|
"Upang makagawa ng packed sequence, maaari nating gamitin ang function na `torch.nn.utils.rnn.pack_padded_sequence`. Lahat ng recurrent layers, kabilang ang RNN, LSTM, at GRU, ay sumusuporta sa packed sequences bilang input, at gumagawa ng packed output, na maaaring i-decode gamit ang `torch.nn.utils.rnn.pad_packed_sequence`.\n",
|
|
"\n",
|
|
"Upang makagawa ng packed sequence, kailangan nating ipasa ang length vector sa network, kaya kailangan natin ng ibang function para maghanda ng mga minibatch:\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": [
|
|
"Ang aktwal na network ay magiging halos katulad ng `LSTMClassifier` sa itaas, ngunit ang `forward` pass ay tatanggap ng parehong padded minibatch at ang vector ng haba ng mga sequence. Pagkatapos ng pag-compute ng embedding, iko-compute natin ang packed sequence, ipapasa ito sa LSTM layer, at pagkatapos ay i-unpack ang resulta pabalik.\n",
|
|
"\n",
|
|
"> **Note**: Sa totoo lang, hindi natin ginagamit ang na-unpack na resulta na `x`, dahil ginagamit natin ang output mula sa mga hidden layer sa mga susunod na kalkulasyon. Kaya, maaari nating alisin ang pag-unpack nang tuluyan mula sa code na ito. Ang dahilan kung bakit inilagay natin ito dito ay upang madali mong mabago ang code na ito, sakaling kailanganin mong gamitin ang output ng network sa mga karagdagang kalkulasyon.\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": [
|
|
"> **Tandaan:** Maaaring napansin mo ang parameter na `use_pack_sequence` na ipinapasa natin sa training function. Sa kasalukuyan, ang `pack_padded_sequence` function ay nangangailangan ng length sequence tensor na nasa CPU device, kaya't kailangang iwasan ng training function ang paglipat ng length sequence data sa GPU kapag nagte-training. Maaari mong tingnan ang implementasyon ng `train_emb` function sa file na [`torchnlp.py`](../../../../../lessons/5-NLP/16-RNN/torchnlp.py).\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Bidirectional at Multilayer RNNs\n",
|
|
"\n",
|
|
"Sa ating mga halimbawa, lahat ng recurrent networks ay gumagana sa isang direksyon lamang, mula simula ng isang sequence hanggang sa dulo. Mukhang natural ito, dahil kahalintulad ito ng paraan ng pagbabasa at pakikinig sa pagsasalita. Gayunpaman, dahil sa maraming praktikal na sitwasyon, may kakayahan tayong magkaroon ng random access sa input sequence, maaaring may kabuluhan na patakbuhin ang recurrent computation sa parehong direksyon. Ang ganitong mga network ay tinatawag na **bidirectional** RNNs, at maaari itong likhain sa pamamagitan ng pagpasa ng `bidirectional=True` na parameter sa RNN/LSTM/GRU constructor.\n",
|
|
"\n",
|
|
"Kapag gumagamit ng bidirectional network, kakailanganin natin ng dalawang hidden state vectors, isa para sa bawat direksyon. Ang PyTorch ay nag-eencode ng mga vectors na ito bilang isang vector na may doble ang laki, na napaka-kombinyente, dahil karaniwan mong ipapasa ang resulting hidden state sa fully-connected linear layer, at kailangan mo lamang isaalang-alang ang pagtaas ng laki nito kapag gumagawa ng layer.\n",
|
|
"\n",
|
|
"Ang recurrent network, one-directional man o bidirectional, ay nakakakuha ng ilang mga pattern sa loob ng isang sequence, at maaaring itago ang mga ito sa state vector o ipasa sa output. Tulad ng convolutional networks, maaari tayong magtayo ng isa pang recurrent layer sa ibabaw ng una upang makuha ang mas mataas na antas ng mga pattern, na binuo mula sa mga low-level pattern na nakuha ng unang layer. Ito ang nagdadala sa atin sa konsepto ng **multi-layer RNN**, na binubuo ng dalawa o higit pang recurrent networks, kung saan ang output ng nakaraang layer ay ipinapasa sa susunod na layer bilang input.\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"*Larawan mula sa [napakagandang post na ito](https://towardsdatascience.com/from-a-lstm-cell-to-a-multilayer-lstm-network-with-pytorch-2899eb5696f3) ni Fernando López*\n",
|
|
"\n",
|
|
"Ginagawang madali ng PyTorch ang pagbuo ng ganitong mga network, dahil kailangan mo lamang ipasa ang `num_layers` na parameter sa RNN/LSTM/GRU constructor upang awtomatikong makabuo ng ilang layers ng recurrence. Ito rin ay nangangahulugan na ang laki ng hidden/state vector ay tataas nang proporsyonal, at kailangan mo itong isaalang-alang kapag hinahandle ang output ng recurrent layers.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## RNNs para sa iba pang mga gawain\n",
|
|
"\n",
|
|
"Sa yunit na ito, nakita natin na maaaring gamitin ang RNNs para sa sequence classification, ngunit sa katunayan, kaya rin nilang hawakan ang mas maraming gawain tulad ng pagbuo ng teksto, pagsasalin ng wika, at iba pa. Tatalakayin natin ang mga gawaing ito sa susunod na yunit.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"\n---\n\n**Paunawa**: \nAng dokumentong ito ay isinalin gamit ang AI translation service na [Co-op Translator](https://github.com/Azure/co-op-translator). Bagama't sinisikap naming maging tumpak, pakitandaan na ang mga awtomatikong pagsasalin ay maaaring maglaman ng mga pagkakamali o hindi pagkakatugma. Ang orihinal na dokumento sa kanyang katutubong wika ang dapat ituring na opisyal na sanggunian. Para sa mahalagang impormasyon, inirerekomenda ang propesyonal na pagsasalin ng tao. Hindi kami mananagot sa anumang hindi pagkakaunawaan o maling interpretasyon na maaaring magmula sa paggamit ng pagsasaling ito.\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-28T04:28:01+00:00",
|
|
"source_file": "lessons/5-NLP/16-RNN/RNNPyTorch.ipynb",
|
|
"language_code": "tl"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 2
|
|
} |