460 lines
23 KiB
Plaintext
460 lines
23 KiB
Plaintext
{
|
||
"cells": [
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"# Rekurentiniai neuroniniai tinklai\n",
|
||
"\n",
|
||
"Ankstesniame modulyje aptarėme turtingas semantines teksto reprezentacijas. Naudota architektūra apima agreguotą žodžių prasmę sakinyje, tačiau ji neatsižvelgia į žodžių **tvarką**, nes agregavimo operacija, atliekama po įterpimų, pašalina šią informaciją iš pradinio teksto. Kadangi šie modeliai negali atspindėti žodžių tvarkos, jie negali spręsti sudėtingesnių ar dviprasmiškų užduočių, tokių kaip teksto generavimas ar klausimų atsakymas.\n",
|
||
"\n",
|
||
"Norėdami užfiksuoti teksto sekos prasmę, naudosime neuroninių tinklų architektūrą, vadinamą **rekurentiniais neuroniniais tinklais** (RNN). Naudojant RNN, mes perduodame savo sakinį per tinklą po vieną žodį, o tinklas sukuria tam tikrą **būseną**, kurią vėliau perduodame tinklui kartu su kitu žodžiu.\n",
|
||
"\n",
|
||
"\n",
|
||
"\n",
|
||
"Turint įvesties žodžių seką $X_0,\\dots,X_n$, RNN sukuria neuroninių tinklų blokų seką ir treniruoja šią seką nuo pradžios iki pabaigos naudodamas atgalinį sklidimą. Kiekvienas tinklo blokas kaip įvestį gauna porą $(X_i,S_i)$ ir kaip rezultatą sukuria $S_{i+1}$. Galutinė būsena $S_n$ arba išvestis $Y_n$ perduodama į linijinį klasifikatorių, kad būtų gautas rezultatas. Visi tinklo blokai dalijasi tais pačiais svoriais ir yra treniruojami nuo pradžios iki pabaigos per vieną atgalinio sklidimo etapą.\n",
|
||
"\n",
|
||
"> Aukščiau pateiktame paveikslėlyje rekurentinis neuroninis tinklas parodytas išskleista forma (kairėje) ir kompaktiškesne rekurentine reprezentacija (dešinėje). Svarbu suprasti, kad visos RNN ląstelės turi tuos pačius **dalijamus svorius**.\n",
|
||
"\n",
|
||
"Kadangi būsenos vektoriai $S_0,\\dots,S_n$ perduodami per tinklą, RNN gali išmokti sekos priklausomybes tarp žodžių. Pavyzdžiui, kai žodis *ne* pasirodo kažkur sekoje, tinklas gali išmokti paneigti tam tikrus elementus būsenos vektoriuje.\n",
|
||
"\n",
|
||
"Kiekvienoje RNN ląstelėje yra du svorio matricos: $W_H$ ir $W_I$, bei poslinkis $b$. Kiekviename RNN žingsnyje, turint įvestį $X_i$ ir įvesties būseną $S_i$, išvesties būsena apskaičiuojama kaip $S_{i+1} = f(W_H\\times S_i + W_I\\times X_i+b)$, kur $f$ yra aktyvavimo funkcija (dažnai $\\tanh$).\n",
|
||
"\n",
|
||
"> Tokiems uždaviniams kaip teksto generavimas (kurį aptarsime kitame skyriuje) ar mašininis vertimas, mes taip pat norime gauti tam tikrą išvesties reikšmę kiekviename RNN žingsnyje. Tokiu atveju yra dar viena matrica $W_O$, o išvestis apskaičiuojama kaip $Y_i=f(W_O\\times S_i+b_O)$.\n",
|
||
"\n",
|
||
"Pažiūrėkime, kaip rekurentiniai neuroniniai tinklai gali padėti klasifikuoti mūsų naujienų duomenų rinkinį.\n",
|
||
"\n",
|
||
"> Smėliadėžės aplinkoje turime paleisti šią langelį, kad įsitikintume, jog reikalinga biblioteka yra įdiegta ir duomenys yra iš anksto užkrauti. Jei dirbate vietoje, galite praleisti šį langelį.\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": [
|
||
"Kai treniruojami dideli modeliai, GPU atminties paskirstymas gali tapti problema. Taip pat gali tekti eksperimentuoti su skirtingais mini paketų dydžiais, kad duomenys tilptų į GPU atmintį, tačiau mokymas būtų pakankamai greitas. Jei vykdote šį kodą savo GPU kompiuteryje, galite eksperimentuoti su mini paketų dydžio koregavimu, kad paspartintumėte mokymą.\n",
|
||
"\n",
|
||
"> **Note**: Kai kurios NVidia tvarkyklių versijos yra žinomos dėl to, kad po modelio mokymo neatlaisvina atminties. Šiame užrašų knygelėje vykdome kelis pavyzdžius, ir tai gali sukelti atminties išsekimą tam tikrose konfigūracijose, ypač jei atliekate savo eksperimentus toje pačioje užrašų knygelėje. Jei susiduriate su keistomis klaidomis pradėdami mokyti modelį, gali tekti iš naujo paleisti užrašų knygelės branduolį.\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": [
|
||
"## Paprastas RNN klasifikatorius\n",
|
||
"\n",
|
||
"Paprasto RNN atveju kiekvienas pasikartojantis vienetas yra paprastas linijinis tinklas, kuris priima įvesties vektorių ir būsenos vektorių, o tada sukuria naują būsenos vektorių. Keras bibliotekoje tai galima atvaizduoti naudojant `SimpleRNN` sluoksnį.\n",
|
||
"\n",
|
||
"Nors galime tiesiogiai perduoti vieno karšto kodavimo (one-hot encoded) žetonus į RNN sluoksnį, tai nėra gera idėja dėl jų didelio dimensionalumo. Todėl naudosime įterpimo (embedding) sluoksnį, kad sumažintume žodžių vektorių dimensionalumą, po to RNN sluoksnį ir galiausiai `Dense` klasifikatorių.\n",
|
||
"\n",
|
||
"> **Note**: Tais atvejais, kai dimensionalumas nėra toks didelis, pavyzdžiui, naudojant simbolių lygmens tokenizaciją, gali būti prasminga tiesiogiai perduoti vieno karšto kodavimo žetonus į RNN ląstelę.\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": [
|
||
"> **Pastaba:** Čia naudojame netreniruotą įterpimo sluoksnį dėl paprastumo, tačiau geresniems rezultatams galime naudoti iš anksto apmokytą įterpimo sluoksnį, pasitelkiant Word2Vec, kaip aprašyta ankstesniame skyriuje. Būtų gera praktika pritaikyti šį kodą darbui su iš anksto apmokytais įterpimais.\n",
|
||
"\n",
|
||
"Dabar apmokykime savo RNN. Apskritai, RNN yra gana sudėtinga treniruoti, nes kai RNN ląstelės yra išskleidžiamos pagal sekos ilgį, sluoksnių, dalyvaujančių atgaliniame sklidime, skaičius tampa labai didelis. Todėl turime pasirinkti mažesnį mokymosi greitį ir treniruoti tinklą su didesniu duomenų rinkiniu, kad gautume gerus rezultatus. Tai gali užtrukti gana ilgai, todėl rekomenduojama naudoti GPU.\n",
|
||
"\n",
|
||
"Kad paspartintume procesą, RNN modelį treniruosime tik su naujienų antraštėmis, praleisdami aprašymą. Galite pabandyti treniruoti su aprašymu ir pažiūrėti, ar pavyks modelį apmokyti.\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": [
|
||
"> **Pastaba**: tikslumas greičiausiai bus mažesnis, nes mokymui naudojami tik naujienų pavadinimai.\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Peržiūrint kintamųjų sekas\n",
|
||
"\n",
|
||
"Atminkite, kad `TextVectorization` sluoksnis automatiškai užpildo kintamo ilgio sekas mini paketų viduje užpildymo ženklais. Pasirodo, kad šie ženklai taip pat dalyvauja mokyme ir gali apsunkinti modelio konvergenciją.\n",
|
||
"\n",
|
||
"Yra keletas būdų, kaip sumažinti užpildymo kiekį. Vienas iš jų – pertvarkyti duomenų rinkinį pagal sekos ilgį ir grupuoti visas sekas pagal dydį. Tai galima padaryti naudojant funkciją `tf.data.experimental.bucket_by_sequence_length` (žr. [dokumentaciją](https://www.tensorflow.org/api_docs/python/tf/data/experimental/bucket_by_sequence_length)).\n",
|
||
"\n",
|
||
"Kitas būdas – naudoti **maskavimą**. Keras bibliotekoje kai kurie sluoksniai palaiko papildomą įvestį, kuri nurodo, kuriuos ženklus reikia atsižvelgti mokymo metu. Norėdami įtraukti maskavimą į mūsų modelį, galime arba pridėti atskirą `Masking` sluoksnį ([dokumentacija](https://keras.io/api/layers/core_layers/masking/)), arba nurodyti `mask_zero=True` parametrą mūsų `Embedding` sluoksnyje.\n",
|
||
"\n",
|
||
"> **Note**: Šis mokymas užtruks apie 5 minutes, kad būtų baigtas vienas epochas visame duomenų rinkinyje. Jei pritrūksite kantrybės, galite bet kada nutraukti mokymą. Taip pat galite apriboti mokymui naudojamų duomenų kiekį, pridėdami `.take(...)` sąlygą po `ds_train` ir `ds_test` duomenų rinkinių.\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": [
|
||
"Dabar, kai naudojame maskavimą, galime treniruoti modelį su visu antraščių ir aprašymų duomenų rinkiniu.\n",
|
||
"\n",
|
||
"> **Pastaba**: Ar pastebėjote, kad naudojome vektorizatorių, ištreniruotą pagal naujienų antraštes, o ne visą straipsnio tekstą? Tai gali lemti, kad kai kurie žetonai bus ignoruojami, todėl geriau būtų pertreniruoti vektorizatorių. Tačiau tai gali turėti tik labai mažą poveikį, todėl dėl paprastumo laikysimės ankstesnio iš anksto ištreniruoto vektorizatoriaus.\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## LSTM: Ilgalaikė trumpalaikė atmintis\n",
|
||
"\n",
|
||
"Viena iš pagrindinių RNN problemų yra **nykstantys gradientai**. RNN tinklai gali būti gana ilgi, todėl jiems gali būti sunku perduoti gradientus atgal iki pat pirmojo tinklo sluoksnio atgalinio sklidimo metu. Kai taip nutinka, tinklas negali išmokti ryšių tarp tolimų žodžių. Vienas iš būdų išvengti šios problemos yra įvesti **aiškų būsenos valdymą** naudojant **vartus**. Dvi dažniausiai naudojamos architektūros, kurios naudoja vartus, yra **ilgalaikė trumpalaikė atmintis** (LSTM) ir **vartų relės vienetas** (GRU). Šiame skyriuje aptarsime LSTM.\n",
|
||
"\n",
|
||
"\n",
|
||
"\n",
|
||
"LSTM tinklas organizuotas panašiai kaip RNN, tačiau čia yra dvi būsenos, kurios perduodamos iš sluoksnio į sluoksnį: tikroji būsena $c$ ir paslėptas vektorius $h$. Kiekviename vienete paslėptas vektorius $h_{t-1}$ yra sujungiamas su įvestimi $x_t$, ir kartu jie kontroliuoja, kas vyksta su būsena $c_t$ ir išvestimi $h_{t}$ per **vartus**. Kiekvienas vartas turi sigmoidinę aktyvaciją (išvestis intervale $[0,1]$), kurią galima laikyti bitiniu masku, kai ji dauginama iš būsenos vektoriaus. LSTM turi šiuos vartus (nuo kairės į dešinę aukščiau esančiame paveikslėlyje):\n",
|
||
"* **užmaršumo vartai**, kurie nustato, kuriuos vektoriaus $c_{t-1}$ komponentus reikia pamiršti, o kuriuos perduoti toliau.\n",
|
||
"* **įvesties vartai**, kurie nustato, kiek informacijos iš įvesties vektoriaus ir ankstesnio paslėpto vektoriaus turėtų būti įtraukta į būsenos vektorių.\n",
|
||
"* **išvesties vartai**, kurie paima naują būsenos vektorių ir nusprendžia, kurie jo komponentai bus naudojami naujam paslėptam vektoriui $h_t$ sukurti.\n",
|
||
"\n",
|
||
"Būsenos $c$ komponentus galima laikyti vėliavėlėmis, kurias galima įjungti arba išjungti. Pavyzdžiui, kai sekos metu sutinkame vardą *Alice*, galime spėti, kad tai moteris, ir pakelti vėliavėlę būsenoje, kuri nurodo, kad sakinyje yra moteriškos giminės daiktavardis. Kai toliau sutinkame žodžius *and Tom*, pakeliame vėliavėlę, kuri nurodo, kad dabar turime daugiskaitos daiktavardį. Taigi, manipuliuodami būsena, galime sekti sakinio gramatines savybes.\n",
|
||
"\n",
|
||
"> **Note**: Štai puikus šaltinis, padedantis suprasti LSTM vidinę struktūrą: [Understanding LSTM Networks](https://colah.github.io/posts/2015-08-Understanding-LSTMs/) Christopher Olah.\n",
|
||
"\n",
|
||
"Nors LSTM ląstelės vidinė struktūra gali atrodyti sudėtinga, Keras paslepia šią įgyvendinimą `LSTM` sluoksnyje, todėl vienintelis dalykas, kurį reikia padaryti aukščiau pateiktame pavyzdyje, yra pakeisti rekursinį sluoksnį:\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": [
|
||
"## Dvikryptės ir daugiasluoksnės RNN\n",
|
||
"\n",
|
||
"Mūsų ankstesniuose pavyzdžiuose rekursiniai tinklai veikė nuo sekos pradžios iki pabaigos. Tai mums atrodo natūralu, nes atitinka kryptį, kuria skaitome ar klausomės kalbos. Tačiau scenarijams, kuriems reikia atsitiktinės prieigos prie įvesties sekos, logiškiau vykdyti rekursinį skaičiavimą abiem kryptimis. RNN, leidžiančios skaičiavimus abiem kryptimis, vadinamos **dvikryptėmis** RNN, ir jos gali būti sukurtos apgaubiant rekursinį sluoksnį specialiu `Bidirectional` sluoksniu.\n",
|
||
"\n",
|
||
"> **Note**: `Bidirectional` sluoksnis sukuria dvi sluoksnio kopijas ir nustato vienos iš jų `go_backwards` savybę į `True`, kad ji eitų priešinga kryptimi per seką.\n",
|
||
"\n",
|
||
"Rekursiniai tinklai, tiek vienkryptės, tiek dvikryptės, fiksuoja sekos šablonus ir saugo juos būsenos vektoriuose arba grąžina juos kaip išvestį. Kaip ir konvoliuciniuose tinkluose, mes galime sukurti dar vieną rekursinį sluoksnį po pirmojo, kad užfiksuotume aukštesnio lygio šablonus, sudarytus iš žemesnio lygio šablonų, kuriuos ištraukė pirmasis sluoksnis. Tai veda mus prie **daugiasluoksnės RNN** sąvokos, kurią sudaro du ar daugiau rekursinių tinklų, kur ankstesnio sluoksnio išvestis perduodama kitam sluoksniui kaip įvestis.\n",
|
||
"\n",
|
||
"\n",
|
||
"\n",
|
||
"*Paveikslėlis iš [šio puikaus straipsnio](https://towardsdatascience.com/from-a-lstm-cell-to-a-multilayer-lstm-network-with-pytorch-2899eb5696f3) autoriaus Fernando López.*\n",
|
||
"\n",
|
||
"Keras leidžia lengvai sukurti šiuos tinklus, nes tereikia pridėti daugiau rekursinių sluoksnių prie modelio. Visuose sluoksniuose, išskyrus paskutinį, reikia nurodyti parametrą `return_sequences=True`, nes mums reikia, kad sluoksnis grąžintų visas tarpines būsenas, o ne tik galutinę rekursinio skaičiavimo būseną.\n",
|
||
"\n",
|
||
"Sukurkime dviejų sluoksnių dvikryptę LSTM mūsų klasifikavimo užduočiai.\n",
|
||
"\n",
|
||
"> **Note** šis kodas vėl užtrunka gana ilgai, tačiau jis suteikia didžiausią tikslumą, kokį iki šiol matėme. Taigi galbūt verta palaukti ir pamatyti rezultatą.\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 kitiems užduočių tipams\n",
|
||
"\n",
|
||
"Iki šiol mes daugiausia dėmesio skyrėme RNN naudojimui tekstų sekų klasifikavimui. Tačiau jos gali atlikti ir daugybę kitų užduočių, tokių kaip teksto generavimas ar mašininis vertimas — šias užduotis aptarsime kitame skyriuje.\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"\n---\n\n**Atsakomybės apribojimas**: \nŠis dokumentas buvo išverstas naudojant AI vertimo paslaugą [Co-op Translator](https://github.com/Azure/co-op-translator). Nors siekiame tikslumo, prašome atkreipti dėmesį, kad automatiniai vertimai gali turėti klaidų ar netikslumų. Originalus dokumentas jo gimtąja kalba turėtų būti laikomas autoritetingu šaltiniu. Kritinei informacijai rekomenduojama naudoti profesionalų žmogaus vertimą. Mes neprisiimame atsakomybės už nesusipratimus ar klaidingus interpretavimus, atsiradusius dėl šio vertimo naudojimo.\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-31T13:58:00+00:00",
|
||
"source_file": "lessons/5-NLP/16-RNN/RNNTF.ipynb",
|
||
"language_code": "lt"
|
||
}
|
||
},
|
||
"nbformat": 4,
|
||
"nbformat_minor": 4
|
||
} |