460 lines
23 KiB
Plaintext
460 lines
23 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Rekurentne neuronske mreže\n",
|
|
"\n",
|
|
"U prethodnom modulu obradili smo bogate semantičke reprezentacije teksta. Arhitektura koju smo koristili hvata agregirano značenje riječi u rečenici, ali ne uzima u obzir **redoslijed** riječi, jer operacija agregacije koja slijedi nakon ugrađivanja uklanja te informacije iz izvornog teksta. Budući da ti modeli ne mogu predstavljati redoslijed riječi, nisu sposobni riješiti složenije ili dvosmislene zadatke poput generiranja teksta ili odgovaranja na pitanja.\n",
|
|
"\n",
|
|
"Kako bismo uhvatili značenje sekvence teksta, koristit ćemo arhitekturu neuronske mreže zvanu **rekurentna neuronska mreža** ili RNN. Kada koristimo RNN, prosljeđujemo našu rečenicu kroz mrežu jedan token po jedan, a mreža proizvodi određeno **stanje**, koje zatim ponovno prosljeđujemo mreži s idućim tokenom.\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"S obzirom na ulaznu sekvencu tokena $X_0,\\dots,X_n$, RNN stvara sekvencu blokova neuronske mreže i trenira ovu sekvencu od početka do kraja koristeći unatrag širenje pogreške (backpropagation). Svaki blok mreže uzima par $(X_i,S_i)$ kao ulaz i proizvodi $S_{i+1}$ kao rezultat. Konačno stanje $S_n$ ili izlaz $Y_n$ ide u linearni klasifikator kako bi se proizveo rezultat. Svi blokovi mreže dijele iste težine i treniraju se od početka do kraja koristeći jedan prolaz unatrag širenja pogreške.\n",
|
|
"\n",
|
|
"> Gornja slika prikazuje rekurentnu neuronsku mrežu u razmotanom obliku (s lijeve strane) i u kompaktnijoj rekurentnoj reprezentaciji (s desne strane). Važno je shvatiti da sve RNN ćelije imaju iste **dijeljive težine**.\n",
|
|
"\n",
|
|
"Budući da se vektori stanja $S_0,\\dots,S_n$ prosljeđuju kroz mrežu, RNN može naučiti sekvencijalne ovisnosti između riječi. Na primjer, kada se riječ *ne* pojavi negdje u sekvenci, mreža može naučiti negirati određene elemente unutar vektora stanja.\n",
|
|
"\n",
|
|
"Unutar svake RNN ćelije nalaze se dvije matrice težina: $W_H$ i $W_I$, te pomak $b$. Na svakom koraku RNN-a, s obzirom na ulaz $X_i$ i ulazno stanje $S_i$, izlazno stanje se računa kao $S_{i+1} = f(W_H\\times S_i + W_I\\times X_i+b)$, gdje je $f$ funkcija aktivacije (često $\\tanh$).\n",
|
|
"\n",
|
|
"> Za probleme poput generiranja teksta (koje ćemo obraditi u sljedećoj jedinici) ili strojne prijevode također želimo dobiti neku izlaznu vrijednost na svakom koraku RNN-a. U tom slučaju postoji još jedna matrica $W_O$, a izlaz se računa kao $Y_i=f(W_O\\times S_i+b_O)$.\n",
|
|
"\n",
|
|
"Pogledajmo kako rekurentne neuronske mreže mogu pomoći u klasifikaciji našeg skupa podataka o vijestima.\n",
|
|
"\n",
|
|
"> Za okruženje sandboxa, potrebno je pokrenuti sljedeću ćeliju kako bismo osigurali da je potrebna biblioteka instalirana i da su podaci unaprijed dohvaćeni. Ako radite lokalno, možete preskočiti sljedeću ćeliju.\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": [
|
|
"Kod treniranja velikih modela, alokacija GPU memorije može postati problem. Također, možda ćemo trebati eksperimentirati s različitim veličinama minibatcha kako bi podaci stali u GPU memoriju, a istovremeno osigurali dovoljno brzu obuku. Ako pokrećete ovaj kod na vlastitom GPU računalu, možete eksperimentirati s prilagodbom veličine minibatcha kako biste ubrzali proces treniranja.\n",
|
|
"\n",
|
|
"> **Napomena**: Poznato je da određene verzije NVidia upravljačkih programa ne oslobađaju memoriju nakon treniranja modela. U ovom bilježniku pokrećemo nekoliko primjera, što može dovesti do iscrpljenja memorije u određenim konfiguracijama, posebno ako provodite vlastite eksperimente unutar istog bilježnika. Ako naiđete na neobične pogreške prilikom pokretanja treniranja modela, možda ćete morati ponovno pokrenuti kernel bilježnika.\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": [
|
|
"## Jednostavni RNN klasifikator\n",
|
|
"\n",
|
|
"U slučaju jednostavnog RNN-a, svaka rekurentna jedinica je jednostavna linearna mreža koja prima ulazni vektor i vektor stanja te proizvodi novi vektor stanja. U Kerasu, ovo se može predstaviti slojem `SimpleRNN`.\n",
|
|
"\n",
|
|
"Iako možemo proslijediti one-hot kodirane tokene izravno u RNN sloj, to nije dobra ideja zbog njihove visoke dimenzionalnosti. Stoga ćemo koristiti sloj za ugrađivanje (embedding) kako bismo smanjili dimenzionalnost vektora riječi, nakon čega slijedi RNN sloj, a na kraju `Dense` klasifikator.\n",
|
|
"\n",
|
|
"> **Napomena**: U slučajevima kada dimenzionalnost nije toliko visoka, na primjer kada se koristi tokenizacija na razini znakova, moglo bi imati smisla proslijediti one-hot kodirane tokene izravno u RNN ćeliju.\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": [
|
|
"> **Napomena:** Ovdje koristimo netrenirani sloj za ugrađivanje radi jednostavnosti, ali za bolje rezultate možemo koristiti unaprijed trenirani sloj za ugrađivanje koristeći Word2Vec, kako je opisano u prethodnoj jedinici. Bilo bi dobro vježbati prilagodbu ovog koda za rad s unaprijed treniranim ugrađivanjima.\n",
|
|
"\n",
|
|
"Sada ćemo trenirati naš RNN. Općenito, RNN-ove je prilično teško trenirati, jer kada se RNN ćelije razmota duž duljine sekvence, broj slojeva uključenih u unatrag širenje pogreške postaje vrlo velik. Zbog toga trebamo odabrati manju stopu učenja i trenirati mrežu na većem skupu podataka kako bismo postigli dobre rezultate. To može potrajati dosta dugo, pa je poželjno koristiti GPU.\n",
|
|
"\n",
|
|
"Kako bismo ubrzali proces, trenirat ćemo RNN model samo na naslovima vijesti, izostavljajući opis. Možete pokušati trenirati s opisom i vidjeti možete li postići da model uspješno trenira.\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": [
|
|
"> **Napomena** da je točnost vjerojatno niža ovdje, jer treniramo samo na naslovima vijesti.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Ponovno razmatranje sekvenci varijabli\n",
|
|
"\n",
|
|
"Zapamtite da će sloj `TextVectorization` automatski popuniti sekvence varijabilne duljine u minibatchu s tokenima za popunjavanje. Ispostavlja se da ti tokeni također sudjeluju u treningu i mogu zakomplicirati konvergenciju modela.\n",
|
|
"\n",
|
|
"Postoji nekoliko pristupa koje možemo koristiti kako bismo smanjili količinu popunjavanja. Jedan od njih je preuređivanje skupa podataka prema duljini sekvence i grupiranje svih sekvenci prema veličini. To se može učiniti pomoću funkcije `tf.data.experimental.bucket_by_sequence_length` (pogledajte [dokumentaciju](https://www.tensorflow.org/api_docs/python/tf/data/experimental/bucket_by_sequence_length)).\n",
|
|
"\n",
|
|
"Drugi pristup je korištenje **maskiranja**. U Kerasu, neki slojevi podržavaju dodatni ulaz koji pokazuje koji tokeni trebaju biti uzeti u obzir tijekom treninga. Da bismo uključili maskiranje u naš model, možemo dodati zaseban sloj `Masking` ([dokumentacija](https://keras.io/api/layers/core_layers/masking/)), ili možemo postaviti parametar `mask_zero=True` u našem sloju `Embedding`.\n",
|
|
"\n",
|
|
"> **Note**: Ovaj trening će trajati oko 5 minuta za dovršavanje jedne epohe na cijelom skupu podataka. Slobodno prekinite trening u bilo kojem trenutku ako izgubite strpljenje. Također možete ograničiti količinu podataka korištenih za trening dodavanjem `.take(...)` klauzule nakon skupova podataka `ds_train` i `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": [
|
|
"Sada kada koristimo maskiranje, možemo trenirati model na cijelom skupu podataka naslova i opisa.\n",
|
|
"\n",
|
|
"> **Note**: Jeste li primijetili da smo koristili vektorizator treniran na naslovima vijesti, a ne na cijelom tekstu članka? Potencijalno, to može uzrokovati da neki tokeni budu ignorirani, pa je bolje ponovno trenirati vektorizator. Međutim, to bi moglo imati samo vrlo mali učinak, pa ćemo se držati prethodno treniranog vektorizatora radi jednostavnosti.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## LSTM: Dugoročna kratkoročna memorija\n",
|
|
"\n",
|
|
"Jedan od glavnih problema RNN-ova je **nestajanje gradijenata**. RNN-ovi mogu biti prilično dugi i mogu imati poteškoća s propagacijom gradijenata sve do prvog sloja mreže tijekom unatrag širenja. Kada se to dogodi, mreža ne može naučiti odnose između udaljenih tokena. Jedan od načina za izbjegavanje ovog problema je uvođenje **eksplicitnog upravljanja stanjem** pomoću **vrata**. Dvije najčešće arhitekture koje uvode vrata su **dugoročna kratkoročna memorija** (LSTM) i **jedinica s upravljanim prijenosom** (GRU). Ovdje ćemo obraditi LSTM-ove.\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"LSTM mreža je organizirana na način sličan RNN-u, ali postoje dva stanja koja se prenose iz sloja u sloj: stvarno stanje $c$ i skriveni vektor $h$. U svakoj jedinici, skriveni vektor $h_{t-1}$ kombinira se s ulazom $x_t$, i zajedno kontroliraju što se događa sa stanjem $c_t$ i izlazom $h_{t}$ putem **vrata**. Svaka vrata imaju sigmoidnu aktivaciju (izlaz u rasponu $[0,1]$), koja se može smatrati maskom po bitovima kada se pomnoži s vektorom stanja. LSTM-ovi imaju sljedeća vrata (s lijeva na desno na slici iznad):\n",
|
|
"* **vrata za zaborav** koja određuju koje komponente vektora $c_{t-1}$ trebamo zaboraviti, a koje proslijediti dalje.\n",
|
|
"* **ulazna vrata** koja određuju koliko informacija iz ulaznog vektora i prethodnog skrivenog vektora treba biti uključeno u vektor stanja.\n",
|
|
"* **izlazna vrata** koja uzimaju novi vektor stanja i odlučuju koji će njegovi dijelovi biti korišteni za stvaranje novog skrivenog vektora $h_t$.\n",
|
|
"\n",
|
|
"Komponente stanja $c$ mogu se smatrati zastavicama koje se mogu uključiti i isključiti. Na primjer, kada naiđemo na ime *Alice* u nizu, pretpostavljamo da se odnosi na ženu i podižemo zastavicu u stanju koja kaže da imamo imenicu ženskog roda u rečenici. Kada dalje naiđemo na riječi *i Tom*, podići ćemo zastavicu koja kaže da imamo množinsku imenicu. Tako, manipulirajući stanjem, možemo pratiti gramatička svojstva rečenice.\n",
|
|
"\n",
|
|
"> **Napomena**: Evo izvrsnog resursa za razumijevanje unutarnje strukture LSTM-ova: [Understanding LSTM Networks](https://colah.github.io/posts/2015-08-Understanding-LSTMs/) autora Christophera Olaha.\n",
|
|
"\n",
|
|
"Iako unutarnja struktura LSTM ćelije može izgledati složeno, Keras skriva ovu implementaciju unutar sloja `LSTM`, tako da je jedino što trebamo učiniti u gornjem primjeru zamijeniti rekurentni sloj:\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": [
|
|
"## Dvosmjerni i višeslojni RNN-ovi\n",
|
|
"\n",
|
|
"U našim dosadašnjim primjerima, rekurentne mreže obrađuju sekvencu od početka do kraja. To nam se čini prirodnim jer slijedi isti smjer kojim čitamo ili slušamo govor. Međutim, za scenarije koji zahtijevaju nasumičan pristup ulaznoj sekvenci, logičnije je pokrenuti rekurentne izračune u oba smjera. RNN-ovi koji omogućuju izračune u oba smjera nazivaju se **dvosmjerni** RNN-ovi, a mogu se stvoriti omatanjem rekurentnog sloja posebnim slojem `Bidirectional`.\n",
|
|
"\n",
|
|
"> **Napomena**: Sloj `Bidirectional` stvara dvije kopije sloja unutar sebe i postavlja svojstvo `go_backwards` jedne od tih kopija na `True`, čime omogućuje da ide u suprotnom smjeru duž sekvence.\n",
|
|
"\n",
|
|
"Rekurentne mreže, bilo jednosmjerne ili dvosmjerne, prepoznaju uzorke unutar sekvence i pohranjuju ih u vektore stanja ili ih vraćaju kao izlaz. Kao i kod konvolucijskih mreža, možemo izgraditi još jedan rekurentni sloj nakon prvog kako bismo prepoznali uzorke višeg nivoa, izgrađene na temelju uzoraka nižeg nivoa koje je izdvojio prvi sloj. To nas dovodi do pojma **višeslojnog RNN-a**, koji se sastoji od dvije ili više rekurentnih mreža, gdje se izlaz prethodnog sloja prosljeđuje sljedećem sloju kao ulaz.\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"*Slika preuzeta iz [ovog izvrsnog članka](https://towardsdatascience.com/from-a-lstm-cell-to-a-multilayer-lstm-network-with-pytorch-2899eb5696f3) autora Fernanda Lópeza.*\n",
|
|
"\n",
|
|
"Keras olakšava izgradnju ovih mreža jer je dovoljno dodati više rekurentnih slojeva u model. Za sve slojeve osim posljednjeg, potrebno je postaviti parametar `return_sequences=True`, jer želimo da sloj vraća sva međustanja, a ne samo konačno stanje rekurentnog izračuna.\n",
|
|
"\n",
|
|
"Izgradimo dvoslojni dvosmjerni LSTM za naš problem klasifikacije.\n",
|
|
"\n",
|
|
"> **Napomena** ovaj kod ponovno traje prilično dugo za izvršavanje, ali pruža najveću točnost koju smo dosad vidjeli. Možda se isplati pričekati i vidjeti 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-ovi za druge zadatke\n",
|
|
"\n",
|
|
"Do sada smo se fokusirali na korištenje RNN-ova za klasifikaciju tekstualnih sekvenci. Međutim, oni mogu obraditi mnogo više zadataka, poput generiranja teksta i strojnog prevođenja — te ćemo zadatke razmotriti u sljedećoj jedinici.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"\n---\n\n**Odricanje od odgovornosti**: \nOvaj dokument je preveden korištenjem AI usluge za prevođenje [Co-op Translator](https://github.com/Azure/co-op-translator). Iako nastojimo osigurati točnost, imajte na umu da automatski prijevodi mogu sadržavati pogreške ili netočnosti. Izvorni dokument na izvornom jeziku treba smatrati mjerodavnim izvorom. Za ključne informacije preporučuje se profesionalni prijevod od strane stručnjaka. Ne preuzimamo odgovornost za bilo kakve nesporazume ili pogrešne interpretacije proizašle iz korištenja ovog prijevoda.\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-30T08:09:04+00:00",
|
|
"source_file": "lessons/5-NLP/16-RNN/RNNTF.ipynb",
|
|
"language_code": "hr"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 4
|
|
} |