Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
tensorflow
GitHub Repository: tensorflow/docs-l10n
Path: blob/master/site/es-419/tutorials/keras/text_classification.ipynb
25118 views
Kernel: Python 3
#@title Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License.
#@title MIT License # # Copyright (c) 2017 François Chollet # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE.

Clasificación básica de textos

En este tutorial se muestra la clasificación de textos a partir de archivos de texto plano almacenados en un disco. Entrenará un clasificador binario para que analice los sentimientos de un conjunto de datos de IMDB. Al final del bloc de notas, hay un ejercicio para que lo ponga a prueba, en el que entrenará un clasificador multiclase para predecir la etiqueta de una pregunta de programación de Stack Overflow.

import matplotlib.pyplot as plt import os import re import shutil import string import tensorflow as tf from tensorflow.keras import layers from tensorflow.keras import losses
print(tf.__version__)

Análisis de sentimientos

En este bloc de notas se entrena un modelo de análisis de sentimiento para clasificar reseñas de películas como positivas o negativas a partir del texto de la reseña. Este es un ejemplo de clasificación binaria (o de dos clases), un tipo de problema de aprendizaje automático importante y ampliamente aplicable.

Usará los enormes conjuntos de datos de reseñas de películas que contienen el texto de 50 000 reseñas de películas de Internet Movie Database. Se divide en 25 000 reseñas para entrenamiento y 25 000 reseñas para prueba. Los conjuntos de entrenamiento y prueba están equilibrados, lo que significa que contienen la misma cantidad de reseñas positivas y negativas.

Descargar y explorar el conjunto de datos de IMDB

Descarguemos y extraigamos los conjuntos de datos, luego, exploremos la estructura del directorio.

url = "https://ai.stanford.edu/~amaas/data/sentiment/aclImdb_v1.tar.gz" dataset = tf.keras.utils.get_file("aclImdb_v1", url, untar=True, cache_dir='.', cache_subdir='') dataset_dir = os.path.join(os.path.dirname(dataset), 'aclImdb')
os.listdir(dataset_dir)
train_dir = os.path.join(dataset_dir, 'train') os.listdir(train_dir)

Los directorios aclImdb/train/pos y aclImdb/train/neg contienen muchos archivos de texto, donde cada uno corresponde a una reseña de película. Echemos un vistazo a uno de ellos.

sample_file = os.path.join(train_dir, 'pos/1181_9.txt') with open(sample_file) as f: print(f.read())

Cargar el conjunto de datos

A continuación, cargará los datos del disco y los preparará en un formato adecuado para el entrenamiento. Para esto, usará la práctica utilidad text_dataset_from_directory, que espera una estructura de directorios como la que se muestra a continuación.

main_directory/ ...class_a/ ......a_text_1.txt ......a_text_2.txt ...class_b/ ......b_text_1.txt ......b_text_2.txt

Para preparar el conjunto de datos para clasificación binaria, necesita dos carpetas en el disco, que correspondan con class_a y class_b. Estas serán las reseñas positivas y negativas de las películas, que se pueden encontrar en aclImdb/train/pos y aclImdb/train/neg. Dado que el conjunto de datos de IMDB contiene carpetas adicionales, deberá eliminarlas antes de usar esta utilidad.

remove_dir = os.path.join(train_dir, 'unsup') shutil.rmtree(remove_dir)

Luego, usará la utilidad text_dataset_from_directory para crear un tf.data.Dataset etiquetado. tf.data es una potente colección de herramientas para trabajar con datos.

A la hora de hacer un experimento de aprendizaje automático, lo mejor es dividir el conjunto de datos en tres partes: entrenamiento, validación y prueba.

El conjunto de datos de IMDB ya está dividido en entrenamiento y prueba, pero no cuenta con un conjunto de validación. Creemos un conjunto de validación mediante una división 80:20 de los datos de entrenamiento con ayuda del argumento validation_split que se muestra a continuación.

batch_size = 32 seed = 42 raw_train_ds = tf.keras.utils.text_dataset_from_directory( 'aclImdb/train', batch_size=batch_size, validation_split=0.2, subset='training', seed=seed)

