460 lines
22 KiB
Plaintext
460 lines
22 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Rekurentne nevronske mreže\n",
|
|
"\n",
|
|
"V prejšnjem modulu smo obravnavali bogate semantične reprezentacije besedila. Arhitektura, ki smo jo uporabljali, zajame združeni pomen besed v stavku, vendar ne upošteva **vrstnega reda** besed, saj operacija združevanja, ki sledi vdelavam, odstrani to informacijo iz izvirnega besedila. Ker ti modeli ne morejo predstavljati vrstnega reda besed, ne morejo reševati bolj zapletenih ali dvoumnih nalog, kot sta generiranje besedila ali odgovarjanje na vprašanja.\n",
|
|
"\n",
|
|
"Za zajemanje pomena zaporedja besedila bomo uporabili arhitekturo nevronske mreže, imenovano **rekurentna nevronska mreža** ali RNN. Pri uporabi RNN stavke pošiljamo skozi mrežo en token naenkrat, mreža pa ustvari neko **stanje**, ki ga nato skupaj z naslednjim tokenom ponovno pošljemo v mrežo.\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"Glede na vhodno zaporedje tokenov $X_0,\\dots,X_n$ RNN ustvari zaporedje blokov nevronske mreže in to zaporedje trenira od začetka do konca z uporabo povratnega razširjanja napake (backpropagation). Vsak blok mreže kot vhod prejme par $(X_i,S_i)$ in kot rezultat ustvari $S_{i+1}$. Končno stanje $S_n$ ali izhod $Y_n$ gre v linearni klasifikator, da ustvari rezultat. Vsi bloki mreže si delijo iste uteži in so trenirani od začetka do konca z enim prehodom povratnega razširjanja napake.\n",
|
|
"\n",
|
|
"> Zgornja slika prikazuje rekurentno nevronsko mrežo v razširjeni obliki (na levi) in v bolj kompaktni rekurentni predstavitvi (na desni). Pomembno je razumeti, da imajo vse RNN celice iste **deljive uteži**.\n",
|
|
"\n",
|
|
"Ker se vektorska stanja $S_0,\\dots,S_n$ prenašajo skozi mrežo, lahko RNN uči zaporedne odvisnosti med besedami. Na primer, ko se beseda *ne* pojavi nekje v zaporedju, se lahko nauči negirati določene elemente znotraj vektorskega stanja.\n",
|
|
"\n",
|
|
"Vsaka RNN celica vsebuje dve matriki uteži: $W_H$ in $W_I$, ter pristranskost $b$. Na vsakem koraku RNN se glede na vhod $X_i$ in vhodno stanje $S_i$ izhodno stanje izračuna kot $S_{i+1} = f(W_H\\times S_i + W_I\\times X_i+b)$, kjer je $f$ aktivacijska funkcija (pogosto $\\tanh$).\n",
|
|
"\n",
|
|
"> Pri težavah, kot sta generiranje besedila (ki ga bomo obravnavali v naslednji enoti) ali strojno prevajanje, želimo dobiti tudi neko izhodno vrednost na vsakem koraku RNN. V tem primeru obstaja še ena matrika $W_O$, izhod pa se izračuna kot $Y_i=f(W_O\\times S_i+b_O)$.\n",
|
|
"\n",
|
|
"Poglejmo, kako nam lahko rekurentne nevronske mreže pomagajo pri klasifikaciji našega nabora novic.\n",
|
|
"\n",
|
|
"> Za peskovnik okolje moramo zagnati naslednjo celico, da zagotovimo, da je potrebna knjižnica nameščena in da so podatki prednaloženi. Če izvajate lokalno, lahko naslednjo celico preskočite.\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": [
|
|
"Ko treniramo velike modele, lahko razporeditev pomnilnika na GPU postane težava. Prav tako moramo eksperimentirati z različnimi velikostmi minibatch, da podatki ustrezajo pomnilniku GPU, hkrati pa je trening dovolj hiter. Če to kodo izvajate na svojem GPU stroju, lahko eksperimentirate z nastavitvijo velikosti minibatch, da pospešite trening.\n",
|
|
"\n",
|
|
"> **Opomba**: Znano je, da določene različice NVidia gonilnikov po treniranju modela ne sprostijo pomnilnika. V teh zvezkih izvajamo več primerov, kar lahko povzroči izčrpanje pomnilnika v določenih nastavitvah, še posebej, če izvajate lastne eksperimente v istem zvezku. Če naletite na nenavadne napake ob začetku treniranja modela, boste morda morali znova zagnati jedro zvezka.\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": [
|
|
"## Preprost RNN klasifikator\n",
|
|
"\n",
|
|
"V primeru preprostega RNN je vsaka rekurentna enota preprosta linearna mreža, ki sprejme vhodni vektor in vektorsko stanje ter ustvari novo vektorsko stanje. V Kerasu to lahko predstavimo s plastjo `SimpleRNN`.\n",
|
|
"\n",
|
|
"Čeprav lahko RNN plasti neposredno posredujemo eno-vroče kodirane tokene, to ni dobra ideja zaradi njihove visoke dimenzionalnosti. Zato bomo uporabili vgradno plast (embedding layer), da zmanjšamo dimenzionalnost vektorskih predstav besed, nato pa plast RNN in na koncu klasifikator `Dense`.\n",
|
|
"\n",
|
|
"> **Opomba**: V primerih, ko dimenzionalnost ni tako visoka, na primer pri uporabi tokenizacije na ravni znakov, bi bilo smiselno eno-vroče kodirane tokene neposredno posredovati celici RNN.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 4,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Model: \"sequential\"\n",
|
|
"_________________________________________________________________\n",
|
|
"Layer (type) Output Shape Param # \n",
|
|
"=================================================================\n",
|
|
"text_vectorization (TextVect (None, None) 0 \n",
|
|
"_________________________________________________________________\n",
|
|
"embedding (Embedding) (None, None, 64) 1280000 \n",
|
|
"_________________________________________________________________\n",
|
|
"simple_rnn (SimpleRNN) (None, 16) 1296 \n",
|
|
"_________________________________________________________________\n",
|
|
"dense (Dense) (None, 4) 68 \n",
|
|
"=================================================================\n",
|
|
"Total params: 1,281,364\n",
|
|
"Trainable params: 1,281,364\n",
|
|
"Non-trainable params: 0\n",
|
|
"_________________________________________________________________\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"vocab_size = 20000\n",
|
|
"\n",
|
|
"vectorizer = keras.layers.experimental.preprocessing.TextVectorization(\n",
|
|
" max_tokens=vocab_size,\n",
|
|
" input_shape=(1,))\n",
|
|
"\n",
|
|
"model = keras.models.Sequential([\n",
|
|
" vectorizer,\n",
|
|
" keras.layers.Embedding(vocab_size, embed_size),\n",
|
|
" keras.layers.SimpleRNN(16),\n",
|
|
" keras.layers.Dense(4,activation='softmax')\n",
|
|
"])\n",
|
|
"\n",
|
|
"model.summary()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"> **Opomba:** Tukaj uporabljamo neizurjeno vgrajeno plast za enostavnost, vendar za boljše rezultate lahko uporabimo vnaprej izurjeno vgrajeno plast z uporabo Word2Vec, kot je opisano v prejšnji enoti. Dobra vaja bi bila prilagoditi to kodo za delo z vnaprej izurjenimi vgrajenimi plastmi.\n",
|
|
"\n",
|
|
"Zdaj bomo izurili naš RNN. RNN-ji so na splošno precej težavni za učenje, saj je število plasti, ki sodelujejo pri povratnem razširjanju napake, zelo veliko, ko se celice RNN razširijo vzdolž dolžine zaporedja. Zato moramo izbrati manjšo stopnjo učenja in mrežo izuriti na večjem naboru podatkov, da dosežemo dobre rezultate. To lahko traja precej dolgo, zato je priporočljiva uporaba GPU-ja.\n",
|
|
"\n",
|
|
"Da pospešimo proces, bomo model RNN izurili samo na naslovih novic in izpustili opise. Lahko poskusite z učenjem na opisih in preverite, ali lahko model izurite.\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": [
|
|
"> **Opomba** da je natančnost tukaj verjetno nižja, ker treniramo samo na naslovih novic.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Ponovno preučimo zaporedja spremenljivk\n",
|
|
"\n",
|
|
"Ne pozabite, da bo plast `TextVectorization` samodejno dopolnila zaporedja spremenljive dolžine v mini seriji z zapolnitvenimi žetoni. Izkazalo se je, da ti žetoni sodelujejo tudi pri učenju, kar lahko oteži konvergenco modela.\n",
|
|
"\n",
|
|
"Obstaja več pristopov, ki jih lahko uporabimo za zmanjšanje količine zapolnitve. Eden od njih je preurejanje nabora podatkov glede na dolžino zaporedja in združevanje vseh zaporedij po velikosti. To lahko storimo z uporabo funkcije `tf.data.experimental.bucket_by_sequence_length` (glejte [dokumentacijo](https://www.tensorflow.org/api_docs/python/tf/data/experimental/bucket_by_sequence_length)).\n",
|
|
"\n",
|
|
"Drug pristop je uporaba **maskiranja**. V Kerasu nekatere plasti podpirajo dodatni vhod, ki kaže, katere žetone je treba upoštevati pri učenju. Da vključimo maskiranje v naš model, lahko dodamo ločeno plast `Masking` ([dokumentacija](https://keras.io/api/layers/core_layers/masking/)), ali pa določimo parameter `mask_zero=True` v naši plasti `Embedding`.\n",
|
|
"\n",
|
|
"> **Opomba**: To učenje bo trajalo približno 5 minut za dokončanje ene epohe na celotnem naboru podatkov. Če vam zmanjka potrpljenja, lahko učenje kadar koli prekinete. Prav tako lahko omejite količino podatkov, uporabljenih za učenje, tako da dodate klavzulo `.take(...)` po naborih podatkov `ds_train` in `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": [
|
|
"Zdaj, ko uporabljamo maskiranje, lahko model učimo na celotnem naboru naslovov in opisov.\n",
|
|
"\n",
|
|
"> **Opomba**: Ste opazili, da smo uporabljali vektorizator, ki je bil naučen na naslovih novic, in ne na celotnem besedilu članka? To lahko povzroči, da so nekateri tokeni prezrti, zato je bolje ponovno naučiti vektorizator. Vendar pa bo to verjetno imelo le zelo majhen učinek, zato bomo zaradi enostavnosti ostali pri prej naučenem vektorizatorju.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## LSTM: Dolgoročni spomin\n",
|
|
"\n",
|
|
"Ena glavnih težav RNN-jev je **izginjanje gradientov**. RNN-ji so lahko precej dolgi in imajo lahko težave pri prenosu gradientov nazaj do prve plasti mreže med povratnim razširjanjem. Ko se to zgodi, mreža ne more učiti odnosov med oddaljenimi tokeni. Eden od načinov za izogibanje tej težavi je uvedba **eksplicitnega upravljanja stanja** z uporabo **vrat**. Dve najpogostejši arhitekturi, ki uvajata vrata, sta **dolgoročni spomin** (LSTM) in **enota z zapornimi vrati** (GRU). Tukaj bomo obravnavali LSTM-je.\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"LSTM mreža je organizirana podobno kot RNN, vendar se iz plasti v plast prenašata dve stanji: dejansko stanje $c$ in skriti vektor $h$. Pri vsaki enoti se skriti vektor $h_{t-1}$ združi z vhodom $x_t$, in skupaj nadzorujeta, kaj se zgodi s stanjem $c_t$ in izhodom $h_{t}$ prek **vrat**. Vsaka vrata imajo sigmoidno aktivacijo (izhod v območju $[0,1]$), ki jo lahko razumemo kot bitno masko, ko jo pomnožimo z vektorskim stanjem. LSTM-ji imajo naslednja vrata (od leve proti desni na zgornji sliki):\n",
|
|
"* **vrata za pozabo**, ki določajo, katere komponente vektorja $c_{t-1}$ moramo pozabiti in katere prenesti naprej.\n",
|
|
"* **vhodna vrata**, ki določajo, koliko informacij iz vhodnega vektorja in prejšnjega skritega vektorja je treba vključiti v vektorsko stanje.\n",
|
|
"* **izhodna vrata**, ki vzamejo novo vektorsko stanje in odločijo, katere njegove komponente bodo uporabljene za ustvarjanje novega skritega vektorja $h_t$.\n",
|
|
"\n",
|
|
"Komponente stanja $c$ lahko razumemo kot zastavice, ki jih lahko vklopimo ali izklopimo. Na primer, ko v zaporedju naletimo na ime *Alice*, domnevamo, da gre za žensko, in dvignemo zastavico v stanju, ki označuje, da imamo v stavku ženski samostalnik. Ko nato naletimo na besede *and Tom*, dvignemo zastavico, ki označuje, da imamo množinski samostalnik. Tako lahko z manipulacijo stanja sledimo slovničnim lastnostim stavka.\n",
|
|
"\n",
|
|
"> **Note**: Tukaj je odličen vir za razumevanje notranje strukture LSTM-jev: [Understanding LSTM Networks](https://colah.github.io/posts/2015-08-Understanding-LSTMs/) avtorja Christopherja Olaha.\n",
|
|
"\n",
|
|
"Čeprav se notranja struktura LSTM celice morda zdi zapletena, Keras to implementacijo skriva znotraj plasti `LSTM`, zato je edina stvar, ki jo moramo narediti v zgornjem primeru, zamenjava povratne plasti:\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": [
|
|
"## Dvosmerni in večplastni RNN-ji\n",
|
|
"\n",
|
|
"V naših dosedanjih primerih so rekurentne mreže delovale od začetka zaporedja do konca. To se nam zdi naravno, saj sledi isti smeri, kot beremo ali poslušamo govor. Vendar pa je za scenarije, ki zahtevajo naključni dostop do vhodnega zaporedja, bolj smiselno izvajati rekurentne izračune v obe smeri. RNN-ji, ki omogočajo izračune v obe smeri, se imenujejo **dvosmerni** RNN-ji, in jih lahko ustvarimo tako, da rekurentni sloj ovijemo s posebnim slojem `Bidirectional`.\n",
|
|
"\n",
|
|
"> **Note**: Sloj `Bidirectional` ustvari dve kopiji sloja znotraj njega in nastavi lastnost `go_backwards` ene od teh kopij na `True`, kar omogoča, da gre v nasprotno smer vzdolž zaporedja.\n",
|
|
"\n",
|
|
"Rekurentne mreže, enosmerne ali dvosmerne, zajamejo vzorce znotraj zaporedja in jih shranijo v vektorske stanja ali jih vrnejo kot izhod. Tako kot pri konvolucijskih mrežah lahko zgradimo še en rekurentni sloj, ki sledi prvemu, da zajame vzorce višje ravni, zgrajene iz vzorcev nižje ravni, ki jih je izvlekel prvi sloj. To nas pripelje do pojma **večplastnega RNN-ja**, ki je sestavljen iz dveh ali več rekurentnih mrež, kjer se izhod prejšnjega sloja posreduje naslednjemu sloju kot vhod.\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"*Slika iz [tega odličnega prispevka](https://towardsdatascience.com/from-a-lstm-cell-to-a-multilayer-lstm-network-with-pytorch-2899eb5696f3) Fernanda Lópeza.*\n",
|
|
"\n",
|
|
"Keras omogoča enostavno sestavljanje teh mrež, saj morate modelu le dodati več rekurentnih slojev. Za vse sloje, razen zadnjega, moramo določiti parameter `return_sequences=True`, ker želimo, da sloj vrne vsa vmesna stanja, ne le končnega stanja rekurentnega izračuna.\n",
|
|
"\n",
|
|
"Zgradimo dvoslojni dvosmerni LSTM za naš klasifikacijski problem.\n",
|
|
"\n",
|
|
"> **Note** Ta koda ponovno zahteva precej časa za izvedbo, vendar nam daje najvišjo natančnost, ki smo jo do zdaj videli. Morda se torej splača počakati in preveriti 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-ji za druge naloge\n",
|
|
"\n",
|
|
"Do zdaj smo se osredotočali na uporabo RNN-jev za razvrščanje zaporedij besedila. Vendar pa lahko obravnavajo še veliko več nalog, kot so generiranje besedila in strojno prevajanje — te naloge bomo obravnavali v naslednji enoti.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"\n---\n\n**Omejitev odgovornosti**: \nTa dokument je bil preveden z uporabo storitve za strojno prevajanje [Co-op Translator](https://github.com/Azure/co-op-translator). Čeprav si prizadevamo za natančnost, vas prosimo, da upoštevate, da lahko avtomatizirani prevodi vsebujejo napake ali netočnosti. Izvirni dokument v njegovem izvirnem jeziku je treba obravnavati kot avtoritativni vir. Za ključne informacije priporočamo strokovno človeško prevajanje. Ne prevzemamo odgovornosti za morebitna nesporazuma ali napačne razlage, ki bi nastale zaradi uporabe tega prevoda.\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:56+00:00",
|
|
"source_file": "lessons/5-NLP/16-RNN/RNNTF.ipynb",
|
|
"language_code": "sl"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 4
|
|
} |