460 lines
24 KiB
Plaintext
460 lines
24 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Reti neurali ricorrenti\n",
|
|
"\n",
|
|
"Nel modulo precedente, abbiamo trattato rappresentazioni semantiche ricche del testo. L'architettura che abbiamo utilizzato cattura il significato aggregato delle parole in una frase, ma non tiene conto dell'**ordine** delle parole, poiché l'operazione di aggregazione che segue gli embeddings elimina questa informazione dal testo originale. Poiché questi modelli non sono in grado di rappresentare l'ordine delle parole, non possono risolvere compiti più complessi o ambigui come la generazione di testo o la risposta a domande.\n",
|
|
"\n",
|
|
"Per catturare il significato di una sequenza di testo, utilizzeremo un'architettura di rete neurale chiamata **rete neurale ricorrente**, o RNN. Quando utilizziamo una RNN, passiamo la nostra frase attraverso la rete un token alla volta, e la rete produce uno **stato**, che poi passiamo nuovamente alla rete insieme al token successivo.\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"Data la sequenza di input di token $X_0,\\dots,X_n$, la RNN crea una sequenza di blocchi di rete neurale e allena questa sequenza end-to-end utilizzando la retropropagazione. Ogni blocco di rete prende una coppia $(X_i,S_i)$ come input e produce $S_{i+1}$ come risultato. Lo stato finale $S_n$ o l'output $Y_n$ viene inviato a un classificatore lineare per produrre il risultato. Tutti i blocchi di rete condividono gli stessi pesi e vengono allenati end-to-end con un unico passaggio di retropropagazione.\n",
|
|
"\n",
|
|
"> La figura sopra mostra una rete neurale ricorrente nella forma \"srotolata\" (a sinistra) e nella rappresentazione ricorrente più compatta (a destra). È importante capire che tutte le celle RNN hanno gli stessi **pesi condivisibili**.\n",
|
|
"\n",
|
|
"Poiché i vettori di stato $S_0,\\dots,S_n$ vengono passati attraverso la rete, la RNN è in grado di apprendere dipendenze sequenziali tra le parole. Ad esempio, quando la parola *non* appare in qualche punto della sequenza, può imparare a negare certi elementi all'interno del vettore di stato.\n",
|
|
"\n",
|
|
"All'interno, ogni cella RNN contiene due matrici di pesi: $W_H$ e $W_I$, e un bias $b$. A ogni passo della RNN, dato l'input $X_i$ e lo stato di input $S_i$, lo stato di output viene calcolato come $S_{i+1} = f(W_H\\times S_i + W_I\\times X_i+b)$, dove $f$ è una funzione di attivazione (spesso $\\tanh$).\n",
|
|
"\n",
|
|
"> Per problemi come la generazione di testo (che tratteremo nell'unità successiva) o la traduzione automatica, vogliamo anche ottenere un valore di output a ogni passo della RNN. In questo caso, c'è anche un'altra matrice $W_O$, e l'output viene calcolato come $Y_i=f(W_O\\times S_i+b_O)$.\n",
|
|
"\n",
|
|
"Vediamo come le reti neurali ricorrenti possono aiutarci a classificare il nostro dataset di notizie.\n",
|
|
"\n",
|
|
"> Per l'ambiente sandbox, dobbiamo eseguire la seguente cella per assicurarci che la libreria necessaria sia installata e che i dati siano pre-caricati. Se stai lavorando in locale, puoi saltare la seguente cella.\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": [
|
|
"Quando si addestrano modelli di grandi dimensioni, l'allocazione della memoria GPU può diventare un problema. Potremmo anche dover sperimentare con diverse dimensioni di minibatch, in modo che i dati si adattino alla memoria della GPU, ma l'addestramento sia comunque abbastanza veloce. Se stai eseguendo questo codice sulla tua macchina GPU, puoi provare a regolare la dimensione del minibatch per accelerare l'addestramento.\n",
|
|
"\n",
|
|
"> **Nota**: È noto che alcune versioni dei driver NVidia non rilasciano la memoria dopo l'addestramento del modello. Stiamo eseguendo diversi esempi in questo notebook, e ciò potrebbe causare l'esaurimento della memoria in alcune configurazioni, soprattutto se stai facendo i tuoi esperimenti nello stesso notebook. Se riscontri errori strani quando inizi ad addestrare il modello, potresti voler riavviare il kernel del notebook.\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": [
|
|
"## Classificatore RNN semplice\n",
|
|
"\n",
|
|
"Nel caso di una RNN semplice, ogni unità ricorrente è una rete lineare semplice, che riceve un vettore di input e un vettore di stato, e produce un nuovo vettore di stato. In Keras, questo può essere rappresentato dal livello `SimpleRNN`.\n",
|
|
"\n",
|
|
"Sebbene sia possibile passare direttamente i token codificati one-hot al livello RNN, questa non è una buona idea a causa della loro alta dimensionalità. Pertanto, utilizzeremo un livello di embedding per ridurre la dimensionalità dei vettori di parole, seguito da un livello RNN e, infine, da un classificatore `Dense`.\n",
|
|
"\n",
|
|
"> **Nota**: Nei casi in cui la dimensionalità non è così alta, ad esempio quando si utilizza la tokenizzazione a livello di carattere, potrebbe avere senso passare direttamente i token codificati one-hot alla cella 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": [
|
|
"> **Nota:** Qui utilizziamo un livello di embedding non addestrato per semplicità, ma per ottenere risultati migliori possiamo utilizzare un livello di embedding pre-addestrato con Word2Vec, come descritto nell'unità precedente. Sarebbe un buon esercizio adattare questo codice per funzionare con embedding pre-addestrati.\n",
|
|
"\n",
|
|
"Ora alleniamo la nostra RNN. In generale, le RNN sono piuttosto difficili da addestrare, perché una volta che le celle della RNN vengono srotolate lungo la lunghezza della sequenza, il numero risultante di livelli coinvolti nella retropropagazione diventa piuttosto elevato. Per questo motivo, è necessario selezionare un tasso di apprendimento più basso e addestrare la rete su un dataset più ampio per ottenere buoni risultati. Questo processo può richiedere molto tempo, quindi è preferibile utilizzare una GPU.\n",
|
|
"\n",
|
|
"Per accelerare il processo, alleneremo il modello RNN solo sui titoli delle notizie, omettendo la descrizione. Puoi provare ad allenare il modello includendo anche la descrizione e vedere se riesci a farlo funzionare.\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": [
|
|
"> **Nota** che l'accuratezza potrebbe essere inferiore qui, perché stiamo addestrando solo sui titoli delle notizie.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Rivisitare le sequenze di variabili\n",
|
|
"\n",
|
|
"Ricorda che il livello `TextVectorization` aggiungerà automaticamente dei token di riempimento per uniformare le sequenze di lunghezza variabile in un minibatch. Si scopre che anche questi token partecipano all'addestramento e possono complicare la convergenza del modello.\n",
|
|
"\n",
|
|
"Ci sono diversi approcci che possiamo adottare per ridurre al minimo la quantità di riempimento. Uno di questi è riordinare il dataset in base alla lunghezza delle sequenze e raggruppare tutte le sequenze per dimensione. Questo può essere fatto utilizzando la funzione `tf.data.experimental.bucket_by_sequence_length` (vedi [documentazione](https://www.tensorflow.org/api_docs/python/tf/data/experimental/bucket_by_sequence_length)).\n",
|
|
"\n",
|
|
"Un altro approccio è utilizzare il **masking**. In Keras, alcuni livelli supportano un input aggiuntivo che indica quali token devono essere presi in considerazione durante l'addestramento. Per integrare il masking nel nostro modello, possiamo includere un livello `Masking` separato ([documentazione](https://keras.io/api/layers/core_layers/masking/)), oppure possiamo specificare il parametro `mask_zero=True` nel nostro livello `Embedding`.\n",
|
|
"\n",
|
|
"> **Nota**: Questo addestramento richiederà circa 5 minuti per completare un'epoca sull'intero dataset. Sentiti libero di interrompere l'addestramento in qualsiasi momento se perdi la pazienza. Un'altra opzione è limitare la quantità di dati utilizzati per l'addestramento, aggiungendo la clausola `.take(...)` dopo i dataset `ds_train` e `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": [
|
|
"Ora che stiamo utilizzando il masking, possiamo addestrare il modello sull'intero dataset di titoli e descrizioni.\n",
|
|
"\n",
|
|
"> **Nota**: Hai notato che abbiamo utilizzato un vettorizzatore addestrato sui titoli delle notizie, e non sull'intero corpo dell'articolo? Potenzialmente, questo potrebbe portare all'ignoranza di alcuni token, quindi sarebbe meglio ri-addestrare il vettorizzatore. Tuttavia, l'effetto potrebbe essere molto limitato, quindi continueremo a utilizzare il vettorizzatore pre-addestrato per semplicità.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## LSTM: Memoria a lungo termine\n",
|
|
"\n",
|
|
"Uno dei principali problemi delle RNN è il **vanishing gradient**. Le RNN possono essere piuttosto lunghe e potrebbero avere difficoltà a propagare i gradienti fino al primo strato della rete durante la retropropagazione. Quando ciò accade, la rete non riesce a imparare le relazioni tra token distanti. Un modo per evitare questo problema è introdurre una **gestione esplicita dello stato** utilizzando i **gate**. Le due architetture più comuni che introducono i gate sono la **long short-term memory** (LSTM) e la **gated relay unit** (GRU). Qui ci concentreremo sulle LSTM.\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"Una rete LSTM è organizzata in modo simile a una RNN, ma ci sono due stati che vengono passati da uno strato all'altro: lo stato effettivo $c$ e il vettore nascosto $h$. In ogni unità, il vettore nascosto $h_{t-1}$ viene combinato con l'input $x_t$, e insieme controllano cosa succede allo stato $c_t$ e all'output $h_{t}$ attraverso i **gate**. Ogni gate ha un'attivazione sigmoide (output nell'intervallo $[0,1]$), che può essere considerata come una maschera bitwise quando moltiplicata per il vettore di stato. Le LSTM hanno i seguenti gate (da sinistra a destra nell'immagine sopra):\n",
|
|
"* **Forget gate** che determina quali componenti del vettore $c_{t-1}$ dobbiamo dimenticare e quali far passare. \n",
|
|
"* **Input gate** che determina quante informazioni dal vettore di input e dal vettore nascosto precedente devono essere incorporate nel vettore di stato.\n",
|
|
"* **Output gate** che prende il nuovo vettore di stato e decide quali delle sue componenti verranno utilizzate per produrre il nuovo vettore nascosto $h_t$.\n",
|
|
"\n",
|
|
"Le componenti dello stato $c$ possono essere considerate come flag che possono essere attivati o disattivati. Ad esempio, quando incontriamo il nome *Alice* nella sequenza, supponiamo che si riferisca a una donna e attiviamo il flag nello stato che indica che abbiamo un sostantivo femminile nella frase. Quando successivamente incontriamo le parole *e Tom*, attiveremo il flag che indica che abbiamo un sostantivo plurale. Così, manipolando lo stato, possiamo tenere traccia delle proprietà grammaticali della frase.\n",
|
|
"\n",
|
|
"> **Nota**: Ecco una risorsa eccellente per comprendere i dettagli interni delle LSTM: [Understanding LSTM Networks](https://colah.github.io/posts/2015-08-Understanding-LSTMs/) di Christopher Olah.\n",
|
|
"\n",
|
|
"Sebbene la struttura interna di una cella LSTM possa sembrare complessa, Keras nasconde questa implementazione all'interno del layer `LSTM`, quindi l'unica cosa che dobbiamo fare nell'esempio sopra è sostituire il layer ricorrente:\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": [
|
|
"## Reti neurali ricorrenti bidirezionali e multilivello\n",
|
|
"\n",
|
|
"Nei nostri esempi precedenti, le reti ricorrenti operano dall'inizio di una sequenza fino alla fine. Questo ci sembra naturale perché segue la stessa direzione in cui leggiamo o ascoltiamo un discorso. Tuttavia, per scenari che richiedono accesso casuale alla sequenza di input, ha più senso eseguire il calcolo ricorrente in entrambe le direzioni. Le RNN che consentono calcoli in entrambe le direzioni sono chiamate **RNN bidirezionali**, e possono essere create avvolgendo il livello ricorrente con uno speciale livello `Bidirectional`.\n",
|
|
"\n",
|
|
"> **Note**: Il livello `Bidirectional` crea due copie del livello al suo interno e imposta la proprietà `go_backwards` di una di queste copie su `True`, facendola andare nella direzione opposta lungo la sequenza.\n",
|
|
"\n",
|
|
"Le reti ricorrenti, unidirezionali o bidirezionali, catturano schemi all'interno di una sequenza e li memorizzano in vettori di stato o li restituiscono come output. Come per le reti convoluzionali, possiamo costruire un altro livello ricorrente dopo il primo per catturare schemi di livello superiore, costruiti a partire dagli schemi di livello inferiore estratti dal primo livello. Questo ci porta al concetto di **RNN multilivello**, che consiste in due o più reti ricorrenti, dove l'output del livello precedente viene passato al livello successivo come input.\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"*Immagine tratta da [questo fantastico articolo](https://towardsdatascience.com/from-a-lstm-cell-to-a-multilayer-lstm-network-with-pytorch-2899eb5696f3) di Fernando López.*\n",
|
|
"\n",
|
|
"Keras rende la costruzione di queste reti un compito semplice, perché basta aggiungere più livelli ricorrenti al modello. Per tutti i livelli tranne l'ultimo, dobbiamo specificare il parametro `return_sequences=True`, perché abbiamo bisogno che il livello restituisca tutti gli stati intermedi, e non solo lo stato finale del calcolo ricorrente.\n",
|
|
"\n",
|
|
"Costruiamo una LSTM bidirezionale a due livelli per il nostro problema di classificazione.\n",
|
|
"\n",
|
|
"> **Note** questo codice richiede di nuovo un tempo piuttosto lungo per completarsi, ma ci offre la massima accuratezza che abbiamo visto finora. Quindi forse vale la pena aspettare e vedere il risultato.\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 per altri compiti\n",
|
|
"\n",
|
|
"Fino ad ora, ci siamo concentrati sull'utilizzo delle RNN per classificare sequenze di testo. Tuttavia, possono gestire molti altri compiti, come la generazione di testo e la traduzione automatica — esamineremo questi compiti nella prossima unità.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"\n---\n\n**Disclaimer**: \nQuesto documento è stato tradotto utilizzando il servizio di traduzione automatica [Co-op Translator](https://github.com/Azure/co-op-translator). Sebbene ci impegniamo per garantire l'accuratezza, si prega di notare che le traduzioni automatiche possono contenere errori o imprecisioni. Il documento originale nella sua lingua nativa dovrebbe essere considerato la fonte autorevole. Per informazioni critiche, si raccomanda una traduzione professionale effettuata da un traduttore umano. Non siamo responsabili per eventuali incomprensioni o interpretazioni errate derivanti dall'uso di questa traduzione.\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-28T14:21:09+00:00",
|
|
"source_file": "lessons/5-NLP/16-RNN/RNNTF.ipynb",
|
|
"language_code": "it"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 4
|
|
} |