460 lines
24 KiB
Plaintext
460 lines
24 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Redes neuronales recurrentes\n",
|
|
"\n",
|
|
"En el módulo anterior, cubrimos representaciones semánticas ricas de texto. La arquitectura que hemos estado utilizando captura el significado agregado de las palabras en una oración, pero no toma en cuenta el **orden** de las palabras, ya que la operación de agregación que sigue a las incrustaciones elimina esta información del texto original. Debido a que estos modelos no pueden representar el orden de las palabras, no pueden resolver tareas más complejas o ambiguas como la generación de texto o la respuesta a preguntas.\n",
|
|
"\n",
|
|
"Para capturar el significado de una secuencia de texto, utilizaremos una arquitectura de red neuronal llamada **red neuronal recurrente**, o RNN. Al usar una RNN, pasamos nuestra oración a través de la red un token a la vez, y la red produce un **estado**, que luego pasamos nuevamente a la red junto con el siguiente token.\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"Dada la secuencia de entrada de tokens $X_0,\\dots,X_n$, la RNN crea una secuencia de bloques de red neuronal y entrena esta secuencia de extremo a extremo utilizando retropropagación. Cada bloque de red toma un par $(X_i,S_i)$ como entrada y produce $S_{i+1}$ como resultado. El estado final $S_n$ o la salida $Y_n$ se pasa a un clasificador lineal para producir el resultado. Todos los bloques de red comparten los mismos pesos y se entrenan de extremo a extremo utilizando una única pasada de retropropagación.\n",
|
|
"\n",
|
|
"> La figura anterior muestra una red neuronal recurrente en su forma desplegada (a la izquierda) y en una representación recurrente más compacta (a la derecha). Es importante entender que todas las celdas RNN tienen los mismos **pesos compartidos**.\n",
|
|
"\n",
|
|
"Debido a que los vectores de estado $S_0,\\dots,S_n$ se pasan a través de la red, la RNN es capaz de aprender dependencias secuenciales entre palabras. Por ejemplo, cuando la palabra *no* aparece en algún lugar de la secuencia, puede aprender a negar ciertos elementos dentro del vector de estado.\n",
|
|
"\n",
|
|
"Internamente, cada celda RNN contiene dos matrices de pesos: $W_H$ y $W_I$, y un sesgo $b$. En cada paso de la RNN, dado el input $X_i$ y el estado de entrada $S_i$, el estado de salida se calcula como $S_{i+1} = f(W_H\\times S_i + W_I\\times X_i+b)$, donde $f$ es una función de activación (a menudo $\\tanh$).\n",
|
|
"\n",
|
|
"> Para problemas como la generación de texto (que cubriremos en la próxima unidad) o la traducción automática, también queremos obtener algún valor de salida en cada paso de la RNN. En este caso, hay otra matriz $W_O$, y la salida se calcula como $Y_i=f(W_O\\times S_i+b_O)$.\n",
|
|
"\n",
|
|
"Veamos cómo las redes neuronales recurrentes pueden ayudarnos a clasificar nuestro conjunto de datos de noticias.\n",
|
|
"\n",
|
|
"> Para el entorno de prueba, necesitamos ejecutar la siguiente celda para asegurarnos de que la biblioteca requerida esté instalada y los datos se hayan predescargado. Si estás trabajando localmente, puedes omitir la siguiente celda.\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": [
|
|
"Al entrenar modelos grandes, la asignación de memoria de la GPU puede convertirse en un problema. También puede ser necesario experimentar con diferentes tamaños de minibatch para que los datos se ajusten a la memoria de la GPU y, al mismo tiempo, el entrenamiento sea lo suficientemente rápido. Si estás ejecutando este código en tu propia máquina con GPU, puedes experimentar ajustando el tamaño del minibatch para acelerar el entrenamiento.\n",
|
|
"\n",
|
|
"> **Nota**: Se sabe que ciertas versiones de los controladores de NVidia no liberan la memoria después de entrenar el modelo. Estamos ejecutando varios ejemplos en este cuaderno, y esto podría agotar la memoria en ciertos entornos, especialmente si estás realizando tus propios experimentos como parte del mismo cuaderno. Si encuentras errores extraños al comenzar a entrenar el modelo, podrías considerar reiniciar el kernel del cuaderno.\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": [
|
|
"## Clasificador RNN simple\n",
|
|
"\n",
|
|
"En el caso de una RNN simple, cada unidad recurrente es una red lineal sencilla que toma un vector de entrada y un vector de estado, y produce un nuevo vector de estado. En Keras, esto puede representarse mediante la capa `SimpleRNN`.\n",
|
|
"\n",
|
|
"Aunque podemos pasar directamente tokens codificados en one-hot a la capa RNN, esto no es una buena idea debido a su alta dimensionalidad. Por lo tanto, utilizaremos una capa de embedding para reducir la dimensionalidad de los vectores de palabras, seguida de una capa RNN y, finalmente, un clasificador `Dense`.\n",
|
|
"\n",
|
|
"> **Nota**: En casos donde la dimensionalidad no sea tan alta, por ejemplo, al usar tokenización a nivel de caracteres, podría tener sentido pasar directamente los tokens codificados en one-hot a la celda 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:** Aquí usamos una capa de embeddings no entrenada por simplicidad, pero para obtener mejores resultados podemos usar una capa de embeddings preentrenada utilizando Word2Vec, como se describió en la unidad anterior. Sería un buen ejercicio para ti adaptar este código para trabajar con embeddings preentrenados.\n",
|
|
"\n",
|
|
"Ahora entrenemos nuestra RNN. En general, las RNN son bastante difíciles de entrenar, porque una vez que las celdas de la RNN se despliegan a lo largo de la longitud de la secuencia, el número resultante de capas involucradas en la retropropagación es bastante grande. Por lo tanto, necesitamos seleccionar una tasa de aprendizaje más pequeña y entrenar la red en un conjunto de datos más grande para obtener buenos resultados. Esto puede tomar bastante tiempo, por lo que se recomienda usar una GPU.\n",
|
|
"\n",
|
|
"Para acelerar el proceso, solo entrenaremos el modelo RNN con los títulos de las noticias, omitiendo la descripción. Puedes intentar entrenar con la descripción y ver si logras que el modelo se entrene.\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** que la precisión probablemente sea menor aquí, porque estamos entrenando solo con títulos de noticias.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Revisitando las secuencias de variables\n",
|
|
"\n",
|
|
"Recuerda que la capa `TextVectorization` rellenará automáticamente las secuencias de longitud variable en un minibatch con tokens de relleno. Resulta que esos tokens también participan en el entrenamiento, y pueden complicar la convergencia del modelo.\n",
|
|
"\n",
|
|
"Existen varios enfoques que podemos tomar para minimizar la cantidad de relleno. Uno de ellos es reordenar el conjunto de datos por longitud de secuencia y agrupar todas las secuencias por tamaño. Esto se puede hacer utilizando la función `tf.data.experimental.bucket_by_sequence_length` (consulta la [documentación](https://www.tensorflow.org/api_docs/python/tf/data/experimental/bucket_by_sequence_length)).\n",
|
|
"\n",
|
|
"Otro enfoque es usar **enmascaramiento**. En Keras, algunas capas admiten entradas adicionales que indican qué tokens deben tomarse en cuenta durante el entrenamiento. Para incorporar el enmascaramiento en nuestro modelo, podemos incluir una capa `Masking` separada ([documentación](https://keras.io/api/layers/core_layers/masking/)), o podemos especificar el parámetro `mask_zero=True` en nuestra capa `Embedding`.\n",
|
|
"\n",
|
|
"> **Nota**: Este entrenamiento tomará alrededor de 5 minutos para completar una época en todo el conjunto de datos. Si pierdes la paciencia, siéntete libre de interrumpir el entrenamiento en cualquier momento. Otra opción es limitar la cantidad de datos utilizados para el entrenamiento, añadiendo la cláusula `.take(...)` después de los conjuntos de datos `ds_train` y `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": [
|
|
"Ahora que estamos utilizando enmascaramiento, podemos entrenar el modelo con todo el conjunto de datos de títulos y descripciones.\n",
|
|
"\n",
|
|
"> **Nota**: ¿Has notado que hemos estado utilizando un vectorizador entrenado en los títulos de las noticias, y no en el cuerpo completo del artículo? Potencialmente, esto puede causar que algunos de los tokens sean ignorados, por lo que es mejor reentrenar el vectorizador. Sin embargo, esto podría tener un efecto muy pequeño, así que nos quedaremos con el vectorizador preentrenado anterior por simplicidad.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## LSTM: Memoria a largo corto plazo\n",
|
|
"\n",
|
|
"Uno de los principales problemas de las RNNs es el **desvanecimiento de gradientes**. Las RNNs pueden ser bastante largas y pueden tener dificultades para propagar los gradientes hasta la primera capa de la red durante la retropropagación. Cuando esto ocurre, la red no puede aprender relaciones entre tokens distantes. Una forma de evitar este problema es introducir una **gestión explícita del estado** mediante el uso de **compuertas**. Las dos arquitecturas más comunes que introducen compuertas son **memoria a largo corto plazo** (LSTM) y **unidad de relé con compuerta** (GRU). Aquí cubriremos las LSTMs.\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"Una red LSTM está organizada de manera similar a una RNN, pero hay dos estados que se pasan de capa a capa: el estado actual $c$ y el vector oculto $h$. En cada unidad, el vector oculto $h_{t-1}$ se combina con la entrada $x_t$, y juntos controlan lo que sucede con el estado $c_t$ y la salida $h_{t}$ a través de **compuertas**. Cada compuerta tiene una activación sigmoide (salida en el rango $[0,1]$), que puede considerarse como una máscara bit a bit cuando se multiplica por el vector de estado. Las LSTMs tienen las siguientes compuertas (de izquierda a derecha en la imagen anterior):\n",
|
|
"* **Compuerta de olvido**, que determina qué componentes del vector $c_{t-1}$ necesitamos olvidar y cuáles pasar.\n",
|
|
"* **Compuerta de entrada**, que determina cuánta información del vector de entrada y del vector oculto anterior debe incorporarse al vector de estado.\n",
|
|
"* **Compuerta de salida**, que toma el nuevo vector de estado y decide cuáles de sus componentes se usarán para producir el nuevo vector oculto $h_t$.\n",
|
|
"\n",
|
|
"Los componentes del estado $c$ pueden considerarse como indicadores que se pueden activar o desactivar. Por ejemplo, cuando encontramos el nombre *Alice* en la secuencia, suponemos que se refiere a una mujer y activamos el indicador en el estado que dice que tenemos un sustantivo femenino en la oración. Cuando más adelante encontramos las palabras *and Tom*, activaremos el indicador que dice que tenemos un sustantivo en plural. Así, manipulando el estado, podemos realizar un seguimiento de las propiedades gramaticales de la oración.\n",
|
|
"\n",
|
|
"> **Nota**: Aquí tienes un excelente recurso para entender el funcionamiento interno de las LSTMs: [Understanding LSTM Networks](https://colah.github.io/posts/2015-08-Understanding-LSTMs/) por Christopher Olah.\n",
|
|
"\n",
|
|
"Aunque la estructura interna de una celda LSTM puede parecer compleja, Keras oculta esta implementación dentro de la capa `LSTM`, por lo que lo único que necesitamos hacer en el ejemplo anterior es reemplazar la capa recurrente:\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": [
|
|
"## RNNs bidireccionales y multicapa\n",
|
|
"\n",
|
|
"En nuestros ejemplos hasta ahora, las redes recurrentes operan desde el inicio de una secuencia hasta el final. Esto nos resulta natural porque sigue la misma dirección en la que leemos o escuchamos el habla. Sin embargo, para escenarios que requieren acceso aleatorio a la secuencia de entrada, tiene más sentido ejecutar el cálculo recurrente en ambas direcciones. Las RNNs que permiten cálculos en ambas direcciones se llaman **RNNs bidireccionales**, y se pueden crear envolviendo la capa recurrente con una capa especial llamada `Bidirectional`.\n",
|
|
"\n",
|
|
"> **Note**: La capa `Bidirectional` crea dos copias de la capa dentro de ella y establece la propiedad `go_backwards` de una de esas copias en `True`, haciendo que avance en la dirección opuesta a lo largo de la secuencia.\n",
|
|
"\n",
|
|
"Las redes recurrentes, ya sean unidireccionales o bidireccionales, capturan patrones dentro de una secuencia y los almacenan en vectores de estado o los devuelven como salida. Al igual que con las redes convolucionales, podemos construir otra capa recurrente después de la primera para capturar patrones de nivel superior, construidos a partir de patrones de nivel inferior extraídos por la primera capa. Esto nos lleva a la noción de una **RNN multicapa**, que consiste en dos o más redes recurrentes, donde la salida de la capa anterior se pasa a la siguiente capa como entrada.\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"*Imagen tomada de [este maravilloso artículo](https://towardsdatascience.com/from-a-lstm-cell-to-a-multilayer-lstm-network-with-pytorch-2899eb5696f3) por Fernando López.*\n",
|
|
"\n",
|
|
"Keras hace que construir estas redes sea una tarea sencilla, porque solo necesitas agregar más capas recurrentes al modelo. Para todas las capas excepto la última, necesitamos especificar el parámetro `return_sequences=True`, porque necesitamos que la capa devuelva todos los estados intermedios, y no solo el estado final del cálculo recurrente.\n",
|
|
"\n",
|
|
"Construyamos una LSTM bidireccional de dos capas para nuestro problema de clasificación.\n",
|
|
"\n",
|
|
"> **Note** Este código nuevamente toma bastante tiempo en completarse, pero nos da la mayor precisión que hemos visto hasta ahora. Así que tal vez valga la pena esperar y ver el resultado.\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": [
|
|
"## RNNs para otras tareas\n",
|
|
"\n",
|
|
"Hasta ahora, nos hemos centrado en usar RNNs para clasificar secuencias de texto. Pero pueden manejar muchas más tareas, como generación de texto y traducción automática — abordaremos esas tareas en la próxima unidad.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"\n---\n\n**Descargo de responsabilidad**: \nEste documento ha sido traducido utilizando el servicio de traducción automática [Co-op Translator](https://github.com/Azure/co-op-translator). Si bien nos esforzamos por garantizar la precisión, tenga en cuenta que las traducciones automatizadas pueden contener errores o imprecisiones. El documento original en su idioma nativo debe considerarse la fuente autorizada. Para información crítica, se recomienda una traducción profesional realizada por humanos. No nos hacemos responsables de malentendidos o interpretaciones erróneas que puedan surgir del uso de esta traducción.\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-31T17:06:11+00:00",
|
|
"source_file": "lessons/5-NLP/16-RNN/RNNTF.ipynb",
|
|
"language_code": "es"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 4
|
|
} |