Como puede ver en el bloque de arriba, hay 25 000 ejemplos en la carpeta de entrenamiento, de lo que usará el 80 % (o 20 000) para entrenamiento. Como verá en un momento, puede entrenar un modelo pasando un conjunto de datos directamente a model.fit. Si es la primera vez que usa tf.data, también puede iterar el conjunto de datos e imprimir algunos ejemplos como se muestra a continuación.

for text_batch, label_batch in raw_train_ds.take(1): for i in range(3): print("Review", text_batch.numpy()[i]) print("Label", label_batch.numpy()[i])

Tenga en cuenta que las reseñas contienen texto bruto (con puntuación y algunas etiquetas HTML como <br/>). En la siguiente sección le mostraremos cómo debe manejar esto.

Las etiquetas son 0 o 1. Para ver cuál corresponde a las reseñas positivas y negativas de las películas, puede consultar la propiedad class_names en el conjunto de datos.

print("Label 0 corresponds to", raw_train_ds.class_names[0]) print("Label 1 corresponds to", raw_train_ds.class_names[1])

A continuación, creará un conjunto de datos de validación y prueba. Usará las 5000 reseñas restantes del conjunto de entrenamiento para ejecutar la validación.

Nota: Cuando use los argumentos validation_split y subset, asegúrese de especificar una semilla o de pasar shuffle=False, para que las fracciones de validación y entrenamiento no se superpongan.

raw_val_ds = tf.keras.utils.text_dataset_from_directory( 'aclImdb/train', batch_size=batch_size, validation_split=0.2, subset='validation', seed=seed)
raw_test_ds = tf.keras.utils.text_dataset_from_directory( 'aclImdb/test', batch_size=batch_size)

Preparar el conjunto de datos para entrenamiento

A continuación, usará la útil capa tf.keras.layers.TextVectorization para estndarizar, tokenizar y vectorizar los datos.

El término estandarización se refiere al preprocesamiento del texto que generalmente se utiliza para eliminar la puntuación o los elementos de HTML con el objetivo de simplificar el conjunto de datos. Tokenizar en este contexto es dividir las cadenas en tokens (por ejemplo, separar una frase en palabras individuales, usando los espacios en blanco para separar). La vetorización se refiere al proceso mediante el cual los tokens se convierten en números que se pueden cargar a la red neuronal. Todas estas tareas se pueden completar con esta capa.

Como pudo ver anteriormente, las reseñas contienen varias etiquetas HTML como <br />. El estandarizador predeterminado de la capa TextVectorization (que convierte texto a minúsculas y elimina la puntuación de forma predeterminada, pero no elimina los elementos de HTML) no eliminará estas etiquetas. Deberá escribir una función de estandarización personalizada para eliminar el HTML.

Nota: Para evitar el sesgo entrenamiento-prueba (también conocido como sesgo entrenamiento-servicio), es importante preprocesar los datos de forma idéntica tanto durante el entrenamiento como en la etapa de prueba. Para simplificar esto, la capa TextVectorization se puede incluir directamente dentro del modelo, como se muestra más adelante en este tutorial.

def custom_standardization(input_data): lowercase = tf.strings.lower(input_data) stripped_html = tf.strings.regex_replace(lowercase, '<br />', ' ') return tf.strings.regex_replace(stripped_html, '[%s]' % re.escape(string.punctuation), '')

Luego, creará una capa TextVectorization. Usará esta capa para estandarizar, tokenizar y vectorizar nuestros datos. Configurará output_mode en int para crear índices enteros únicos para cada token.

Tenga en cuenta que está usando la función de separación predeterminada y la función de estandarización personalizada que definió anteriormente. También deberá definir algunas constantes para el modelo, como un valor máximo explícito de sequence_length, que hará que cada capa amortigüe o trunque las secuencias exactamente a los valores sequence_length.

max_features = 10000 sequence_length = 250 vectorize_layer = layers.TextVectorization( standardize=custom_standardization, max_tokens=max_features, output_mode='int', output_sequence_length=sequence_length)

