460 lines
24 KiB
Plaintext
460 lines
24 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Rețele neuronale recurente\n",
|
|
"\n",
|
|
"În modulul anterior, am discutat despre reprezentările semantice bogate ale textului. Arhitectura pe care am folosit-o captează semnificația agregată a cuvintelor dintr-o propoziție, dar nu ia în considerare **ordinea** cuvintelor, deoarece operația de agregare care urmează după încorporări elimină această informație din textul original. Deoarece aceste modele nu pot reprezenta ordonarea cuvintelor, ele nu pot rezolva sarcini mai complexe sau ambigue, cum ar fi generarea de text sau răspunsul la întrebări.\n",
|
|
"\n",
|
|
"Pentru a capta semnificația unei secvențe de text, vom folosi o arhitectură de rețea neuronală numită **rețea neuronală recurentă**, sau RNN. Când utilizăm o RNN, trecem propoziția prin rețea, un token pe rând, iar rețeaua produce un **statut**, pe care îl transmitem din nou rețelei împreună cu următorul token.\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"Având secvența de intrare de tokeni $X_0,\\dots,X_n$, RNN creează o secvență de blocuri de rețea neuronală și antrenează această secvență cap-coadă folosind retropropagarea. Fiecare bloc de rețea ia o pereche $(X_i,S_i)$ ca intrare și produce $S_{i+1}$ ca rezultat. Statutul final $S_n$ sau ieșirea $Y_n$ este transmisă unui clasificator liniar pentru a produce rezultatul. Toate blocurile de rețea împărtășesc aceleași greutăți și sunt antrenate cap-coadă folosind o singură trecere de retropropagare.\n",
|
|
"\n",
|
|
"> Figura de mai sus arată rețeaua neuronală recurentă în formă desfășurată (în stânga) și într-o reprezentare recurentă mai compactă (în dreapta). Este important de realizat că toate celulele RNN au aceleași **greutăți partajabile**.\n",
|
|
"\n",
|
|
"Deoarece vectorii de statut $S_0,\\dots,S_n$ sunt transmiși prin rețea, RNN este capabilă să învețe dependențele secvențiale dintre cuvinte. De exemplu, atunci când cuvântul *nu* apare undeva în secvență, poate învăța să nege anumite elemente din vectorul de statut.\n",
|
|
"\n",
|
|
"În interior, fiecare celulă RNN conține două matrici de greutăți: $W_H$ și $W_I$, și un bias $b$. La fiecare pas RNN, având intrarea $X_i$ și statutul de intrare $S_i$, statutul de ieșire este calculat ca $S_{i+1} = f(W_H\\times S_i + W_I\\times X_i+b)$, unde $f$ este o funcție de activare (adesea $\\tanh$).\n",
|
|
"\n",
|
|
"> Pentru probleme precum generarea de text (pe care o vom acoperi în unitatea următoare) sau traducerea automată, dorim să obținem și o valoare de ieșire la fiecare pas RNN. În acest caz, există și o altă matrice $W_O$, iar ieșirea este calculată ca $Y_i=f(W_O\\times S_i+b_O)$.\n",
|
|
"\n",
|
|
"Să vedem cum rețelele neuronale recurente ne pot ajuta să clasificăm setul nostru de date de știri.\n",
|
|
"\n",
|
|
"> Pentru mediul sandbox, trebuie să rulăm celula următoare pentru a ne asigura că biblioteca necesară este instalată și datele sunt preluate. Dacă rulați local, puteți sări peste celula următoare.\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": [
|
|
"Când antrenăm modele mari, alocarea memoriei GPU poate deveni o problemă. De asemenea, este posibil să fie nevoie să experimentăm cu dimensiuni diferite ale minibatch-urilor, astfel încât datele să încapă în memoria GPU, iar antrenarea să fie suficient de rapidă. Dacă rulați acest cod pe propria mașină GPU, puteți experimenta ajustarea dimensiunii minibatch-ului pentru a accelera antrenarea.\n",
|
|
"\n",
|
|
"> **Notă**: Se știe că anumite versiuni ale driverelor NVidia nu eliberează memoria după antrenarea modelului. Rulăm mai multe exemple în acest notebook, iar acest lucru ar putea duce la epuizarea memoriei în anumite configurații, mai ales dacă efectuați propriile experimente în cadrul aceluiași notebook. Dacă întâmpinați erori ciudate la începutul antrenării modelului, este posibil să fie necesar să reporniți kernelul notebook-ului.\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": [
|
|
"## Clasificator RNN simplu\n",
|
|
"\n",
|
|
"În cazul unui RNN simplu, fiecare unitate recurentă este o rețea liniară simplă, care primește un vector de intrare și un vector de stare, și produce un nou vector de stare. În Keras, acest lucru poate fi reprezentat prin stratul `SimpleRNN`.\n",
|
|
"\n",
|
|
"Deși putem transmite direct tokenii codificați one-hot către stratul RNN, aceasta nu este o idee bună din cauza dimensionalității lor ridicate. Prin urmare, vom folosi un strat de embedding pentru a reduce dimensionalitatea vectorilor de cuvinte, urmat de un strat RNN și, în final, un clasificator `Dense`.\n",
|
|
"\n",
|
|
"> **Notă**: În cazurile în care dimensionalitatea nu este atât de mare, de exemplu atunci când se utilizează tokenizarea la nivel de caractere, ar putea avea sens să se transmită tokenii codificați one-hot direct în celula 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": [
|
|
"> **Notă:** Folosim un strat de încorporare neantrenat aici pentru simplitate, dar pentru rezultate mai bune putem utiliza un strat de încorporare preantrenat folosind Word2Vec, așa cum s-a descris în unitatea anterioară. Ar fi un exercițiu bun pentru tine să adaptezi acest cod pentru a funcționa cu încorporări preantrenate.\n",
|
|
"\n",
|
|
"Acum să antrenăm RNN-ul nostru. În general, RNN-urile sunt destul de dificile de antrenat, deoarece, odată ce celulele RNN sunt desfășurate pe lungimea secvenței, numărul de straturi implicate în retropropagare devine foarte mare. Prin urmare, trebuie să selectăm o rată de învățare mai mică și să antrenăm rețeaua pe un set de date mai mare pentru a obține rezultate bune. Acest lucru poate dura destul de mult timp, așa că utilizarea unui GPU este de preferat.\n",
|
|
"\n",
|
|
"Pentru a accelera lucrurile, vom antrena modelul RNN doar pe titlurile știrilor, omisând descrierea. Poți încerca să antrenezi și cu descrierea și să vezi dacă reușești să faci modelul să se antreneze.\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": [
|
|
"> **Notă** că acuratețea este probabil mai scăzută aici, deoarece antrenăm doar pe titluri de știri.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Revizuirea secvențelor variabile\n",
|
|
"\n",
|
|
"Rețineți că stratul `TextVectorization` va adăuga automat token-uri de umplere pentru a completa secvențele de lungime variabilă într-un minibatch. Se pare că acești token-uri participă și la antrenament, ceea ce poate complica convergența modelului.\n",
|
|
"\n",
|
|
"Există mai multe abordări pe care le putem adopta pentru a minimiza cantitatea de umplere. Una dintre ele este reordonarea dataset-ului în funcție de lungimea secvenței și gruparea tuturor secvențelor după dimensiune. Acest lucru poate fi realizat folosind funcția `tf.data.experimental.bucket_by_sequence_length` (vezi [documentația](https://www.tensorflow.org/api_docs/python/tf/data/experimental/bucket_by_sequence_length)).\n",
|
|
"\n",
|
|
"O altă abordare este utilizarea **maskării**. În Keras, unele straturi acceptă un input suplimentar care indică ce token-uri ar trebui luate în considerare în timpul antrenamentului. Pentru a integra maskarea în modelul nostru, putem fie să includem un strat separat `Masking` ([documentație](https://keras.io/api/layers/core_layers/masking/)), fie să specificăm parametrul `mask_zero=True` al stratului nostru `Embedding`.\n",
|
|
"\n",
|
|
"> **Note**: Acest antrenament va dura aproximativ 5 minute pentru a finaliza o epocă pe întregul dataset. Simțiți-vă liberi să întrerupeți antrenamentul în orice moment dacă vă pierdeți răbdarea. De asemenea, puteți limita cantitatea de date utilizate pentru antrenament, adăugând clauza `.take(...)` după dataset-urile `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": [
|
|
"Acum că folosim mascarea, putem antrena modelul pe întregul set de date de titluri și descrieri.\n",
|
|
"\n",
|
|
"> **Notă**: Ai observat că am folosit un vectorizator antrenat pe titlurile știrilor și nu pe întregul corp al articolului? Potențial, acest lucru poate face ca unii dintre tokeni să fie ignorați, așa că ar fi mai bine să re-antrenăm vectorizatorul. Totuși, efectul ar putea fi foarte mic, așa că vom rămâne la vectorizatorul pre-antrenat anterior pentru simplitate.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## LSTM: Memorie pe termen lung și scurt\n",
|
|
"\n",
|
|
"Una dintre principalele probleme ale RNN-urilor este **dispariția gradientului**. RNN-urile pot fi destul de lungi și pot întâmpina dificultăți în propagarea gradientelor până la primul strat al rețelei în timpul backpropagation-ului. Când se întâmplă acest lucru, rețeaua nu poate învăța relațiile dintre tokenii distanți. O modalitate de a evita această problemă este introducerea **gestionării explicite a stării** prin utilizarea **porților**. Cele mai comune două arhitecturi care introduc porți sunt **memoria pe termen lung și scurt** (LSTM) și **unitatea de retransmisie cu porți** (GRU). Vom discuta despre LSTM-uri aici.\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"O rețea LSTM este organizată într-un mod similar cu un RNN, dar există două stări care sunt transmise de la un strat la altul: starea propriu-zisă $c$ și vectorul ascuns $h$. La fiecare unitate, vectorul ascuns $h_{t-1}$ este combinat cu intrarea $x_t$, iar împreună controlează ce se întâmplă cu starea $c_t$ și ieșirea $h_{t}$ prin intermediul **porților**. Fiecare poartă are o activare sigmoidă (ieșire în intervalul $[0,1]$), care poate fi considerată ca o mască bit cu bit atunci când este înmulțită cu vectorul de stare. LSTM-urile au următoarele porți (de la stânga la dreapta în imaginea de mai sus):\n",
|
|
"* **Poarta de uitare**, care determină ce componente ale vectorului $c_{t-1}$ trebuie să uităm și care să fie transmise mai departe.\n",
|
|
"* **Poarta de intrare**, care determină câtă informație din vectorul de intrare și vectorul ascuns anterior ar trebui să fie încorporată în vectorul de stare.\n",
|
|
"* **Poarta de ieșire**, care ia noul vector de stare și decide care dintre componentele sale vor fi utilizate pentru a produce noul vector ascuns $h_t$.\n",
|
|
"\n",
|
|
"Componentele stării $c$ pot fi considerate ca niște steaguri care pot fi activate sau dezactivate. De exemplu, când întâlnim numele *Alice* într-o secvență, presupunem că se referă la o femeie și ridicăm steagul în stare care indică faptul că avem un substantiv feminin în propoziție. Când întâlnim ulterior cuvintele *și Tom*, vom ridica steagul care indică faptul că avem un substantiv la plural. Astfel, prin manipularea stării, putem urmări proprietățile gramaticale ale propoziției.\n",
|
|
"\n",
|
|
"> **Note**: Iată o resursă excelentă pentru a înțelege structura internă a LSTM-urilor: [Understanding LSTM Networks](https://colah.github.io/posts/2015-08-Understanding-LSTMs/) de Christopher Olah.\n",
|
|
"\n",
|
|
"Deși structura internă a unei celule LSTM poate părea complexă, Keras ascunde această implementare în interiorul stratului `LSTM`, astfel încât singurul lucru pe care trebuie să-l facem în exemplul de mai sus este să înlocuim stratul recurent:\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": [
|
|
"## RNN-uri bidirecționale și multilayer\n",
|
|
"\n",
|
|
"În exemplele noastre de până acum, rețelele recurente operează de la începutul unei secvențe până la sfârșit. Acest lucru ni se pare natural, deoarece urmează aceeași direcție în care citim sau ascultăm vorbirea. Totuși, pentru scenarii care necesită acces aleatoriu la secvența de intrare, are mai mult sens să rulăm calculul recurent în ambele direcții. RNN-urile care permit calcule în ambele direcții se numesc **RNN-uri bidirecționale**, și pot fi create prin învelirea stratului recurent cu un strat special `Bidirectional`.\n",
|
|
"\n",
|
|
"> **Note**: Stratul `Bidirectional` face două copii ale stratului din interiorul său și setează proprietatea `go_backwards` a uneia dintre aceste copii la `True`, făcând-o să meargă în direcția opusă de-a lungul secvenței.\n",
|
|
"\n",
|
|
"Rețelele recurente, unidirecționale sau bidirecționale, captează modele dintr-o secvență și le stochează în vectori de stare sau le returnează ca ieșire. La fel ca în cazul rețelelor convoluționale, putem construi un alt strat recurent care urmează primului pentru a capta modele de nivel superior, construite din modelele de nivel inferior extrase de primul strat. Acest lucru ne conduce la noțiunea de **RNN multilayer**, care constă din două sau mai multe rețele recurente, unde ieșirea stratului anterior este transmisă stratului următor ca intrare.\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"*Imagine din [această postare minunată](https://towardsdatascience.com/from-a-lstm-cell-to-a-multilayer-lstm-network-with-pytorch-2899eb5696f3) de Fernando López.*\n",
|
|
"\n",
|
|
"Keras face construirea acestor rețele o sarcină ușoară, deoarece trebuie doar să adăugați mai multe straturi recurente la model. Pentru toate straturile, cu excepția ultimului, trebuie să specificăm parametrul `return_sequences=True`, deoarece avem nevoie ca stratul să returneze toate stările intermediare, nu doar starea finală a calculului recurent.\n",
|
|
"\n",
|
|
"Să construim un LSTM bidirecțional cu două straturi pentru problema noastră de clasificare.\n",
|
|
"\n",
|
|
"> **Note** acest cod durează din nou destul de mult timp pentru a se finaliza, dar ne oferă cea mai mare acuratețe pe care am văzut-o până acum. Deci, poate merită să așteptăm și să vedem rezultatul.\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-uri pentru alte sarcini\n",
|
|
"\n",
|
|
"Până acum, ne-am concentrat pe utilizarea RNN-urilor pentru clasificarea secvențelor de text. Dar ele pot gestiona multe alte sarcini, cum ar fi generarea de text și traducerea automată — vom analiza aceste sarcini în unitatea următoare.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"\n---\n\n**Declinare de responsabilitate**: \nAcest document a fost tradus folosind serviciul de traducere AI [Co-op Translator](https://github.com/Azure/co-op-translator). Deși ne străduim să asigurăm acuratețea, vă rugăm să rețineți că traducerile automate pot conține erori sau inexactități. Documentul original în limba sa natală ar trebui considerat sursa autoritară. Pentru informații critice, se recomandă traducerea profesională realizată de un specialist uman. Nu ne asumăm responsabilitatea pentru eventualele neînțelegeri sau interpretări greșite care pot apărea din utilizarea acestei traduceri.\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-30T00:55:46+00:00",
|
|
"source_file": "lessons/5-NLP/16-RNN/RNNTF.ipynb",
|
|
"language_code": "ro"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 4
|
|
} |