言語処理100本ノック 2020 (Rev2)の「第8章: ニューラルネット」の71本目「単層ニューラルネットワークによる予測」記録です。前回、TFRecord形式で保存したファイルを読み込み、ニューラルネットワークに流し込みます。やはりTFRecord形式ファイルの扱いに手間取りました。この時点では訓練なしで流し込むのですが、そんな処理は初めて書きました。本当はSoftmaxとか解説すべきなのでしょうが、ググればいい記事がすぐに見つかるので解説しません。
記事「まとめ: 言語処理100本ノックで学べることと成果」に言語処理100本ノック 2015についてはまとめていますが、追加で差分の言語処理100本ノック 2020 (Rev2)についても更新します。
参考リンク
リンク | 備考 |
---|---|
71_単層ニューラルネットワークによる予測.ipynb | 回答プログラムのGitHubリンク |
言語処理100本ノック 2020 第8章: ニューラルネット | (PyTorchだけど)解き方の参考 |
【言語処理100本ノック 2020】第8章: ニューラルネット | (PyTorchだけど)解き方の参考 |
まとめ: 言語処理100本ノックで学べることと成果 | 言語処理100本ノックまとめ記事 |
TFRecords と tf.Example の使用法 | 今回の課題メイン |
環境
後々GPUを使わないと厳しいので、Goolge Colaboratory使いました。Pythonやそのパッケージでより新しいバージョンありますが、新機能使っていないので、プリインストールされているものをそのまま使っています。
種類 | バージョン | 内容 |
---|---|---|
Python | 3.7.12 | Google Colaboratoryのバージョン |
2.0.3 | Google Driveのマウントに使用 | |
tensorflow | 2.6.0 | ディープラーニングの主要処理 |
第8章: ニューラルネット
学習内容
深層学習フレームワークの使い方を学び,ニューラルネットワークに基づくカテゴリ分類を実装します.
ノック内容
第6章で取り組んだニュース記事のカテゴリ分類を題材として,ニューラルネットワークでカテゴリ分類モデルを実装する.なお,この章ではPyTorch, TensorFlow, Chainerなどの機械学習プラットフォームを活用せよ.
71. 単層ニューラルネットワークによる予測
問題70で保存した行列を読み込み,学習データについて以下の計算を実行せよ.
\hat{\boldsymbol y_1} = {\rm softmax}(\boldsymbol x_1 W), \\ \hat{Y} = {\rm softmax}(X_{[1:4]} W)
ただし,${\rm softmax}$はソフトマックス関数,$X_{[1:4]} \in \mathbb{R}^{4 \times d}$は特徴ベクトル$\boldsymbol x_1, \boldsymbol x_2, \boldsymbol x_3, \boldsymbol x_4$を縦に並べた行列である.
X_{[1:4]} = \begin{pmatrix}
\boldsymbol x_1 \\
\boldsymbol x_2 \\
\boldsymbol x_3 \\
\boldsymbol x_4 \\
\end{pmatrix}
行列$W \in \mathbb{R}^{d \times L}$は単層ニューラルネットワークの重み行列で,ここではランダムな値で初期化すればよい(問題73以降で学習して求める).なお,$\hat{\boldsymbol y_1} \in \mathbb{N}^L$は未学習の行列$W$で事例$x_1$を分類したときに,各カテゴリに属する確率を表すベクトルである.
同様に,$\hat{Y} \in \mathbb{N}^{n \times L}$は,学習データの事例$x_1, x_2, x_3, x_4$について,各カテゴリに属する確率を行列として表現している.
回答
回答結果
[[0.23627585 0.25216544 0.25520897 0.25634965]]
[[0.23627585 0.25216544 0.25520897 0.25634965]
[0.24906628 0.2570258 0.2513765 0.24253134]
[0.2553226 0.24705489 0.24583162 0.25179094]
[0.24834852 0.25343078 0.24328355 0.2549371 ]]
回答プログラム 71_単層ニューラルネットワークによる予測.ipynb
GitHubには確認用コードも含めていますが、ここには必要なものだけ載せています。
import tensorflow as tf
from google.colab import drive
drive.mount('/content/drive')
BASE_PATH = '/content/drive/MyDrive/ColabNotebooks/ML/NLP100_2020/08.NeuralNetworks/'
train_raw = tf.data.TFRecordDataset(BASE_PATH+'train.tfrecord')
def _parse_function(example_proto):
# 特徴の記述
feature_description = {
'title': tf.io.FixedLenFeature([], tf.string),
'category': tf.io.FixedLenFeature([], tf.string)}
# 上記の記述を使って入力の tf.Example を処理
features = tf.io.parse_single_example(example_proto, feature_description)
X = tf.io.decode_raw(features['title'], tf.float32)
y = tf.io.decode_raw(features['category'], tf.int32)
return X, y
dense_layer = tf.keras.layers.Dense(4, activation='softmax', use_bias=False, input_dim=300, kernel_initializer='random_uniform')
def output_result(batch_size):
for X, y in train_raw.map(_parse_function).batch(batch_size).take(1):
# 入力データ
print('---入力データ---')
print(X.numpy().shape)
print(y.numpy()) # 使わないけど確認だけする
print('---出力データ---')
print(dense_layer(X).numpy())
output_result(1)
output_result(4)
回答解説
TFRecord形式ファイル読込
TFRecord形式ファイルの読込は以下のコード。
BASE_PATH = '/content/drive/MyDrive/ColabNotebooks/ML/NLP100_2020/08.NeuralNetworks/'
train_raw = tf.data.TFRecordDataset(BASE_PATH+'train.tfrecord')
print(train_raw)
このままだと、データとして使えないので形式変更にする必要あり。
<TFRecordDatasetV2 shapes: (), types: tf.string>
parseするための関数を作ります。正直、面倒なのでデータ量が機械学習のボトルネックにならないのであれば、pickleやnumpy形式でファイルI/Oすべきです。
def _parse_function(example_proto):
# 特徴の記述
feature_description = {
'title': tf.io.FixedLenFeature([], tf.string),
'category': tf.io.FixedLenFeature([], tf.string)}
# 上記の記述を使って入力の tf.Example を処理
features = tf.io.parse_single_example(example_proto, feature_description)
X = tf.io.decode_raw(features['title'], tf.float32)
y = tf.io.decode_raw(features['category'], tf.int32)
return X, y
で、map
関数を使って先程定義した関数_parse_function
を呼んで、batchとtakeでデータ取得の調整をします。
def output_result(batch_size):
for X, y in train_raw.map(_parse_function).batch(batch_size).take(1):
# 入力データ
print('---入力データ---')
print(X.numpy().shape)
print(y.numpy()) # 使わないけど確認だけする
print('---出力データ---')
print(dense_layer(X).numpy())
あとは、バッチサイズを渡して先に定義した関数を実行。
output_result(1)
output_result(4)
訓練などをしていない状態なので、1/4に近い数値がSoftmax結果として返ってきます。
---入力データ---
(1, 300)
[[1 0 0 0]]
---出力データ---
[[0.23627585 0.25216544 0.25520897 0.25634965]]
---入力データ---
(4, 300)
[[1 0 0 0]
[0 1 0 0]
[0 0 0 1]
[0 0 1 0]]
---出力データ---
[[0.23627585 0.25216544 0.25520897 0.25634965]
[0.24906628 0.2570258 0.2513765 0.24253134]
[0.2553226 0.24705489 0.24583162 0.25179094]
[0.24834852 0.25343078 0.24328355 0.2549371 ]]
softmax計算
以下のノック内容部分です。活性化関数activation
をsoftmaxに、入力input_dim
を300次元にしています。
kernel_initializer
でランダムな値で初期化をして、use_bias
をFalse
にすることでバイアス項をなくしています。バイアス項をなくしている理由は、ノック79本目に「バイアス項の導入や多層化など,ニューラルネットワークの形状を変更しながら,高性能なカテゴリ分類器を構築せよ」と書かれていて、ここではまだ入れてはいけないことに気づきました。
行列$W \in \mathbb{R}^{d \times L}$は単層ニューラルネットワークの重み行列で,ここではランダムな値で初期化すればよい
dense_layer = tf.keras.layers.Dense(
4, activation='softmax',
use_bias=False,
input_dim=300,
kernel_initializer='random_uniform')