ようやく終わりが見えてきましたね。

他の 100 本ノックシリーズ

言語処理 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

TensorFlow API Versions

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 は

t=1-hot vector

この時交差エントロピーは

L = -\x0crac{1}{N}\\sum^N_{n=1}t_nlogy_n\\\n= -\x0crac{1}{4}(t_1*logy_1+t_2*logy_2+t_3*logy_3+t_4*logy_4)

($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$で求まる。

notes.pdf

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

TensorFlowPyTorchでの勾配の導出は調べきれなかったのでパスします。

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()
blog top page