Bekerja dengan lapisan preprocessing

Lihat di TensorFlow.org Jalankan di Google Colab Lihat sumber di GitHub Unduh buku catatan

Pra-pemrosesan keras

API lapisan prapemrosesan Keras memungkinkan pengembang membangun saluran pemrosesan input asli Keras. Pipeline pemrosesan input ini dapat digunakan sebagai kode prapemrosesan independen dalam alur kerja non-Keras, digabungkan langsung dengan model Keras, dan diekspor sebagai bagian dari Keras SavedModel.

Dengan lapisan prapemrosesan Keras, Anda dapat membuat dan mengekspor model yang benar-benar menyeluruh: model yang menerima gambar mentah atau data terstruktur mentah sebagai masukan; model yang menangani normalisasi fitur atau pengindeksan nilai fitur sendiri.

Pra-pemrosesan yang tersedia

Pra-pemrosesan teks

Pemrosesan fitur numerik

Fitur kategoris preprocessing

Pra-pemrosesan gambar

Lapisan ini untuk standarisasi input dari model gambar.

Augmentasi data gambar

Lapisan ini menerapkan transformasi augmentasi acak ke kumpulan gambar. Mereka hanya aktif selama pelatihan.

The adapt() metode

Beberapa lapisan prapemrosesan memiliki status internal yang dapat dihitung berdasarkan sampel data pelatihan. Daftar lapisan preprocessing stateful adalah:

  • TextVectorization : memegang pemetaan antara token tali dan indeks bilangan bulat
  • StringLookup dan IntegerLookup : mengadakan pemetaan antara nilai input dan indeks integer.
  • Normalization : memegang deviasi mean dan standar fitur.
  • Discretization : menyimpan informasi tentang nilai ember batas.

Krusial, lapisan ini adalah non-dilatih. Status mereka tidak diatur selama pelatihan; itu harus ditetapkan sebelum pelatihan, baik dengan menginisialisasi mereka dari sebuah konstanta precomputed, atau dengan "mengadaptasi" mereka pada data.

Anda mengatur keadaan lapisan preprocessing dengan mengekspos data pelatihan, melalui adapt() metode:

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()))
Features mean: -0.00
Features std: 1.00

The adapt() metode mengambil baik array Numpy atau tf.data.Dataset objek. Dalam kasus StringLookup dan TextVectorization , Anda juga dapat melewati daftar string:

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)

Selain itu, lapisan yang dapat disesuaikan selalu mengekspos opsi untuk mengatur status secara langsung melalui argumen konstruktor atau penetapan bobot. Jika nilai-nilai negara dimaksudkan diketahui pada saat konstruksi lapisan, atau dihitung di luar adapt() panggilan, mereka dapat diatur tanpa bergantung pada perhitungan internal layer. Misalnya, jika file kosakata eksternal untuk TextVectorization , StringLookup , atau IntegerLookup lapisan sudah ada, mereka dapat dimuat langsung ke dalam tabel lookup dengan melewati jalan ke file kosakata dalam argumen konstruktor layer.

Berikut ini adalah contoh di mana kita instantiate StringLookup layer dengan kosa kata Precomputed:

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)

Memproses data sebelum model atau di dalam model

Ada dua cara Anda dapat menggunakan lapisan prapemrosesan:

Opsi 1: Membuat mereka bagian dari model, seperti ini:

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

Dengan opsi ini, pra-pemrosesan akan terjadi pada perangkat, secara sinkron dengan eksekusi model lainnya, yang berarti akan mendapat manfaat dari akselerasi GPU. Jika Anda pelatihan berada di GPU, ini merupakan pilihan terbaik untuk Normalization lapisan, dan untuk semua gambar preprocessing dan lapisan data yang augmentasi.

Opsi 2: menerapkannya ke Anda tf.data.Dataset , sehingga memperoleh dataset yang menghasilkan batch data preprocessed, seperti ini:

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

Dengan opsi ini, pra-pemrosesan Anda akan terjadi pada CPU, secara asinkron, dan akan di-buffer sebelum masuk ke model. Selain itu, jika Anda memanggil dataset.prefetch(tf.data.AUTOTUNE) pada dataset Anda, preprocessing akan terjadi efisien secara paralel dengan pelatihan:

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

Ini adalah pilihan terbaik bagi TextVectorization , dan semua data terstruktur preprocessing lapisan. Ini juga bisa menjadi pilihan yang baik jika Anda berlatih di CPU dan Anda menggunakan lapisan pra-pemrosesan gambar.

Ketika berjalan di TPU, Anda harus selalu menempatkan preprocessing lapisan dalam tf.data pipa (dengan pengecualian Normalization dan Rescaling , yang berjalan baik pada TPU dan biasanya digunakan sebagai lapisan pertama adalah model gambar).

Manfaat melakukan preprocessing di dalam model pada waktu inferensi

Bahkan jika Anda menggunakan opsi 2, Anda mungkin ingin mengekspor model ujung-ke-ujung khusus inferensi yang akan menyertakan lapisan prapemrosesan. Manfaat utama untuk melakukan hal ini adalah bahwa hal itu membuat model Anda portabel dan membantu mengurangi pelatihan / melayani miring .

