前処理レイヤーを使用する

TensorFlow.org で実行 Google Colab で実行 GitHubでソースを表示 ノートブックをダウンロード

Keras 前処理レイヤー

Keras 前処理レイヤー API を使用すると、開発者は Keras ネイティブの入力処理パイプラインを構築できます。これらの入力処理パイプラインは、Keras 以外のワークフローで独立した前処理コードとして使用し、Keras モデルと直接組み合わせて、Keras SavedModel の一部としてエクスポートできます。

Keras 前処理レイヤーを使用すると、真にエンドツーエンドのモデル(生の画像または生の構造化データを入力として受け入れるモデルや特徴の正規化または特徴値のインデックス作成を独自に処理するモデル)を構築およびエクスポートできます。

利用可能な前処理

テキストの前処理

  • tf.keras.layers.TextVectorization: 生の文字列を、Embedding レイヤーまたは Dense レイヤーで読み取ることができるエンコードされた表現に変換します。

数値特徴量の前処理

カテゴリカル特徴量の前処理

  • tf.keras.layers.CategoryEncoding: 整数のカテゴリカル特徴量をワンホット、マルチホット、またはカウントデンス表現に変換します。
  • tf.keras.layers.Hashing: カテゴリカル特徴量ハッシュ (ハッシュトリック) を実行します。
  • tf.keras.layers.StringLookup: 文字列のカテゴリカル値を、Embedding レイヤーや Dense レイヤーで読み取れるエンコードされた表現に変換します。
  • tf.keras.layers.IntegerLookup: 整数のカテゴリ値を、Embedding レイヤーまたは Dense レイヤーで読み取ることができるエンコードされた表現に変換します。

画像の前処理

これらのレイヤーは、画像モデルの入力を標準化するためのものです。

  • tf.keras.layers.Resizing: 画像のバッチのサイズをターゲットサイズに変更します。
  • tf.keras.layers.Rescaling: 画像のバッチの値を再スケーリングおよびオフセットします(たとえば、[0, 255] 範囲の入力から [0, 1] 範囲の入力に移動します。)
  • tf.keras.layers.CenterCrop: 画像のバッチの中央の切り抜きを返します。

画像データ増強(デバイス上)

これらのレイヤーは、ランダムな増強変換を画像のバッチに適用します。これらはトレーニング中にのみアクティブになります。

adapt() メソッド

一部の前処理レイヤーには、トレーニングデータのサンプルに基づいて計算できる内部状態があります。ステートフル前処理レイヤーのリストは次のとおりです。

  • TextVectorization: 文字列トークンと整数インデックス間のマッピングを保持します。
  • StringLookupIntegerLookup: 入力値と整数インデックスの間のマッピングを保持します。
  • Normalization: 特徴量の平均と標準偏差を保持します。
  • Discretization: 値バケットの境界に関する情報を保持します。

これらのレイヤーはトレーニング不可であることに注意してください。これらの状態はトレーニング中に設定されません。事前に計算された定数から初期化するか、データに「適応」させることにより、トレーニングの前に設定する必要があります。

adapt() メソッドを使用して、トレーニングデータに前処理レイヤーを公開することにより、前処理レイヤーの状態を設定します。

import numpy as np
import tensorflow as tf
from tensorflow.keras import layers

data = np.array([[0.1, 0.2, 0.3], [0.8, 0.9, 1.0], [1.5, 1.6, 1.7],])
layer = layers.Normalization()
layer.adapt(data)
normalized_data = layer(data)

print("Features mean: %.2f" % (normalized_data.numpy().mean()))
print("Features std: %.2f" % (normalized_data.numpy().std()))
2022-12-14 21:28:56.053993: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libnvinfer.so.7'; dlerror: libnvinfer.so.7: cannot open shared object file: No such file or directory
2022-12-14 21:28:56.054084: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libnvinfer_plugin.so.7'; dlerror: libnvinfer_plugin.so.7: cannot open shared object file: No such file or directory
2022-12-14 21:28:56.054094: W tensorflow/compiler/tf2tensorrt/utils/py_utils.cc:38] TF-TRT Warning: Cannot dlopen some TensorRT libraries. If you would like to use Nvidia GPU with TensorRT, please make sure the missing libraries mentioned above are installed properly.
Features mean: -0.00
Features std: 1.00

