言語処理100本ノック 2020 (Rev2)の「第9章: RNN, CNN」の82本目「確率的勾配降下法による学習」記録です。前回(81本目)ノックから、大差ありません。訓練・評価するだけです。100Epoch訓練して、テストデータセットに対してLoss0.56、正答率84%まで行きました。まぁまぁではないでしょうか。
記事「まとめ: 言語処理100本ノックで学べることと成果」に言語処理100本ノック 2015についてはまとめていますが、追加で差分の言語処理100本ノック 2020 (Rev2)についても更新します。
参考リンク
リンク | 備考 |
---|---|
82_確率的勾配降下法による学習.ipynb | 回答プログラムのGitHubリンク |
言語処理100本ノック 2020 第9章: RNN, CNN | (PyTorchだけど)解き方の参考 |
【言語処理100本ノック 2020】第9章: RNN, CNN | (PyTorchだけど)解き方の参考 |
まとめ: 言語処理100本ノックで学べることと成果 | 言語処理100本ノックまとめ記事 |
環境
後々GPUを使わないと厳しいので、Google Colaboratory使いました。Pythonやそのパッケージでより新しいバージョンありますが、新機能使っていないので、プリインストールされているものをそのまま使っています。
種類 | バージョン | 内容 |
---|---|---|
Python | 3.7.12 | Google Colaboratoryのバージョン |
2.0.3 | Google Driveのマウントに使用 | |
tensorflow | 2.7.0 | ディープラーニングの主要処理 |
nltk | 3.2.5 | Tokenの辞書作成に使用 |
pandas | 1.1.5 | 行列に関する処理に使用 |
第8章: ニューラルネット
学習内容
深層学習フレームワークを用い,再帰型ニューラルネットワーク(RNN)や畳み込みニューラルネットワーク(CNN)を実装します.
82. 確率的勾配降下法による学習
確率的勾配降下法(SGD: Stochastic Gradient Descent)を用いて,問題81で構築したモデルを学習せよ.訓練データ上の損失と正解率,評価データ上の損失と正解率を表示しながらモデルを学習し,適当な基準(例えば10エポックなど)で終了させよ.
回答
回答結果
まとめるとこんな精度です。
データセット | Loss | 正答率 |
---|---|---|
訓練 | 0.1618 | 94.73% |
検証 | 0.6502 | 82.11% |
評価 | 0.5557 | 83.76% |
訓練後のテストデータセットの結果です。100Epochで訓練しています。
42/42 [==============================] - 0s 8ms/step - loss: 0.5557 - acc: 0.8376
[0.5556892156600952, 0.83757483959198]
参考までに訓練時の最後(100Epoch目)の結果です。GPU使わずに訓練して、約14分かかっています。訓練のLossは100Epoch目まで下がっていますが、検証のLossがだいたい80Epoch目くらいから下げ止まりになっているのでEarly Stoppingすれば、もう少し精度が良くなるかもしれません。また、RNN(GRU)にDropOutも入れておけばよかったかもしれません(デフォルトでDropoutの値がゼロ)。
Epoch 1/100
334/334 [==============================] - 14s 28ms/step - loss: 1.2010 - acc: 0.4574 - val_loss: 1.1561 - val_acc: 0.4731
Epoch 2/100
334/334 [==============================] - 8s 24ms/step - loss: 1.1497 - acc: 0.4863 - val_loss: 1.1481 - val_acc: 0.4850
中略
Epoch 99/100
334/334 [==============================] - 8s 24ms/step - loss: 0.1686 - acc: 0.9443 - val_loss: 0.6509 - val_acc: 0.8286
Epoch 100/100
334/334 [==============================] - 8s 24ms/step - loss: 0.1618 - acc: 0.9473 - val_loss: 0.6502 - val_acc: 0.8211
回答プログラム 82_確率的勾配降下法による学習.ipynb
GitHubには確認用コードも含めていますが、ここには必要なものだけ載せています。
import nltk
import pandas as pd
import tensorflow as tf
from google.colab import drive
drive.mount('/content/drive')
BASE_PATH = '/content/drive/MyDrive/ColabNotebooks/ML/NLP100_2020/06.MachineLearning/'
max_len = 0
vocabulary = []
def read_dataset(type_):
global max_len
global vocabulary
df = pd.read_table(BASE_PATH+type_+'.feature.txt')
df.info()
sr_title = df['title'].str.split().explode()
max_len_ = df['title'].map(lambda x: len(x.split())).max()
if max_len < max_len_:
max_len = max_len_
if len(vocabulary) == 0:
vocabulary = [k for k, v in nltk.FreqDist(sr_title).items() if v > 1]
else:
vocabulary.extend([k for k, v in nltk.FreqDist(sr_title).items() if v > 1])
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') # あまりこだわらずにテストデータセットも追加
# setで重複削除し、タプル形式に設定
tup_voc = tuple(set(vocabulary))
print(f'vocabulary size before removing duplicates: {len(vocabulary)}')
print(f'vocabulary size after removing duplicates: {len(tup_voc)}')
print(f'sample vocabulary: {tup_voc[:10]}')
print(f'max length is {max_len}')
vectorize_layer = tf.keras.layers.TextVectorization(
output_mode='int',
vocabulary=tup_voc,
output_sequence_length=max_len)
print(f'sample vocabulary: {vectorize_layer.get_vocabulary()[:10]}')
model = tf.keras.models.Sequential()
model.add(tf.keras.Input(shape=(1,), dtype=tf.string))
model.add(vectorize_layer)
model.add(tf.keras.layers.Embedding(vectorize_layer.vocabulary_size(), 300, mask_zero=True))
model.add(tf.keras.layers.GRU(50))
model.add(tf.keras.layers.Dense(4, activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='sgd', metrics=['acc'])
model.summary()
model.fit(X_train, y_train, epochs=100, validation_data=(X_valid, y_valid))
回答解説
全体的にあまり変更点ないです。
ラベル
ラベルをto_categoriacal
関数使ってOne-Hot化しています。それに伴い、関数から返す変数をX
とy
の2つにしています。
def read_dataset(type_):
global max_len
global vocabulary
df = pd.read_table(BASE_PATH+type_+'.feature.txt')
df.info()
sr_title = df['title'].str.split().explode()
max_len_ = df['title'].map(lambda x: len(x.split())).max()
if max_len < max_len_:
max_len = max_len_
if len(vocabulary) == 0:
vocabulary = [k for k, v in nltk.FreqDist(sr_title).items() if v > 1]
else:
vocabulary.extend([k for k, v in nltk.FreqDist(sr_title).items() if v > 1])
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化
メトリックス追加
メトリックスにAccuracyを追加しています。
model.compile(loss='categorical_crossentropy', optimizer='sgd', metrics=['acc'])
訓練
fit
関数で100Epoch訓練をまわしています。
model.fit(X_train, y_train, epochs=100, validation_data=(X_valid, y_valid))
評価
最後にテストデートセットを使って評価。混合行列表示等まですればいいのかもしれませんが、していません。
model.evaluate(X_test, y_test)