LoginSignup
0
0

More than 1 year has passed since last update.

言語処理100本ノック(2020)-89: 事前学習済み言語モデルからの転移学習(Keras)

Posted at

言語処理100本ノック 2020 (Rev2)「第9章: RNN, CNN」89本目「事前学習済み言語モデルからの転移学習」記録です。初めてBERT使いました。まだ、あまりBERTを深く理解できていませんが、AttentionとTransformerまで理解した状態です。学習済モデルを再利用するだけなので、理解しないでもこのレベルでは問題ないです。
記事「まとめ: 言語処理100本ノックで学べることと成果」言語処理100本ノック 2015についてはまとめていますが、追加で差分の言語処理100本ノック 2020 (Rev2)についても更新します。

参考リンク

リンク 備考
89_事前学習済み言語モデルからの転移学習.ipynb 回答プログラムのGitHubリンク
言語処理100本ノック 2020 第9章: RNN, CNN (PyTorchだけど)解き方の参考
【言語処理100本ノック 2020】第9章: RNN, CNN (PyTorchだけど)解き方の参考
まとめ: 言語処理100本ノックで学べることと成果 言語処理100本ノックまとめ記事
BERTでテキストを分類する TensorFlow公式チュートリアル
[Attention入門]seq2seqとAttentionの解説(TensorFlow) Attentionに関する解説
[Transformer入門]TensorFlowチュートリアル: Transformer Transformerに関する解説

環境

GPUを使いたかったので、Google Colaboratory使いました。Pythonやそのパッケージでより新しいバージョンありますが、新機能使っていないので、プリインストールされているものをそのまま使っています。
tensorflow-textはPython上で明示的に使用していませんが、Importしないとエラーになったので使っています。

種類 バージョン 内容
Python 3.7.14 Google Colaboratoryのバージョン
google 2.0.3 Google Driveのマウントに使用
pandas 1.3.5 行列に関する処理に使用
tensorflow 2.10.0 ディープラーニングの主要処理
tensorflow-text 2.10.0 ディープラーニングのNLP処理
tf-models-official 2.10.0 BERTのOptimizer用

第8章: ニューラルネット

学習内容

深層学習フレームワークを用い,再帰型ニューラルネットワーク(RNN)や畳み込みニューラルネットワーク(CNN)を実装します.

89. 事前学習済み言語モデルからの転移学習

事前学習済み言語モデル(例えばBERTなど)を出発点として,ニュース記事見出しをカテゴリに分類するモデルを構築せよ.

回答

回答結果

88本目ノック(パラメータチューニング)との精度比較です。超向上してます。BERTやばいな。

データセット Loss 正答率
訓練 0.346 :arrow_right: 0.262(-0.084) 88.3% :arrow_right: 91.3%(+3.0%)
検証 0.430 :arrow_right: 0.298(-0.132) 85.3% :arrow_right: 91.5%(+6.2%)
評価 0.400 :arrow_right: 0.242(-0.158) 85.8% :arrow_right: 92.6%(+6.8%)

参考に評価データセットの結果です。

結果
> model.evaluate(X_test, y_test)
42/42 [==============================] - 4s 95ms/step - loss: 0.2419 - acc: 0.9259

訓練経過に至っては、(検証結果が良くないので採用はしていませんが)訓練Lossは0.1以下まで下がっています。

訓練経過
Epoch 1/30
334/334 [==============================] - 78s 217ms/step - loss: 0.6090 - acc: 0.7742 - val_loss: 0.3382 - val_acc: 0.8885
Epoch 2/30
334/334 [==============================] - 75s 225ms/step - loss: 0.2616 - acc: 0.9125 - val_loss: 0.2982 - val_acc: 0.9147
Epoch 3/30
334/334 [==============================] - 74s 221ms/step - loss: 0.1819 - acc: 0.9405 - val_loss: 0.3111 - val_acc: 0.9124
Epoch 4/30
334/334 [==============================] - 73s 219ms/step - loss: 0.1337 - acc: 0.9571 - val_loss: 0.3245 - val_acc: 0.9162
Epoch 5/30
334/334 [==============================] - 72s 216ms/step - loss: 0.0997 - acc: 0.9677 - val_loss: 0.3327 - val_acc: 0.9184
Epoch 6/30
334/334 [==============================] - 73s 219ms/step - loss: 0.0947 - acc: 0.9701 - val_loss: 0.3327 - val_acc: 0.9184
Epoch 7/30
334/334 [==============================] - 71s 214ms/step - loss: 0.0913 - acc: 0.9715 - val_loss: 0.3327 - val_acc: 0.9184