adapt()メソッドは、Numpy 配列またはtf.data.Datasetオブジェクトのいずれかを取ります。StringLookupおよびTextVectorizationの場合、文字列のリストを渡すこともできます。

data = [
    "ξεῖν᾽, ἦ τοι μὲν ὄνειροι ἀμήχανοι ἀκριτόμυθοι",
    "γίγνοντ᾽, οὐδέ τι πάντα τελείεται ἀνθρώποισι.",
    "δοιαὶ γάρ τε πύλαι ἀμενηνῶν εἰσὶν ὀνείρων:",
    "αἱ μὲν γὰρ κεράεσσι τετεύχαται, αἱ δ᾽ ἐλέφαντι:",
    "τῶν οἳ μέν κ᾽ ἔλθωσι διὰ πριστοῦ ἐλέφαντος,",
    "οἵ ῥ᾽ ἐλεφαίρονται, ἔπε᾽ ἀκράαντα φέροντες:",
    "οἱ δὲ διὰ ξεστῶν κεράων ἔλθωσι θύραζε,",
    "οἵ ῥ᾽ ἔτυμα κραίνουσι, βροτῶν ὅτε κέν τις ἴδηται.",
]
layer = layers.TextVectorization()
layer.adapt(data)
vectorized_text = layer(data)
print(vectorized_text)
tf.Tensor(
[[37 12 25  5  9 20 21  0  0]
 [51 34 27 33 29 18  0  0  0]
 [49 52 30 31 19 46 10  0  0]
 [ 7  5 50 43 28  7 47 17  0]
 [24 35 39 40  3  6 32 16  0]
 [ 4  2 15 14 22 23  0  0  0]
 [36 48  6 38 42  3 45  0  0]
 [ 4  2 13 41 53  8 44 26 11]], shape=(8, 9), dtype=int64)

さらに、適応可能なレイヤーは、コンストラクタ引数または重みの割り当てを介して状態を直接設定するオプションを常に公開します。意図した状態値がレイヤー構築時にわかっている場合、またはadapt()呼び出しの外で計算される場合は、レイヤーの内部計算に依存せずに設定できます。例えば、TextVectorizationStringLookup、または、IntegerLookupレイヤーの外部語彙ファイルがすでに存在する場合、レイヤーのコンストラクタ引数で語彙ファイルへのパスを渡すことにより、それらをルックアップテーブルに直接読み込めます。

以下の例では、事前に計算された語彙を使用してStringLookupレイヤーをインスタンス化します。

vocab = ["a", "b", "c", "d"]
data = tf.constant([["a", "c", "d"], ["d", "z", "b"]])
layer = layers.StringLookup(vocabulary=vocab)
vectorized_data = layer(data)
print(vectorized_data)
tf.Tensor(
[[1 3 4]
 [4 0 2]], shape=(2, 3), dtype=int64)

モデルの前またはモデル内のデータの前処理

前処理レイヤーを使用する方法は 2 つあります。

オプション 1: 次のように、それらをモデルの一部にします。

inputs = keras.Input(shape=input_shape)
x = preprocessing_layer(inputs)
outputs = rest_of_the_model(x)
model = keras.Model(inputs, outputs)

このオプションを使用すると、モデルの残りの実行と同期してデバイス上で前処理が行われるため、GPU アクセラレーションの恩恵を受けることができます。GPU でトレーニングしている場合、これはNormalizationレイヤー、およびすべての画像前処理レイヤーとデータ増強レイヤーに最適なオプションです。

オプション 2: これをtf.data.Datasetに適用して、次のように前処理されたデータのバッチを生成するデータセットを取得します。

dataset = dataset.map(lambda x, y: (preprocessing_layer(x), y))

このオプションを使用すると、前処理は CPU で非同期に行われ、モデルに入れる前にバッファリングされます。

