Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
tensorflow
GitHub Repository: tensorflow/docs-l10n
Path: blob/master/site/ja/guide/keras/preprocessing_layers.ipynb
25118 views
Kernel: Python 3
#@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.

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

Keras 前処理レイヤー

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

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

利用可能な前処理

テキストの前処理

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

数値特徴量の前処理

  • tf.keras.layers.Normalization: 入力した特徴量を特徴量ごとに正規化します。

  • tf.keras.layers.Discretization: 連続数値の特徴量を整数カテゴリカル特徴量に変換します。

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

  • 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: 画像のバッチの中央の切り抜きを返します。

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

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

  • tf.keras.layers.RandomCrop

  • tf.keras.layers.RandomFlip

  • tf.keras.layers.RandomTranslation

  • tf.keras.layers.RandomRotation

  • tf.keras.layers.RandomZoom

  • tf.keras.layers.RandomHeight

  • tf.keras.layers.RandomWidth

  • tf.keras.layers.RandomContrast

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()))

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

data = [ "ξεῖν᾽, ἦ τοι μὲν ὄνειροι ἀμήχανοι ἀκριτόμυθοι", "γίγνοντ᾽, οὐδέ τι πάντα τελείεται ἀνθρώποισι.", "δοιαὶ γάρ τε πύλαι ἀμενηνῶν εἰσὶν ὀνείρων:", "αἱ μὲν γὰρ κεράεσσι τετεύχαται, αἱ δ᾽ ἐλέφαντι:", "τῶν οἳ μέν κ᾽ ἔλθωσι διὰ πριστοῦ ἐλέφαντος,", "οἵ ῥ᾽ ἐλεφαίρονται, ἔπε᾽ ἀκράαντα φέροντες:", "οἱ δὲ διὰ ξεστῶν κεράων ἔλθωσι θύραζε,", "οἵ ῥ᾽ ἔτυμα κραίνουσι, βροτῶν ὅτε κέν τις ἴδηται.", ] layer = layers.TextVectorization() layer.adapt(data) vectorized_text = layer(data) print(vectorized_text)

さらに、適応可能なレイヤーは、コンストラクタ引数または重みの割り当てを介して状態を直接設定するオプションを常に公開します。意図した状態値がレイヤー構築時にわかっている場合、または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)

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

前処理レイヤーを使用する方法は 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)

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

数値的特徴の正規化

# 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)

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

# 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)

ここで、インデックス 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)

インデックス 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)

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

以下は、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)

テキスト分類を最初から行うの例では、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)

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)

重要な点

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

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

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

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

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