460 lines
24 KiB
Plaintext
460 lines
24 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Rekurrens neurális hálózatok\n",
|
|
"\n",
|
|
"Az előző modulban a szövegek gazdag szemantikai reprezentációit tárgyaltuk. Az általunk használt architektúra a mondatokban szereplő szavak összesített jelentését ragadja meg, de nem veszi figyelembe a szavak **sorrendjét**, mivel az embeddingeket követő aggregációs művelet eltávolítja ezt az információt az eredeti szövegből. Mivel ezek a modellek nem képesek a szórendet reprezentálni, nem tudnak megoldani összetettebb vagy kétértelmű feladatokat, például szövegalkotást vagy kérdésmegértést.\n",
|
|
"\n",
|
|
"Ahhoz, hogy egy szövegszekvencia jelentését megragadjuk, egy **rekurrens neurális hálózatnak** (angolul recurrent neural network, RNN) nevezett neurális hálózati architektúrát fogunk használni. Az RNN használatakor a mondatot egyesével, tokenenként vezetjük át a hálózaton, amely minden lépésben előállít egy **állapotot**, amit aztán a következő tokennel együtt ismét átadunk a hálózatnak.\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"A tokenekből álló bemeneti szekvencia $X_0,\\dots,X_n$ alapján az RNN egy neurális hálózati blokkokból álló szekvenciát hoz létre, és ezt a szekvenciát végig, visszaterjesztéses tanulással (backpropagation) tanítja. Minden hálózati blokk egy $(X_i,S_i)$ párt kap bemenetként, és eredményként előállítja $S_{i+1}$-et. A végső állapot $S_n$ vagy a kimenet $Y_n$ egy lineáris osztályozóba kerül, amely előállítja az eredményt. Az összes hálózati blokk ugyanazokat a súlyokat osztja meg, és egyetlen visszaterjesztési lépés során tanulják meg azokat.\n",
|
|
"\n",
|
|
"> A fenti ábra a rekurrens neurális hálózatot kibontott formában (bal oldalon), illetve kompaktabb, rekurrens reprezentációban (jobb oldalon) mutatja. Fontos megérteni, hogy az összes RNN cella ugyanazokat a **megosztható súlyokat** használja.\n",
|
|
"\n",
|
|
"Mivel az állapotvektorok $S_0,\\dots,S_n$ végighaladnak a hálózaton, az RNN képes megtanulni a szavak közötti szekvenciális függőségeket. Például, ha a *nem* szó megjelenik valahol a szekvenciában, a hálózat megtanulhatja bizonyos elemek tagadását az állapotvektoron belül.\n",
|
|
"\n",
|
|
"Egy RNN cellán belül két súlymátrix található: $W_H$ és $W_I$, valamint egy bias $b$. Minden RNN lépésben, adott $X_i$ bemenet és $S_i$ bemeneti állapot esetén a kimeneti állapotot az alábbi képlet alapján számítjuk ki: $S_{i+1} = f(W_H\\times S_i + W_I\\times X_i+b)$, ahol $f$ egy aktivációs függvény (gyakran $\\tanh$).\n",
|
|
"\n",
|
|
"> Olyan problémák esetén, mint például a szövegalkotás (amit a következő egységben tárgyalunk) vagy a gépi fordítás, minden RNN lépésnél szeretnénk valamilyen kimeneti értéket is kapni. Ebben az esetben van egy másik mátrix is, $W_O$, és a kimenetet az alábbi módon számítjuk ki: $Y_i=f(W_O\\times S_i+b_O)$.\n",
|
|
"\n",
|
|
"Nézzük meg, hogyan segíthetnek a rekurrens neurális hálózatok a hírek adatbázisának osztályozásában.\n",
|
|
"\n",
|
|
"> A sandbox környezetben az alábbi cellát kell futtatnunk, hogy biztosítsuk a szükséges könyvtár telepítését és az adatok előzetes letöltését. Ha helyben futtatja a kódot, ezt a cellát kihagyhatja.\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": [
|
|
"Amikor nagy modelleket tanítunk, a GPU memória kiosztása problémát jelenthet. Emellett szükség lehet különböző minibatch méretek kipróbálására, hogy az adatok beleférjenek a GPU memóriába, ugyanakkor a tanítás elég gyors legyen. Ha ezt a kódot saját GPU gépen futtatod, kísérletezhetsz a minibatch méret beállításával a tanítás felgyorsítása érdekében.\n",
|
|
"\n",
|
|
"> **Note**: Bizonyos NVidia driver verziókról ismert, hogy nem szabadítják fel a memóriát a modell tanítása után. Ebben a jegyzetfüzetben több példát futtatunk, ami bizonyos beállításoknál memória kimerüléséhez vezethet, különösen akkor, ha saját kísérleteket végzel ugyanazon jegyzetfüzet részeként. Ha furcsa hibákba ütközöl a modell tanításának megkezdésekor, érdemes lehet újraindítani a jegyzetfüzet kernelét.\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": [
|
|
"## Egyszerű RNN osztályozó\n",
|
|
"\n",
|
|
"Egy egyszerű RNN esetében minden rekurrens egység egy egyszerű lineáris hálózat, amely bemeneti vektort és állapotvektort fogad, majd egy új állapotvektort hoz létre. Keras-ban ezt a `SimpleRNN` réteg képviseli.\n",
|
|
"\n",
|
|
"Bár közvetlenül átadhatunk egy-egy forró kódolású tokeneket az RNN rétegnek, ez nem jó ötlet a magas dimenzionalitásuk miatt. Ezért egy beágyazási réteget fogunk használni, hogy csökkentsük a szavak vektorainak dimenzionalitását, majd egy RNN réteget, végül pedig egy `Dense` osztályozót.\n",
|
|
"\n",
|
|
"> **Megjegyzés**: Olyan esetekben, amikor a dimenzionalitás nem olyan magas, például karakter szintű tokenizálásnál, érdemes lehet az egy-egy forró kódolású tokeneket közvetlenül az RNN cellába továbbítani.\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": [
|
|
"> **Megjegyzés:** Itt egy nem tanított beágyazási réteget használunk az egyszerűség kedvéért, de jobb eredmények érdekében használhatunk egy előre betanított beágyazási réteget a Word2Vec segítségével, ahogy az előző egységben leírtuk. Jó gyakorlat lenne, ha ezt a kódot úgy alakítanád át, hogy előre betanított beágyazásokat használjon.\n",
|
|
"\n",
|
|
"Most tanítsuk be az RNN-t. Az RNN-ek általában elég nehezen taníthatók, mivel amikor az RNN cellákat kibontjuk a szekvencia hosszának megfelelően, a visszaterjesztésben részt vevő rétegek száma jelentősen megnő. Ezért kisebb tanulási rátát kell választanunk, és nagyobb adathalmazon kell tanítani a hálózatot, hogy jó eredményeket érjünk el. Ez elég sok időt vehet igénybe, ezért előnyös GPU-t használni.\n",
|
|
"\n",
|
|
"A folyamat felgyorsítása érdekében csak a hírek címein fogjuk tanítani az RNN modellt, kihagyva a leírást. Kipróbálhatod a leírással való tanítást, és megnézheted, hogy sikerül-e a modellt betanítani.\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": [
|
|
"> **Megjegyzés**: a pontosság valószínűleg alacsonyabb lesz, mivel csak hírcímeken képezzük a modelt.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## A változó hosszúságú szekvenciák újragondolása\n",
|
|
"\n",
|
|
"Ne feledd, hogy a `TextVectorization` réteg automatikusan kitölti a változó hosszúságú szekvenciákat egy minibatch-ben kitöltő tokenekkel. Kiderült, hogy ezek a tokenek is részt vesznek a tanításban, és megnehezíthetik a modell konvergenciáját.\n",
|
|
"\n",
|
|
"Számos megközelítést alkalmazhatunk a kitöltés mennyiségének minimalizálására. Az egyik lehetőség, hogy az adathalmazt szekvenciahossz szerint rendezzük, és az összes szekvenciát méret szerint csoportosítjuk. Ez a `tf.data.experimental.bucket_by_sequence_length` függvénnyel valósítható meg (lásd [dokumentáció](https://www.tensorflow.org/api_docs/python/tf/data/experimental/bucket_by_sequence_length)).\n",
|
|
"\n",
|
|
"Egy másik megközelítés a **maszkolás** használata. A Keras-ban bizonyos rétegek támogatják az olyan kiegészítő bemeneteket, amelyek megmutatják, hogy mely tokeneket kell figyelembe venni a tanítás során. A maszkolás beépítéséhez a modellünkbe vagy egy külön `Masking` réteget kell hozzáadnunk ([dokumentáció](https://keras.io/api/layers/core_layers/masking/)), vagy meg kell adnunk a `mask_zero=True` paramétert az `Embedding` rétegünkben.\n",
|
|
"\n",
|
|
"> **Note**: Ez a tanítás körülbelül 5 percet vesz igénybe, hogy az egész adathalmazon egy epochot lefuttasson. Nyugodtan megszakíthatod a tanítást bármikor, ha elfogy a türelmed. Amit még tehetsz, hogy korlátozod a tanításhoz használt adatok mennyiségét, például `.take(...)` kifejezést hozzáadva a `ds_train` és `ds_test` adathalmazok után.\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": [
|
|
"Most, hogy maszkolást használunk, az egész címek és leírások adatállományán tudjuk tanítani a modellt.\n",
|
|
"\n",
|
|
"> **Megjegyzés**: Észrevetted, hogy eddig a hírek címein tanított vektorizálót használtuk, nem pedig a cikk teljes szövegét? Ez esetleg azt eredményezheti, hogy néhány token figyelmen kívül marad, ezért jobb lenne újra tanítani a vektorizálót. Azonban ennek valószínűleg csak nagyon csekély hatása lenne, így az egyszerűség kedvéért maradunk a korábban betanított vektorizálónál.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## LSTM: Hosszú távú memória\n",
|
|
"\n",
|
|
"Az RNN-ek egyik fő problémája a **eltűnő gradiens**. Az RNN-ek elég hosszúak lehetnek, és nehézséget okozhat a gradiens visszavezetése egészen a hálózat első rétegéig a visszaterjesztés során. Amikor ez megtörténik, a hálózat nem tud tanulni távoli tokenek közötti kapcsolatokat. Ennek a problémának az elkerülésére egy megoldás az **explicit állapotkezelés** bevezetése **kapuk** használatával. A két leggyakoribb architektúra, amely kapukat alkalmaz, a **hosszú távú memória** (LSTM) és a **kapuzott reléegység** (GRU). Itt az LSTM-eket fogjuk tárgyalni.\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"Az LSTM hálózat felépítése hasonló az RNN-hez, de két állapotot adunk át rétegről rétegre: az aktuális állapotot $c$, és a rejtett vektort $h$. Minden egységnél a rejtett vektor $h_{t-1}$ kombinálódik a bemenettel $x_t$, és együtt irányítják, hogy mi történik az állapottal $c_t$ és a kimenettel $h_{t}$ **kapukon** keresztül. Minden kapunak szigmoid aktivációja van (kimenet tartománya $[0,1]$), amely bitmaszkként értelmezhető, amikor megszorozzuk az állapotvektorral. Az LSTM-eknek a következő kapui vannak (a fenti képen balról jobbra):\n",
|
|
"* **felejtő kapu**, amely meghatározza, hogy az $c_{t-1}$ vektor mely komponenseit kell elfelejtenünk, és melyeket kell továbbadnunk.\n",
|
|
"* **bemeneti kapu**, amely meghatározza, hogy mennyi információt kell a bemeneti vektorból és az előző rejtett vektorból beépíteni az állapotvektorba.\n",
|
|
"* **kimeneti kapu**, amely az új állapotvektort veszi, és eldönti, hogy annak mely komponenseit használjuk az új rejtett vektor $h_t$ előállításához.\n",
|
|
"\n",
|
|
"Az állapot $c$ komponensei zászlókként értelmezhetők, amelyeket be- és kikapcsolhatunk. Például, amikor a *Alice* nevet találjuk a sorozatban, feltételezzük, hogy egy nőre utal, és felállítjuk az állapotban azt a zászlót, amely azt jelzi, hogy nőnemű főnév van a mondatban. Amikor később találkozunk az *és Tom* szavakkal, felállítjuk azt a zászlót, amely azt jelzi, hogy többes számú főnév van. Így az állapot manipulálásával nyomon követhetjük a mondat nyelvtani tulajdonságait.\n",
|
|
"\n",
|
|
"> **Note**: Itt egy remek forrás az LSTM-ek belső működésének megértéséhez: [Understanding LSTM Networks](https://colah.github.io/posts/2015-08-Understanding-LSTMs/) Christopher Olah-tól.\n",
|
|
"\n",
|
|
"Bár az LSTM cella belső szerkezete bonyolultnak tűnhet, a Keras elrejti ezt a megvalósítást az `LSTM` rétegben, így az egyetlen dolog, amit az előző példában tennünk kell, az az, hogy lecseréljük a visszatérő réteget:\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": [
|
|
"## Kétirányú és többrétegű RNN-ek\n",
|
|
"\n",
|
|
"Az eddigi példáinkban a rekurrens hálózatok a szekvencia elejétől a végéig működtek. Ez számunkra természetesnek tűnik, mivel ugyanazt az irányt követi, ahogy olvasunk vagy beszédet hallgatunk. Azonban olyan helyzetekben, ahol az input szekvenciához véletlenszerű hozzáférésre van szükség, logikusabb, ha a rekurrens számítást mindkét irányban futtatjuk. Azokat az RNN-eket, amelyek lehetővé teszik a számítást mindkét irányban, **kétirányú** RNN-eknek nevezzük, és létrehozhatók úgy, hogy a rekurrens réteget egy speciális `Bidirectional` réteggel csomagoljuk be.\n",
|
|
"\n",
|
|
"> **Note**: A `Bidirectional` réteg két példányt készít a benne lévő rétegből, és az egyik példány `go_backwards` tulajdonságát `True` értékre állítja, így az a szekvencia mentén ellentétes irányba halad.\n",
|
|
"\n",
|
|
"A rekurrens hálózatok, legyenek egyirányúak vagy kétirányúak, mintákat ragadnak meg egy szekvenciában, és ezeket állapotvektorokba tárolják vagy kimenetként adják vissza. Akárcsak a konvolúciós hálózatok esetében, építhetünk egy másik rekurrens réteget az első után, hogy magasabb szintű mintákat ragadjunk meg, amelyeket az első réteg által kinyert alacsonyabb szintű mintákból építünk fel. Ez vezet el minket a **többrétegű RNN** fogalmához, amely két vagy több rekurrens hálózatból áll, ahol az előző réteg kimenete bemenetként kerül a következő réteghez.\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"*Kép [ebből a nagyszerű bejegyzésből](https://towardsdatascience.com/from-a-lstm-cell-to-a-multilayer-lstm-network-with-pytorch-2899eb5696f3) Fernando López tollából.*\n",
|
|
"\n",
|
|
"A Keras megkönnyíti ezeknek a hálózatoknak a létrehozását, mivel csak több rekurrens réteget kell hozzáadni a modellhez. Az utolsó rétegen kívül minden rétegnél meg kell adni a `return_sequences=True` paramétert, mivel szükségünk van arra, hogy a réteg az összes köztes állapotot visszaadja, ne csak a rekurrens számítás végső állapotát.\n",
|
|
"\n",
|
|
"Építsünk egy két rétegű kétirányú LSTM-et a klasszifikációs problémánkhoz.\n",
|
|
"\n",
|
|
"> **Note** Ez a kód ismét meglehetősen hosszú időt vesz igénybe, de ez adja a legmagasabb pontosságot, amit eddig láttunk. Talán érdemes várni és megnézni az eredményt.\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": [
|
|
"## RNN-ek más feladatokra\n",
|
|
"\n",
|
|
"Eddig az RNN-eket szövegszekvenciák osztályozására használtuk. Azonban sok más feladatot is képesek kezelni, például szöveg generálását és gépi fordítást — ezeket a feladatokat a következő egységben fogjuk megvizsgálni.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"\n---\n\n**Felelősségkizárás**: \nEz a dokumentum az [Co-op Translator](https://github.com/Azure/co-op-translator) AI fordítási szolgáltatás segítségével készült. Bár törekszünk a pontosságra, kérjük, vegye figyelembe, hogy az automatikus fordítások hibákat vagy pontatlanságokat tartalmazhatnak. Az eredeti dokumentum az eredeti nyelvén tekintendő hiteles forrásnak. Kritikus információk esetén javasolt a professzionális, emberi fordítás igénybevétele. Nem vállalunk felelősséget a fordítás használatából eredő félreértésekért vagy téves értelmezésekért.\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-29T16:15:11+00:00",
|
|
"source_file": "lessons/5-NLP/16-RNN/RNNTF.ipynb",
|
|
"language_code": "hu"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 4
|
|
} |