回答プログラム 89_事前学習済み言語モデルからの転移学習.ipynb

GitHubには確認用コードも含めていますが、ここには必要なものだけ載せています。

回答: パッケージインストール部分

特別なことはしておらず普通のpip installです。

!pip install -q -U tensorflow-text tf-models-official

回答: Script処理

Scrip処理の部分です

from google.colab import drive
from official.nlp import optimization  # to create AdamW optimizer
import pandas as pd
import tensorflow as tf
import tensorflow_hub as hub
import tensorflow_text as text # 明示的に使っていないが必要

drive.mount('/content/drive')

BASE_PATH = '/content/drive/MyDrive/ColabNotebooks/ML/NLP100_2020/'

def read_dataset(type_):
    df = pd.read_table(BASE_PATH+'06.MachineLearning/'+type_+'.feature.txt')
    df.info()
    sr_title = df['title'].str.split().explode()
    y = df['category'].replace({'b':0, 't':1, 'e':2, 'm':3})
    return df['title'], tf.keras.utils.to_categorical(y, dtype='int32')  # 4値分類なので訓練・検証・テスト共通でone-hot化

X_train, y_train = read_dataset('train')
X_valid, y_valid = read_dataset('valid')
X_test, y_test = read_dataset('test')

preprocessor = hub.KerasLayer(
        'https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3',
        name='preprocessor')

encoder = hub.KerasLayer(
        'https://tfhub.dev/tensorflow/small_bert/bert_en_uncased_L-4_H-512_A-8/2',
        trainable=True, name='BERT_encoder')

def build_classifier_model(preprocessor, encoder):
    text_input = tf.keras.layers.Input(shape=(), dtype=tf.string, name='text')
    encoder_inputs = preprocessor(text_input)
    outputs = encoder(encoder_inputs)
    net = outputs['pooled_output'] # [batch_size, 512].
    net = tf.keras.layers.Dropout(0.1)(net)
    net = tf.keras.layers.Dense(4, activation='softmax', name='classifier')(net)
    return tf.keras.Model(text_input, net)    

model = build_classifier_model(preprocessor, encoder)

epochs = 5
# cardinality は データ件数/バッチサイズ を切り上げ
steps_per_epoch = (len(X_train)+32-1)//32 # 除算切り上げ
num_train_steps = steps_per_epoch * epochs
num_warmup_steps = int(0.1*num_train_steps)

optimizer = optimization.create_optimizer(init_lr=3e-5,
                                          num_train_steps=num_train_steps,
                                          num_warmup_steps=num_warmup_steps,
                                          optimizer_type='adamw')
model.compile(optimizer=optimizer,
              loss='categorical_crossentropy',
              metrics=['acc'])

model.fit(X_train, y_train, validation_data=(X_valid, y_valid), epochs=30,
          callbacks=[tf.keras.callbacks.EarlyStopping(
              patience=5, restore_best_weights=False)])

model.evaluate(X_test, y_test)

回答解説

Scriptの一部分を解説します。

Import

import tensorflow_text as text は Python Script内で明示的に使用していないのが注意点です。

from google.colab import drive
from official.nlp import optimization  # to create AdamW optimizer
import pandas as pd
import tensorflow as tf
import tensorflow_hub as hub
import tensorflow_text as text # 明示的に使っていないが必要

モデル読込

BERTの前処理とEncoderをTensorFlow Hubから読み込みます。TensorFlow HubにはALBERTなど多くのモデルがあるのですが、公式チュートリアル「BERTでテキストを分類する」と同じものを使用します。

Preprocessor

preprocessor = hub.KerasLayer(
        'https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3',
        name='preprocessor')
text_preprocessed = preprocessor([X_train[0]])

print(f'Text        : {[X_train[0]]}')
print(f'Keys       : {list(text_preprocessed.keys())}')
print(f'Shape      : {text_preprocessed["input_word_ids"].shape}')
print(f'Word Ids   : {text_preprocessed["input_word_ids"][0, :12]}')
print(f'Input Mask : {text_preprocessed["input_mask"][0, :12]}')
print(f'Type Ids   : {text_preprocessed["input_type_ids"][0, :12]}')

3種類の出力(Word Ids, Input Mask, Type Ids)があるのがわかります。

結果
Text        : ['REFILE UPDATE car sale up for sixth month as economy recover']
Keys       : ['input_type_ids', 'input_word_ids', 'input_mask']
Shape      : (1, 128)
Word Ids   : [  101 25416  9463 10651  2482  5096  2039  2005  4369  3204  2004  4610]
Input Mask : [1 1 1 1 1 1 1 1 1 1 1 1]
Type Ids   : [0 0 0 0 0 0 0 0 0 0 0 0]

