460 lines
23 KiB
Plaintext
460 lines
23 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Rekurrente neurale netværk\n",
|
|
"\n",
|
|
"I det forrige modul dækkede vi rige semantiske repræsentationer af tekst. Den arkitektur, vi har brugt, fanger den samlede betydning af ordene i en sætning, men den tager ikke højde for **rækkefølgen** af ordene, fordi aggregeringsoperationen, der følger efter embeddings, fjerner denne information fra den oprindelige tekst. Da disse modeller ikke kan repræsentere ords rækkefølge, kan de ikke løse mere komplekse eller tvetydige opgaver som tekstgenerering eller besvarelse af spørgsmål.\n",
|
|
"\n",
|
|
"For at fange betydningen af en tekstsekvens vil vi bruge en neural netværksarkitektur kaldet **rekurrente neurale netværk**, eller RNN. Når vi bruger en RNN, sender vi vores sætning gennem netværket én token ad gangen, og netværket producerer en **tilstand**, som vi derefter sender videre til netværket sammen med den næste token.\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"Givet inputsekvensen af tokens $X_0,\\dots,X_n$, skaber RNN en sekvens af neurale netværksblokke og træner denne sekvens ende-til-ende ved hjælp af backpropagation. Hver netværksblok tager et par $(X_i,S_i)$ som input og producerer $S_{i+1}$ som resultat. Den endelige tilstand $S_n$ eller output $Y_n$ går ind i en lineær klassifikator for at producere resultatet. Alle netværksblokke deler de samme vægte og trænes ende-til-ende ved hjælp af én backpropagation-pass.\n",
|
|
"\n",
|
|
"> Figuren ovenfor viser rekurrente neurale netværk i den udfoldede form (til venstre) og i en mere kompakt rekurrent repræsentation (til højre). Det er vigtigt at forstå, at alle RNN-celler har de samme **delbare vægte**.\n",
|
|
"\n",
|
|
"Da tilstandsvektorerne $S_0,\\dots,S_n$ sendes gennem netværket, er RNN i stand til at lære sekventielle afhængigheder mellem ord. For eksempel, når ordet *ikke* optræder et sted i sekvensen, kan det lære at negere visse elementer inden for tilstandsvektoren.\n",
|
|
"\n",
|
|
"Indeni indeholder hver RNN-celle to vægtmatricer: $W_H$ og $W_I$, samt bias $b$. Ved hvert RNN-trin, givet input $X_i$ og inputtilstand $S_i$, beregnes outputtilstanden som $S_{i+1} = f(W_H\\times S_i + W_I\\times X_i+b)$, hvor $f$ er en aktiveringsfunktion (ofte $\\tanh$).\n",
|
|
"\n",
|
|
"> For problemer som tekstgenerering (som vi vil dække i den næste enhed) eller maskinoversættelse ønsker vi også at få en outputværdi ved hvert RNN-trin. I dette tilfælde er der også en anden matrix $W_O$, og output beregnes som $Y_i=f(W_O\\times S_i+b_O)$.\n",
|
|
"\n",
|
|
"Lad os se, hvordan rekurrente neurale netværk kan hjælpe os med at klassificere vores nyhedsdatamængde.\n",
|
|
"\n",
|
|
"> For sandkassemiljøet skal vi køre følgende celle for at sikre, at det nødvendige bibliotek er installeret, og data er forudhentet. Hvis du kører lokalt, kan du springe den følgende celle over.\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": [
|
|
"Når man træner store modeller, kan GPU-hukommelsestildeling blive et problem. Vi kan også have behov for at eksperimentere med forskellige minibatch-størrelser, så dataen passer ind i vores GPU-hukommelse, samtidig med at træningen er hurtig nok. Hvis du kører denne kode på din egen GPU-maskine, kan du eksperimentere med at justere minibatch-størrelsen for at accelerere træningen.\n",
|
|
"\n",
|
|
"> **Note**: Visse versioner af NVidia-drivere er kendt for ikke at frigive hukommelsen efter træning af modellen. Vi kører flere eksempler i denne notebook, og det kan føre til, at hukommelsen bliver opbrugt i visse opsætninger, især hvis du laver dine egne eksperimenter som en del af den samme notebook. Hvis du støder på mærkelige fejl, når du begynder at træne modellen, kan det være en god idé at genstarte notebook-kernen.\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": [
|
|
"## Enkel RNN-klassifikator\n",
|
|
"\n",
|
|
"I tilfælde af en enkel RNN er hver rekurrent enhed et simpelt lineært netværk, som modtager en inputvektor og en tilstandsvektor og producerer en ny tilstandsvektor. I Keras kan dette repræsenteres ved `SimpleRNN`-laget.\n",
|
|
"\n",
|
|
"Selvom vi kan sende one-hot kodede tokens direkte til RNN-laget, er dette ikke en god idé på grund af deres høje dimensionalitet. Derfor vil vi bruge et embedding-lag til at reducere dimensionaliteten af ordvektorer, efterfulgt af et RNN-lag og til sidst en `Dense`-klassifikator.\n",
|
|
"\n",
|
|
"> **Note**: I tilfælde hvor dimensionaliteten ikke er så høj, for eksempel ved brug af tokenisering på tegnniveau, kan det give mening at sende one-hot kodede tokens direkte ind i RNN-cellen.\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": [
|
|
"> **Bemærk:** Vi bruger her et utrænet embedding-lag for enkelhedens skyld, men for bedre resultater kan vi bruge et forudtrænet embedding-lag ved hjælp af Word2Vec, som beskrevet i den forrige enhed. Det ville være en god øvelse for dig at tilpasse denne kode til at arbejde med forudtrænede embeddings.\n",
|
|
"\n",
|
|
"Lad os nu træne vores RNN. RNN'er er generelt ret svære at træne, fordi når RNN-cellerne bliver udfoldet langs sekvenslængden, bliver antallet af lag, der er involveret i backpropagation, ret stort. Derfor skal vi vælge en mindre læringsrate og træne netværket på et større datasæt for at opnå gode resultater. Dette kan tage ret lang tid, så det er at foretrække at bruge en GPU.\n",
|
|
"\n",
|
|
"For at fremskynde processen vil vi kun træne RNN-modellen på nyhedstitler og udelade beskrivelsen. Du kan prøve at træne med beskrivelsen og se, om du kan få modellen til at træne.\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": [
|
|
"> **Bemærk** at nøjagtigheden sandsynligvis vil være lavere her, fordi vi kun træner på nyhedstitler.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Genbesøg af variabelsekvenser\n",
|
|
"\n",
|
|
"Husk, at `TextVectorization`-laget automatisk vil udfylde sekvenser med variabel længde i en minibatch med pad-tokens. Det viser sig, at disse tokens også deltager i træningen, og de kan komplicere modellens konvergens.\n",
|
|
"\n",
|
|
"Der er flere tilgange, vi kan tage for at minimere mængden af padding. En af dem er at omorganisere datasættet efter sekvenslængde og gruppere alle sekvenser efter størrelse. Dette kan gøres ved hjælp af funktionen `tf.data.experimental.bucket_by_sequence_length` (se [dokumentation](https://www.tensorflow.org/api_docs/python/tf/data/experimental/bucket_by_sequence_length)).\n",
|
|
"\n",
|
|
"En anden tilgang er at bruge **maskering**. I Keras understøtter nogle lag ekstra input, der viser, hvilke tokens der skal tages i betragtning under træning. For at inkorporere maskering i vores model kan vi enten inkludere et separat `Masking`-lag ([dokumentation](https://keras.io/api/layers/core_layers/masking/)), eller vi kan specificere parameteren `mask_zero=True` i vores `Embedding`-lag.\n",
|
|
"\n",
|
|
"> **Note**: Denne træning vil tage omkring 5 minutter at gennemføre én epoke på hele datasættet. Du kan til enhver tid afbryde træningen, hvis du mister tålmodigheden. Hvad du også kan gøre, er at begrænse mængden af data, der bruges til træning, ved at tilføje `.take(...)`-klausul efter `ds_train`- og `ds_test`-datasættene.\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": [
|
|
"Nu hvor vi bruger maskering, kan vi træne modellen på hele datasættet af titler og beskrivelser.\n",
|
|
"\n",
|
|
"> **Bemærk**: Har du bemærket, at vi har brugt en vectorizer, der er trænet på nyhedstitlerne og ikke hele artiklens indhold? Dette kan potentielt føre til, at nogle af tokens bliver ignoreret, så det er bedre at gen-træne vectorizeren. Dog vil det sandsynligvis kun have en meget lille effekt, så vi holder os til den tidligere fortrænede vectorizer for enkelhedens skyld.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## LSTM: Langtids-korttids-hukommelse\n",
|
|
"\n",
|
|
"Et af de største problemer med RNN'er er **forsvindende gradienter**. RNN'er kan være ret lange og kan have svært ved at propagere gradienterne hele vejen tilbage til det første lag i netværket under backpropagation. Når dette sker, kan netværket ikke lære relationer mellem fjerne tokens. En måde at undgå dette problem på er at introducere **eksplicit tilstandshåndtering** ved hjælp af **gates**. De to mest almindelige arkitekturer, der introducerer gates, er **langtids-korttids-hukommelse** (LSTM) og **gated relay unit** (GRU). Vi vil dække LSTM'er her.\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"Et LSTM-netværk er organiseret på en måde, der ligner et RNN, men der er to tilstande, der overføres fra lag til lag: den faktiske tilstand $c$ og den skjulte vektor $h$. Ved hver enhed kombineres den skjulte vektor $h_{t-1}$ med input $x_t$, og sammen styrer de, hvad der sker med tilstanden $c_t$ og outputtet $h_{t}$ gennem **gates**. Hver gate har sigmoid-aktivering (output i intervallet $[0,1]$), som kan betragtes som en bitmaske, når den multipliceres med tilstandsvektoren. LSTM'er har følgende gates (fra venstre mod højre på billedet ovenfor):\n",
|
|
"* **forget gate**, som bestemmer, hvilke komponenter af vektoren $c_{t-1}$ vi skal glemme, og hvilke vi skal lade passere.\n",
|
|
"* **input gate**, som bestemmer, hvor meget information fra inputvektoren og den tidligere skjulte vektor der skal inkorporeres i tilstandsvektoren.\n",
|
|
"* **output gate**, som tager den nye tilstandsvektor og beslutter, hvilke af dens komponenter der skal bruges til at producere den nye skjulte vektor $h_t$.\n",
|
|
"\n",
|
|
"Komponenterne i tilstanden $c$ kan betragtes som flag, der kan tændes og slukkes. For eksempel, når vi støder på navnet *Alice* i sekvensen, gætter vi på, at det refererer til en kvinde, og vi hæver flaget i tilstanden, der angiver, at vi har et kvindeligt substantiv i sætningen. Når vi senere støder på ordene *og Tom*, vil vi hæve flaget, der angiver, at vi har et flertalssubstantiv. På denne måde kan vi ved at manipulere tilstanden holde styr på de grammatiske egenskaber i sætningen.\n",
|
|
"\n",
|
|
"> **Note**: Her er en fremragende ressource til at forstå de interne mekanismer i LSTM'er: [Understanding LSTM Networks](https://colah.github.io/posts/2015-08-Understanding-LSTMs/) af Christopher Olah.\n",
|
|
"\n",
|
|
"Selvom den interne struktur af en LSTM-celle kan se kompleks ud, skjuler Keras denne implementering inde i `LSTM`-laget, så det eneste, vi skal gøre i eksemplet ovenfor, er at erstatte det rekurrente lag:\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": [
|
|
"## Bidirektionelle og flerlags RNN'er\n",
|
|
"\n",
|
|
"I vores eksempler indtil nu har de rekurrente netværk opereret fra starten af en sekvens til slutningen. Dette føles naturligt for os, da det følger den samme retning, som vi læser eller lytter til tale. Men i scenarier, der kræver tilfældig adgang til inputsekvensen, giver det mere mening at udføre den rekurrente beregning i begge retninger. RNN'er, der tillader beregninger i begge retninger, kaldes **bidirektionelle** RNN'er, og de kan oprettes ved at indpakke det rekurrente lag med et specielt `Bidirectional`-lag.\n",
|
|
"\n",
|
|
"> **Note**: `Bidirectional`-laget laver to kopier af laget inden i det og sætter egenskaben `go_backwards` for en af disse kopier til `True`, hvilket får det til at gå i den modsatte retning langs sekvensen.\n",
|
|
"\n",
|
|
"Rekurrente netværk, enten unidirektionelle eller bidirektionelle, fanger mønstre inden for en sekvens og gemmer dem i tilstandsvektorer eller returnerer dem som output. Ligesom med konvolutionelle netværk kan vi bygge et andet rekurrent lag efter det første for at fange mønstre på et højere niveau, bygget fra mønstre på lavere niveau, som det første lag har udtrukket. Dette fører os til begrebet **flerlags RNN**, som består af to eller flere rekurrente netværk, hvor outputtet fra det foregående lag gives videre til det næste lag som input.\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"*Billede fra [denne fantastiske artikel](https://towardsdatascience.com/from-a-lstm-cell-to-a-multilayer-lstm-network-with-pytorch-2899eb5696f3) af Fernando López.*\n",
|
|
"\n",
|
|
"Keras gør det nemt at konstruere disse netværk, fordi du blot skal tilføje flere rekurrente lag til modellen. For alle lag undtagen det sidste skal vi angive parameteren `return_sequences=True`, fordi vi har brug for, at laget returnerer alle mellemliggende tilstande og ikke kun den endelige tilstand af den rekurrente beregning.\n",
|
|
"\n",
|
|
"Lad os bygge en to-lags bidirektionel LSTM til vores klassifikationsproblem.\n",
|
|
"\n",
|
|
"> **Note** denne kode tager igen ret lang tid at fuldføre, men den giver os den højeste nøjagtighed, vi har set indtil videre. Så måske er det værd at vente og se resultatet.\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'er til andre opgaver\n",
|
|
"\n",
|
|
"Indtil nu har vi fokuseret på at bruge RNN'er til at klassificere tekstsekvenser. Men de kan håndtere mange flere opgaver, såsom tekstgenerering og maskinoversættelse — vi vil se nærmere på disse opgaver i den næste enhed.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"\n---\n\n**Ansvarsfraskrivelse**: \nDette dokument er blevet oversat ved hjælp af AI-oversættelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selvom vi bestræber os på nøjagtighed, skal du være opmærksom på, at automatiserede oversættelser kan indeholde fejl eller unøjagtigheder. Det originale dokument på dets oprindelige sprog bør betragtes som den autoritative kilde. For kritisk information anbefales professionel menneskelig oversættelse. Vi påtager os ikke ansvar for eventuelle misforståelser eller fejltolkninger, der opstår som følge af brugen af denne oversættelse.\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-28T17:41:31+00:00",
|
|
"source_file": "lessons/5-NLP/16-RNN/RNNTF.ipynb",
|
|
"language_code": "da"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 4
|
|
} |