A continuación, llamará adapt para que ajuste el estado de la capa de preprocesamiento al conjunto de datos. Esto hará que el modelo convierta un índice de cadenas a enteros.

Nota: Es importante que solo use sus datos de entrenamiento para al llamar adapt (si usa el conjunto de prueba, se podría filtrar información).

# Make a text-only dataset (without labels), then call adapt train_text = raw_train_ds.map(lambda x, y: x) vectorize_layer.adapt(train_text)

Creemos una función para ver los resultados del uso de esta capa para preprocesar algunos datos.

def vectorize_text(text, label): text = tf.expand_dims(text, -1) return vectorize_layer(text), label
# retrieve a batch (of 32 reviews and labels) from the dataset text_batch, label_batch = next(iter(raw_train_ds)) first_review, first_label = text_batch[0], label_batch[0] print("Review", first_review) print("Label", raw_train_ds.class_names[first_label]) print("Vectorized review", vectorize_text(first_review, first_label))

Como pudo ver anteriormente, cada token ha sido reemplazo por un entero. Puede buscar el token (cadena) al que corresponde cada entero llamando .get_vocabulary() en la capa.

print("1287 ---> ",vectorize_layer.get_vocabulary()[1287]) print(" 313 ---> ",vectorize_layer.get_vocabulary()[313]) print('Vocabulary size: {}'.format(len(vectorize_layer.get_vocabulary())))

Ya está casi listo para entrenar su modelo. Como último paso de preprocesamiento, debe aplicar la capa TextVectorization que creó anteriormente a los conjuntos de datos de entrenamiento, validación y prueba.

train_ds = raw_train_ds.map(vectorize_text) val_ds = raw_val_ds.map(vectorize_text) test_ds = raw_test_ds.map(vectorize_text)

Configurar el conjunto de datos para mejorar el rendimiento

Hay dos métodos importantes que debería usar al cargar los datos para asegurarse de que la E/S no se bloquee.

.cache() conserva los datos en la memoria después de que descarga del disco. Esto evitará que el conjunto de datos se transforme en un cuello de botella mientras entrena su modelo. Si su conjunto de datos es demasiado grande para caber en la memoria, también puede usar este método para crear un potente caché en disco, que se lee de forma más eficiente que muchos archivos pequeños.

.prefetch() superpone el preprocesamiento de los datos y la ejecución del modelo durante el entrenamiento.

Puede obtener más información sobre ambos métodos y sobre cómo almacenar datos en caché en disco en la guía de rendimiento de datos.

AUTOTUNE = tf.data.AUTOTUNE train_ds = train_ds.cache().prefetch(buffer_size=AUTOTUNE) val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE) test_ds = test_ds.cache().prefetch(buffer_size=AUTOTUNE)

Crear el modelo

Llegó la hora de que cree su red neuronal:

embedding_dim = 16
model = tf.keras.Sequential([ layers.Embedding(max_features + 1, embedding_dim), layers.Dropout(0.2), layers.GlobalAveragePooling1D(), layers.Dropout(0.2), layers.Dense(1)]) model.summary()

Las capas se apilan secuencialmente para generar el clasificador:

  1. La primera capa es una capa Embedding. Esta capa toma las reseñas cifradas con números enteros y busca un vector de incorporación para cada índice de palabra. Estos vectores se aprenden a medida que se entrena el modelo. Los vectores agregan una dimensión al arreglo de salida. Las dimensiones resultantes son las siguientes: (batch, sequence, embedding). Para obtener más información sobre las incorporaciones, consulte el tutorial Incorporaciones de palabras.

  2. A continuación, una capa GlobalAveragePooling1D devuelve un vector de salida de longitud fija para cada ejemplo calculando el promedio sobre la dimensión de la secuencia. Esto le permite a modelo manejar entradas de longitud variable, de la forma más sencilla posible.

  3. La última capa está densamente conectada con un único nodo de salida.

Función de pérdida y optimizador

Un modelo necesita una función de pérdida y un optimizador para el entrenamiento. Dado que este es un problema de clasificación binaria y el modelo genera una probabilidad (una capa de una sola unidad con una activación sigmoide), usaremos la función de pérdida losses.BinaryCrossentropy.