Ketika semua prapemrosesan data adalah bagian dari model, orang lain dapat memuat dan menggunakan model Anda tanpa harus mengetahui bagaimana setiap fitur diharapkan dikodekan & dinormalisasi. Model inferensi Anda akan dapat memproses gambar mentah atau data terstruktur mentah, dan tidak akan mengharuskan pengguna model untuk mengetahui detail misalnya skema tokenisasi yang digunakan untuk teks, skema pengindeksan yang digunakan untuk fitur kategoris, apakah nilai piksel gambar dinormalisasi untuk [-1, +1] atau [0, 1] , dll Hal ini terutama kuat jika Anda mengekspor model Anda ke runtime lain, seperti TensorFlow.js: Anda tidak perlu reimplement preprocessing Anda pipa di JavaScript.

Jika Anda awalnya menempatkan lapisan preprocessing Anda di Anda tf.data pipa, Anda dapat mengekspor model kesimpulan bahwa paket preprocessing. Cukup buat contoh model baru yang menghubungkan lapisan prapemrosesan dan model pelatihan Anda:

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

resep cepat

Augmentasi data gambar

Perhatikan bahwa lapisan data gambar augmentation hanya aktif selama pelatihan (mirip dengan Dropout layer).

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
170500096/170498071 [==============================] - 6s 0us/step
170508288/170498071 [==============================] - 6s 0us/step
5/5 [==============================] - 11s 44ms/step - loss: 8.8927
<keras.callbacks.History at 0x7f1c0c3f16d0>

Anda dapat melihat setup yang sama dalam aksi pada contoh klasifikasi citra dari awal .

Menormalkan fitur numerik

# 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 [==============================] - 3s 2ms/step - loss: 2.1304
<keras.callbacks.History at 0x7f1bc43f40d0>

Encoding fitur kategoris string melalui enkode satu-panas

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

Perhatikan bahwa, di sini, indeks 0 dicadangkan untuk out-of-kosakata nilai (nilai-nilai yang tidak terlihat selama adapt() ).

Anda dapat melihat StringLookup beraksi di klasifikasi data terstruktur dari awal misalnya.

Encoding fitur kategoris integer melalui enkode satu-panas

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

Perhatikan bahwa indeks 0 dicadangkan untuk nilai-nilai yang hilang (yang Anda harus menentukan sebagai nilai 0), dan indeks 1 dicadangkan untuk out-of-kosakata nilai (nilai-nilai yang tidak terlihat selama adapt() ). Anda dapat mengkonfigurasi ini dengan menggunakan mask_token dan oov_token argumen konstruktor dari IntegerLookup .

Anda dapat melihat IntegerLookup beraksi dalam contoh terstruktur klasifikasi data dari awal .

Menerapkan trik hashing ke fitur kategorikal integer

Jika Anda memiliki fitur kategorikal yang dapat mengambil banyak nilai yang berbeda (pada urutan 10e3 atau lebih tinggi), di mana setiap nilai hanya muncul beberapa kali dalam data, menjadi tidak praktis dan tidak efektif untuk mengindeks dan mengkodekan nilai fitur sekaligus. Sebagai gantinya, sebaiknya terapkan "trik hashing": nilai hash ke vektor dengan ukuran tetap. Ini membuat ukuran ruang fitur dapat dikelola, dan menghilangkan kebutuhan untuk pengindeksan eksplisit.

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

Encoding teks sebagai urutan indeks token

Ini adalah bagaimana Anda harus preprocess teks yang akan diteruskan ke sebuah Embedding lapisan.

# 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 [==============================] - 3s 3s/step - loss: 0.4776

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

Anda dapat melihat TextVectorization lapisan dalam tindakan, dikombinasikan dengan Embedding modus, dalam contoh klasifikasi teks dari awal .

Perhatikan bahwa ketika pelatihan model seperti itu, untuk kinerja terbaik, Anda harus selalu menggunakan TextVectorization lapisan sebagai bagian dari pipa masukan.

Mengkodekan teks sebagai matriks padat ngram dengan pengkodean multi-panas

Ini adalah bagaimana Anda harus preprocess teks yang akan dilewatkan ke Dense lapisan.

# 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 0x7f1b9c5c5290> 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 experimental_relax_shapes=True option that relaxes argument shapes 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 231ms/step - loss: 1.0046

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

Mengkodekan teks sebagai matriks padat ngram dengan pembobotan TF-IDF

Ini adalah cara alternatif preprocessing teks sebelum diteruskan ke Dense lapisan.

# 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 0x7f1b9f6eae60> 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 experimental_relax_shapes=True option that relaxes argument shapes 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 239ms/step - loss: 4.4868

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

Gotcha penting

Bekerja dengan lapisan pencarian dengan kosakata yang sangat besar

Anda mungkin menemukan diri Anda bekerja dengan kosa kata yang sangat besar dalam TextVectorization , sebuah StringLookup lapisan, atau IntegerLookup lapisan. Biasanya, kosakata yang lebih besar dari 500MB akan dianggap "sangat besar".

Dalam hal demikian, untuk kinerja terbaik, Anda harus menghindari menggunakan adapt() . Sebagai gantinya, hitung kosakata Anda terlebih dahulu (Anda bisa menggunakan Apache Beam atau TF Transform untuk ini) dan simpan dalam file. Kemudian memuat kosa kata ke dalam lapisan pada saat konstruksi dengan melewati filepath sebagai vocabulary argumen.

Menggunakan lapisan lookup pada pod TPU atau dengan ParameterServerStrategy .

Ada masalah luar biasa yang menyebabkan kinerja untuk mendegradasi bila menggunakan TextVectorization , StringLookup , atau IntegerLookup lapisan saat pelatihan pada pod TPU atau pada beberapa mesin melalui ParameterServerStrategy . Ini dijadwalkan untuk diperbaiki di TensorFlow 2.7.