Clasificación de artículos en bengalí con TF-Hub

Ver en TensorFlow.org Ejecutar en Google Colab Ver en GitHub Descargar cuaderno

Este Colab es una demostración de la utilización de Tensorflow Hub para la clasificación texto en distintos al inglés / idiomas locales. Aquí elegimos bengalí como el idioma local y el uso pretrained incrustaciones de palabras para resolver una tarea de clasificación multiclase, donde clasificamos Bangla artículos de noticias en 5 categorías. Las inclusiones pretrained para Bangla proviene de FastText que es una biblioteca de Facebook con vectores de palabras pretrained liberados de 157 idiomas.

Vamos a utilizar la incrustación exportador pretrained de TF-Hub para convertir las palabras incrustaciones a un módulo de texto incrustar primero y luego utilizar el módulo para entrenar a un clasificador con tf.keras , fácil de usar API de alto nivel de Tensorflow para construir modelos de aprendizaje profundo. Incluso si usamos incrustaciones fastText aquí, es posible exportar cualquier otra incrustación previamente entrenada de otras tareas y obtener resultados rápidamente con Tensorflow Hub.

Configuración

# https://github.com/pypa/setuptools/issues/1694#issuecomment-466010982
pip install gdown --no-use-pep517
sudo apt-get install -y unzip
Reading package lists...
Building dependency tree...
Reading state information...
unzip is already the newest version (6.0-21ubuntu1.1).
The following packages were automatically installed and are no longer required:
  linux-gcp-5.4-headers-5.4.0-1040 linux-gcp-5.4-headers-5.4.0-1043
  linux-gcp-5.4-headers-5.4.0-1044 linux-gcp-5.4-headers-5.4.0-1049
  linux-headers-5.4.0-1049-gcp linux-image-5.4.0-1049-gcp
  linux-modules-5.4.0-1049-gcp linux-modules-extra-5.4.0-1049-gcp
Use 'sudo apt autoremove' to remove them.
0 upgraded, 0 newly installed, 0 to remove and 143 not upgraded.
import os

import tensorflow as tf
import tensorflow_hub as hub

import gdown
import numpy as np
from sklearn.metrics import classification_report
import matplotlib.pyplot as plt
import seaborn as sns
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/pkg_resources/__init__.py:119: PkgResourcesDeprecationWarning: 0.18ubuntu0.18.04.1 is an invalid version and will not be supported in a future release
  PkgResourcesDeprecationWarning,

Conjunto de datos

Vamos a utilizar BARD (Bangla artículo conjunto de datos), que cuenta con alrededor de 376.226 artículos recogidos de diferentes portales de noticias en bengalí y etiquetados con 5 categorías: economía, estado, internacionales, deportes y entretenimiento. Descargamos el archivo desde Google Drive esto ( bit.ly/BARD_DATASET enlace) se está refiriendo a partir de este repositorio GitHub.

gdown.download(
    url='https://drive.google.com/uc?id=1Ag0jd21oRwJhVFIBohmX_ogeojVtapLy',
    output='bard.zip',
    quiet=True
)
'bard.zip'
unzip -qo bard.zip

Exportar vectores de palabras previamente entrenados al módulo TF-Hub

TF-Hub proporciona algunas secuencias de comandos útiles para la conversión de incrustaciones de palabras para los módulos de texto de incrustación TF-hub aquí . Para hacer que el módulo de Bangla o cualquier otro idioma, simplemente tenemos que descargar la palabra incrustar .txt o .vec archivo en el mismo directorio que export_v2.py y ejecutar el script.

El exportador lee los vectores de incrustación y la exporta a un Tensorflow SavedModel . Un modelo guardado contiene un programa TensorFlow completo que incluye pesos y gráficos. TF-Hub puede cargar el SavedModel como un módulo , el cual utilizaremos para construir el modelo de clasificación de texto. Puesto que estamos utilizando tf.keras para construir el modelo, vamos a utilizar hub.KerasLayer , que proporciona un contenedor para un módulo TF-Hub para su uso como capa Keras.