Ahora, configure el modelo para usar un optimizador y una función de pérdida:

model.compile(loss=losses.BinaryCrossentropy(from_logits=True), optimizer='adam', metrics=tf.metrics.BinaryAccuracy(threshold=0.0))

Entrenar el modelo

Entrenará el modelo pasando el objeto dataset al método fit.

epochs = 10 history = model.fit( train_ds, validation_data=val_ds, epochs=epochs)

Evaluar el modelo

Veamos el rendimiento del modelo. Nos devolverá dos valores; la pérdida (un número que representa nuestro error, los valores bajos son mejores) y la precisión.

loss, accuracy = model.evaluate(test_ds) print("Loss: ", loss) print("Accuracy: ", accuracy)

Este enfoque bastante sencillo alcanza una precisión del 86 %.

Cree un gráfico de precisión y pérdida a lo largo del tiempo

model.fit() devuelve un objeto History que contiene un diccionario con todo lo que pasó durante el entrenamiento:

history_dict = history.history history_dict.keys()

Hay cuatro entradas: una por cada métrica que se monitoreó durante el entrenamiento y la validación. Puede usarlas para trazar la pérdida de entrenamiento y validación para compararlas, puede hacer lo mismo con la precisión:

acc = history_dict['binary_accuracy'] val_acc = history_dict['val_binary_accuracy'] loss = history_dict['loss'] val_loss = history_dict['val_loss'] epochs = range(1, len(acc) + 1) # "bo" is for "blue dot" plt.plot(epochs, loss, 'bo', label='Training loss') # b is for "solid blue line" plt.plot(epochs, val_loss, 'b', label='Validation loss') plt.title('Training and validation loss') plt.xlabel('Epochs') plt.ylabel('Loss') plt.legend() plt.show()
plt.plot(epochs, acc, 'bo', label='Training acc') plt.plot(epochs, val_acc, 'b', label='Validation acc') plt.title('Training and validation accuracy') plt.xlabel('Epochs') plt.ylabel('Accuracy') plt.legend(loc='lower right') plt.show()

En este gráfico, los puntos representan la pérdida y la precisión del entrenamiento y las líneas continuas reflejan la pérdida y la precisión de la validación.

Como puede ver, la pérdida del entrenamiento se reduce época tras época y la precisión del entrenamiento aumenta a medida que pasan las épocas. Esto es lo que suele pasar cuando se usa una optimización con descenso de gradiente, debe reducir al mínimo la cantidad deseada en cada iteración.

Esto no es lo que sucede en el caso de la pérdida y la precisión de la validación, al parecer llegan a su punto máximo antes que la precisión del entrenamiento. Este es un ejemplo de sobreajuste: el modelo funciona mejor con los datos de entrenamiento que con los datos que no ha visto anteriormente. Pasado este punto, el modelo se sobreoptimiza y aprende representaciones específicas de los datos de entrenamiento que no se generalizan a los datos de prueba.

En este caso particular, podría evitar el sobreajuste con tan solo detener el entrenamiento cuando la precisión de validación deje de aumentar. Una forma de hacerlo es con la retrollamada tf.keras.callbacks.EarlyStopping.

Exportar el modelo

En el código que vimos arriba, se aplicó la capa TextVectorization al conjunto de datos antes de cargar texto al modelo. Si desea que su modelo sea capaz de procesar cadenas sin procesar (por ejemplo, para simplificar la implementación), puede incluir la capa TextVectorization en su modelo. Para ello, puede crear un nuevo modelo a partir de los pesos que acaba de entrenar.

export_model = tf.keras.Sequential([ vectorize_layer, model, layers.Activation('sigmoid') ]) export_model.compile( loss=losses.BinaryCrossentropy(from_logits=False), optimizer="adam", metrics=['accuracy'] ) # Test it with `raw_test_ds`, which yields raw strings loss, accuracy = export_model.evaluate(raw_test_ds) print(accuracy)

Inferencia en los nuevos datos

Para obtener predicciones para ejemplos nuevos, puede sencillamente llamar model.predict().

