TensorFlow 2.0 : 上級 Tutorials : テキスト :- RNN でテキスト分類 (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 11/09/2019
* 本ページは、TensorFlow org サイトの TF 2.0 – Advanced Tutorials – Text の以下のページを翻訳した上で
適宜、補足説明したものです:
* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
- お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。
- Windows PC のブラウザからご参加が可能です。スマートデバイスもご利用可能です。
◆ お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。
株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション |
E-Mail:sales-info@classcat.com ; WebSite: https://www.classcat.com/ |
Facebook: https://www.facebook.com/ClassCatJP/ |
テキスト :- RNN でテキスト分類
このテキスト分類チュートリアルはセンチメント分析のために IMDB 巨大映画レビュー・データセット 上で リカレント・ニューラルネットワーク を訓練します。
セットアップ
from __future__ import absolute_import, division, print_function, unicode_literals import tensorflow_datasets as tfds import tensorflow as tf
matplotlib をインポートしてグラフをプロットするためのヘルパー関数を作成します :
import matplotlib.pyplot as plt def plot_graphs(history, string): plt.plot(history.history[string]) plt.plot(history.history['val_'+string], '') plt.xlabel("Epochs") plt.ylabel(string) plt.legend([string, 'val_'+string]) plt.show()
入力パイプラインをセットアップする
IMDB 巨大映画レビュー・データセットは二値分類データセットです — 総てのレビューはポジティブかネガティブなセンチメント (感情) を持ちます。
TFDS を使用してデータセットをダウンロードします。
dataset, info = tfds.load('imdb_reviews/subwords8k', with_info=True, as_supervised=True) train_dataset, test_dataset = dataset['train'], dataset['test']
dataset info はエンコーダを含みます ( tfds.features.text.SubwordTextEncoder )。
encoder = info.features['text'].encoder
print ('Vocabulary size: {}'.format(encoder.vocab_size))
Vocabulary size: 8185
このテキスト・エンコーダは任意の文字列を可逆的にエンコードし、必要であればバイト・エンコーディングに戻します。
sample_string = 'Hello TensorFlow.' encoded_string = encoder.encode(sample_string) print ('Encoded string is {}'.format(encoded_string)) original_string = encoder.decode(encoded_string) print ('The original string: "{}"'.format(original_string))
Encoded string is [4025, 222, 6307, 2327, 4043, 2120, 7975] The original string: "Hello TensorFlow."
assert original_string == sample_string
for index in encoded_string: print ('{} ----> {}'.format(index, encoder.decode([index])))
4025 ----> Hell 222 ----> o 6307 ----> Ten 2327 ----> sor 4043 ----> Fl 2120 ----> ow 7975 ----> .
訓練のためにデータを準備する
次にこれらのエンコードされた文字列のバッチを作成します。
シークエンスをバッチの最長文字列の長さにゼロ・パッドするために padded_batch メソッドを使用します :
BUFFER_SIZE = 10000 BATCH_SIZE = 64
train_dataset = train_dataset.shuffle(BUFFER_SIZE) train_dataset = train_dataset.padded_batch(BATCH_SIZE, train_dataset.output_shapes) test_dataset = test_dataset.padded_batch(BATCH_SIZE, test_dataset.output_shapes)
モデルを作成する
tf.keras.Sequential モデルを構築して埋め込み層から始めます。埋め込み層は単語毎に一つのベクトルをストアします。呼び出されたとき、それは単語インデックスのシークエンスをベクトルのシークエンスに変換します。これらのベクトルは訓練可能です。(十分なデータ上で) 訓練後、類似の意味を持つ単語はしばしば類似したベクトルを持ちます。
このインデックス検索は tf.keras.layers.Dense 層を通した one-hot エンコード・ベクトルを渡すのと等価の演算よりも遥かにより効率的です。
リカレント・ニューラルネットワーク (RNN) は要素を通して反復することによってシークエンス入力を処理します。RNN は一つの時間ステップからの出力をそれらの入力 — とそれから次へと渡します。
tf.keras.layers.Bidirectional ラッパーはまた RNN 層とともに使用できます。これは RNN 層を通して入力を foward そして backward に伝播してそれから出力を結合します。これは RNN が長期の (= long range) 依存性を学習する手助けをします。
model = tf.keras.Sequential([ tf.keras.layers.Embedding(encoder.vocab_size, 64), tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(64)), tf.keras.layers.Dense(64, activation='relu'), tf.keras.layers.Dense(1, activation='sigmoid') ])
訓練プロセスを構成するために Keras モデルをコンパイルします :
model.compile(loss='binary_crossentropy', optimizer=tf.keras.optimizers.Adam(1e-4), metrics=['accuracy'])
モデルを訓練する
history = model.fit(train_dataset, epochs=10, validation_data=test_dataset, validation_steps=30)
Epoch 1/10 391/391 [==============================] - 55s 141ms/step - loss: 0.6530 - accuracy: 0.5983 - val_loss: 0.0000e+00 - val_accuracy: 0.0000e+00 Epoch 2/10 391/391 [==============================] - 48s 124ms/step - loss: 0.3648 - accuracy: 0.8521 - val_loss: 0.3552 - val_accuracy: 0.8484 Epoch 3/10 391/391 [==============================] - 48s 124ms/step - loss: 0.2560 - accuracy: 0.9024 - val_loss: 0.3304 - val_accuracy: 0.8609 Epoch 4/10 391/391 [==============================] - 48s 123ms/step - loss: 0.2153 - accuracy: 0.9230 - val_loss: 0.3230 - val_accuracy: 0.8615 Epoch 5/10 391/391 [==============================] - 48s 123ms/step - loss: 0.1887 - accuracy: 0.9346 - val_loss: 0.3435 - val_accuracy: 0.8615 Epoch 6/10 391/391 [==============================] - 48s 123ms/step - loss: 0.1711 - accuracy: 0.9416 - val_loss: 0.3324 - val_accuracy: 0.8641 Epoch 7/10 391/391 [==============================] - 48s 122ms/step - loss: 0.1452 - accuracy: 0.9524 - val_loss: 0.3489 - val_accuracy: 0.8531 Epoch 8/10 391/391 [==============================] - 48s 122ms/step - loss: 0.1326 - accuracy: 0.9570 - val_loss: 0.4295 - val_accuracy: 0.8589 Epoch 9/10 391/391 [==============================] - 48s 123ms/step - loss: 0.1168 - accuracy: 0.9643 - val_loss: 0.4225 - val_accuracy: 0.8578 Epoch 10/10 391/391 [==============================] - 48s 122ms/step - loss: 0.1139 - accuracy: 0.9648 - val_loss: 0.4547 - val_accuracy: 0.8568
test_loss, test_acc = model.evaluate(test_dataset) print('Test Loss: {}'.format(test_loss)) print('Test Accuracy: {}'.format(test_acc))
391/391 [==============================] - 18s 46ms/step - loss: 0.4621 - accuracy: 0.8581 Test Loss: 0.46211091787232766 Test Accuracy: 0.8581200242042542
上のモデルはシークエンスに適用されるパディングをマスクしていません。これはパッドされたシークエンス上で訓練してパッドされていないシークエンス上でテストする場合に歪みに繋がる可能性があります。理想的にはこれを回避するために マスキングを使用する でしょうが、下で見れるようにそれは出力上で小さい影響を持つだけです。
prediction が >= 0.5 であれば、それはポジティブでそうでなければネガティブです。
def pad_to_size(vec, size): zeros = [0] * (size - len(vec)) vec.extend(zeros) return vec
def sample_predict(sentence, pad): encoded_sample_pred_text = encoder.encode(sample_pred_text) if pad: encoded_sample_pred_text = pad_to_size(encoded_sample_pred_text, 64) encoded_sample_pred_text = tf.cast(encoded_sample_pred_text, tf.float32) predictions = model.predict(tf.expand_dims(encoded_sample_pred_text, 0)) return (predictions)
# predict on a sample text without padding. sample_pred_text = ('The movie was cool. The animation and the graphics ' 'were out of this world. I would recommend this movie.') predictions = sample_predict(sample_pred_text, pad=False) print (predictions)
[[0.41101742]]
# predict on a sample text with padding sample_pred_text = ('The movie was cool. The animation and the graphics ' 'were out of this world. I would recommend this movie.') predictions = sample_predict(sample_pred_text, pad=True) print (predictions)
[[0.4186573]]
plot_graphs(history, 'accuracy')
plot_graphs(history, 'loss')
2 つあるいはそれ以上の LSTM 層をスタックする
Keras リカレント層は return_sequences コンストラクタ引数で制御される 2 つの利用可能なモードを持ちます :
- 各時間ステップのための連続する出力の完全なシークエンスを返すか (shape (batch_size, timesteps, output_features) の 3D tensor)、
- 各入力シークエンスのための最後の出力だけを返します (shape (batch_size, output_features) の 2D tensor)。
model = tf.keras.Sequential([ tf.keras.layers.Embedding(encoder.vocab_size, 64), tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(64, return_sequences=True)), tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(32)), tf.keras.layers.Dense(64, activation='relu'), tf.keras.layers.Dropout(0.5), tf.keras.layers.Dense(1, activation='sigmoid') ])
model.compile(loss='binary_crossentropy', optimizer=tf.keras.optimizers.Adam(1e-4), metrics=['accuracy'])
history = model.fit(train_dataset, epochs=10, validation_data=test_dataset, validation_steps=30)
Epoch 1/10 391/391 [==============================] - 89s 228ms/step - loss: 0.6491 - accuracy: 0.5914 - val_loss: 0.0000e+00 - val_accuracy: 0.0000e+00 Epoch 2/10 391/391 [==============================] - 81s 207ms/step - loss: 0.3512 - accuracy: 0.8623 - val_loss: 0.3416 - val_accuracy: 0.8505 Epoch 3/10 391/391 [==============================] - 81s 207ms/step - loss: 0.2710 - accuracy: 0.9048 - val_loss: 0.3342 - val_accuracy: 0.8672 Epoch 4/10 391/391 [==============================] - 81s 208ms/step - loss: 0.2143 - accuracy: 0.9285 - val_loss: 0.3488 - val_accuracy: 0.8766 Epoch 5/10 391/391 [==============================] - 81s 206ms/step - loss: 0.1845 - accuracy: 0.9406 - val_loss: 0.3586 - val_accuracy: 0.8625 Epoch 6/10 391/391 [==============================] - 81s 207ms/step - loss: 0.1615 - accuracy: 0.9513 - val_loss: 0.3647 - val_accuracy: 0.8646 Epoch 7/10 391/391 [==============================] - 81s 207ms/step - loss: 0.1420 - accuracy: 0.9575 - val_loss: 0.4124 - val_accuracy: 0.8693 Epoch 8/10 391/391 [==============================] - 81s 207ms/step - loss: 0.1175 - accuracy: 0.9677 - val_loss: 0.4462 - val_accuracy: 0.8594 Epoch 9/10 391/391 [==============================] - 80s 206ms/step - loss: 0.1034 - accuracy: 0.9734 - val_loss: 0.4958 - val_accuracy: 0.8510 Epoch 10/10 391/391 [==============================] - 82s 209ms/step - loss: 0.0923 - accuracy: 0.9777 - val_loss: 0.5143 - val_accuracy: 0.8573
test_loss, test_acc = model.evaluate(test_dataset) print('Test Loss: {}'.format(test_loss)) print('Test Accuracy: {}'.format(test_acc))
391/391 [==============================] - 34s 86ms/step - loss: 0.5185 - accuracy: 0.8543 Test Loss: 0.5184965819844505 Test Accuracy: 0.8543199896812439
# predict on a sample text without padding. sample_pred_text = ('The movie was not good. The animation and the graphics ' 'were terrible. I would not recommend this movie.') predictions = sample_predict(sample_pred_text, pad=False) print (predictions)
[[0.05746633]]
# predict on a sample text with padding sample_pred_text = ('The movie was not good. The animation and the graphics ' 'were terrible. I would not recommend this movie.') predictions = sample_predict(sample_pred_text, pad=True) print (predictions)
[[0.01921545]]
plot_graphs(history, 'accuracy')
plot_graphs(history, 'loss')
GRU 層 のような他の存在するリカレント層を調べてください。
カスタム RNN を構築することに興味があれば、Keras RNN ガイド を見てください。
以上