En primer lugar vamos a tener en nuestras inmersiones Palabras FastText y exportador de la incrustación de TF-Hub de recompra .

curl -O https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.bn.300.vec.gz
curl -O https://raw.githubusercontent.com/tensorflow/hub/master/examples/text_embeddings_v2/export_v2.py
gunzip -qf cc.bn.300.vec.gz --k
% Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  840M  100  840M    0     0  11.6M      0  0:01:12  0:01:12 --:--:-- 12.0M
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  7469  100  7469    0     0  19053      0 --:--:-- --:--:-- --:--:-- 19005

Luego, ejecutaremos el script exportador en nuestro archivo de incrustación. Dado que las incrustaciones de fastText tienen una línea de encabezado y son bastante grandes (alrededor de 3.3 GB para bengalí después de convertir a un módulo) ignoramos la primera línea y exportamos solo los primeros 100.000 tokens al módulo de incrustación de texto.

python export_v2.py --embedding_file=cc.bn.300.vec --export_path=text_module --num_lines_to_ignore=1 --num_lines_to_use=100000
INFO:tensorflow:Assets written to: text_module/assets
I1105 11:55:29.817717 140238757988160 builder_impl.py:784] Assets written to: text_module/assets
module_path = "text_module"
embedding_layer = hub.KerasLayer(module_path, trainable=False)

El módulo de incrustación de texto toma un lote de oraciones en un tensor de cadenas 1D como entrada y genera los vectores de incrustación de forma (batch_size, embedding_dim) correspondientes a las oraciones. Preprocesa la entrada dividiéndola en espacios. Incrustaciones de palabras se combinan para incrustaciones oración con la sqrtn combinador (Ver aquí ). Para la demostración, pasamos una lista de palabras en bengalí como entrada y obtenemos los vectores de incrustación correspondientes.

embedding_layer(['বাস', 'বসবাস', 'ট্রেন', 'যাত্রী', 'ট্রাক'])
<tf.Tensor: shape=(5, 300), dtype=float64, numpy=
array([[ 0.0462, -0.0355,  0.0129, ...,  0.0025, -0.0966,  0.0216],
       [-0.0631, -0.0051,  0.085 , ...,  0.0249, -0.0149,  0.0203],
       [ 0.1371, -0.069 , -0.1176, ...,  0.029 ,  0.0508, -0.026 ],
       [ 0.0532, -0.0465, -0.0504, ...,  0.02  , -0.0023,  0.0011],
       [ 0.0908, -0.0404, -0.0536, ..., -0.0275,  0.0528,  0.0253]])>

Convertir a conjunto de datos de Tensorflow

Dado que el conjunto de datos es muy grande en lugar de cargar todo el conjunto de datos en la memoria vamos a utilizar un generador para producir muestras en tiempo de ejecución en lotes utilizando Tensorflow del conjunto de datos funciones. El conjunto de datos también está muy desequilibrado, por lo que, antes de usar el generador, barajaremos el conjunto de datos.

dir_names = ['economy', 'sports', 'entertainment', 'state', 'international']

file_paths = []
labels = []
for i, dir in enumerate(dir_names):
  file_names = ["/".join([dir, name]) for name in os.listdir(dir)]
  file_paths += file_names
  labels += [i] * len(os.listdir(dir))

np.random.seed(42)
permutation = np.random.permutation(len(file_paths))

file_paths = np.array(file_paths)[permutation]
labels = np.array(labels)[permutation]

Podemos comprobar la distribución de etiquetas en los ejemplos de formación y validación después de barajar.

train_frac = 0.8
train_size = int(len(file_paths) * train_frac)
# plot training vs validation distribution
plt.subplot(1, 2, 1)
plt.hist(labels[0:train_size])
plt.title("Train labels")
plt.subplot(1, 2, 2)
plt.hist(labels[train_size:])
plt.title("Validation labels")
plt.tight_layout()

