Ich konnte in dem Wettbewerb, an dem ich teilgenommen habe, sehr einfach ein BERT-Modell mit TPU + Transformers bauen, also werde ich es teilen.
Wie auch immer, wir werden die Geschwindigkeit priorisieren und BERT erstellen, was ein aktueller NLP-Trend ist.
BERT Es ist ein von Google entwickeltes Allzweck-Sprachmodell. Dieses Mal verwenden wir ein zeitsparendes Rezept mit Distil BERT, einem leichteren und schnelleren Destillationsmodell als BERT. Das Pretrain-Modell verwendet japanisches Modell, veröffentlicht vom Bandai Namco Research Institute.
TPU TPU (Tensor Processing Unit) ist ein von Google entwickelter Prozessor, der sich auf Berechnungen zum maschinellen Lernen spezialisiert hat. Durch Ersetzen der arithmetischen Schaltung von 32 Bit auf 8 oder 16 Bit oder durch Übergeben von Werten zwischen arithmetischen Schaltungen ohne Lese- und Schreibspeicher scheint die Matrixarithmetik schneller als Allzweckprozessoren ausgeführt werden zu können. Vor kurzem gab es TPUs für Edge, die mit GCP verwendet und in Raspberry pi installiert werden können.
Dieses Mal werden wir TPU in Google Colab verwenden, um die Lernzeit zu explodieren.
Transformers Dies ist ein Deep Learning-Framework von Hugging Face, das sich auf Transformer-Modelle spezialisiert hat. Sie können die Tokenizer- und Pretrain-Modelle, die zum Erstellen von Transformer-Modellen erforderlich sind, problemlos aus den auf Hugging Face's HP veröffentlichten Modellen laden. (Natürlich können Sie auch auf das lokal gespeicherte Modell verweisen.) Zu Beginn der Entwicklung war es nur mit Pytorch kompatibel, jetzt ist es auch mit Tensorflow kompatibel. Dieses Mal werden wir ein Modell für Tensorflow (Keras) laden und erstellen, um die Einfachheit von I / F und die Benutzerfreundlichkeit von TPU zu berücksichtigen. (Wenn ich TPU mit Pytorch verwende, verwende ich Tensorflow, das relativ einfach zu verwenden ist, da es schwierig ist, XLA zu verwenden und Mehrfachverarbeitung zu schreiben.)
Dieses Mal ist es solide, aber wir werden einen Klassifikator für mehrere Klassen mit dem Livedoor-Korpus erstellen, den jeder liebt.
Ändern Sie die Laufzeit von Notebooks Runtime-> Change Runtime Type in TPU. (Standard ist Keine)
Da Transfomere nicht in der Colab-Umgebung enthalten sind, werde ich sie mit pip fallen lassen. Da der diesmal verwendete Tokenizer Mecab verwendet, installieren Sie ihn ebenfalls.
!pip install transformers
!apt install aptitude
!aptitude install mecab libmecab-dev mecab-ipadic-utf8 git make curl xz-utils file -y
!pip install mecab-python3==0.7
from google.colab import drive
drive.mount('/gdrive')
%cd "/gdrive/My Drive/workspace/python/bakusoku"
Dieses Mal werde ich AutoTokenizer verwenden. Wenn Sie ein Pretrain-Modell angeben, wird der für dieses Modell geeignete Tokenizer automatisch geladen. (In diesem Fall laden wir eine Instanz von BertJapaneseTokenizer.) Sie können die Anweisung mit der Tokenize-Methode tokenisieren. Dieser Tokenizer ist in Wortstückeinheiten unterteilt. (Es gibt andere Teilungsmethoden wie Satzstücke.)
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("cl-tohoku/bert-base-japanese-whole-word-masking")
print(tokenizer.tokenize('Machen Sie BERT mit explosiver Geschwindigkeit'))
# ['LOL', '##Schnell', 'damit', 'BE', '##R', '##T', 'Zu', 'erstellen']
Da der Satz zum Lernen in eine Folge von Wort-IDs konvertiert werden muss, wird er mit der folgenden Methode konvertiert.
import numpy as np
def encode_texts(texts, tokenizer, maxlen=512):
enc_di = tokenizer.batch_encode_plus(
texts,
return_attention_masks=False,
return_token_type_ids=False,
pad_to_max_length=True,
max_length=maxlen
)
return np.array(enc_di['input_ids'])
x_train = encode_texts(train_df['text'].values, tokenizer)
x_valid = encode_texts(valid_df['text'].values, tokenizer)
x_test = encode_texts(test_df['text'].values, tokenizer)
print(x_train)
# [[ 2 281 306 ... 2478 9 3]
# [ 2 1519 7 ... 15 16 3]
# [ 2 11634 3217 ... 2478 7 3]
# ...
# [ 2 6093 16562 ... 0 0 0]
# [ 2 885 2149 ... 0 0 0]
# [ 2 5563 2037 ... 0 0 0]]
Das richtige Antwortetikett ist ebenfalls One-Hot-codiert.
from tensorflow.keras.utils import to_categorical
y_train = to_categorical(train_df['label'].values)
y_valid = to_categorical(valid_df['label'].values)
print(y_train)
# [[1. 0. 0. ... 0. 0. 0.]
# [1. 0. 0. ... 0. 0. 0.]
# [1. 0. 0. ... 0. 0. 0.]
# ...
# [0. 0. 0. ... 0. 0. 1.]
# [0. 0. 0. ... 0. 0. 1.]
# [0. 0. 0. ... 0. 0. 1.]]
Im Gegensatz zur GPU-Laufzeit, die nur durch Umschalten verwendet werden kann, muss für die TPU-Laufzeit der folgende Code geschrieben werden. Es ist fast ein magisches Level, aber es ist schneller, die Stapelgröße entsprechend der Anzahl der TPU-Kerne zu ändern.
import tensorflow as tf
try:
tpu = tf.distribute.cluster_resolver.TPUClusterResolver()
print('Running on TPU ', tpu.master())
except ValueError:
tpu = None
if tpu:
tf.config.experimental_connect_to_cluster(tpu)
tf.tpu.experimental.initialize_tpu_system(tpu)
strategy = tf.distribute.experimental.TPUStrategy(tpu)
else:
strategy = tf.distribute.get_strategy()
num_replicas = strategy.num_replicas_in_sync
print("REPLICAS: ", num_replicas)
# REPLICAS: 8
BATCH_SIZE = 16 * num_replicas # 128
Dieses Mal werden wir einen Klassifikator für mehrere Klassen erstellen, der Distil BERT als Encoder verwendet. Von der Ausgabe des Codierers ist diejenige, die dem ersten Token entspricht (ein spezielles Token, das den Satzanfang [CLS] angibt), mit dem HEAD verbunden (Ausgabeschicht von Softmax). Ich denke, es ist auch möglich, nach Global Pooling den gesamten Ausgang des Encoders zu verbinden.
from tensorflow.keras.layers import Dense, Input
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.models import Model
def build_model(transformer, num_cls=1, max_len=512):
input_word_ids = Input(shape=(max_len,), dtype=tf.int32, name="input_word_ids")
sequence_output = transformer(input_word_ids)[0]
cls_token = sequence_output[:, 0, :]
out = Dense(num_cls, activation='softmax')(cls_token)
model = Model(inputs=input_word_ids, outputs=out)
model.compile(Adam(lr=2e-4), loss='categorical_crossentropy', metrics=['accuracy']) # lr = 5e-5 * 4
return model
Laden Sie das auf dem Hugging Face HP veröffentlichte Pretrain-Modell und erstellen Sie das obige Modell. Wie der Tokenizer verwendet er TFAutoModel, um das Pretrain-Modell auf die TPU zu laden. (In diesem Fall laden wir eine Instanz von TFDistilBertModel.)
from transformers import TFAutoModel
with strategy.scope():
transformer_layer = (TFAutoModel.from_pretrained('bandainamco-mirai/distilbert-base-japanese', from_pt=True))
model = build_model(transformer_layer, num_cls=9, max_len=512)
model.summary()
# Model: "model_1"
# _________________________________________________________________
# Layer (type) Output Shape Param #
# =================================================================
# input_word_ids (InputLayer) [(None, 512)] 0
# _________________________________________________________________
# tf_distil_bert_model_1 (TFDi ((None, 512, 768), ((None 67497984
# _________________________________________________________________
# tf_op_layer_strided_slice_1 [(None, 768)] 0
# _________________________________________________________________
# dense_1 (Dense) (None, 9) 6921
# =================================================================
# Total params: 67,504,905
# Trainable params: 67,504,905
# Non-trainable params: 0
# _________________________________________________________________
fine-tuning Lassen Sie uns das Modell trainieren, das wir zuvor gemacht haben. Im folgenden Beispiel schließen 5.500 Fälle - 4 Epochen das Lernen in etwa 70 Sekunden ab.
AUTO = tf.data.experimental.AUTOTUNE
train_dataset = (
tf.data.Dataset
.from_tensor_slices((x_train, y_train))
.repeat()
.shuffle(2048)
.batch(BATCH_SIZE)
.prefetch(AUTO)
)
valid_dataset = (
tf.data.Dataset
.from_tensor_slices((x_valid, y_valid))
.batch(BATCH_SIZE)
.cache()
.prefetch(AUTO)
)
test_dataset = (
tf.data.Dataset
.from_tensor_slices(x_test)
.batch(BATCH_SIZE)
)
n_steps = x_train.shape[0] // BATCH_SIZE
train_history = model.fit(
train_dataset,
steps_per_epoch=n_steps,
validation_data=valid_dataset,
epochs=4
)
# Epoch 1/4
# 43/43 [==============================] - 31s 715ms/step - accuracy: 0.2473 - loss: 2.0548 - val_accuracy: 0.3355 - val_loss: 1.9584
# Epoch 2/4
# 43/43 [==============================] - 13s 308ms/step - accuracy: 0.6726 - loss: 1.4064 - val_accuracy: 0.6612 - val_loss: 1.1878
# Epoch 3/4
# 43/43 [==============================] - 13s 309ms/step - accuracy: 0.8803 - loss: 0.7522 - val_accuracy: 0.7877 - val_loss: 0.8257
# Epoch 4/4
# 43/43 [==============================] - 13s 309ms/step - accuracy: 0.9304 - loss: 0.4401 - val_accuracy: 0.8181 - val_loss: 0.6747
Die Genauigkeit beträgt 81% ... Das Ergebnis ist Bimyo. Ich denke, dass sich die Genauigkeit verbessern wird, wenn Sie etwas mehr Datenbereinigung und die Modellstruktur nach Encoder entwickeln.
from sklearn.metrics import classification_report
test_df['predict'] = model.predict(test_dataset, verbose=1).argmax(axis=1)
print(classification_report(test_df['label'], test_df['predict'], target_names=target_names))
# 12/12 [==============================] - 11s 890ms/step
# precision recall f1-score support
#
# dokujo-tsushin 0.73 0.95 0.83 174
# it-life-hack 0.66 0.91 0.76 174
# kaden-channel 0.79 0.47 0.59 173
# livedoor-homme 0.91 0.31 0.47 102
# movie-enter 0.81 0.96 0.88 174
# peachy 0.81 0.71 0.76 169
# smax 0.91 0.97 0.94 174
# sports-watch 0.88 1.00 0.94 180
# topic-news 0.91 0.75 0.83 154
#
# accuracy 0.81 1474
# macro avg 0.83 0.78 0.78 1474
# weighted avg 0.82 0.81 0.79 1474
Recommended Posts