dataset = dataset.map(lambda x, y: (preprocessing_layer(x), y))
dataset = dataset.prefetch(tf.data.AUTOTUNE)
model.fit(dataset, ...)

これは、TextVectorizationおよびすべての構造化データ前処理レイヤーに最適なオプションです。CPU でトレーニングしていて、画像前処理レイヤーを使用している場合にも、これは良いオプションです。

TPU で実行する場合は、常に前処理レイヤーを tf.data パイプラインに配置する必要があります (NormalizationRescaling は、TPU で正常に実行され、最初のレイヤーが画像モデルとして一般的に使用されます)。

推論時にモデル内で前処理を行うことの利点

オプション 2 を選択した場合でも、後で前処理レイヤーを含む推論のみのエンドツーエンドモデルをエクスポートしたい場合があります。これを行う主な利点は、モデルを移植可能にすることと、トレーニング/サービングスキューを減らせることです。

すべてのデータ前処理がモデルの一部である場合、他のユーザーは、各特徴がどのようにエンコードおよび正規化されるかを知らなくても、モデルを読み込んで使用できます。推論モデルは生の画像または生の構造化データを処理できるようになり、モデルのユーザーは画像のピクセル値が[-1, +1]、または、[0, 1]に正規化されていても、テキストに使用されるトークン化スキーム、カテゴリカルフィーチャに使用されるインデックススキームの詳細を認識する必要はありません。これは、モデルを TensorFlow.js などの別のランタイムにエクスポートする場合に特に有用です。JavaScript で前処理パイプラインを再実装する必要はありません。

最初に前処理レイヤーをtf.dataパイプラインに配置した場合、前処理をパッケージ化する推論モデルをエクスポートできます。前処理レイヤーとトレーニングモデルをチェーンする新しいモデルをインスタンス化するだけです。

inputs = keras.Input(shape=input_shape)
x = preprocessing_layer(inputs)
outputs = training_model(x)
inference_model = keras.Model(inputs, outputs)

クイックレシピ

画像データ増強(デバイス上)

画像データ増強レイヤーはトレーニング中にのみアクティブになることに注意してください(Dropoutレイヤーと同様)。

from tensorflow import keras
from tensorflow.keras import layers

# Create a data augmentation stage with horizontal flipping, rotations, zooms
data_augmentation = keras.Sequential(
    [
        layers.RandomFlip("horizontal"),
        layers.RandomRotation(0.1),
        layers.RandomZoom(0.1),
    ]
)

# Load some data
(x_train, y_train), _ = keras.datasets.cifar10.load_data()
input_shape = x_train.shape[1:]
classes = 10

# Create a tf.data pipeline of augmented images (and their labels)
train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train))
train_dataset = train_dataset.batch(16).map(lambda x, y: (data_augmentation(x), y))


