ホーム » RNN » TensorFlow 2.0 : 上級 Tutorials : テキスト :- RNN でテキスト分類

TensorFlow 2.0 : 上級 Tutorials : テキスト :- RNN でテキスト分類

TensorFlow 2.0 : 上級 Tutorials : テキスト :- RNN でテキスト分類 (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 11/09/2019

* 本ページは、TensorFlow org サイトの TF 2.0 – Advanced Tutorials – Text の以下のページを翻訳した上で
適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

無料セミナー開催中 クラスキャット主催 人工知能 & ビジネス Web セミナー

人工知能とビジネスをテーマにウェビナー (WEB セミナー) を定期的に開催しています。スケジュールは弊社 公式 Web サイト でご確認頂けます。
  • お住まいの地域に関係なく 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 ガイド を見てください。

 

以上



AI導入支援 #2 ウェビナー

スモールスタートを可能としたAI導入支援   Vol.2
[無料 WEB セミナー] [詳細]
「画像認識 AI PoC スターターパック」の紹介
既に AI 技術を実ビジネスで活用し、成果を上げている日本企業も多く存在しており、競争優位なビジネスを展開しております。
しかしながら AI を導入したくとも PoC (概念実証) だけでも高額な費用がかかり取組めていない企業も少なくないようです。A I導入時には欠かせない PoC を手軽にしかも短期間で認知度を確認可能とするサービの紹介と共に、AI 技術の特性と具体的な導入プロセスに加え運用時のポイントについても解説いたします。
日時:2021年10月13日(水)
会場:WEBセミナー
共催:クラスキャット、日本FLOW(株)
後援:働き方改革推進コンソーシアム
参加費: 無料 (事前登録制)
人工知能開発支援
◆ クラスキャットは 人工知能研究開発支援 サービスを提供しています :
  • テクニカルコンサルティングサービス
  • 実証実験 (プロトタイプ構築)
  • アプリケーションへの実装
  • 人工知能研修サービス
◆ お問合せ先 ◆
(株)クラスキャット
セールス・インフォメーション
E-Mail:sales-info@classcat.com