TensorFlow 2.0 : Beginner Tutorials : データのロードと前処理 :- テキストをロードする (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 10/06/2019
* 本ページは、TensorFlow org サイトの TF 2.0 – Beginner Tutorials – Load and preprocess data の以下のページを
翻訳した上で適宜、補足説明したものです:
* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
- お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。
- Windows PC のブラウザからご参加が可能です。スマートデバイスもご利用可能です。
◆ お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。
株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション |
E-Mail:sales-info@classcat.com ; WebSite: https://www.classcat.com/ |
Facebook: https://www.facebook.com/ClassCatJP/ |
データのロードと前処理 :- テキストをロードする
このチュートリアルはテキストファイルからのサンプルをロードするために tf.data.TextLineDataset をどのように使用するかのサンプルを提供します。TextLineDataset はテキストファイルからのデータセットを作成するために設計されています、そこでは各サンプルは元のファイルからのテキストの 1 行です。これは主として行ベースの任意のテキストデータ (例えば、詩やエラーログ) のために潜在的に有用です。
このチュートリアルでは、同じワーク Homer’s Illiad の 3 つの異なる英語翻訳を使用します、そしてテキストのシングル行が与えられたとき翻訳者を識別するためにモデルを訓練します。
セットアップ
from __future__ import absolute_import, division, print_function, unicode_literals import tensorflow as tf import tensorflow_datasets as tfds import os
3 つの翻訳のテキストは次によります :
このチュートリアルで使用されるテキストファイルは幾つかの典型的な前処理タスクを受けています、殆どは何かを除去しています — ドキュメント・ヘッダとフッタ、行番号、章タイトルです。これらの軽く変換された (= munged) ファイルをローカルにダウンロードします。
DIRECTORY_URL = 'https://storage.googleapis.com/download.tensorflow.org/data/illiad/' FILE_NAMES = ['cowper.txt', 'derby.txt', 'butler.txt'] for name in FILE_NAMES: text_dir = tf.keras.utils.get_file(name, origin=DIRECTORY_URL+name) parent_dir = os.path.dirname(text_dir) parent_dir
Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/illiad/cowper.txt 819200/815980 [==============================] - 0s 0us/step Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/illiad/derby.txt 811008/809730 [==============================] - 0s 0us/step Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/illiad/butler.txt 811008/807992 [==============================] - 0s 0us/step '/home/kbuilder/.keras/datasets'
テキストをデータセットにロードする
ファイルを通して反復して、各一つをそれ自身のデータセットにロードします。
各サンプルは個々にラベル付けされる必要がありますので、各一つに labeler 関数を適用するために tf.data.Dataset.map を使用します。これはデータセットの総てのサンプルに渡り反復し、 (example, label) ペアを返します。
def labeler(example, index): return example, tf.cast(index, tf.int64) labeled_data_sets = [] for i, file_name in enumerate(FILE_NAMES): lines_dataset = tf.data.TextLineDataset(os.path.join(parent_dir, file_name)) labeled_dataset = lines_dataset.map(lambda ex: labeler(ex, i)) labeled_data_sets.append(labeled_dataset)
これらのラベル付けされたデータセットを単一のデータセットに結合して、そしてそれをシャッフルします。
BUFFER_SIZE = 50000 BATCH_SIZE = 64 TAKE_SIZE = 5000
all_labeled_data = labeled_data_sets[0] for labeled_dataset in labeled_data_sets[1:]: all_labeled_data = all_labeled_data.concatenate(labeled_dataset) all_labeled_data = all_labeled_data.shuffle( BUFFER_SIZE, reshuffle_each_iteration=False)
tf.data.Dataset.take を使用して (example, label) ペアがどのようなものか見るためにプリントすることができます。numpy プロパティは各 Tensor の値を表示します。
for ex in all_labeled_data.take(5): print(ex)
(<tf.Tensor: id=74, shape=(), dtype=string, numpy=b'Fulfilling some, in others he shall fail,'>, <tf.Tensor: id=75, shape=(), dtype=int64, numpy=1>) (<tf.Tensor: id=76, shape=(), dtype=string, numpy=b"Of straight-horn'd oxen, and your flowing cups">, <tf.Tensor: id=77, shape=(), dtype=int64, numpy=1>) (<tf.Tensor: id=78, shape=(), dtype=string, numpy=b'him by the head, while Patroclus kept fast hold of his feet, and <tf.Tensor: id=79, shape=(), dtype=int64, numpy=2>) (<tf.Tensor: id=80, shape=(), dtype=string, numpy=b"So thick and dark, about th' Ajaces stirr'd,">, <tf.Tensor: id=81, shape=(), dtype=int64, numpy=1>) (<tf.Tensor: id=82, shape=(), dtype=string, numpy=b'Then, as the life ebbed out of you, you answered, O knight Patroclus:'>, <tf.Tensor: id=83, shape=(), dtype=int64, numpy=2>)
テキスト行を数値としてエンコードする
機械学習モデルは数値上で動作します、単語上ではありませんので、文字列値は数値のリストに変換される必要があります。それを行なうために、各一意の単語を一意の整数にマップします。
語彙を構築する
最初に、テキストを個々の一意な単語のコレクションにトークン化して語彙を構築します。TensorFlow と Python の両者でこれを行なう 2, 3 の方法があります。このチュートリアルのためには :
- 各サンプルの numpy 値に渡り反復する。
- tfds.features.text.Tokenizer を使用してそれをトークンに分割する。
- これらのトークンを Python set に集めます、重複を除去するためです。
- 後で使用するために語彙のサイズを得ます。
tokenizer = tfds.features.text.Tokenizer() vocabulary_set = set() for text_tensor, _ in all_labeled_data: some_tokens = tokenizer.tokenize(text_tensor.numpy()) vocabulary_set.update(some_tokens) vocab_size = len(vocabulary_set) vocab_size
17178
サンプルをエンコードする
vocabulary_set を tfds.features.text.TokenTextEncoder に渡すことによりエンコーダを作成します。エンコーダの encode メソッドはテキストの文字列を取り整数のリストを返します。
encoder = tfds.features.text.TokenTextEncoder(vocabulary_set)
これを単一行の上で出力がどのようなものか見るために試すことができます。
example_text = next(iter(all_labeled_data))[0].numpy() print(example_text)
b'Fulfilling some, in others he shall fail,'
encoded_example = encoder.encode(example_text) print(encoded_example)
[8801, 4457, 10895, 9164, 12881, 7887, 2100]
今はデータセットの上でエンコーダをそれを tf.py_function にラッピングしてそれをデータセットの map メソッドに渡すことにより実行します。
def encode(text_tensor, label): encoded_text = encoder.encode(text_tensor.numpy()) return encoded_text, label def encode_map_fn(text, label): return tf.py_function(encode, inp=[text, label], Tout=(tf.int64, tf.int64)) all_encoded_data = all_labeled_data.map(encode_map_fn)
データセットをテストと訓練バッチに分割する
小さいテスト・データセットとより大きな訓練セットを作成するために tf.data.Dataset.take と tf.data.Dataset.skip を使用します。
モデルに渡される前に、データセットはバッチ化される必要があります。典型的には、バッチ内部のサンプルは同じサイズと shape である必要があります。しかし、これらのデータセットのサンプルは総てが同じサイズではありません — テキストの各行は単語の異なる数を持っていました。そこでサンプルを同じサイズにパッドするために (batch の代わりに) tf.data.Dataset.padded_batch を使用します。
train_data = all_encoded_data.skip(TAKE_SIZE).shuffle(BUFFER_SIZE) train_data = train_data.padded_batch(BATCH_SIZE, padded_shapes=([-1],[])) test_data = all_encoded_data.take(TAKE_SIZE) test_data = test_data.padded_batch(BATCH_SIZE, padded_shapes=([-1],[]))
今では、test_data と train_data は (example, label) ペアのコレクションではなく、バッチのコレクションです。各バッチは配列として現れる (多くのサンプル, 多くのラベル) のペアです。
例示するために :
sample_text, sample_labels = next(iter(test_data)) sample_text[0], sample_labels[0]
(<tf.Tensor: id=99547, shape=(16,), dtype=int64, numpy= array([ 8801, 4457, 10895, 9164, 12881, 7887, 2100, 0, 0, 0, 0, 0, 0, 0, 0, 0])>, <tf.Tensor: id=99551, shape=(), dtype=int64, numpy=1>)
新しいトークン・エンコーディングを導入したので (パディングのために使用されるゼロ)、語彙サイズは 1 つ増えました。
vocab_size += 1
モデルを構築する
model = tf.keras.Sequential()
最初の層は整数表現を密ベクトル埋め込みに変換します。より多くの詳細については Word Embeddings チュートリアルを見てください。
model.add(tf.keras.layers.Embedding(vocab_size, 64))
次の層は Long Short-Term Memory 層で、これはモデルに単語をそれらの他の単語とのコンテキストで理解させます。LSTM 上の双方向ラッパーはその前とその後に来たデータポイントとの関係でデータポイントについて学習します。
model.add(tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(64)))
最後に一つまたはそれ以上の密結合層のシリーズを持ちます、最後の一つは出力層です。出力層は総てのラベルのための確率を生成します。最も高い確率を持つ一つはサンプルのラベルのモデル予測です。
# One or more dense layers. # Edit the list in the `for` line to experiment with layer sizes. for units in [64, 64]: model.add(tf.keras.layers.Dense(units, activation='relu')) # Output layer. The first argument is the number of labels. model.add(tf.keras.layers.Dense(3, activation='softmax'))
最後に、モデルをコンパイルします。softmax カテゴリー化モデルのためには、損失関数として sparse_categorical_crossentropy を使用します。他の optimizer も試すことができますが、adam は非常に一般的です。
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
モデルを訓練する
このデータ上で実行するこのモデルは妥当な結果を生成します (約 83%)。
model.fit(train_data, epochs=3, validation_data=test_data)
Epoch 1/3 697/697 [==============================] - 32s 46ms/step - loss: 0.5213 - accuracy: 0.7403 - val_loss: 0.0000e+00 - val_accuracy: 0.0000e+00 Epoch 2/3 697/697 [==============================] - 26s 37ms/step - loss: 0.2968 - accuracy: 0.8667 - val_loss: 0.3581 - val_accuracy: 0.8400 Epoch 3/3 697/697 [==============================] - 26s 37ms/step - loss: 0.2214 - accuracy: 0.9019 - val_loss: 0.3703 - val_accuracy: 0.8428 <tensorflow.python.keras.callbacks.History at 0x7efb903020f0>
eval_loss, eval_acc = model.evaluate(test_data) print('\nEval loss: {:.3f}, Eval accuracy: {:.3f}'.format(eval_loss, eval_acc))
79/79 [==============================] - 3s 40ms/step - loss: 0.3703 - accuracy: 0.8428 Eval loss: 0.370, Eval accuracy: 0.843
以上