# Create a model and train it on the augmented image data
inputs = keras.Input(shape=input_shape)
x = layers.Rescaling(1.0 / 255)(inputs)  # Rescale inputs
outputs = keras.applications.ResNet50(  # Add the rest of the model
    weights=None, input_shape=input_shape, classes=classes
)(x)
model = keras.Model(inputs, outputs)
model.compile(optimizer="rmsprop", loss="sparse_categorical_crossentropy")
model.fit(train_dataset, steps_per_epoch=5)
Downloading data from https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz
170498071/170498071 [==============================] - 2s 0us/step
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.9/site-packages/tensorflow/python/autograph/pyct/static_analysis/liveness.py:83: Analyzer.lamba_check (from tensorflow.python.autograph.pyct.static_analysis.liveness) is deprecated and will be removed after 2023-09-23.
Instructions for updating:
Lambda fuctions will be no more assumed to be used in the statement where they are used, or at least in the same block. https://github.com/tensorflow/tensorflow/issues/56089
WARNING:tensorflow:Using a while_loop for converting RngReadAndSkip cause there is no registered converter for this op.
WARNING:tensorflow:Using a while_loop for converting Bitcast cause there is no registered converter for this op.
WARNING:tensorflow:Using a while_loop for converting Bitcast cause there is no registered converter for this op.
WARNING:tensorflow:Using a while_loop for converting StatelessRandomUniformV2 cause there is no registered converter for this op.
WARNING:tensorflow:Using a while_loop for converting ImageProjectiveTransformV3 cause there is no registered converter for this op.
WARNING:tensorflow:Using a while_loop for converting RngReadAndSkip cause there is no registered converter for this op.
WARNING:tensorflow:Using a while_loop for converting Bitcast cause there is no registered converter for this op.
WARNING:tensorflow:Using a while_loop for converting Bitcast cause there is no registered converter for this op.
WARNING:tensorflow:Using a while_loop for converting StatelessRandomUniformV2 cause there is no registered converter for this op.
WARNING:tensorflow:Using a while_loop for converting ImageProjectiveTransformV3 cause there is no registered converter for this op.
WARNING:tensorflow:Using a while_loop for converting RngReadAndSkip cause there is no registered converter for this op.
WARNING:tensorflow:Using a while_loop for converting Bitcast cause there is no registered converter for this op.
WARNING:tensorflow:Using a while_loop for converting Bitcast cause there is no registered converter for this op.
WARNING:tensorflow:Using a while_loop for converting StatelessRandomUniformV2 cause there is no registered converter for this op.
WARNING:tensorflow:Using a while_loop for converting ImageProjectiveTransformV3 cause there is no registered converter for this op.
WARNING:tensorflow:Using a while_loop for converting RngReadAndSkip cause there is no registered converter for this op.
WARNING:tensorflow:Using a while_loop for converting Bitcast cause there is no registered converter for this op.
WARNING:tensorflow:Using a while_loop for converting Bitcast cause there is no registered converter for this op.
WARNING:tensorflow:Using a while_loop for converting StatelessRandomUniformV2 cause there is no registered converter for this op.
WARNING:tensorflow:Using a while_loop for converting ImageProjectiveTransformV3 cause there is no registered converter for this op.
5/5 [==============================] - 23s 38ms/step - loss: 10.3676
<keras.callbacks.History at 0x7f10440baa30>

画像分類を最初から行うの例で同様の設定が実際に行われていることを確認できます。

数値的特徴の正規化

# Load some data
(x_train, y_train), _ = keras.datasets.cifar10.load_data()
x_train = x_train.reshape((len(x_train), -1))
input_shape = x_train.shape[1:]
classes = 10

# Create a Normalization layer and set its internal state using the training data
normalizer = layers.Normalization()
normalizer.adapt(x_train)

# Create a model that include the normalization layer
inputs = keras.Input(shape=input_shape)
x = normalizer(inputs)
outputs = layers.Dense(classes, activation="softmax")(x)
model = keras.Model(inputs, outputs)

# Train the model
model.compile(optimizer="adam", loss="sparse_categorical_crossentropy")
model.fit(x_train, y_train)
1563/1563 [==============================] - 4s 2ms/step - loss: 2.1255
<keras.callbacks.History at 0x7f0fb423d760>

ワンホットエンコーディングによる文字列カテゴリカルフィーチャのエンコーディング

# Define some toy data
data = tf.constant([["a"], ["b"], ["c"], ["b"], ["c"], ["a"]])

# Use StringLookup to build an index of the feature values and encode output.
lookup = layers.StringLookup(output_mode="one_hot")
lookup.adapt(data)

# Convert new test data (which includes unknown feature values)
test_data = tf.constant([["a"], ["b"], ["c"], ["d"], ["e"], [""]])
encoded_data = lookup(test_data)
print(encoded_data)
tf.Tensor(
[[0. 0. 0. 1.]
 [0. 0. 1. 0.]
 [0. 1. 0. 0.]
 [1. 0. 0. 0.]
 [1. 0. 0. 0.]
 [1. 0. 0. 0.]], shape=(6, 4), dtype=float32)

ここで、インデックス 0 は、語彙外の値 (adapt() 中に表示されなかった値) 用に予約されていることに注意してください。

構造化データ分類を最初から行うの例で、StringLookup の動作を確認できます。

