4 minutes
今さら言語処理100本ノック #8 前半
ようやく終わりが見えてきましたね。
言語処理 100 本ノック(第 8 章: ニューラルネット)
70. 単語ベクトルの和による特徴量
問題 50 で構築した学習データ,検証データ,評価データを行列・ベクトルに変換したい.(以下略)
記事見出し内を単語化->単語を単語ベクトルに変換->記事見出し内の単語ベクトルの平均を求め、それをその記事の特徴量にする。
といったことを行う。
記事見出し内を単語化というのは第 6 章 51.で同じようなことをしていたので、この時作成した中間ファイルをそのまま流用する。
モデルの読み込み、特徴量生成
from gensim.models import KeyedVectors
model = KeyedVectors.load_word2vec_format('GoogleNews-vectors-negative300.bin.gz', binary=True)
with open("train.middle.txt") as f:
lines = f.readlines()
category_pair = {"b":0, "t":1, "e":2, "m":3}
category, data = [], []
for line in lines:
cat, body = line.rstrip("\n").split("\t")
token_vecs = [model[t] for t in body.split() if t in model]
if len(token_vecs) == 0:
continue
category.append(category_pair.get(cat))
mean = sum(token_vecs)/len(token_vecs)
data.append(mean)
特徴量保存
h5py
の使い方いまいち理解していないのに使う
import h5py
with h5py.File('train_nlp100_08_70.hdf5', mode='x') as f:
f.create_dataset('label', data=category)
f.create_dataset('vect', data=data)
71. 単層ニューラルネットワークによる予測
70
で作ったデータの読み込み
import h5py
with h5py.File('train_nlp100_08_70.hdf5', "r") as f:
label = f["label"][...]
vect = f["vect"][...]
print(label.shape, vect.shape) #> (10683,) (10683, 300)
NumPy
ソフトマックス関数は簡単にいえば出力値の合計を 1 にする関数で、各値の範囲が 0~1 となる。
行列 W をランダムな値で初期化とあるのでここではゼロ行列で初期化する。
この時出力のベクトルは[0.25, 0.25, 0.25, 0.25]
であることが期待される。(入力値が全て 0 のため、各事例に分類される可能性は全て等しい)
w = np.zeros((300, 4))
softmax = lambda x: np.exp(x) / np.sum(np.exp(x), axis=1)
print(softmax(np.dot(vect[:1], w)))
print(softmax(np.dot(vect[:4], w)))
[[0.25 0.25 0.25 0.25]]
[[0.25 0.25 0.25 0.25]
[0.25 0.25 0.25 0.25]
[0.25 0.25 0.25 0.25]
[0.25 0.25 0.25 0.25]]
PyTorch
import torch
x_train = torch.tensor(vect, dtype=torch.float32)
w = torch.zeros(300, 4)
softmax = torch.nn.Softmax(dim=-1)
print(softmax(torch.matmul(x_train[:1], w)))
print(softmax(torch.matmul(x_train[:4], w)))
TensorFlow
import tensorflow as tf
w = tf.zeros((300, 4))
print(tf.nn.softmax(tf.matmul(vect[:1],w), axis=1))
print(tf.nn.softmax(tf.matmul(vect[:4],w), axis=1))
参考は特記がなければ以下を参考にしている。
PyTorch documentation — PyTorch 1.5.0 documentation
print(torch.__version__) #> '1.5.0+cu101'
print(tensorflow.__version__) #> '2.2.0'
72. 損失と勾配の計算
$x_1-x_4$の正解ラベルを[0, 1, 2, 3]
とする。
正解ラベルのインデックスを 1,それ以外のインデックスを 0 とした1-hotベクトル
で構成される行列 t は
この時交差エントロピーは
($y_n$は先ほどの確率分布)
参考: 3. ニューラルネットワークの基礎 — メディカル AI 専門コース オンライン講義資料 documentation
NumPy (交差エントロピー)
w = np.zeros((300, 4))
softmax = lambda x: np.exp(x) / np.sum(np.exp(x), axis=1)
one_hot = np.identity(4)[[0, 1, 2, 3]]
l = -np.mean(np.sum(one_hot * np.ma.log(softmax(np.dot(vect[:4], w))), axis=-1))
print(l) #> 1.3862943611198906
PyTorch (交差エントロピー)
x_train = torch.tensor(vect[:4], dtype=torch.float32, requires_grad=True)
y_train = torch.tensor([0, 1, 2, 3])
w = torch.zeros(300, 4)
loss = torch.nn.CrossEntropyLoss()
output = loss(torch.matmul(x_train, w), y_train)
output.backward()
print(output.item()) #> 1.3862943649291992
y_train
のラベルを float 型にしてしまっていったり、requires_grad=True
にしていなかったりで時間食いました。
困ったらドキュメントをみましょう
TensorFlow (交差エントロピー)
x_train = tf.constant(vect[:4], dtype=tf.float32)
y_train = tf.one_hot([0,1,2,3], depth=4) # one-hotベクトルなので注意
w = tf.zeros((300, 4))
output = tf.compat.v1.losses.softmax_cross_entropy(y_train, tf.matmul(x_train, w))
print(output.numpy()) #> 1.3862944
勾配
Softmax と Cross Entropy 損失を用いた分類における$w_i$における勾配は$y_i-t_i$で求まる。
w = np.zeros((300, 4))
softmax = lambda x: np.exp(x) / np.sum(np.exp(x), axis=1)
t = np.identity(4)[[0, 1, 2, 3]]
y = one_hot * np.ma.log(softmax(np.dot(vect[:4], w)))
print(y - t)
[[-2.386294361119891 -0.0 -0.0 -0.0]
[-0.0 -2.386294361119891 -0.0 -0.0]
[-0.0 -0.0 -2.386294361119891 -0.0]
[-0.0 -0.0 -0.0 -2.386294361119891]]
TensorFlow
とPyTorch
での勾配の導出は調べきれなかったのでパスします。
73. 確率的勾配降下法による学習
確率的勾配降下法(SGD: Stochastic Gradient Descent)を用いて,行列 W を学習せよ.なお,学習は適当な基準で終了させればよい(例えば「100 エポックで終了」など).
トレーニング, 検証データの読み込み
import h5py
with h5py.File('train_nlp100_08_70.hdf5', "r") as f:
train_label = f["label"][...]
train_vect = f["vect"][...]
with h5py.File('valid_nlp100_08_70.hdf5', "r") as f:
valid_label = f["label"][...]
valid_vect = f["vect"][...]
Keras を用いた実装
あまり自信がないが、それらしきものは得た。
import tensorflow as tf
from tensorflow import keras
model = keras.Sequential()
model.add(keras.layers.Dense(300))
model.add(keras.layers.Dense(4, activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='sgd', metrics=['accuracy'])
history = model.fit(train_vect, tf.one_hot(train_label, depth=4), epochs=100, validation_data=(valid_vect, tf.one_hot(valid_label, depth=4)), verbose=2)
model.summary()
Model: "sequential_23"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
dense_33 (Dense) multiple 90300
_________________________________________________________________
dense_34 (Dense) multiple 1204
=================================================================
Total params: 91,504
Trainable params: 91,504
Non-trainable params: 0
_________________________________________________________________
print(*map(lambda x: x.shape, [train_vect, train_label, valid_vect, valid_label]))
#> (10683, 300) (10683,) (1335, 300) (1335,)
はじめてのニューラルネットワーク:分類問題の初歩 | TensorFlow Core
74. 正解率の計測
問題 73 で求めた行列を用いて学習データおよび評価データの事例を分類したとき,その正解率をそれぞれ求めよ.
print("train: ", model.evaluate(train_vect, tf.one_hot(train_label, depth=4), verbose=2))
print("valid: ", model.evaluate(valid_vect, tf.one_hot(valid_label, depth=4), verbose=2))
334/334 - 0s - loss: 0.2365 - accuracy: 0.9219
train: [0.2365119755268097, 0.92193204164505]
42/42 - 0s - loss: 0.2907 - accuracy: 0.9004
valid: [0.29069963097572327, 0.9003745317459106]
75. 損失と正解率のプロット
問題 73 のコードを改変し,各エポックのパラメータ更新が完了するたびに,訓練データでの損失,正解率,検証データでの損失,正解率をグラフにプロットし,学習の進捗状況を確認できるようにせよ.
import matplotlib.pyplot as plt
plt.figure(figsize=(16,10))
for k, v in history.history.items():
plt.plot(history.epoch, v, label=k)
plt.xlabel('Epochs')
plt.legend()
plt.xlim([0,max(history.epoch)])
plt.show()