BERTモデル

encoder = hub.KerasLayer(
        'https://tfhub.dev/tensorflow/small_bert/bert_en_uncased_L-4_H-512_A-8/2',
        trainable=True, name='BERT_encoder')
bert_results = encoder(text_preprocessed)

print(f'Pooled Outputs Shape:{bert_results["pooled_output"].shape}')
print(f'Pooled Outputs Values:{bert_results["pooled_output"][0, :12]}')
print(f'Sequence Outputs Shape:{bert_results["sequence_output"].shape}')
print(f'Sequence Outputs Values:{bert_results["sequence_output"][0, :12]}')

こちらは2種類の出力(Pooled Output, Sequence Output)があるのがわかります。使うのはPooled Outputです。

結果
Pooled Outputs Shape:(1, 512)
Pooled Outputs Values:[ 0.83857816  0.8714344  -0.05869783 -0.21945992  0.14879902  0.62879145
  0.99516565 -0.9972128   0.15385738 -0.99931264  0.3135587  -0.8180948 ]
Sequence Outputs Shape:(1, 128, 512)
Sequence Outputs Values:[[ 0.33862212 -0.11791104  0.3418219  ... -1.1574944   0.29415578
   0.9472706 ]
 [ 0.28497177  0.39325225  0.60013586 ... -0.35047954  0.5410543
   1.267122  ]
 [-0.8773053   0.01777261  0.68712896 ...  0.60109943 -0.22280091
   1.305879  ]
 ...
 [ 0.00644314  0.17899063  0.6403154  ... -0.34970805  0.08075863
   0.14510325]
 [-0.33870348  0.36234245  0.05213176 ... -0.7976972  -0.43191314
   0.8580584 ]
 [-0.10936005  0.2492041   0.58715516 ...  0.34737933 -0.64347273
   0.71053106]]

モデル全体作成

モデル全体を作成します。BERTからpooled_outputを受け取り、Dropout層と全結合層を経て4クラス分類にしています。
tf.keras.utils.plot_modelはshapeを出力しようとするとエラーとなったので、やめました。バグのようですが、重要ではないので出力をやめました。

def build_classifier_model(preprocessor, encoder):
    text_input = tf.keras.layers.Input(shape=(), dtype=tf.string, name='text')
    encoder_inputs = preprocessor(text_input)
    outputs = encoder(encoder_inputs)
    net = outputs['pooled_output'] # [batch_size, 512].
    net = tf.keras.layers.Dropout(0.1)(net)
    net = tf.keras.layers.Dense(4, activation='softmax', name='classifier')(net)
    return tf.keras.Model(text_input, net)    

model = build_classifier_model(preprocessor, encoder)
bert_raw_result = model(tf.constant([X_train[0]]))
print(tf.sigmoid(bert_raw_result))
model.summary()

# show_shapes=True , then error
tf.keras.utils.plot_model(model)
結果
tf.Tensor([[0.5128579  0.56176686 0.63317573 0.5385126 ]], shape=(1, 4), dtype=float32)

Model: "model"
__________________________________________________________________________________________________
 Layer (type)                   Output Shape         Param #     Connected to                     
==================================================================================================
 text (InputLayer)              [(None,)]            0           []                               
                                                                                                  
 preprocessor (KerasLayer)      {'input_type_ids':   0           ['text[0][0]']                   
                                (None, 128),                                                      
                                 'input_word_ids':                                                
                                (None, 128),                                                      
                                 'input_mask': (Non                                               
                                e, 128)}                                                          
                                                                                                  
 BERT_encoder (KerasLayer)      {'encoder_outputs':  28763649    ['preprocessor[0][0]',           
                                 [(None, 128, 512),               'preprocessor[0][1]',           
                                 (None, 128, 512),                'preprocessor[0][2]']           
                                 (None, 128, 512),                                                
                                 (None, 128, 512)],                                               
                                 'sequence_output':                                               
                                 (None, 128, 512),                                                
                                 'default': (None,                                                
                                512),                                                             
                                 'pooled_output': (                                               
                                None, 512)}                                                       
                                                                                                  
 dropout (Dropout)              (None, 512)          0           ['BERT_encoder[0][5]']           
                                                                                                  
 classifier (Dense)             (None, 4)            2052        ['dropout[0][0]']                
                                                                                                  
==================================================================================================
Total params: 28,765,701
Trainable params: 28,765,700
Non-trainable params: 1

image.png

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0