png

Para crear un conjunto de datos utilizando un generador, primero escribimos una función de generador que lee cada uno de los artículos de file_paths y las etiquetas de la agrupación de etiquetas, y los rendimientos ejemplo una formación en cada etapa. Pasamos esta función generador a la tf.data.Dataset.from_generator método y especificar los tipos de salida. Cada ejemplo de formación es una tupla que contiene un artículo de tf.string tipo de datos y la etiqueta codificada de una sola caliente. Dividimos el conjunto de datos con una fracción de tren-validación de 80-20 usando tf.data.Dataset.skip y tf.data.Dataset.take métodos.

def load_file(path, label):
    return tf.io.read_file(path), label
def make_datasets(train_size):
  batch_size = 256

  train_files = file_paths[:train_size]
  train_labels = labels[:train_size]
  train_ds = tf.data.Dataset.from_tensor_slices((train_files, train_labels))
  train_ds = train_ds.map(load_file).shuffle(5000)
  train_ds = train_ds.batch(batch_size).prefetch(tf.data.AUTOTUNE)

  test_files = file_paths[train_size:]
  test_labels = labels[train_size:]
  test_ds = tf.data.Dataset.from_tensor_slices((test_files, test_labels))
  test_ds = test_ds.map(load_file)
  test_ds = test_ds.batch(batch_size).prefetch(tf.data.AUTOTUNE)


  return train_ds, test_ds
train_data, validation_data = make_datasets(train_size)

Formación y evaluación de modelos

Como ya hemos añadido una envoltura alrededor de nuestro módulo de utilizarlo como cualquier otra capa en Keras, podemos crear una pequeña secuencial modelo que es una pila lineal de capas. Podemos añadir nuestro módulo de la incrustación de texto con model.add al igual que cualquier otra capa. Compilamos el modelo especificando la pérdida y el optimizador y lo entrenamos durante 10 épocas. El tf.keras API puede manejar Tensorflow conjuntos de datos como entrada, por lo que podemos pasar a una instancia de conjunto de datos para el método de ajuste para el entrenamiento del modelo. Ya que estamos usando la función de generador, tf.data se encargará de generar las muestras, preparación de lotes ellos y alimentarlos al modelo.

Modelo

def create_model():
  model = tf.keras.Sequential([
    tf.keras.layers.Input(shape=[], dtype=tf.string),
    embedding_layer,
    tf.keras.layers.Dense(64, activation="relu"),
    tf.keras.layers.Dense(16, activation="relu"),
    tf.keras.layers.Dense(5),
  ])
  model.compile(loss=tf.losses.SparseCategoricalCrossentropy(from_logits=True),
      optimizer="adam", metrics=['accuracy'])
  return model
model = create_model()
# Create earlystopping callback
early_stopping_callback = tf.keras.callbacks.EarlyStopping(monitor='val_loss', min_delta=0, patience=3)

Capacitación

history = model.fit(train_data, 
                    validation_data=validation_data, 
                    epochs=5, 
                    callbacks=[early_stopping_callback])
Epoch 1/5
1176/1176 [==============================] - 34s 28ms/step - loss: 0.2181 - accuracy: 0.9279 - val_loss: 0.1580 - val_accuracy: 0.9449
Epoch 2/5
1176/1176 [==============================] - 32s 27ms/step - loss: 0.1411 - accuracy: 0.9505 - val_loss: 0.1411 - val_accuracy: 0.9503
Epoch 3/5
1176/1176 [==============================] - 32s 27ms/step - loss: 0.1307 - accuracy: 0.9534 - val_loss: 0.1359 - val_accuracy: 0.9524
Epoch 4/5
1176/1176 [==============================] - 32s 27ms/step - loss: 0.1248 - accuracy: 0.9555 - val_loss: 0.1318 - val_accuracy: 0.9527
Epoch 5/5
1176/1176 [==============================] - 32s 27ms/step - loss: 0.1196 - accuracy: 0.9567 - val_loss: 0.1247 - val_accuracy: 0.9555

