460 lines
23 KiB
Plaintext
460 lines
23 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Toistuvat neuroverkot\n",
|
|
"\n",
|
|
"Edellisessä moduulissa käsittelimme tekstin monipuolisia semanttisia esityksiä. Käyttämämme arkkitehtuuri tallentaa lauseen sanojen yhdistetyn merkityksen, mutta se ei ota huomioon sanojen **järjestystä**, koska upotuksia seuraava yhdistämisoperaatio poistaa tämän tiedon alkuperäisestä tekstistä. Koska nämä mallit eivät pysty edustamaan sanojen järjestystä, ne eivät kykene ratkaisemaan monimutkaisempia tai monitulkintaisia tehtäviä, kuten tekstin generointia tai kysymysten vastaamista.\n",
|
|
"\n",
|
|
"Tekstijonon merkityksen tallentamiseksi käytämme neuroverkkoarkkitehtuuria nimeltä **toistuva neuroverkko** (recurrent neural network, RNN). RNN:ää käytettäessä syötämme lauseen verkon läpi yksi token kerrallaan, ja verkko tuottaa jonkin **tilan**, jonka syötämme verkkoon uudelleen seuraavan tokenin kanssa.\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"Kun syötteenä on tokenien jono $X_0,\\dots,X_n$, RNN luo neuroverkkolohkojen sarjan ja kouluttaa tämän sarjan päästä päähän takaisinlevityksen avulla. Jokainen verkkolohko ottaa syötteenä parin $(X_i,S_i)$ ja tuottaa tuloksena $S_{i+1}$. Lopullinen tila $S_n$ tai tulos $Y_n$ syötetään lineaariseen luokittelijaan tuloksen tuottamiseksi. Kaikilla verkkolohkoilla on samat painot, ja ne koulutetaan päästä päähän yhden takaisinlevityskierroksen avulla.\n",
|
|
"\n",
|
|
"> Yllä oleva kuva esittää toistuvan neuroverkon avattuna (vasemmalla) ja tiiviimmässä toistuvassa muodossa (oikealla). On tärkeää ymmärtää, että kaikilla RNN-solmuilla on samat **jaettavat painot**.\n",
|
|
"\n",
|
|
"Koska tilavektorit $S_0,\\dots,S_n$ kulkevat verkon läpi, RNN pystyy oppimaan sanojen välisiä järjestyksellisiä riippuvuuksia. Esimerkiksi, kun sana *ei* esiintyy jossain kohtaa jonoa, verkko voi oppia kumoamaan tiettyjä elementtejä tilavektorin sisällä.\n",
|
|
"\n",
|
|
"Jokaisen RNN-solmun sisällä on kaksi painomatriisia: $W_H$ ja $W_I$, sekä harha $b$. Jokaisessa RNN-askeleessa, kun syötteenä on $X_i$ ja syöttötila $S_i$, tulostustila lasketaan kaavalla $S_{i+1} = f(W_H\\times S_i + W_I\\times X_i+b)$, missä $f$ on aktivointifunktio (usein $\\tanh$).\n",
|
|
"\n",
|
|
"> Tehtävissä, kuten tekstin generointi (jota käsittelemme seuraavassa yksikössä) tai konekäännös, haluamme myös saada jonkin tulosarvon jokaisessa RNN-askeleessa. Tässä tapauksessa on olemassa toinen matriisi $W_O$, ja tulos lasketaan kaavalla $Y_i=f(W_O\\times S_i+b_O)$.\n",
|
|
"\n",
|
|
"Katsotaanpa, kuinka toistuvat neuroverkot voivat auttaa meitä luokittelemaan uutisaineistomme.\n",
|
|
"\n",
|
|
"> Hiekkalaatikkoympäristössä meidän täytyy suorittaa seuraava solu varmistaaksemme, että tarvittava kirjasto on asennettu ja data on esihakemistoitu. Jos suoritat koodin paikallisesti, voit ohittaa seuraavan solun.\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": [
|
|
"Kun koulutetaan suuria malleja, GPU-muistin käyttö voi muodostua ongelmaksi. Meidän voi myös olla tarpeen kokeilla erilaisia minibatch-kokoja, jotta data mahtuu GPU-muistiin ja koulutus on silti riittävän nopeaa. Jos ajat tätä koodia omalla GPU-koneellasi, voit kokeilla säätää minibatch-kokoa koulutuksen nopeuttamiseksi.\n",
|
|
"\n",
|
|
"> **Note**: Tietyt NVidian ajuriversiot eivät vapauta muistia mallin koulutuksen jälkeen. Tässä muistikirjassa ajetaan useita esimerkkejä, ja tämä voi johtaa muistin loppumiseen tietyissä kokoonpanoissa, erityisesti jos teet omia kokeiluja samassa muistikirjassa. Jos kohtaat outoja virheitä mallin koulutuksen alkaessa, kannattaa harkita muistikirjan ytimen uudelleenkäynnistämistä.\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": [
|
|
"## Yksinkertainen RNN-luokitin\n",
|
|
"\n",
|
|
"Yksinkertaisessa RNN:ssä jokainen toistuva yksikkö on yksinkertainen lineaarinen verkko, joka ottaa syötteenä vektorin ja tilavektorin ja tuottaa uuden tilavektorin. Kerasissa tämä voidaan toteuttaa `SimpleRNN`-kerroksella.\n",
|
|
"\n",
|
|
"Vaikka voimme syöttää yksi-kuuma-koodattuja (one-hot encoded) tokeneita suoraan RNN-kerrokseen, tämä ei ole hyvä idea niiden korkean ulottuvuuden vuoksi. Siksi käytämme upotuskerrosta (embedding layer) pienentämään sanavektoreiden ulottuvuutta, jonka jälkeen tulee RNN-kerros ja lopuksi `Dense`-luokitin.\n",
|
|
"\n",
|
|
"> **Note**: Tapauksissa, joissa ulottuvuus ei ole kovin suuri, esimerkiksi käytettäessä merkkitason tokenointia, voi olla järkevää syöttää yksi-kuuma-koodatut tokenit suoraan RNN-soluun.\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": [
|
|
"> **Huom:** Käytämme tässä yksinkertaisuuden vuoksi kouluttamatonta upotuskerrosta, mutta parempien tulosten saavuttamiseksi voimme käyttää valmiiksi koulutettua upotuskerrosta, kuten Word2Veciä, joka esiteltiin edellisessä yksikössä. Hyvä harjoitus olisi mukauttaa tämä koodi toimimaan valmiiksi koulutettujen upotusten kanssa.\n",
|
|
"\n",
|
|
"Nyt koulutetaan RNN. RNN:t ovat yleisesti ottaen melko vaikeita kouluttaa, koska kun RNN-solut avataan sekvenssin pituuden mukaan, takaisinlevityksessä mukana olevien kerrosten määrä kasvaa huomattavasti. Tämän vuoksi meidän täytyy valita pienempi oppimisnopeus ja kouluttaa verkkoa suuremmalla tietojoukolla hyvien tulosten saavuttamiseksi. Tämä voi kestää melko kauan, joten GPU:n käyttö on suositeltavaa.\n",
|
|
"\n",
|
|
"Nopeuttaaksemme prosessia koulutamme RNN-mallin vain uutisotsikoilla ja jätämme kuvauksen pois. Voit kokeilla kouluttaa myös kuvauksen kanssa ja katsoa, saatko mallin koulutettua.\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": [
|
|
"> **Huomaa** että tarkkuus on todennäköisesti alhaisempi tässä, koska koulutamme vain uutisotsikoilla.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Uudelleen tarkastelu: muuttujien sekvenssit\n",
|
|
"\n",
|
|
"Muista, että `TextVectorization`-kerros lisää automaattisesti täytemerkkejä vaihtelevan pituisille sekvensseille minibatchissa. Käy ilmi, että nämä täytemerkit osallistuvat myös mallin koulutukseen, mikä voi vaikeuttaa mallin konvergenssia.\n",
|
|
"\n",
|
|
"On olemassa useita lähestymistapoja, joilla voimme vähentää täytemerkkien määrää. Yksi tapa on järjestää datasetti sekvenssin pituuden mukaan ja ryhmitellä kaikki sekvenssit koon perusteella. Tämä voidaan tehdä käyttämällä `tf.data.experimental.bucket_by_sequence_length`-funktiota (katso [dokumentaatio](https://www.tensorflow.org/api_docs/python/tf/data/experimental/bucket_by_sequence_length)).\n",
|
|
"\n",
|
|
"Toinen lähestymistapa on käyttää **maskingia**. Kerasissa jotkut kerrokset tukevat lisäsyötettä, joka osoittaa, mitkä tokenit tulisi ottaa huomioon koulutuksessa. Maskingin sisällyttämiseksi malliin voimme joko lisätä erillisen `Masking`-kerroksen ([dokumentaatio](https://keras.io/api/layers/core_layers/masking/)) tai määrittää `mask_zero=True`-parametrin `Embedding`-kerroksessamme.\n",
|
|
"\n",
|
|
"> **Huomio**: Tämä koulutus kestää noin 5 minuuttia yhden epochin suorittamiseen koko datasetillä. Voit keskeyttää koulutuksen milloin tahansa, jos kärsivällisyytesi loppuu. Voit myös rajoittaa koulutuksessa käytettävän datan määrää lisäämällä `.take(...)`-lausekkeen `ds_train`- ja `ds_test`-datasetien jälkeen.\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": [
|
|
"Nyt kun käytämme maskausta, voimme kouluttaa mallin koko otsikoiden ja kuvausten datasetillä.\n",
|
|
"\n",
|
|
"> **Huomio**: Oletko huomannut, että olemme käyttäneet vektoroijaa, joka on koulutettu uutisotsikoilla, eikä koko artikkelin tekstillä? Tämä voi mahdollisesti johtaa siihen, että jotkut tokenit jäävät huomiotta, joten vektoroija olisi parempi kouluttaa uudelleen. Tämä saattaa kuitenkin vaikuttaa vain hyvin vähän, joten yksinkertaisuuden vuoksi pysymme aiemmin esikoulutetussa vektoroijassa.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## LSTM: Pitkäkestoinen muisti\n",
|
|
"\n",
|
|
"Yksi RNN-verkkojen suurimmista ongelmista on **häviävät gradientit**. RNN:t voivat olla melko pitkiä, ja niillä voi olla vaikeuksia välittää gradientteja takaisin verkon ensimmäiseen kerrokseen takaisinvirtausvaiheessa. Kun näin tapahtuu, verkko ei pysty oppimaan suhteita kaukaisten tokenien välillä. Yksi tapa välttää tämä ongelma on ottaa käyttöön **eksplisiittinen tilanhallinta** käyttämällä **portteja**. Kaksi yleisintä arkkitehtuuria, jotka käyttävät portteja, ovat **pitkäkestoinen muisti** (LSTM) ja **porttiohjattu relaysolu** (GRU). Tässä käsittelemme LSTM:ää.\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"LSTM-verkko on järjestetty samalla tavalla kuin RNN, mutta kerroksesta toiseen siirtyy kaksi tilaa: varsinainen tila $c$ ja piilovektori $h$. Jokaisessa yksikössä piilovektori $h_{t-1}$ yhdistetään syötteeseen $x_t$, ja yhdessä ne ohjaavat, mitä tilalle $c_t$ ja ulostulolle $h_{t}$ tapahtuu **porttien** kautta. Jokaisella portilla on sigmoid-aktivaatio (tulos alueella $[0,1]$), jota voidaan ajatella bittimaskina, kun se kerrotaan tilavektorilla. LSTM:ssä on seuraavat portit (kuvassa vasemmalta oikealle):\n",
|
|
"* **unohtamisportti**, joka määrittää, mitkä osat vektorista $c_{t-1}$ täytyy unohtaa ja mitkä siirtää eteenpäin.\n",
|
|
"* **syöttöportti**, joka määrittää, kuinka paljon tietoa syötevektorista ja edellisestä piilovektorista tulisi sisällyttää tilavektoriin.\n",
|
|
"* **ulostuloportti**, joka ottaa uuden tilavektorin ja päättää, mitkä sen osat käytetään uuden piilovektorin $h_t$ tuottamiseen.\n",
|
|
"\n",
|
|
"Tilan $c$ komponentteja voidaan ajatella lippuina, jotka voidaan kytkeä päälle ja pois päältä. Esimerkiksi, kun kohtaamme sekvenssissä nimen *Alice*, arvaamme, että se viittaa naiseen, ja nostamme tilassa lipun, joka kertoo, että lauseessa on naispuolinen substantiivi. Kun myöhemmin kohtaamme sanat *and Tom*, nostamme lipun, joka kertoo, että lauseessa on monikollinen substantiivi. Näin manipuloimalla tilaa voimme seurata lauseen kieliopillisia ominaisuuksia.\n",
|
|
"\n",
|
|
"> **Note**: Tässä on erinomainen resurssi LSTM:n sisäisen rakenteen ymmärtämiseen: [Understanding LSTM Networks](https://colah.github.io/posts/2015-08-Understanding-LSTMs/) kirjoittanut Christopher Olah.\n",
|
|
"\n",
|
|
"Vaikka LSTM-solun sisäinen rakenne saattaa näyttää monimutkaiselta, Keras piilottaa tämän toteutuksen `LSTM`-kerroksen sisälle, joten ainoa asia, joka meidän täytyy tehdä yllä olevassa esimerkissä, on korvata rekursiivinen kerros:\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": [
|
|
"## Kaksisuuntaiset ja monikerroksiset RNN:t\n",
|
|
"\n",
|
|
"Aiemmissa esimerkeissämme toistuvat verkot toimivat sekvenssin alusta loppuun. Tämä tuntuu luonnolliselta, koska se noudattaa samaa suuntaa, jossa luemme tai kuuntelemme puhetta. Kuitenkin tilanteissa, joissa tarvitaan satunnaista pääsyä syötteen sekvenssiin, on järkevämpää suorittaa toistuva laskenta molempiin suuntiin. RNN:t, jotka mahdollistavat laskennan molempiin suuntiin, kutsutaan **kaksisuuntaisiksi** RNN:iksi, ja ne voidaan luoda kietomalla toistuva kerros erityisellä `Bidirectional`-kerroksella.\n",
|
|
"\n",
|
|
"> **Note**: `Bidirectional`-kerros tekee kaksi kopiota sisällään olevasta kerroksesta ja asettaa yhden kopion `go_backwards`-ominaisuuden arvoksi `True`, jolloin se kulkee sekvenssin vastakkaiseen suuntaan.\n",
|
|
"\n",
|
|
"Toistuvat verkot, olivatpa ne yksisuuntaisia tai kaksisuuntaisia, tunnistavat sekvenssin sisäisiä kuvioita ja tallentavat ne tilavektoreihin tai palauttavat ne ulostulona. Kuten konvoluutiokerroksissa, voimme rakentaa toisen toistuvan kerroksen ensimmäisen jälkeen tunnistamaan korkeammantason kuvioita, jotka on muodostettu ensimmäisen kerroksen tunnistamista alemman tason kuvioista. Tämä johtaa käsitteeseen **monikerroksinen RNN**, joka koostuu kahdesta tai useammasta toistuvasta verkosta, joissa edellisen kerroksen ulostulo välitetään seuraavalle kerrokselle syötteenä.\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"*Kuva [tästä upeasta artikkelista](https://towardsdatascience.com/from-a-lstm-cell-to-a-multilayer-lstm-network-with-pytorch-2899eb5696f3) kirjoittanut Fernando López.*\n",
|
|
"\n",
|
|
"Keras tekee näiden verkkojen rakentamisesta helppoa, koska sinun tarvitsee vain lisätä enemmän toistuvia kerroksia malliin. Kaikille kerroksille paitsi viimeiselle, meidän täytyy määrittää `return_sequences=True`-parametri, koska tarvitsemme kerroksen palauttamaan kaikki välitilat, eikä vain toistuvan laskennan lopullista tilaa.\n",
|
|
"\n",
|
|
"Rakennetaan kaksikerroksinen kaksisuuntainen LSTM luokittelutehtäväämme varten.\n",
|
|
"\n",
|
|
"> **Note** tämä koodi vie jälleen melko paljon aikaa valmistua, mutta se antaa meille parhaan tarkkuuden, jonka olemme tähän mennessä nähneet. Joten ehkä kannattaa odottaa ja nähdä tulos.\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:t muihin tehtäviin\n",
|
|
"\n",
|
|
"Tähän asti olemme keskittyneet käyttämään RNN:itä tekstijonojen luokitteluun. Mutta ne voivat käsitellä monia muitakin tehtäviä, kuten tekstin generointia ja konekäännöstä — tarkastelemme näitä tehtäviä seuraavassa osiossa.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"\n---\n\n**Vastuuvapauslauseke**: \nTämä asiakirja on käännetty käyttämällä tekoälypohjaista käännöspalvelua [Co-op Translator](https://github.com/Azure/co-op-translator). Vaikka pyrimme tarkkuuteen, huomioithan, että automaattiset käännökset voivat sisältää virheitä tai epätarkkuuksia. Alkuperäistä asiakirjaa sen alkuperäisellä kielellä tulisi pitää ensisijaisena lähteenä. Kriittisen tiedon osalta suositellaan ammattimaista ihmiskäännöstä. Emme ole vastuussa väärinkäsityksistä tai virhetulkinnoista, jotka johtuvat tämän käännöksen käytöstä.\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-28T21:46:34+00:00",
|
|
"source_file": "lessons/5-NLP/16-RNN/RNNTF.ipynb",
|
|
"language_code": "fi"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 4
|
|
} |