460 lines
23 KiB
Plaintext
460 lines
23 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Rekurentné neurónové siete\n",
|
|
"\n",
|
|
"V predchádzajúcom module sme sa venovali bohatým sémantickým reprezentáciám textu. Architektúra, ktorú sme používali, zachytáva agregovaný význam slov vo vete, ale nezohľadňuje **poradie** slov, pretože operácia agregácie, ktorá nasleduje po vkladaní, túto informáciu z pôvodného textu odstráni. Keďže tieto modely nedokážu reprezentovať 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 použijeme architektúru neurónovej siete nazývanú **rekurentná neurónová sieť** (RNN). Pri použití RNN prechádzame vetou cez sieť po jednom tokene, pričom sieť produkuje určitý **stav**, ktorý následne odovzdáme s ďalším tokenom späť do siete.\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"Pri danej vstupnej sekvencii 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 ako vstup dvojicu $(X_i,S_i)$ a produkuje výsledok $S_{i+1}$. Konečný stav $S_n$ alebo výstup $Y_n$ sa odovzdáva do lineárneho klasifikátora na produkciu výsledku. Všetky bloky siete zdieľajú rovnaké váhy a sú trénované end-to-end jedným priechodom spätného šírenia.\n",
|
|
"\n",
|
|
"> Obrázok vyššie znázorňuje rekurentnú neurónovú sieť v rozvinutej forme (vľavo) a v kompaktnejšej rekurentnej reprezentácii (vpravo). Je dôležité si uvedomiť, že všetky RNN bunky majú rovnaké **zdieľateľné váhy**.\n",
|
|
"\n",
|
|
"Keďže stavové vektory $S_0,\\dots,S_n$ prechádzajú sieťou, RNN dokáže učiť sekvenčné závislosti medzi slovami. Napríklad, keď sa v sekvencii objaví slovo *not*, môže sa naučiť negovať určité prvky v stavovom vektore.\n",
|
|
"\n",
|
|
"Vo vnútri každá RNN bunka obsahuje dve matice váh: $W_H$ a $W_I$, a bias $b$. Pri každom kroku RNN, pri danom vstupe $X_i$ a vstupnom stave $S_i$, sa výstupný stav vypočíta ako $S_{i+1} = f(W_H\\times S_i + W_I\\times X_i+b)$, kde $f$ je aktivačná funkcia (často $\\tanh$).\n",
|
|
"\n",
|
|
"> Pri problémoch, ako je generovanie textu (ktoré pokryjeme v ďalšej jednotke) alebo strojový preklad, chceme získať aj nejakú výstupnú hodnotu pri každom kroku RNN. V tomto prípade existuje aj ďalšia matica $W_O$, a výstup sa vypočíta ako $Y_i=f(W_O\\times S_i+b_O)$.\n",
|
|
"\n",
|
|
"Pozrime sa, ako nám rekurentné neurónové siete môžu pomôcť klasifikovať našu dátovú sadu správ.\n",
|
|
"\n",
|
|
"> Pre sandboxové prostredie musíme spustiť nasledujúcu bunku, aby sme sa uistili, že požadovaná knižnica je nainštalovaná a dáta sú prednačítané. Ak pracujete lokálne, môžete túto bunku preskočiť.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 1,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"import sys\n",
|
|
"!{sys.executable} -m pip install --quiet tensorflow_datasets==4.4.0\n",
|
|
"!cd ~ && wget -q -O - https://mslearntensorflowlp.blob.core.windows.net/data/tfds-ag-news.tgz | tar xz"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 2,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"import tensorflow as tf\n",
|
|
"from tensorflow import keras\n",
|
|
"import tensorflow_datasets as tfds\n",
|
|
"import numpy as np\n",
|
|
"\n",
|
|
"# We are going to be training pretty large models. In order not to face errors, we need\n",
|
|
"# to set tensorflow option to grow GPU memory allocation when required\n",
|
|
"physical_devices = tf.config.list_physical_devices('GPU') \n",
|
|
"if len(physical_devices)>0:\n",
|
|
" tf.config.experimental.set_memory_growth(physical_devices[0], True)\n",
|
|
"\n",
|
|
"ds_train, ds_test = tfds.load('ag_news_subset').values()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {
|
|
"nteract": {
|
|
"transient": {
|
|
"deleting": false
|
|
}
|
|
}
|
|
},
|
|
"source": [
|
|
"Pri trénovaní veľkých modelov môže byť problémom alokácia pamäte GPU. Tiež môže byť potrebné experimentovať s rôznymi veľkosťami minibatchov, aby sa dáta zmestili do pamäte GPU a zároveň bolo trénovanie dostatočne rýchle. Ak tento kód spúšťate na vlastnom GPU zariadení, môžete experimentovať s úpravou veľkosti minibatchov na zrýchlenie trénovania.\n",
|
|
"\n",
|
|
"> **Note**: Je známe, že určité verzie ovládačov NVidia neoslobodzujú pamäť po trénovaní modelu. V tomto notebooku spúšťame niekoľko príkladov, čo môže v určitých nastaveniach spôsobiť vyčerpanie pamäte, najmä ak vykonávate vlastné experimenty v rámci toho istého notebooku. Ak narazíte na zvláštne chyby pri spúšťaní trénovania modelu, môže byť vhodné reštartovať jadro notebooku.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 3,
|
|
"metadata": {
|
|
"collapsed": true,
|
|
"jupyter": {
|
|
"outputs_hidden": false,
|
|
"source_hidden": false
|
|
},
|
|
"nteract": {
|
|
"transient": {
|
|
"deleting": false
|
|
}
|
|
}
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"batch_size = 16\n",
|
|
"embed_size = 64"
|
|
]
|
|
},
|
|
{
|
|
"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 vstupný vektor a stavový vektor a vytvára nový stavový vektor. V Keras je to možné reprezentovať pomocou vrstvy `SimpleRNN`.\n",
|
|
"\n",
|
|
"Aj keď môžeme priamo posielať tokeny zakódované metódou one-hot do vrstvy RNN, nie je to dobrý nápad kvôli ich vysokej dimenzionalite. Preto použijeme vrstvu embedding na zníženie dimenzionality slovných vektorov, nasledovanú vrstvou RNN a nakoniec klasifikátorom `Dense`.\n",
|
|
"\n",
|
|
"> **Note**: V prípadoch, keď dimenzionalita nie je taká vysoká, napríklad pri použití tokenizácie na úrovni znakov, môže byť rozumné posielať tokeny zakódované metódou one-hot priamo do bunky RNN.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 4,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Model: \"sequential\"\n",
|
|
"_________________________________________________________________\n",
|
|
"Layer (type) Output Shape Param # \n",
|
|
"=================================================================\n",
|
|
"text_vectorization (TextVect (None, None) 0 \n",
|
|
"_________________________________________________________________\n",
|
|
"embedding (Embedding) (None, None, 64) 1280000 \n",
|
|
"_________________________________________________________________\n",
|
|
"simple_rnn (SimpleRNN) (None, 16) 1296 \n",
|
|
"_________________________________________________________________\n",
|
|
"dense (Dense) (None, 4) 68 \n",
|
|
"=================================================================\n",
|
|
"Total params: 1,281,364\n",
|
|
"Trainable params: 1,281,364\n",
|
|
"Non-trainable params: 0\n",
|
|
"_________________________________________________________________\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"vocab_size = 20000\n",
|
|
"\n",
|
|
"vectorizer = keras.layers.experimental.preprocessing.TextVectorization(\n",
|
|
" max_tokens=vocab_size,\n",
|
|
" input_shape=(1,))\n",
|
|
"\n",
|
|
"model = keras.models.Sequential([\n",
|
|
" vectorizer,\n",
|
|
" keras.layers.Embedding(vocab_size, embed_size),\n",
|
|
" keras.layers.SimpleRNN(16),\n",
|
|
" keras.layers.Dense(4,activation='softmax')\n",
|
|
"])\n",
|
|
"\n",
|
|
"model.summary()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"> **Poznámka:** Tu používame netrénovanú vrstvu embedding pre jednoduchosť, ale pre lepšie výsledky môžeme použiť predtrénovanú embedding vrstvu pomocou Word2Vec, ako bolo popísané v predchádzajúcej jednotke. Bolo by dobrým cvičením upraviť tento kód tak, aby fungoval s predtrénovanými embeddingami.\n",
|
|
"\n",
|
|
"Teraz poďme trénovať našu RNN. RNN sú vo všeobecnosti dosť náročné na trénovanie, pretože keď sa bunky RNN rozvinú pozdĺž dĺžky sekvencie, výsledný počet vrstiev zapojených do spätného šírenia je pomerne veľký. Preto musíme zvoliť menšiu rýchlosť učenia a trénovať sieť na väčšej množine dát, aby sme dosiahli dobré výsledky. To môže trvať pomerne dlho, takže je preferované používanie GPU.\n",
|
|
"\n",
|
|
"Aby sme proces urýchlili, budeme model RNN trénovať iba na nadpisoch správ, pričom popis vynecháme. Môžete skúsiť trénovať aj s popisom a zistiť, či sa vám podarí model natrénovať.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 5,
|
|
"metadata": {
|
|
"scrolled": true
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Training vectorizer\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"def extract_title(x):\n",
|
|
" return x['title']\n",
|
|
"\n",
|
|
"def tupelize_title(x):\n",
|
|
" return (extract_title(x),x['label'])\n",
|
|
"\n",
|
|
"print('Training vectorizer')\n",
|
|
"vectorizer.adapt(ds_train.take(2000).map(extract_title))"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 6,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"7500/7500 [==============================] - 82s 11ms/step - loss: 0.6629 - acc: 0.7623 - val_loss: 0.5559 - val_acc: 0.7995\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\n"
|
|
]
|
|
},
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"<tensorflow.python.keras.callbacks.History at 0x7f3e0030d350>"
|
|
]
|
|
},
|
|
"execution_count": 6,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'], optimizer='adam')\n",
|
|
"model.fit(ds_train.map(tupelize_title).batch(batch_size),validation_data=ds_test.map(tupelize_title).batch(batch_size))"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {
|
|
"nteract": {
|
|
"transient": {
|
|
"deleting": false
|
|
}
|
|
}
|
|
},
|
|
"source": [
|
|
"> **Poznámka** že presnosť je pravdepodobne nižšia, pretože trénujeme iba na nadpisoch správ.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Opätovné preskúmanie sekvencií premenných\n",
|
|
"\n",
|
|
"Pamätajte, že vrstva `TextVectorization` automaticky doplní sekvencie s premennou dĺžkou v minibatchi pomocou výplňových tokenov. Ukazuje sa, že tieto tokeny sa tiež zapájajú do tréningu a môžu komplikovať konvergenciu modelu.\n",
|
|
"\n",
|
|
"Existuje niekoľko prístupov, ktoré môžeme použiť na minimalizáciu množstva výplne. Jedným z nich je preusporiadanie datasetu podľa dĺžky sekvencie a zoskupenie všetkých sekvencií podľa veľkosti. To sa dá dosiahnuť pomocou funkcie `tf.data.experimental.bucket_by_sequence_length` (pozrite si [dokumentáciu](https://www.tensorflow.org/api_docs/python/tf/data/experimental/bucket_by_sequence_length)).\n",
|
|
"\n",
|
|
"Ďalším prístupom je použitie **maskovania**. V Keras niektoré vrstvy podporujú dodatočný vstup, ktorý ukazuje, ktoré tokeny by sa mali brať do úvahy počas tréningu. Na začlenenie maskovania do nášho modelu môžeme buď pridať samostatnú vrstvu `Masking` ([dokumentácia](https://keras.io/api/layers/core_layers/masking/)), alebo môžeme špecifikovať parameter `mask_zero=True` vo vrstve `Embedding`.\n",
|
|
"\n",
|
|
"> **Note**: Tento tréning bude trvať približne 5 minút na dokončenie jednej epochy na celom datasete. Ak stratíte trpezlivosť, môžete tréning kedykoľvek prerušiť. Taktiež môžete obmedziť množstvo dát použitých na tréning pridaním klauzuly `.take(...)` po datasetoch `ds_train` a `ds_test`.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 7,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"7500/7500 [==============================] - 371s 49ms/step - loss: 0.5401 - acc: 0.8079 - val_loss: 0.3780 - val_acc: 0.8822\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\n"
|
|
]
|
|
},
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"<tensorflow.python.keras.callbacks.History at 0x7f3dec118850>"
|
|
]
|
|
},
|
|
"execution_count": 7,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"def extract_text(x):\n",
|
|
" return x['title']+' '+x['description']\n",
|
|
"\n",
|
|
"def tupelize(x):\n",
|
|
" return (extract_text(x),x['label'])\n",
|
|
"\n",
|
|
"model = keras.models.Sequential([\n",
|
|
" vectorizer,\n",
|
|
" keras.layers.Embedding(vocab_size,embed_size,mask_zero=True),\n",
|
|
" keras.layers.SimpleRNN(16),\n",
|
|
" keras.layers.Dense(4,activation='softmax')\n",
|
|
"])\n",
|
|
"\n",
|
|
"model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'], optimizer='adam')\n",
|
|
"model.fit(ds_train.map(tupelize).batch(batch_size),validation_data=ds_test.map(tupelize).batch(batch_size))"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Teraz, keď používame maskovanie, môžeme model trénovať na celom súbore údajov obsahujúcom názvy a popisy.\n",
|
|
"\n",
|
|
"> **Note**: Všimli ste si, že sme používali vektorizér trénovaný na názvoch správ, a nie na celom tele článku? Potenciálne to môže spôsobiť, že niektoré tokeny budú ignorované, takže je lepšie vektorizér pretrénovať. Avšak, môže to mať len veľmi malý efekt, takže pre jednoduchosť zostaneme pri predchádzajúcom predtrénovanom vektorizéri.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## LSTM: Dlhodobá krátkodobá pamäť\n",
|
|
"\n",
|
|
"Jedným z hlavných problémov RNN je **miznutie gradientov**. RNN môžu byť pomerne dlhé a môžu mať problém s propagáciou gradientov späť až k prvej vrstve siete počas spätného šírenia. Keď sa to stane, 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 **brán**. Dve najbežnejšie architektúry, ktoré zavádzajú brány, sú **dlhodobá krátkodobá pamäť** (LSTM) a **jednotka s bránou** (GRU). Tu sa budeme venovať LSTM.\n",
|
|
"\n",
|
|
"\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_{t-1}$ kombinuje so vstupom $x_t$, a spolu riadia, čo sa stane so stavom $c_t$ a výstupom $h_{t}$ prostredníctvom **brán**. Každá brána má sigmoidovú aktiváciu (výstup v rozsahu $[0,1]$), ktorú si môžeme predstaviť ako bitovú masku pri násobení stavového vektora. LSTM majú nasledujúce brány (zľava doprava na obrázku vyššie):\n",
|
|
"* **brána zabudnutia**, ktorá určuje, ktoré komponenty vektora $c_{t-1}$ je potrebné zabudnúť a ktoré preniesť ďalej.\n",
|
|
"* **vstupná brána**, ktorá určuje, koľko informácií zo vstupného vektora a predchádzajúceho skrytého vektora by malo byť začlenených do stavového vektora.\n",
|
|
"* **výstupná brána**, ktorá vezme nový stavový vektor a rozhodne, ktoré jeho komponenty budú použité na vytvorenie nového skrytého vektora $h_t$.\n",
|
|
"\n",
|
|
"Komponenty stavu $c$ si môžeme predstaviť ako príznaky, ktoré môžu byť zapnuté alebo vypnuté. Napríklad, keď v sekvencii narazíme na meno *Alice*, predpokladáme, že ide o ženu, a aktivujeme príznak v stave, ktorý hovorí, že máme ženské podstatné meno vo vete. Keď ďalej narazíme na slová *a Tom*, aktivujeme príznak, ktorý hovorí, že máme množné číslo podstatného mena. Takto môžeme manipuláciou stavu sledovať gramatické vlastnosti vety.\n",
|
|
"\n",
|
|
"> **Note**: Tu je skvelý zdroj na pochopenie vnútorných štruktúr LSTM: [Understanding LSTM Networks](https://colah.github.io/posts/2015-08-Understanding-LSTMs/) od Christophera Olaha.\n",
|
|
"\n",
|
|
"Aj keď vnútorná štruktúra bunky LSTM môže vyzerať zložito, Keras skrýva túto implementáciu vo vrstve `LSTM`, takže jediná vec, ktorú musíme urobiť v príklade vyššie, je nahradiť rekurentnú vrstvu:\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 8,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"15000/15000 [==============================] - 188s 13ms/step - loss: 0.5692 - acc: 0.7916 - val_loss: 0.3441 - val_acc: 0.8870\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\n"
|
|
]
|
|
},
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"<tensorflow.python.keras.callbacks.History at 0x7f3d6af5c350>"
|
|
]
|
|
},
|
|
"execution_count": 8,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"model = keras.models.Sequential([\n",
|
|
" vectorizer,\n",
|
|
" keras.layers.Embedding(vocab_size, embed_size),\n",
|
|
" keras.layers.LSTM(8),\n",
|
|
" keras.layers.Dense(4,activation='softmax')\n",
|
|
"])\n",
|
|
"\n",
|
|
"model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'], optimizer='adam')\n",
|
|
"model.fit(ds_train.map(tupelize).batch(8),validation_data=ds_test.map(tupelize).batch(8))"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": []
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Obojsmerné a viacvrstvové RNN\n",
|
|
"\n",
|
|
"V našich doterajších príkladoch rekurentné siete spracúvajú sekvenciu od začiatku až po jej koniec. To nám pripadá prirodzené, pretože to zodpovedá smeru, v ktorom čítame alebo počúvame reč. Avšak v situáciách, ktoré vyžadujú náhodný prístup k vstupnej sekvencii, dáva väčší zmysel vykonávať rekurentné výpočty v oboch smeroch. RNN, ktoré umožňujú výpočty v oboch smeroch, sa nazývajú **obojsmerné** RNN a môžu byť vytvorené obalením rekurentnej vrstvy špeciálnou vrstvou `Bidirectional`.\n",
|
|
"\n",
|
|
"> **Note**: Vrstva `Bidirectional` vytvorí dve kópie vrstvy, ktorú obsahuje, a nastaví vlastnosť `go_backwards` jednej z týchto kópií na hodnotu `True`, čím zabezpečí, že sa bude pohybovať opačným smerom pozdĺž sekvencie.\n",
|
|
"\n",
|
|
"Rekurentné siete, či už jednosmerné alebo obojsmerné, zachytávajú vzory v rámci sekvencie a ukladajú ich do stavových vektorov alebo ich vracajú ako výstup. Podobne ako pri konvolučných sieťach, môžeme za prvú rekurentnú vrstvu pridať ďalšiu, aby sme zachytili vzory vyššej úrovne, ktoré sú vytvorené z nižších úrovní 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 slúži ako vstup pre nasledujúcu vrstvu.\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"*Obrázok pochádza 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",
|
|
"Keras uľahčuje konštrukciu týchto sietí, pretože stačí pridať viac rekurentných vrstiev do modelu. Pre všetky vrstvy okrem poslednej je potrebné špecifikovať parameter `return_sequences=True`, pretože potrebujeme, aby vrstva vrátila všetky medzistavy, a nie len konečný stav rekurentného výpočtu.\n",
|
|
"\n",
|
|
"Poďme vytvoriť dvojvrstvovú obojsmernú LSTM pre náš klasifikačný problém.\n",
|
|
"\n",
|
|
"> **Note** tento kód opäť trvá pomerne dlho, kým sa dokončí, ale poskytuje najvyššiu presnosť, akú sme doteraz videli. Možno sa teda oplatí počkať a pozrieť si výsledok.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 9,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"5044/7500 [===================>..........] - ETA: 2:33 - loss: 0.3709 - acc: 0.8706\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r5045/7500 [===================>..........] - ETA: 2:33 - loss: 0.3709 - acc: 0.8706"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"model = keras.models.Sequential([\n",
|
|
" vectorizer,\n",
|
|
" keras.layers.Embedding(vocab_size, 128, mask_zero=True),\n",
|
|
" keras.layers.Bidirectional(keras.layers.LSTM(64,return_sequences=True)),\n",
|
|
" keras.layers.Bidirectional(keras.layers.LSTM(64)), \n",
|
|
" keras.layers.Dense(4,activation='softmax')\n",
|
|
"])\n",
|
|
"\n",
|
|
"model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'], optimizer='adam')\n",
|
|
"model.fit(ds_train.map(tupelize).batch(batch_size),\n",
|
|
" validation_data=ds_test.map(tupelize).batch(batch_size))"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## RNNs pre iné úlohy\n",
|
|
"\n",
|
|
"Doteraz sme sa zameriavali na používanie RNN na klasifikáciu sekvencií textu. Avšak dokážu zvládnuť oveľa viac úloh, ako napríklad generovanie textu a strojový preklad — týmito úlohami sa budeme zaoberať 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). Hoci sa snažíme o presnosť, prosím, berte na vedomie, že automatizované preklady môžu obsahovať chyby alebo nepresnosti. Pôvodný dokument v jeho pôvodnom 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": {
|
|
"kernel_info": {
|
|
"name": "conda-env-py37_tensorflow-py"
|
|
},
|
|
"kernelspec": {
|
|
"display_name": "py37_tensorflow",
|
|
"language": "python",
|
|
"name": "conda-env-py37_tensorflow-py"
|
|
},
|
|
"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.7.9"
|
|
},
|
|
"nteract": {
|
|
"version": "nteract-front-end@1.0.0"
|
|
},
|
|
"coopTranslator": {
|
|
"original_hash": "81351e61f619b432ff51010a4f993194",
|
|
"translation_date": "2025-08-30T00:54:55+00:00",
|
|
"source_file": "lessons/5-NLP/16-RNN/RNNTF.ipynb",
|
|
"language_code": "sk"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 4
|
|
} |