Evaluación

Podemos visualizar las curvas de pérdida de precisión y para el entrenamiento y validación de datos utilizando el tf.keras.callbacks.History objeto devuelto por el tf.keras.Model.fit método, que contiene el valor de la pérdida y la precisión de cada época.

# Plot training & validation accuracy values
plt.plot(history.history['accuracy'])
plt.plot(history.history['val_accuracy'])
plt.title('Model accuracy')
plt.ylabel('Accuracy')
plt.xlabel('Epoch')
plt.legend(['Train', 'Test'], loc='upper left')
plt.show()

# Plot training & validation loss values
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.title('Model loss')
plt.ylabel('Loss')
plt.xlabel('Epoch')
plt.legend(['Train', 'Test'], loc='upper left')
plt.show()

png

png

Predicción

Podemos obtener las predicciones para los datos de validación y verificar la matriz de confusión para ver el desempeño del modelo para cada una de las 5 clases. Debido tf.keras.Model.predict método devuelve una matriz nd para probabilidades para cada clase, pueden ser convertidos a etiquetas de clase utilizando np.argmax .

y_pred = model.predict(validation_data)
y_pred = np.argmax(y_pred, axis=1)
samples = file_paths[0:3]
for i, sample in enumerate(samples):
  f = open(sample)
  text = f.read()
  print(text[0:100])
  print("True Class: ", sample.split("/")[0])
  print("Predicted Class: ", dir_names[y_pred[i]])
  f.close()
রবিন উইলিয়ামস তাঁর হ্যাপি ফিট ছবিতে মজা করে বলেছিলেন, ‘আমি শুনতে পাচ্ছি, মানুষ কিছু একটা চাইছে...সে
True Class:  entertainment
Predicted Class:  state

নির্মাণ শেষে ফিতা কেটে মন্ত্রী ভবন উদ্বোধন করেছেন বহু আগেই। তবে এখনো চালু করা যায়নি খাগড়াছড়ি জেল
True Class:  state
Predicted Class:  state

কমলাপুর বীরশ্রেষ্ঠ মোস্তফা কামাল স্টেডিয়ামে কাল ফকিরেরপুল ইয়ংমেন্স ক্লাব ৩-০ গোলে হারিয়েছে স্বাধ
True Class:  sports
Predicted Class:  state

Comparar rendimiento

Ahora podemos tomar las etiquetas correctas de los datos de validación de labels y los compare con nuestras predicciones para obtener una classification_report .

y_true = np.array(labels[train_size:])
print(classification_report(y_true, y_pred, target_names=dir_names))
precision    recall  f1-score   support

      economy       0.83      0.76      0.79      3897
       sports       0.99      0.98      0.98     10204
entertainment       0.90      0.94      0.92      6256
        state       0.97      0.97      0.97     48512
international       0.92      0.93      0.93      6377

     accuracy                           0.96     75246
    macro avg       0.92      0.92      0.92     75246
 weighted avg       0.96      0.96      0.96     75246

También podemos comparar el rendimiento de nuestro modelo con los resultados publicados obtenidos en el original de papel , que tenía un 0,96 precisión .Los autores originales describen muchos pasos de preprocesamiento realizan en el conjunto de datos, como la caída de signos de puntuación y dígitos, que quita la tapa 25 la mayoría de las palabras vacías fRequest. Como podemos ver en la classification_report , también gestionamos para obtener una precisión y exactitud de 0,96 después del entrenamiento por sólo 5 épocas sin ningún procesamiento previo!

En este ejemplo, cuando creamos la capa Keras de nuestro módulo de incrustación, fijamos el parámetro trainable=False , lo que significa que los pesos de incrustación no se actualizará durante el entrenamiento. Inténtelo a True para llegar a alrededor del 97% de precisión usando este conjunto de datos después de sólo 2 épocas.