ワンホットエンコーディングによる整数カテゴリカルフィーチャのエンコーディング

# Define some toy data
data = tf.constant([[10], [20], [20], [10], [30], [0]])

# Use IntegerLookup to build an index of the feature values and encode output.
lookup = layers.IntegerLookup(output_mode="one_hot")
lookup.adapt(data)

# Convert new test data (which includes unknown feature values)
test_data = tf.constant([[10], [10], [20], [50], [60], [0]])
encoded_data = lookup(test_data)
print(encoded_data)
tf.Tensor(
[[0. 0. 1. 0. 0.]
 [0. 0. 1. 0. 0.]
 [0. 1. 0. 0. 0.]
 [1. 0. 0. 0. 0.]
 [1. 0. 0. 0. 0.]
 [0. 0. 0. 0. 1.]], shape=(6, 5), dtype=float32)

インデックス 0 は欠落している値 (値 0 として指定する必要があります) 用に予約されており、インデックス 1 は語彙外の値 (adapt() 中に表示されなかった値) 用に予約されていることに注意してください) 。これは、IntegerLookupmask_token と ​oov_token コンストラクター引数を使用して構成できます

構造化データ分類を最初から行うの例で、IntegerLookup の動作を確認できます。

整数のカテゴリカルフィーチャにハッシュトリックを適用する

多くの異なる値(10 の 3 乗以上の桁)をとることができるカテゴリカルフィーチャがあり、各値がデータに数回しか表示されない場合、特徴値にインデックスを付けてワンホットエンコードすることは非現実的で効果的ではありません。このような場合は、代わりに、固定サイズのベクトルに値をハッシュする「ハッシュトリック」を適用することをお勧めします。これにより、特徴スペースのサイズが管理しやすくなり、明示的なインデックス作成が不要になります。

# Sample data: 10,000 random integers with values between 0 and 100,000
data = np.random.randint(0, 100000, size=(10000, 1))

# Use the Hashing layer to hash the values to the range [0, 64]
hasher = layers.Hashing(num_bins=64, salt=1337)

# Use the CategoryEncoding layer to multi-hot encode the hashed values
encoder = layers.CategoryEncoding(num_tokens=64, output_mode="multi_hot")
encoded_data = encoder(hasher(data))
print(encoded_data.shape)
(10000, 64)

トークンインデックスのシーケンスとしてテキストをエンコードする

以下は、Embeddedレイヤーに渡されるテキストを前処理する方法です。

# Define some text data to adapt the layer
adapt_data = tf.constant(
    [
        "The Brain is wider than the Sky",
        "For put them side by side",
        "The one the other will contain",
        "With ease and You beside",
    ]
)

# Create a TextVectorization layer
text_vectorizer = layers.TextVectorization(output_mode="int")
# Index the vocabulary via `adapt()`
text_vectorizer.adapt(adapt_data)

# Try out the layer
print(
    "Encoded text:\n", text_vectorizer(["The Brain is deeper than the sea"]).numpy(),
)

# Create a simple model
inputs = keras.Input(shape=(None,), dtype="int64")
x = layers.Embedding(input_dim=text_vectorizer.vocabulary_size(), output_dim=16)(inputs)
x = layers.GRU(8)(x)
outputs = layers.Dense(1)(x)
model = keras.Model(inputs, outputs)

# Create a labeled dataset (which includes unknown tokens)
train_dataset = tf.data.Dataset.from_tensor_slices(
    (["The Brain is deeper than the sea", "for if they are held Blue to Blue"], [1, 0])
)

# Preprocess the string inputs, turning them into int sequences
train_dataset = train_dataset.batch(2).map(lambda x, y: (text_vectorizer(x), y))
# Train the model on the int sequences
print("\nTraining model...")
model.compile(optimizer="rmsprop", loss="mse")
model.fit(train_dataset)

# For inference, you can export a model that accepts strings as input
inputs = keras.Input(shape=(1,), dtype="string")
x = text_vectorizer(inputs)
outputs = model(x)
end_to_end_model = keras.Model(inputs, outputs)