examples = [ "The movie was great!", "The movie was okay.", "The movie was terrible..." ] export_model.predict(examples)

Incluir la lógica de preprocesamiento de textos en su modelo le permitirá exportar un modelo para producción que simplifique la implementación y reduzca la probabilidad de que se produzca un sesgo entre entrenamiento y prueba.

Hay una diferencia de rendimiento que tenemos que tener en cuenta a la hora de elegir dónde aplicar la capa TextVectorization. Usarla fuera de su modelo le permite hacer un procesamiento asíncrono en CPU y almacenar en búfer los datos cuando se entrena en GPU. Por lo tanto, si está entrenando su modelo en GPU, probablemente debería elegir esta opción para obtener el mejor rendimiento mientras desarrolla su modelo, y luego cambiar para incluir la capa TextVectorization dentro de su modelo cuando esté listo para prepararse para la implementación.

Visite este tutorial para obtener más información sobre cómo guardar modelos.

Ejercicio: clasificación multiclase en preguntas de Stack Overflow

En este tutorial, le mostramos cómo entrenar un clasificador binario desde cero con los conjuntos de datos de IMDB. A modo de ejercicio práctico, puede modificar este bloc de notas para entrenar un clasificador multiclase para predecir la etiqueta de una pregunta de programación en Stack Overflow.

Le preparamos un conjunto de datos que contiene el cuerpo de varios miles de preguntas de programación, (por ejemplo, "¿Como puedo ordenar un diccionario por valor en Python?") que se publicaron en Stack Overflow. Cada una de ellas se etiquetó con una sola etiqueta (que puede ser Python, CSharp, JavaScript o Java). Su tarea consiste en tomar una pregunta como entrada y predecir la etiqueta correspondiente, en este caso, Python.

El conjunto de datos con el que trabajará contiene miles de preguntas que fueron extraídas del conjunto de datos público de Stack Overflow en BigQuery, que es mucho más grande y contiene más de 17 millones de publicaciones.

Tras descargar el conjunto de datos, verá que tiene una estructura de directorio similar al conjunto de datos de IMDB con el que trabajó anteriormente:

train/ ...python/ ......0.txt ......1.txt ...javascript/ ......0.txt ......1.txt ...csharp/ ......0.txt ......1.txt ...java/ ......0.txt ......1.txt

Nota: Para elevar el nivel de dificultad del problema de clasificación, las apariciones de las palabras Python, CSharp, JavaScript o Java en las preguntas de programación han sido reemplazadas por las palabras en blanco (ya que muchas preguntas mencionan el lenguaje al que se refieren).

Para completar este ejercicio, debería modificar este bloc de notas para trabajar con el conjunto de datos de Stack Overflow aplicando los siguientes cambios:

  1. En la parte superior del bloc de notas, actualice el código que descarga el conjunto de datos de IMDB con el código para descargar el conjunto de datos de Stack Overflow que ya ha sido preparado. Como el conjunto de datos de Stack Overflow tiene una estructura de directorio similar, no será necesario que realice muchas modificaciones.

  2. Modifique la última capa de su modelo para que sea Dense(4), ya que ahora son cuatro las clases de salida.

  3. Cuando compile el modelo, cambie la pérdida a tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True). Esta es la función de pérdida adecuada para usar con un problema de clasificación multiclase, cuando las etiquetas de cada clase son números enteros (en este caso, pueden ser 0, 1, 2 o 3). Además, cambie las métricas a metrics=['accuracy'], ya que este es un problema de clasificación multiclase (tf.metrics.BinaryAccuracy se usa solamente para clasificadores binarios).

  4. A la hora de trazar la precisión a lo largo del tiempo, cambie binary_accuracy y val_binary_accuracy por accuracy y val_accuracy, respectivamente.

  5. Una vez que haya hecho todos estos cambios, estará listo para entrenar un clasificador multiclase.

Más información

En este tutorial, le presentamos la clasificación de textos desde cero. Para obtener más información sobre el flujo de trabajo de la clasificación de textos en términos generales, consulte la guía Clasificación de textos de Google Developers.