460 lines
24 KiB
Plaintext
460 lines
24 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Recurrente neurale netwerken\n",
|
|
"\n",
|
|
"In de vorige module hebben we rijke semantische representaties van tekst behandeld. De architectuur die we hebben gebruikt, vangt de geaggregeerde betekenis van woorden in een zin op, maar houdt geen rekening met de **volgorde** van de woorden, omdat de aggregatiebewerking die volgt op de embeddings deze informatie uit de oorspronkelijke tekst verwijdert. Omdat deze modellen de woordvolgorde niet kunnen representeren, kunnen ze geen complexere of dubbelzinnige taken oplossen, zoals tekstgeneratie of vraagbeantwoording.\n",
|
|
"\n",
|
|
"Om de betekenis van een tekstsequentie vast te leggen, gebruiken we een neurale netwerkarchitectuur genaamd **recurrente neurale netwerken**, of RNN. Bij het gebruik van een RNN voeren we onze zin één token tegelijk door het netwerk, en het netwerk produceert een bepaalde **toestand**, die we vervolgens weer doorgeven aan het netwerk met het volgende token.\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"Gegeven de invoersequentie van tokens $X_0,\\dots,X_n$, creëert de RNN een reeks neurale netwerkblokken en traint deze reeks end-to-end met behulp van backpropagation. Elk netwerkblok neemt een paar $(X_i,S_i)$ als invoer en produceert $S_{i+1}$ als resultaat. De uiteindelijke toestand $S_n$ of uitvoer $Y_n$ gaat naar een lineaire classifier om het resultaat te produceren. Alle netwerkblokken delen dezelfde gewichten en worden end-to-end getraind met één backpropagation-pass.\n",
|
|
"\n",
|
|
"> De bovenstaande afbeelding toont een recurrent neuraal netwerk in de uitgevouwen vorm (links) en in een meer compacte recurrente representatie (rechts). Het is belangrijk te begrijpen dat alle RNN-cellen dezelfde **deelbare gewichten** hebben.\n",
|
|
"\n",
|
|
"Omdat toestandsvectoren $S_0,\\dots,S_n$ door het netwerk worden doorgegeven, kan de RNN sequentiële afhankelijkheden tussen woorden leren. Bijvoorbeeld, wanneer het woord *niet* ergens in de sequentie voorkomt, kan het leren om bepaalde elementen binnen de toestandsvector te ontkennen.\n",
|
|
"\n",
|
|
"Binnenin bevat elke RNN-cel twee gewichtsmatrices: $W_H$ en $W_I$, en een bias $b$. Bij elke RNN-stap, gegeven invoer $X_i$ en invoertoestand $S_i$, wordt de uitvoertoestand berekend als $S_{i+1} = f(W_H\\times S_i + W_I\\times X_i+b)$, waarbij $f$ een activatiefunctie is (vaak $\\tanh$).\n",
|
|
"\n",
|
|
"> Voor problemen zoals tekstgeneratie (die we in de volgende eenheid zullen behandelen) of machinale vertaling willen we ook een uitvoerwaarde bij elke RNN-stap verkrijgen. In dit geval is er ook een andere matrix $W_O$, en wordt de uitvoer berekend als $Y_i=f(W_O\\times S_i+b_O)$.\n",
|
|
"\n",
|
|
"Laten we eens kijken hoe recurrente neurale netwerken ons kunnen helpen om onze nieuwsdataset te classificeren.\n",
|
|
"\n",
|
|
"> Voor de sandbox-omgeving moeten we de volgende cel uitvoeren om ervoor te zorgen dat de vereiste bibliotheek is geïnstalleerd en de gegevens zijn vooraf opgehaald. Als je lokaal werkt, kun je de volgende cel overslaan.\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": [
|
|
"Bij het trainen van grote modellen kan GPU-geheugentoewijzing een probleem worden. We moeten mogelijk ook experimenteren met verschillende minibatch-groottes, zodat de data in het GPU-geheugen past en de training toch snel genoeg verloopt. Als je deze code op je eigen GPU-machine uitvoert, kun je experimenteren met het aanpassen van de minibatch-grootte om de training te versnellen.\n",
|
|
"\n",
|
|
"> **Opmerking**: Van bepaalde versies van NVidia-drivers is bekend dat ze het geheugen niet vrijgeven na het trainen van het model. We voeren meerdere voorbeelden uit in deze notebooks, en dit kan ertoe leiden dat het geheugen uitgeput raakt in bepaalde configuraties, vooral als je je eigen experimenten uitvoert binnen dezelfde notebook. Als je vreemde fouten tegenkomt bij het starten van de modeltraining, kan het helpen om de notebookkernel opnieuw te starten.\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": [
|
|
"## Eenvoudige RNN-classificator\n",
|
|
"\n",
|
|
"Bij een eenvoudige RNN is elke recurrente eenheid een simpel lineair netwerk, dat een invoervector en een toestandsvector ontvangt en een nieuwe toestandsvector produceert. In Keras kan dit worden weergegeven door de `SimpleRNN`-laag.\n",
|
|
"\n",
|
|
"Hoewel we één-hot gecodeerde tokens direct aan de RNN-laag kunnen doorgeven, is dit geen goed idee vanwege hun hoge dimensionaliteit. Daarom gebruiken we een embedding-laag om de dimensionaliteit van woordvectoren te verlagen, gevolgd door een RNN-laag en tot slot een `Dense`-classificator.\n",
|
|
"\n",
|
|
"> **Opmerking**: In gevallen waarin de dimensionaliteit niet zo hoog is, bijvoorbeeld bij het gebruik van tokenisatie op karakterniveau, kan het logisch zijn om één-hot gecodeerde tokens direct in de RNN-cel door te geven.\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": [
|
|
"> **Opmerking:** We gebruiken hier een niet-getrainde embedding-laag voor eenvoud, maar voor betere resultaten kunnen we een vooraf getrainde embedding-laag gebruiken met Word2Vec, zoals beschreven in de vorige eenheid. Het zou een goede oefening zijn om deze code aan te passen om te werken met vooraf getrainde embeddings.\n",
|
|
"\n",
|
|
"Laten we nu onze RNN trainen. RNN's zijn over het algemeen vrij moeilijk te trainen, omdat wanneer de RNN-cellen worden uitgerold over de lengte van de sequentie, het resulterende aantal lagen dat betrokken is bij backpropagation behoorlijk groot is. Daarom moeten we een kleinere leersnelheid selecteren en het netwerk trainen op een grotere dataset om goede resultaten te behalen. Dit kan behoorlijk lang duren, dus het gebruik van een GPU heeft de voorkeur.\n",
|
|
"\n",
|
|
"Om het proces te versnellen, zullen we het RNN-model alleen trainen op nieuwstitels en de beschrijving weglaten. Je kunt proberen te trainen met de beschrijving en kijken of je het model kunt laten trainen.\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": [
|
|
"> **Opmerking** dat de nauwkeurigheid hier waarschijnlijk lager zal zijn, omdat we alleen trainen op nieuwstitels.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Variabele sequenties herzien\n",
|
|
"\n",
|
|
"Onthoud dat de `TextVectorization`-laag automatisch sequenties van variabele lengte in een minibatch opvult met pad-tokens. Het blijkt dat deze tokens ook deelnemen aan de training, en dit kan de convergentie van het model bemoeilijken.\n",
|
|
"\n",
|
|
"Er zijn verschillende benaderingen die we kunnen gebruiken om de hoeveelheid padding te minimaliseren. Een daarvan is om de dataset te herschikken op basis van sequentielengte en alle sequenties per grootte te groeperen. Dit kan worden gedaan met behulp van de functie `tf.data.experimental.bucket_by_sequence_length` (zie [documentatie](https://www.tensorflow.org/api_docs/python/tf/data/experimental/bucket_by_sequence_length)).\n",
|
|
"\n",
|
|
"Een andere aanpak is het gebruik van **maskering**. In Keras ondersteunen sommige lagen extra invoer die aangeeft welke tokens in aanmerking moeten worden genomen tijdens de training. Om maskering in ons model op te nemen, kunnen we ofwel een aparte `Masking`-laag toevoegen ([docs](https://keras.io/api/layers/core_layers/masking/)), of we kunnen de parameter `mask_zero=True` specificeren in onze `Embedding`-laag.\n",
|
|
"\n",
|
|
"> **Opmerking**: Deze training duurt ongeveer 5 minuten om één epoch op de hele dataset te voltooien. Voel je vrij om de training op elk moment te onderbreken als je ongeduldig wordt. Wat je ook kunt doen, is de hoeveelheid data die wordt gebruikt voor training beperken door een `.take(...)`-clausule toe te voegen na de `ds_train`- en `ds_test`-datasets.\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 we gebruik maken van masking, kunnen we het model trainen op de volledige dataset van titels en beschrijvingen.\n",
|
|
"\n",
|
|
"> **Opmerking**: Is het je opgevallen dat we een vectorizer hebben gebruikt die is getraind op de nieuwstitels, en niet op de volledige tekst van het artikel? Mogelijk kan dit ervoor zorgen dat sommige tokens worden genegeerd, dus het is beter om de vectorizer opnieuw te trainen. Echter, het effect hiervan zal waarschijnlijk minimaal zijn, dus we blijven voor de eenvoud bij de eerder getrainde vectorizer.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## LSTM: Long short-term memory\n",
|
|
"\n",
|
|
"Een van de belangrijkste problemen van RNN's is **vervaagde gradiënten**. RNN's kunnen behoorlijk lang zijn en hebben mogelijk moeite om de gradiënten tijdens backpropagation helemaal terug te voeren naar de eerste laag van het netwerk. Wanneer dit gebeurt, kan het netwerk geen relaties leren tussen verre tokens. Een manier om dit probleem te vermijden is door **expliciet toestandsbeheer** te introduceren met behulp van **poorten**. De twee meest voorkomende architecturen die poorten introduceren zijn **long short-term memory** (LSTM) en **gated relay unit** (GRU). Hier behandelen we LSTMs.\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"Een LSTM-netwerk is georganiseerd op een manier die lijkt op een RNN, maar er zijn twee toestanden die van laag naar laag worden doorgegeven: de werkelijke toestand $c$ en de verborgen vector $h$. Bij elke eenheid wordt de verborgen vector $h_{t-1}$ gecombineerd met de invoer $x_t$, en samen bepalen ze wat er gebeurt met de toestand $c_t$ en de uitvoer $h_{t}$ via **poorten**. Elke poort heeft een sigmoid-activatie (uitvoer in het bereik $[0,1]$), die kan worden gezien als een bitmasker wanneer vermenigvuldigd met de toestandsvector. LSTMs hebben de volgende poorten (van links naar rechts op de bovenstaande afbeelding):\n",
|
|
"* **vergeetpoort**, die bepaalt welke componenten van de vector $c_{t-1}$ we moeten vergeten en welke we moeten doorgeven.\n",
|
|
"* **invoegpoort**, die bepaalt hoeveel informatie van de invoervector en de vorige verborgen vector moet worden opgenomen in de toestandsvector.\n",
|
|
"* **uitvoerpoort**, die de nieuwe toestandsvector neemt en beslist welke van zijn componenten zullen worden gebruikt om de nieuwe verborgen vector $h_t$ te produceren.\n",
|
|
"\n",
|
|
"De componenten van de toestand $c$ kunnen worden gezien als vlaggen die aan- en uitgezet kunnen worden. Bijvoorbeeld, wanneer we de naam *Alice* tegenkomen in de reeks, vermoeden we dat het verwijst naar een vrouw, en zetten we de vlag aan in de toestand die aangeeft dat we een vrouwelijk zelfstandig naamwoord in de zin hebben. Wanneer we vervolgens de woorden *en Tom* tegenkomen, zetten we de vlag aan die aangeeft dat we een meervoudig zelfstandig naamwoord hebben. Door de toestand te manipuleren, kunnen we dus de grammaticale eigenschappen van de zin bijhouden.\n",
|
|
"\n",
|
|
"> **Note**: Hier is een geweldige bron om de interne werking van LSTMs te begrijpen: [Understanding LSTM Networks](https://colah.github.io/posts/2015-08-Understanding-LSTMs/) door Christopher Olah.\n",
|
|
"\n",
|
|
"Hoewel de interne structuur van een LSTM-cel complex kan lijken, verbergt Keras deze implementatie in de `LSTM`-laag, dus het enige wat we hoeven te doen in het bovenstaande voorbeeld is de recurrente laag te vervangen:\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": [
|
|
"## Bidirectionele en meerlaagse RNN's\n",
|
|
"\n",
|
|
"In onze voorbeelden tot nu toe werken de recurrente netwerken van het begin van een reeks tot het einde. Dit voelt natuurlijk aan, omdat het dezelfde richting volgt waarin we lezen of naar spraak luisteren. Voor scenario's waarbij willekeurige toegang tot de invoerreeks nodig is, is het echter logischer om de recurrente berekening in beide richtingen uit te voeren. RNN's die berekeningen in beide richtingen toestaan, worden **bidirectionele** RNN's genoemd, en ze kunnen worden gemaakt door de recurrente laag te omhullen met een speciale `Bidirectional` laag.\n",
|
|
"\n",
|
|
"> **Note**: De `Bidirectional` laag maakt twee kopieën van de laag binnenin, en stelt de eigenschap `go_backwards` van een van die kopieën in op `True`, waardoor deze in de tegenovergestelde richting langs de reeks gaat.\n",
|
|
"\n",
|
|
"Recurrente netwerken, unidirectioneel of bidirectioneel, leggen patronen binnen een reeks vast en slaan deze op in toestandsvectoren of geven ze terug als output. Net zoals bij convolutionele netwerken kunnen we een andere recurrente laag bouwen na de eerste om hogere-orde patronen vast te leggen, opgebouwd uit lagere-orde patronen die door de eerste laag zijn geëxtraheerd. Dit leidt ons naar het concept van een **meerlaagse RNN**, die bestaat uit twee of meer recurrente netwerken, waarbij de output van de vorige laag als invoer wordt doorgegeven aan de volgende laag.\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"*Afbeelding afkomstig uit [deze geweldige post](https://towardsdatascience.com/from-a-lstm-cell-to-a-multilayer-lstm-network-with-pytorch-2899eb5696f3) van Fernando López.*\n",
|
|
"\n",
|
|
"Keras maakt het bouwen van deze netwerken eenvoudig, omdat je alleen maar meer recurrente lagen aan het model hoeft toe te voegen. Voor alle lagen behalve de laatste moeten we de parameter `return_sequences=True` specificeren, omdat we willen dat de laag alle tussenliggende toestanden retourneert, en niet alleen de eindtoestand van de recurrente berekening.\n",
|
|
"\n",
|
|
"Laten we een tweelaagse bidirectionele LSTM bouwen voor ons classificatieprobleem.\n",
|
|
"\n",
|
|
"> **Note** deze code duurt opnieuw vrij lang om te voltooien, maar het geeft ons de hoogste nauwkeurigheid die we tot nu toe hebben gezien. Dus misschien is het de moeite waard om te wachten en het resultaat te bekijken.\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's voor andere taken\n",
|
|
"\n",
|
|
"Tot nu toe hebben we ons gericht op het gebruik van RNN's om tekstreeksen te classificeren. Maar ze kunnen nog veel meer taken aan, zoals het genereren van tekst en machinevertaling — we zullen die taken in de volgende eenheid behandelen.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"\n---\n\n**Disclaimer**: \nDit document is vertaald met behulp van de AI-vertalingsservice [Co-op Translator](https://github.com/Azure/co-op-translator). Hoewel we streven naar nauwkeurigheid, dient u zich ervan bewust te zijn dat geautomatiseerde vertalingen fouten of onnauwkeurigheden kunnen bevatten. Het originele document in zijn oorspronkelijke taal moet worden beschouwd als de gezaghebbende bron. Voor cruciale informatie wordt professionele menselijke vertaling aanbevolen. Wij zijn niet aansprakelijk voor eventuele misverstanden of verkeerde interpretaties die voortvloeien uit het gebruik van deze vertaling.\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-28T21:47:24+00:00",
|
|
"source_file": "lessons/5-NLP/16-RNN/RNNTF.ipynb",
|
|
"language_code": "nl"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 4
|
|
} |