Classificação de artigos em Bangla com TF-Hub

Ver no TensorFlow.org Executar no Google Colab Ver no GitHub Baixar caderno

Este Colab é uma demonstração do uso de Tensorflow Hub para a classificação texto em não-Inglês / línguas locais. Aqui podemos escolher Bangla como a língua local e usar pré-treinado embeddings palavra para resolver uma tarefa de classificação multiclasse onde classificamos Bangla artigos de notícias em 5 categorias. As incorporações pré-treinado para Bangla vem de fastText que é uma biblioteca de Facebook com vetores palavra pré-treinado liberados para 157 línguas.

Usaremos exportador incorporar pré-treinado do TF-Hub para converter a palavra embeddings a um módulo de texto incorporar em primeiro lugar e, em seguida, utilizar o módulo para treinar um classificador com tf.keras , alto nível de usuário API amigável do Tensorflow para construir modelos de aprendizagem de profundidade. Mesmo que estejamos usando embeddings fastText aqui, é possível exportar quaisquer outros embeddings pré-treinados de outras tarefas e obter resultados rapidamente com o hub Tensorflow.

Configurar

# 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 dados

Usaremos BARD (Bangla artigo Dataset), que tem cerca de 376.226 artigos coletados em diferentes portais de notícias Bangla e rotulados com 5 categorias: economia, estado, internacional, esportes e entretenimento. Nós baixar o arquivo de Google Drive este ( bit.ly/BARD_DATASET link) está se referindo a partir deste repositório GitHub.

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

Exportar vetores de palavras pré-treinados para o módulo TF-Hub

TF-Hub fornece alguns scripts úteis para converter embeddings palavras para TF-hub módulos de texto de incorporação aqui . Para fazer o módulo para Bangla ou quaisquer outros idiomas, nós simplesmente temos que baixar a palavra incorporar .txt ou .vec arquivo para o mesmo diretório que export_v2.py e executar o script.

O exportador lê os vetores de incorporação e exporta-lo para um Tensorflow SavedModel . Um SavedModel contém um programa TensorFlow completo, incluindo pesos e gráfico. TF-Hub pode carregar o SavedModel como um módulo , que vamos usar para construir o modelo para classificação de texto. Como estamos usando tf.keras para construir o modelo, vamos usar hub.KerasLayer , que fornece um wrapper para um módulo de TF-Hub para uso como uma camada Keras.

Primeiro vamos começar nossas embeddings palavra de fastText e incorporação exportador da TF-Hub repo .

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

Em seguida, executaremos o script exportador em nosso arquivo de incorporação. Como os embeddings fastText têm uma linha de cabeçalho e são muito grandes (cerca de 3,3 GB para Bangla após a conversão para um módulo), ignoramos a primeira linha e exportamos apenas os primeiros 100.000 tokens para o módulo de embedding 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)

O módulo de incorporação de texto recebe um lote de frases em um tensor 1D de strings como entrada e produz os vetores de incorporação de forma (batch_size, embedding_dim) correspondentes às frases. Ele pré-processa a entrada dividindo em espaços. Embeddings Word são combinados para embeddings frase com as sqrtn combinador (Veja aqui ). Para demonstração, passamos uma lista de palavras Bangla como entrada e obtemos os vetores de incorporação correspondentes.

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]])>

Converter em conjunto de dados Tensorflow

Uma vez que o conjunto de dados é muito grande em vez de carregar todo o conjunto de dados na memória, vamos utilizar um gerador para produzir amostras em tempo de execução em lotes usando Tensorflow Conjunto de Dados funções. O conjunto de dados também é muito desequilibrado, então, antes de usar o gerador, iremos embaralhar o conjunto de dados.

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 verificar a distribuição de rótulos nos exemplos de treinamento e validação após embaralhar.

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 criar um conjunto de dados usando um gerador, primeiro escrever uma função gerador que lê cada um dos artigos de file_paths e os rótulos a partir da matriz de etiquetas, e os rendimentos exemplo, uma formação em cada etapa. Nós passamos esta função gerador para o tf.data.Dataset.from_generator método e especificar os tipos de saída. Cada exemplo é a formação de um tuplo contendo um artigo de tf.string tipo de dados e uma etiqueta codificada-quente. Nós dividir o conjunto de dados com uma divisão trem de validação de 80-20 usando tf.data.Dataset.skip e 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)

Treinamento e avaliação de modelo

Uma vez que já adicionou um invólucro em torno do nosso módulo para usá-lo como qualquer outra camada no Keras, podemos criar um pequeno Sequential modelo que é uma pilha linear de camadas. Nós podemos adicionar o nosso módulo de embutir texto com model.add como qualquer outra camada. Compilamos o modelo especificando a perda e o otimizador e o treinamos por 10 épocas. O tf.keras API pode lidar com Tensorflow conjuntos de dados como entrada, para que possamos passar uma instância de DataSet para o método de ajuste para treinamento do modelo. Uma vez que estamos a utilizar a função de gerador, tf.data irá lidar com gerar as amostras, dosagem los e alimentá-los para o 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)

Treinamento

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

Avaliação

Podemos visualizar as curvas de precisão e de perda para a formação e validação de dados usando o tf.keras.callbacks.History objeto retornado pela tf.keras.Model.fit método, que contém o valor da perda e precisão para 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

Predição

Podemos obter as previsões para os dados de validação e verificar a matriz de confusão para ver o desempenho do modelo para cada uma das 5 classes. Porque tf.keras.Model.predict método retorna uma matriz nd para probabilidades para cada classe, eles podem ser convertidos para etiquetas de classe usando 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

Compare o desempenho

Agora podemos tomar os rótulos corretos para os dados de validação de labels e compará-los com as nossas previsões para obter um 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

Podemos também comparar o desempenho do nosso modelo com os resultados publicados obtidos no original de papel , que tinha um 0,96 precisão .Os autores originais descrito muitas etapas de pré-processamento executada no conjunto de dados, como deixar cair pontuações e algarismos, removendo top 25 mais frequest palavras de parada. Como podemos ver na classification_report , nós também conseguimos obter uma precisão e exatidão 0,96 após o treinamento por apenas 5 épocas sem qualquer pré-processamento!

Neste exemplo, quando criamos a camada Keras do nosso módulo de incorporação, vamos definir o parâmetro trainable=False , o que significa que os pesos de incorporação não será atualizado durante o treinamento. Tentar defini-la como True para alcançar cerca de 97% de precisão usando este conjunto de dados depois de apenas 2 épocas.