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

479 lines
24 KiB
Plaintext

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Rekurentné neurónové siete\n",
"\n",
"V predchádzajúcom module sme používali bohaté sémantické reprezentácie textu a jednoduchý lineárny klasifikátor nad embeddingmi. Táto architektúra zachytáva agregovaný význam slov vo vete, ale nezohľadňuje **poradie** slov, pretože operácia agregácie nad embeddingmi túto informáciu z pôvodného textu odstránila. Keďže tieto modely nedokážu modelovať poradie slov, nemôžu riešiť zložitejšie alebo nejednoznačné úlohy, ako je generovanie textu alebo odpovedanie na otázky.\n",
"\n",
"Na zachytenie významu textovej sekvencie potrebujeme použiť inú architektúru neurónovej siete, ktorá sa nazýva **rekurentná neurónová sieť** (RNN). V RNN prechádzame vetou cez sieť po jednom symbole a sieť produkuje určitý **stav**, ktorý následne odovzdáme s ďalším symbolom opäť do siete.\n",
"\n",
"Daná vstupná sekvencia tokenov $X_0,\\dots,X_n$, RNN vytvára sekvenciu blokov neurónovej siete a trénuje túto sekvenciu end-to-end pomocou spätného šírenia. Každý blok siete prijíma dvojicu $(X_i,S_i)$ ako vstup a produkuje $S_{i+1}$ ako výsledok. Konečný stav $S_n$ alebo výstup $X_n$ sa odovzdáva do lineárneho klasifikátora na produkciu výsledku. Všetky bloky siete zdieľajú rovnaké váhy a trénujú sa end-to-end jedným priechodom spätného šírenia.\n",
"\n",
"Keďže stavové vektory $S_0,\\dots,S_n$ prechádzajú sieťou, táto dokáže učiť sekvenčné závislosti medzi slovami. Napríklad, keď sa niekde v sekvencii objaví slovo *not*, sieť sa môže naučiť negovať určité prvky v stavovom vektore, čo vedie k negácii.\n",
"\n",
"> Keďže váhy všetkých blokov RNN na obrázku sú zdieľané, ten istý obrázok môže byť reprezentovaný ako jeden blok (vpravo) s rekurentnou spätnou väzbou, ktorá odovzdáva výstupný stav siete späť na vstup.\n",
"\n",
"Pozrime sa, ako nám rekurentné neurónové siete môžu pomôcť klasifikovať našu dátovú sadu správ.\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": [
"## Jednoduchý RNN klasifikátor\n",
"\n",
"V prípade jednoduchého RNN je každá rekurentná jednotka jednoduchá lineárna sieť, ktorá prijíma zreťazený vstupný vektor a stavový vektor a vytvára nový stavový vektor. PyTorch reprezentuje túto jednotku pomocou triedy `RNNCell` a sieť takýchto buniek ako vrstvu `RNN`.\n",
"\n",
"Na definovanie RNN klasifikátora najskôr použijeme vrstvu embedding na zníženie dimenzionality vstupnej slovnej zásoby a potom na ňu pridáme vrstvu 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": [
"> **Poznámka:** V tomto prípade používame nevytrénovanú embedding vrstvu pre jednoduchosť, ale pre ešte lepšie výsledky môžeme použiť predtrénovanú embedding vrstvu s Word2Vec alebo GloVe embeddingami, ako bolo popísané v predchádzajúcej jednotke. Pre lepšie pochopenie by ste mohli tento kód prispôsobiť tak, aby pracoval s predtrénovanými embeddingami.\n",
"\n",
"V našom prípade použijeme dávkovač s vypĺňaním (padded data loader), takže každá dávka bude obsahovať určitý počet sekvencií doplnených na rovnakú dĺžku. RNN vrstva prijme sekvenciu embedding tensorov a vygeneruje dva výstupy:\n",
"* $x$ je sekvencia výstupov RNN buniek na každom kroku\n",
"* $h$ je konečný skrytý stav pre posledný prvok sekvencie\n",
"\n",
"Následne aplikujeme plne prepojený lineárny klasifikátor, aby sme získali počet tried.\n",
"\n",
"> **Poznámka:** RNN sú pomerne náročné na trénovanie, pretože keď sa RNN bunky rozvinú pozdĺž dĺžky sekvencie, výsledný počet vrstiev zapojených do spätného šírenia je pomerne veľký. Preto je potrebné zvoliť malú rýchlosť učenia a trénovať sieť na väčšej množine údajov, aby sa dosiahli dobré výsledky. Môže to trvať pomerne dlho, takže použitie GPU je preferované.\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",
"Jedným z hlavných problémov klasických RNN je takzvaný problém **miznúcich gradientov**. Keďže RNN sa trénujú end-to-end v jednom priechode spätného šírenia, majú problém prenášať chybu do prvých vrstiev siete, a preto sa sieť nedokáže naučiť vzťahy medzi vzdialenými tokenmi. Jedným zo spôsobov, ako sa tomuto problému vyhnúť, je zavedenie **explicitného riadenia stavu** pomocou takzvaných **brán**. Existujú dve najznámejšie architektúry tohto druhu: **Long Short Term Memory** (LSTM) a **Gated Relay Unit** (GRU).\n",
"\n",
"![Obrázok znázorňujúci príklad bunky Long Short Term Memory](../../../../../lessons/5-NLP/16-RNN/images/long-short-term-memory-cell.svg)\n",
"\n",
"LSTM sieť je organizovaná podobne ako RNN, ale existujú dva stavy, ktoré sa prenášajú z vrstvy do vrstvy: aktuálny stav $c$ a skrytý vektor $h$. V každej jednotke sa skrytý vektor $h_i$ spája so vstupom $x_i$, a spolu riadia, čo sa deje so stavom $c$ prostredníctvom **brán**. Každá brána je neurónová sieť so sigmoidovou aktiváciou (výstup v rozsahu $[0,1]$), ktorú si môžeme predstaviť ako bitovú masku, keď sa vynásobí stavovým vektorom. Existujú nasledujúce brány (zľava doprava na obrázku vyššie):\n",
"* **forget gate** (brána zabúdania) berie skrytý vektor a určuje, ktoré komponenty vektora $c$ potrebujeme zabudnúť a ktoré preniesť ďalej.\n",
"* **input gate** (vstupná brána) berie niektoré informácie zo vstupu a skrytého vektora a vkladá ich do stavu.\n",
"* **output gate** (výstupná brána) transformuje stav cez nejakú lineárnu vrstvu s $\\tanh$ aktiváciou, potom vyberá niektoré z jeho komponentov pomocou skrytého vektora $h_i$, aby vytvorila nový stav $c_{i+1}$.\n",
"\n",
"Komponenty stavu $c$ si môžeme predstaviť ako nejaké príznaky, ktoré môžeme zapínať a vypínať. Napríklad, keď v sekvencii narazíme na meno *Alice*, môžeme predpokladať, že ide o ženskú postavu, a nastaviť príznak v stave, že vety obsahujú ženské podstatné meno. Keď ďalej narazíme na frázu *and Tom*, nastavíme príznak, že máme množné číslo. Takto môžeme manipuláciou so stavom údajne sledovať gramatické vlastnosti častí vety.\n",
"\n",
"> **Note**: Skvelým zdrojom na pochopenie vnútorných štruktúr LSTM je tento výborný článok [Understanding LSTM Networks](https://colah.github.io/posts/2015-08-Understanding-LSTMs/) od Christophera Olaha.\n",
"\n",
"Aj keď vnútorná štruktúra LSTM bunky môže vyzerať zložito, PyTorch túto implementáciu skrýva vo vnútri triedy `LSTMCell` a poskytuje objekt `LSTM` na reprezentáciu celej LSTM vrstvy. Implementácia LSTM klasifikátora bude preto veľmi podobná jednoduchému RNN, ktorý sme videli vyššie:\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": [
"## Zbalené sekvencie\n",
"\n",
"V našom príklade sme museli doplniť všetky sekvencie v minibatch nulovými vektormi. Hoci to vedie k určitému plytvaniu pamäťou, pri RNN je ešte kritickejšie, že sa vytvárajú ďalšie RNN bunky pre doplnené položky vstupu, ktoré sa zúčastňujú tréningu, no nenesú žiadne dôležité vstupné informácie. Bolo by oveľa lepšie trénovať RNN iba na skutočnú veľkosť sekvencie.\n",
"\n",
"Na tento účel je v PyTorch zavedený špeciálny formát ukladania doplnených sekvencií. Predpokladajme, že máme vstupný doplnený minibatch, ktorý vyzerá takto:\n",
"```\n",
"[[1,2,3,4,5],\n",
" [6,7,8,0,0],\n",
" [9,0,0,0,0]]\n",
"```\n",
"Tu 0 predstavuje doplnené hodnoty a skutočný vektor dĺžok vstupných sekvencií je `[5,3,1]`.\n",
"\n",
"Aby sme mohli efektívne trénovať RNN s doplnenou sekvenciou, chceme začať tréning prvej skupiny RNN buniek s veľkým minibatch (`[1,6,9]`), ale potom ukončiť spracovanie tretej sekvencie a pokračovať v tréningu s kratšími minibatchmi (`[2,7]`, `[3,8]`) a tak ďalej. Takto je zbalená sekvencia reprezentovaná ako jeden vektor - v našom prípade `[1,6,9,2,7,3,8,4,5]`, a vektor dĺžok (`[5,3,1]`), z ktorého môžeme ľahko rekonštruovať pôvodný doplnený minibatch.\n",
"\n",
"Na vytvorenie zbalenej sekvencie môžeme použiť funkciu `torch.nn.utils.rnn.pack_padded_sequence`. Všetky rekurentné vrstvy, vrátane RNN, LSTM a GRU, podporujú zbalené sekvencie ako vstup a produkujú zbalený výstup, ktorý je možné dekódovať pomocou `torch.nn.utils.rnn.pad_packed_sequence`.\n",
"\n",
"Aby sme mohli vytvoriť zbalenú sekvenciu, musíme do siete odovzdať vektor dĺžok, a preto potrebujeme inú funkciu na prípravu minibatchov:\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": [
"Skutočná sieť by bola veľmi podobná `LSTMClassifier` uvedenému vyššie, ale `forward` prechod dostane ako vstup nielen vypočítanú dávku (padded minibatch), ale aj vektor dĺžok sekvencií. Po výpočte embeddingu vytvoríme zabalenú sekvenciu (packed sequence), prejdeme ju cez LSTM vrstvu a potom výsledok rozbalíme späť.\n",
"\n",
"> **Note**: V skutočnosti nepoužívame rozbalený výsledok `x`, pretože vo výpočtoch, ktoré nasledujú, používame výstup zo skrytých vrstiev. Preto môžeme rozbaľovanie z tohto kódu úplne odstrániť. Dôvod, prečo ho tu uvádzame, je, aby ste mohli tento kód ľahko upraviť, ak by ste potrebovali použiť výstup siete v ďalších výpočtoch.\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": [
"> **Poznámka:** Môžete si všimnúť parameter `use_pack_sequence`, ktorý odovzdávame do tréningovej funkcie. Momentálne funkcia `pack_padded_sequence` vyžaduje, aby bol tensor dĺžky sekvencie na zariadení CPU, a preto tréningová funkcia musí zabrániť presunu dát o dĺžke sekvencie na GPU počas tréningu. Môžete sa pozrieť na implementáciu funkcie `train_emb` v súbore [`torchnlp.py`](../../../../../lessons/5-NLP/16-RNN/torchnlp.py).\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Obojsmerné a viacvrstvové RNN\n",
"\n",
"V našich príkladoch všetky rekurentné siete fungovali jedným smerom, od začiatku sekvencie až po jej koniec. Vyzerá to prirodzene, pretože to pripomína spôsob, akým čítame a počúvame reč. Avšak, keďže v mnohých praktických prípadoch máme náhodný prístup k vstupnej sekvencii, môže dávať zmysel vykonávať rekurentné výpočty v oboch smeroch. Takéto siete sa nazývajú **obojsmerné** RNN a môžu byť vytvorené pridaním parametra `bidirectional=True` do konštruktora RNN/LSTM/GRU.\n",
"\n",
"Pri práci s obojsmernou sieťou budeme potrebovať dva vektory skrytého stavu, jeden pre každý smer. PyTorch kóduje tieto vektory ako jeden vektor dvojnásobnej veľkosti, čo je veľmi praktické, pretože výsledný skrytý stav zvyčajne odovzdávate do plne prepojenej lineárnej vrstvy, a stačí len zohľadniť toto zvýšenie veľkosti pri vytváraní vrstvy.\n",
"\n",
"Rekurentná sieť, či už jednosmerná alebo obojsmerná, zachytáva určité vzory v rámci sekvencie a môže ich uložiť do vektora stavu alebo odovzdať do výstupu. Rovnako ako pri konvolučných sieťach, môžeme na prvú vrstvu postaviť ďalšiu rekurentnú vrstvu, aby sme zachytili vzory na vyššej úrovni, ktoré sú vytvorené z nízkoúrovňových vzorov extrahovaných prvou vrstvou. To nás privádza k pojmu **viacvrstvová RNN**, ktorá pozostáva z dvoch alebo viacerých rekurentných sietí, kde výstup predchádzajúcej vrstvy je odovzdaný ako vstup do nasledujúcej vrstvy.\n",
"\n",
"![Obrázok zobrazujúci viacvrstvovú LSTM-RNN](../../../../../translated_images/sk/multi-layer-lstm.dd975e29bb2a59fe.webp)\n",
"\n",
"*Obrázok z [tohto skvelého článku](https://towardsdatascience.com/from-a-lstm-cell-to-a-multilayer-lstm-network-with-pytorch-2899eb5696f3) od Fernanda Lópeza*\n",
"\n",
"PyTorch uľahčuje konštrukciu takýchto sietí, pretože stačí pridať parameter `num_layers` do konštruktora RNN/LSTM/GRU, aby sa automaticky vytvorilo niekoľko vrstiev rekurencie. To tiež znamená, že veľkosť vektora skrytého stavu sa úmerne zvýši, a budete musieť toto zvýšenie zohľadniť pri práci s výstupom rekurentných vrstiev.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## RNN pre iné úlohy\n",
"\n",
"V tejto jednotke sme videli, že RNN môžu byť použité na klasifikáciu sekvencií, ale v skutočnosti dokážu zvládnuť oveľa viac úloh, ako je generovanie textu, strojový preklad a ďalšie. Týmto úlohám sa budeme venovať v nasledujúcej jednotke.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Upozornenie**: \nTento dokument bol preložený pomocou služby AI prekladu [Co-op Translator](https://github.com/Azure/co-op-translator). Aj keď sa snažíme o presnosť, prosím, berte na vedomie, že automatizované preklady môžu obsahovať chyby alebo nepresnosti. Pôvodný dokument v jeho rodnom jazyku by mal byť považovaný za autoritatívny zdroj. Pre kritické informácie sa odporúča profesionálny ľudský preklad. Nie sme zodpovední za akékoľvek nedorozumenia alebo nesprávne interpretácie vyplývajúce z použitia tohto prekladu.\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-30T00:58:35+00:00",
"source_file": "lessons/5-NLP/16-RNN/RNNPyTorch.ipynb",
"language_code": "sk"
}
},
"nbformat": 4,
"nbformat_minor": 2
}