In [ ]:
#@title Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
Note: これらのドキュメントは私たちTensorFlowコミュニティが翻訳したものです。コミュニティによる 翻訳はベストエフォートであるため、この翻訳が正確であることや英語の公式ドキュメントの 最新の状態を反映したものであることを保証することはできません。 この翻訳の品質を向上させるためのご意見をお持ちの方は、GitHubリポジトリtensorflow/docsにプルリクエストをお送りください。 コミュニティによる翻訳やレビューに参加していただける方は、 docs-ja@tensorflow.org メーリングリストにご連絡ください。
このチュートリアルでは、tf.data.TextLineDataset
を使ってテキストファイルからサンプルを読み込む方法を例示します。TextLineDataset
は、テキストファイルからデータセットを作成するために設計されています。この中では、元のテキストファイルの一行一行がサンプルです。これは、(たとえば、詩やエラーログのような)基本的に行ベースのテキストデータを扱うのに便利でしょう。
このチュートリアルでは、おなじ作品であるホーマーのイリアッドの異なる 3 つの英語翻訳版を使い、テキスト 1 行から翻訳者を特定するモデルを訓練します。
In [ ]:
!pip install tf-nightly
import tensorflow as tf
import tensorflow_datasets as tfds
import os
3 つの翻訳のテキストは次のとおりです。
このチュートリアルで使われているテキストファイルは、ヘッダ、フッタ、行番号、章のタイトルの削除など、いくつかの典型的な前処理を行ったものです。前処理後のファイルをダウンロードしましょう。
In [ ]:
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
In [ ]:
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)
ラベル付けの終わったデータセットを結合して一つのデータセットにし、シャッフルします。
In [ ]:
BUFFER_SIZE = 50000
BATCH_SIZE = 64
TAKE_SIZE = 5000
In [ ]:
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
と print
を使って、(example, label)
のペアがどのようなものかを見ることができます。numpy
プロパティがそれぞれのテンソルの値を示します。
In [ ]:
for ex in all_labeled_data.take(5):
print(ex)
機械学習モデルが扱うのは単語ではなくて数字であるため、文字列は数字のリストに変換する必要があります。このため、一意の単語を一意の数字にマッピングします。
まず最初に、テキストをトークン化し、個々の一意な単語の集まりとして、ボキャブラリーを構築します。これを行うには、TensorFlow やPython を使ういくつかの方法があります。ここでは次のようにします。
numpy
値をイテレートします。tfds.features.text.Tokenizer
を使って、それをトークンに分割します。
In [ ]:
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
In [ ]:
encoder = tfds.features.text.TokenTextEncoder(vocabulary_set)
1行だけにこれを適用し、出力がどの様になるか確かめることができます。
In [ ]:
example_text = next(iter(all_labeled_data))[0].numpy()
print(example_text)
In [ ]:
encoded_example = encoder.encode(example_text)
print(encoded_example)
次に、このエンコーダーを tf.py_function
でラッピングして、データセットの map
メソッドに渡し、データセットに適用します。
In [ ]:
def encode(text_tensor, label):
encoded_text = encoder.encode(text_tensor.numpy())
return encoded_text, label
データセットの個々の要素にこの関数を適用するために Dataset.map
を使いたくなるかもしれません。Dataset.map
はグラフモードで動作します。
したがって、この関数を直接 .map
で用いることはできません。それを tf.py_function
でラップする必要があります。tf.py_function
は通常の Tensor (値を持ち、.numpy()
メソッドでそれにアクセスできるもの) をラップされた Python の関数に渡します。
In [ ]:
def encode_map_fn(text, label):
# py_func は返り値の Tensor に shape を設定しません
encoded_text, label = tf.py_function(encode,
inp=[text, label],
Tout=(tf.int64, tf.int64))
# `tf.data.Datasets` はすべての要素に shape が設定されているときにうまく動きます
# なので、shape を手動で設定しましょう
encoded_text.set_shape([None])
label.set_shape([])
return encoded_text, label
all_encoded_data = all_labeled_data.map(encode_map_fn)
tf.data.Dataset.take
とtf.data.Dataset.skip
を使って、小さなテスト用データセットと、より大きな訓練用セットを作成します。
モデルに渡す前に、データセットをバッチ化する必要があります。通常、バッチの中のサンプルはおなじサイズと形状である必要があります。しかし、これらのデータセットの中のサンプルはすべておなじサイズではありません。テキストの各行の単語数は異なっています。このため、(batch
の代わりに)tf.data.Dataset.padded_batch
メソッドを使ってサンプルをおなじサイズにパディングします。
In [ ]:
train_data = all_encoded_data.skip(TAKE_SIZE).shuffle(BUFFER_SIZE)
train_data = train_data.padded_batch(BATCH_SIZE, padded_shapes=([None],[]))
test_data = all_encoded_data.take(TAKE_SIZE)
test_data = test_data.padded_batch(BATCH_SIZE, padded_shapes=([None],[]))
Note: TensorFlow 2.2 から、padded_shapes
は必須ではなくなりました。デフォルトではすべての軸をバッチ中で最も長いものに合わせてパディングします。
In [ ]:
train_data = all_encoded_data.skip(TAKE_SIZE).shuffle(BUFFER_SIZE)
train_data = train_data.padded_batch(BATCH_SIZE)
test_data = all_encoded_data.take(TAKE_SIZE)
test_data = test_data.padded_batch(BATCH_SIZE)
もう、test_data
と train_data
は、(example, label
)というペアのコレクションではなく、バッチのコレクションです。それぞれのバッチは、(たくさんのサンプル, たくさんのラベル)という配列のペアです。
見てみましょう。
In [ ]:
sample_text, sample_labels = next(iter(test_data))
sample_text[0], sample_labels[0]
(ゼロをパディングに使用した)新しいトークン番号を1つ導入したので、ボキャブラリーサイズは1つ増えています。
In [ ]:
vocab_size += 1
In [ ]:
model = tf.keras.Sequential()
最初の層は、整数表現を密なベクトル埋め込みに変換します。詳細は単語埋め込みのチュートリアルを参照ください。
In [ ]:
model.add(tf.keras.layers.Embedding(vocab_size, 64))
次の層はLong Short-Term Memory 層です。この層により、モデルは単語をほかの単語の文脈の中で解釈します。LSTM の Bidirectional ラッパーにより、データポイントを、その前とその後のデータポイントとの関連で学習することができます。
In [ ]:
model.add(tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(64)))
最後に、一つ以上の全結合層があり、最後の層は出力層です。出力層はラベルすべての確率を生成します。もっと複雑なも確率の高いラベルが、モデルが予測するサンプルのラベルです。
In [ ]:
# 1 つ以上の Dense 層
# `for` 行の中のリストを編集して、層のサイズの実験をしてください
for units in [64, 64]:
model.add(tf.keras.layers.Dense(units, activation='relu'))
# 出力層 最初の引数はラベルの数
model.add(tf.keras.layers.Dense(3))
最後にモデルをコンパイルします。ソフトマックスによるカテゴリー分類モデルでは、損失関数として sparse_categorical_crossentropy
を使用します。ほかのオプティマイザを使うこともできますが、adam
がよく使われます。
In [ ]:
model.compile(optimizer='adam',
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=['accuracy'])
In [ ]:
model.fit(train_data, epochs=3, validation_data=test_data)
In [ ]:
eval_loss, eval_acc = model.evaluate(test_data)
print('\nEval loss: {:.3f}, Eval accuracy: {:.3f}'.format(eval_loss, eval_acc))