# Call the end-to-end model on test data (which includes unknown tokens)
print("\nCalling end-to-end model on test string...")
test_data = tf.constant(["The one the other will absorb"])
test_output = end_to_end_model(test_data)
print("Model output:", test_output)
Encoded text:
 [[ 2 19 14  1  9  2  1]]

Training model...
1/1 [==============================] - 2s 2s/step - loss: 0.4559

Calling end-to-end model on test string...
Model output: tf.Tensor([[0.02034554]], shape=(1, 1), dtype=float32)

テキスト分類を最初から行うの例では、Embeddedモードと組み合わされてTextVectorizationレイヤーが動作する方法を確認できます。

このようなモデルをトレーニングする場合、最高のパフォーマンスを得るには、入力パイプラインの一部としてTextVectorizationレイヤーを使用する必要があることに注意してください(上記のテキスト分類の例で示すように)。

マルチホットエンコーディングを使用した ngram の密な行列としてのテキストのエンコーディング

これは、Denseレイヤーに渡されるテキストを前処理する方法です。

# Define some text data to adapt the layer
adapt_data = tf.constant(
    [
        "The Brain is wider than the Sky",
        "For put them side by side",
        "The one the other will contain",
        "With ease and You beside",
    ]
)
# Instantiate TextVectorization with "multi_hot" output_mode
# and ngrams=2 (index all bigrams)
text_vectorizer = layers.TextVectorization(output_mode="multi_hot", ngrams=2)
# Index the bigrams via `adapt()`
text_vectorizer.adapt(adapt_data)

# Try out the layer
print(
    "Encoded text:\n", text_vectorizer(["The Brain is deeper than the sea"]).numpy(),
)

# Create a simple model
inputs = keras.Input(shape=(text_vectorizer.vocabulary_size(),))
outputs = layers.Dense(1)(inputs)
model = keras.Model(inputs, outputs)

# Create a labeled dataset (which includes unknown tokens)
train_dataset = tf.data.Dataset.from_tensor_slices(
    (["The Brain is deeper than the sea", "for if they are held Blue to Blue"], [1, 0])
)

# Preprocess the string inputs, turning them into int sequences
train_dataset = train_dataset.batch(2).map(lambda x, y: (text_vectorizer(x), y))
# Train the model on the int sequences
print("\nTraining model...")
model.compile(optimizer="rmsprop", loss="mse")
model.fit(train_dataset)

# For inference, you can export a model that accepts strings as input
inputs = keras.Input(shape=(1,), dtype="string")
x = text_vectorizer(inputs)
outputs = model(x)
end_to_end_model = keras.Model(inputs, outputs)

# Call the end-to-end model on test data (which includes unknown tokens)
print("\nCalling end-to-end model on test string...")
test_data = tf.constant(["The one the other will absorb"])
test_output = end_to_end_model(test_data)
print("Model output:", test_output)
WARNING:tensorflow:5 out of the last 1567 calls to <function PreprocessingLayer.make_adapt_function.<locals>.adapt_step at 0x7f0fb46204c0> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings could be due to (1) creating @tf.function repeatedly in a loop, (2) passing tensors with different shapes, (3) passing Python objects instead of tensors. For (1), please define your @tf.function outside of the loop. For (2), @tf.function has reduce_retracing=True option that can avoid unnecessary retracing. For (3), please refer to https://www.tensorflow.org/guide/function#controlling_retracing and https://www.tensorflow.org/api_docs/python/tf/function for  more details.
Encoded text:
 [[1. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 1. 1. 0. 0. 0. 0. 0.

  0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 1. 1. 0. 0. 0.]]

Training model...
1/1 [==============================] - 0s 375ms/step - loss: 1.1152

Calling end-to-end model on test string...
Model output: tf.Tensor([[-0.26651537]], shape=(1, 1), dtype=float32)

TF-IDF 重み付けを使用した ngramの 密な行列としてのテキストのエンコード

これは、テキストをDenseレイヤーに渡す前に前処理する別の方法です。

# Define some text data to adapt the layer
adapt_data = tf.constant(
    [
        "The Brain is wider than the Sky",
        "For put them side by side",
        "The one the other will contain",
        "With ease and You beside",
    ]
)
# Instantiate TextVectorization with "tf-idf" output_mode
# (multi-hot with TF-IDF weighting) and ngrams=2 (index all bigrams)
text_vectorizer = layers.TextVectorization(output_mode="tf-idf", ngrams=2)
# Index the bigrams and learn the TF-IDF weights via `adapt()`

with tf.device("CPU"):
    # A bug that prevents this from running on GPU for now.
    text_vectorizer.adapt(adapt_data)

# Try out the layer
print(
    "Encoded text:\n", text_vectorizer(["The Brain is deeper than the sea"]).numpy(),
)

# Create a simple model
inputs = keras.Input(shape=(text_vectorizer.vocabulary_size(),))
outputs = layers.Dense(1)(inputs)
model = keras.Model(inputs, outputs)

# Create a labeled dataset (which includes unknown tokens)
train_dataset = tf.data.Dataset.from_tensor_slices(
    (["The Brain is deeper than the sea", "for if they are held Blue to Blue"], [1, 0])
)

# Preprocess the string inputs, turning them into int sequences
train_dataset = train_dataset.batch(2).map(lambda x, y: (text_vectorizer(x), y))
# Train the model on the int sequences
print("\nTraining model...")
model.compile(optimizer="rmsprop", loss="mse")
model.fit(train_dataset)

# For inference, you can export a model that accepts strings as input
inputs = keras.Input(shape=(1,), dtype="string")
x = text_vectorizer(inputs)
outputs = model(x)
end_to_end_model = keras.Model(inputs, outputs)

# Call the end-to-end model on test data (which includes unknown tokens)
print("\nCalling end-to-end model on test string...")
test_data = tf.constant(["The one the other will absorb"])
test_output = end_to_end_model(test_data)
print("Model output:", test_output)
WARNING:tensorflow:6 out of the last 1568 calls to <function PreprocessingLayer.make_adapt_function.<locals>.adapt_step at 0x7f11fc0f68b0> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings could be due to (1) creating @tf.function repeatedly in a loop, (2) passing tensors with different shapes, (3) passing Python objects instead of tensors. For (1), please define your @tf.function outside of the loop. For (2), @tf.function has reduce_retracing=True option that can avoid unnecessary retracing. For (3), please refer to https://www.tensorflow.org/guide/function#controlling_retracing and https://www.tensorflow.org/api_docs/python/tf/function for  more details.
Encoded text:
 [[5.461647  1.6945957 0.        0.        0.        0.        0.

  0.        0.        0.        0.        0.        0.        0.
  0.        0.        1.0986123 1.0986123 1.0986123 0.        0.
  0.        0.        0.        0.        0.        0.        0.
  1.0986123 0.        0.        0.        0.        0.        0.
  0.        1.0986123 1.0986123 0.        0.        0.       ]]

Training model...
1/1 [==============================] - 0s 348ms/step - loss: 2.9832

Calling end-to-end model on test string...
Model output: tf.Tensor([[1.6719354]], shape=(1, 1), dtype=float32)

重要な点

ルックアップレイヤーの語彙が非常に多い場合

TextVectorizationStringLookup レイヤー、または IntegerLookup レイヤーで非常に多くの語彙がある場合があります。通常、500MB を超える語彙は「非常に多い」と見なされます。

このような場合、最高のパフォーマンスを得るには、adapt() の使用を避ける必要があります。代わりに、事前に語彙を計算し (これには、Apache Beam または TF Transform を使用できます)、ファイルに保存します。次に、ファイルパスを vocabulary 引数として渡すことにより、構築時に語彙をレイヤーに読み込みます。

TPU ポッドまたは ParameterServerStrategy でルックアップレイヤーを使用する

TPU ポッドや複数のマシンで ParameterServerStrategy によってトレーニングする際に、TextVectorizationStringLookup、または IntegerLookup レイヤーを使用すると、パフォーマンスが劣化する未解決の問題があります。これは、TensorFLおw2.7 で修正される予定です。