Ta strona została przetłumaczona przez Cloud Translation API.
Switch to English

Praca z warstwami przetwarzania wstępnego

Zobacz na TensorFlow.org Uruchom w Google Colab Wyświetl źródło na GitHub Pobierz notatnik

Keras wstępnie przetwarza warstwy

Interfejs API warstw wstępnego przetwarzania Keras umożliwia programistom tworzenie natywnych potoków przetwarzania danych wejściowych Keras. Te potoki przetwarzania danych wejściowych mogą być używane jako niezależny kod przetwarzania wstępnego w przepływach pracy innych niż Keras, łączone bezpośrednio z modelami Keras i eksportowane jako część Keras SavedModel.

Dzięki warstwom wstępnie przetwarzającym Keras możesz budować i eksportować modele, które są naprawdę kompleksowe: modele, które przyjmują nieprzetworzone obrazy lub surowe dane strukturalne jako dane wejściowe; modele, które samodzielnie obsługują normalizację cech lub indeksowanie wartości cech.

Dostępne warstwy przetwarzania wstępnego

Podstawowe warstwy przetwarzania wstępnego

  • Warstwa TextVectorization : zamienia nieprzetworzone ciągi znaków w zakodowaną reprezentację, którą można odczytać przez warstwę Embedding lub warstwę Dense .
  • Warstwa Normalization : przeprowadza normalizację funkcji wejściowych pod względem funkcji.

Strukturyzowane warstwy przetwarzania wstępnego danych

Warstwy te służą do kodowania danych strukturalnych i inżynierii funkcji.

  • Warstwa CategoryEncoding : zamienia całkowite cechy kategorialne w jedną gorącą, wielokrotną lub gęstą reprezentację TF-IDF.
  • Warstwa Hashing : wykonuje haszowanie cech kategorialnych, znane również jako „sztuczka mieszająca”.
  • Warstwa Discretization : zamienia ciągłe cechy numeryczne w całkowite cechy jakościowe.
  • Warstwa StringLookup : zamienia wartości kategorialne łańcuchów na indeksy całkowite.
  • Warstwa IntegerLookup : zamienia całkowite wartości IntegerLookup na indeksy liczb całkowitych.
  • CategoryCrossing warstwa: łączy cechy kategorialne w cechy współwystępowania. Np. Jeśli masz wartości cech „a” i „b”, może to zapewnić kombinację funkcji „a i b są obecne w tym samym czasie”.

Warstwy przetwarzania wstępnego obrazu

Warstwy te służą do standaryzacji danych wejściowych modelu obrazu.

  • Resizing warstwy: zmienia rozmiar partii obrazów do rozmiaru docelowego.
  • Rescaling warstwa: przeskalowanie i przesunięcia Wartości partii obrazu (np przejść z wejść w [0, 255] zakresie, aby wejść w [0, 1] zakresu.
  • CenterCrop layer: zwraca środkowy CenterCrop jeśli jest to partia obrazów.

Warstwy rozszerzania danych obrazu

Warstwy te stosują losowe transformacje rozszerzające do partii obrazów. Są aktywne tylko podczas treningu.

  • Warstwa RandomCrop
  • Warstwa RandomFlip
  • Warstwa RandomTranslation
  • Warstwa RandomRotation
  • Warstwa RandomZoom
  • Warstwa RandomHeight
  • Warstwa RandomWidth

Metoda adapt()

Niektóre warstwy przetwarzania wstępnego mają stan wewnętrzny, który należy obliczyć na podstawie próbki danych uczących. Lista stanowych warstw przetwarzania wstępnego to:

  • TextVectorization : przechowuje mapowanie między znacznikami łańcuchowymi a indeksami całkowitymi
  • Normalization : zawiera średnią i odchylenie standardowe cech
  • StringLookup i IntegerLookup : przechowują mapowanie między wartościami wejściowymi a indeksami wyjściowymi.
  • CategoryEncoding : przechowuje indeks wartości wejściowych.
  • Discretization : przechowuje informacje o granicach segmentu wartości.

Co najważniejsze, tych warstw nie można trenować . Ich stan nie jest ustalany podczas treningu; należy go ustawić przed treningiem , krok zwany „adaptacją”.

Stan warstwy przetwarzania wstępnego ustawiasz, wystawiając ją na dane uczące za pomocą metody adapt() :

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

data = np.array([[0.1, 0.2, 0.3], [0.8, 0.9, 1.0], [1.5, 1.6, 1.7],])
layer = preprocessing.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

Metoda adapt() pobiera tablicę Numpy lub obiekttf.data.Dataset . W przypadku StringLookup i TextVectorization możesz również przekazać listę ciągów:

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

Ponadto warstwy adaptowalne zawsze udostępniają opcję bezpośredniego ustawiania stanu za pomocą argumentów konstruktora lub przypisania wagi. Jeśli zamierzone wartości stanu są znane w czasie tworzenia warstwy lub są obliczane poza wywołaniem adapt() , można je ustawić bez polegania na wewnętrznych obliczeniach warstwy. Na przykład, jeśli zewnętrzne pliki słownictwa dla TextVectorization , StringLookup lub IntegerLookup już istnieją, można je załadować bezpośrednio do tabel przeglądowych, przekazując ścieżkę do pliku słownika w argumentach konstruktora warstwy.

Oto przykład, w którym tworzymy wystąpienie warstwy StringLookup z wstępnie StringLookup słownikiem:

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

Wstępne przetwarzanie danych przed modelem lub wewnątrz modelu

Warstwy przetwarzania wstępnego można wykorzystać na dwa sposoby:

Opcja 1: uczyń je częścią modelu, na przykład:

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

Dzięki tej opcji wstępne przetwarzanie będzie odbywać się na urządzeniu, synchronicznie z pozostałą częścią wykonania modelu, co oznacza, że ​​skorzysta na akceleracji GPU. Jeśli trenujesz na GPU, jest to najlepsza opcja dla warstwy Normalization i dla wszystkich warstw wstępnego przetwarzania obrazu i rozszerzania danych.

Opcja 2: zastosuj ją do swojegotf.data.Dataset , aby uzyskać zbiór danych, który dostarcza partie wstępnie przetworzonych danych, na przykład:

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

Dzięki tej opcji przetwarzanie wstępne będzie odbywać się na procesorze, asynchronicznie i będzie buforowane przed przejściem do modelu.

Jest to najlepsza opcja dla TextVectorization i wszystkich warstw wstępnego przetwarzania danych strukturalnych. Może to być również dobra opcja, jeśli trenujesz na procesorze i używasz warstw wstępnego przetwarzania obrazu.

Korzyści z wykonywania wstępnego przetwarzania wewnątrz modelu w czasie wnioskowania

Nawet jeśli wybierzesz opcję 2, możesz później chcieć wyeksportować model end-to-end tylko do wnioskowania, który będzie zawierał warstwy przetwarzania wstępnego. Kluczową korzyścią z tego jest to, że sprawia, że ​​model jest przenośny i pomaga zmniejszyć odchylenie treningu / serwowania .

Gdy wszystkie wstępne przetwarzanie danych jest częścią modelu, inne osoby mogą załadować i używać Twojego modelu bez konieczności zdawania sobie sprawy z tego, jak każda funkcja ma być kodowana i normalizowana. Twój model wnioskowania będzie w stanie przetwarzać nieprzetworzone obrazy lub surowe dane strukturalne i nie będzie wymagał od użytkowników modelu znajomości szczegółów, np. Schematu tokenizacji używanego dla tekstu, schematu indeksowania używanego dla cech kategorialnych, czy wartości pikseli obrazu są znormalizowane do [-1, +1] lub do [0, 1] , itd. Jest to szczególnie przydatne, jeśli eksportujesz swój model do innego środowiska wykonawczego, takiego jak TensorFlow.js: nie będziesz musiał ponownie wdrażać przetwarzania wstępnego potok w JavaScript.

Jeśli początkowo umieścisz warstwy przetwarzania wstępnego w potoku tf.data , możesz wyeksportować model wnioskowania, który pakuje przetwarzanie tf.data . Po prostu utwórz wystąpienie nowego modelu, który łączy warstwy przetwarzania wstępnego i model szkoleniowy:

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

Szybkie przepisy

Rozszerzanie danych obrazu (na urządzeniu)

Zwróć uwagę, że warstwy rozszerzania danych obrazu są aktywne tylko podczas uczenia (podobnie jak warstwa Dropout ).

from tensorflow import keras
from tensorflow.keras import layers

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

# Create a model that includes the augmentation stage
input_shape = (32, 32, 3)
classes = 10
inputs = keras.Input(shape=input_shape)
# Augment images
x = data_augmentation(inputs)
# Rescale image values to [0, 1]
x = preprocessing.Rescaling(1.0 / 255)(x)
# Add the rest of the model
outputs = keras.applications.ResNet50(
    weights=None, input_shape=input_shape, classes=classes
)(x)
model = keras.Model(inputs, outputs)

Możesz zobaczyć podobną konfigurację w akcji w przykładowej klasyfikacji obrazu od podstaw .

Normalizowanie cech numerycznych

# 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 = preprocessing.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)
Downloading data from https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz
170500096/170498071 [==============================] - 11s 0us/step
1563/1563 [==============================] - 2s 1ms/step - loss: 2.1245

<tensorflow.python.keras.callbacks.History at 0x7f1aec079e10>

Kodowanie cech jakościowych ciągów za pomocą kodowania typu one-hot

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

# Use StringLookup to build an index of the feature values
indexer = preprocessing.StringLookup()
indexer.adapt(data)

# Use CategoryEncoding to encode the integer indices to a one-hot vector
encoder = preprocessing.CategoryEncoding(output_mode="binary")
encoder.adapt(indexer(data))

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

Zauważ, że indeks 0 jest zarezerwowany dla brakujących wartości (które należy określić jako pusty łańcuch "" ), a indeks 1 jest zarezerwowany dla wartości spoza słownika (wartości, które nie były widoczne podczas adapt() ). Można to skonfigurować za pomocą argumentów konstruktora mask_token i oov_token metody StringLookup .

Warstwy StringLookup i CategoryEncoding można zobaczyć w akcji w przykładowej klasyfikacji danych strukturalnych od podstaw .

Kodowanie funkcji kategorialnych w liczbach całkowitych za pomocą kodowania typu one-hot

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

# Use IntegerLookup to build an index of the feature values
indexer = preprocessing.IntegerLookup()
indexer.adapt(data)

# Use CategoryEncoding to encode the integer indices to a one-hot vector
encoder = preprocessing.CategoryEncoding(output_mode="binary")
encoder.adapt(indexer(data))

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

Zauważ, że indeks 0 jest zarezerwowany dla brakujących wartości (które należy określić jako wartość 0), a indeks 1 jest zarezerwowany dla wartości spoza słownictwa (wartości, które nie były widoczne podczas adapt() ). Można to skonfigurować za pomocą argumentów konstruktorów mask_value i oov_value IntegerLookup .

Warstwy IntegerLookup i CategoryEncoding w akcji można zobaczyć od podstaw w przykładowej klasyfikacji danych strukturalnych .

Stosowanie sztuczki haszującej do całkowitej funkcji kategorialnej

Jeśli masz cechę kategorialną, która może przyjmować wiele różnych wartości (rzędu 10e3 lub wyższych), gdzie każda wartość pojawia się w danych tylko kilka razy, indeksowanie i kodowanie wartości funkcji na gorąco staje się niepraktyczne i nieskuteczne. Zamiast tego dobrym pomysłem może być zastosowanie „sztuczki mieszającej”: zaszyfruj wartości do wektora o stałym rozmiarze. Zapewnia to możliwość zarządzania wielkością przestrzeni funkcji i eliminuje potrzebę jawnego indeksowania.

# 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 = preprocessing.Hashing(num_bins=64, salt=1337)

# Use the CategoryEncoding layer to one-hot encode the hashed values
encoder = preprocessing.CategoryEncoding(max_tokens=64, output_mode="binary")
encoded_data = encoder(hasher(data))
print(encoded_data.shape)
(10000, 64)

Kodowanie tekstu jako sekwencji indeksów tokenów

W ten sposób należy wstępnie przetworzyć tekst, który ma zostać przesłany do warstwy Embedding .

# Define some text data to adapt the layer
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 "int" output_mode
text_vectorizer = preprocessing.TextVectorization(output_mode="int")
# Index the vocabulary via `adapt()`
text_vectorizer.adapt(data)

# You can retrieve the vocabulary we indexed via get_vocabulary()
vocab = text_vectorizer.get_vocabulary()
print("Vocabulary:", vocab)

# Create an Embedding + LSTM model
inputs = keras.Input(shape=(1,), dtype="string")
x = text_vectorizer(inputs)
x = layers.Embedding(input_dim=len(vocab), output_dim=64)(x)
outputs = layers.LSTM(1)(x)
model = keras.Model(inputs, outputs)

# Call the model on test data (which includes unknown tokens)
test_data = tf.constant(["The Brain is deeper than the sea"])
test_output = model(test_data)
Vocabulary: ['', '[UNK]', 'the', 'side', 'you', 'with', 'will', 'wider', 'them', 'than', 'sky', 'put', 'other', 'one', 'is', 'for', 'ease', 'contain', 'by', 'brain', 'beside', 'and']

Możesz zobaczyć warstwę TextVectorization w akcji w połączeniu z trybem Embedding w przykładowej klasyfikacji tekstu od podstaw .

Zauważ, że podczas uczenia takiego modelu, aby uzyskać najlepszą wydajność, powinieneś użyć warstwy TextVectorization jako części potoku wejściowego (co robimy w powyższym przykładzie klasyfikacji tekstu).

Kodowanie tekstu jako gęstej macierzy ngramów z kodowaniem multi-hot

W ten sposób należy wstępnie przetworzyć tekst, który ma zostać przesłany do warstwy Dense .

# Define some text data to adapt the layer
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 "binary" output_mode (multi-hot)
# and ngrams=2 (index all bigrams)
text_vectorizer = preprocessing.TextVectorization(output_mode="binary", ngrams=2)
# Index the bigrams via `adapt()`
text_vectorizer.adapt(data)

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

# Create a Dense model
inputs = keras.Input(shape=(1,), dtype="string")
x = text_vectorizer(inputs)
outputs = layers.Dense(1)(x)
model = keras.Model(inputs, outputs)

# Call the model on test data (which includes unknown tokens)
test_data = tf.constant(["The Brain is deeper than the sea"])
test_output = model(test_data)

print("Model output:", test_output)
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.]] 

Model output: tf.Tensor([[0.24204311]], shape=(1, 1), dtype=float32)

Kodowanie tekstu jako gęstej macierzy ngramów z wagą TF-IDF

Jest to alternatywny sposób wstępnego przetwarzania tekstu przed przekazaniem go do warstwy Dense .

# Define some text data to adapt the layer
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 = preprocessing.TextVectorization(output_mode="tf-idf", ngrams=2)
# Index the bigrams and learn the TF-IDF weights via `adapt()`
text_vectorizer.adapt(data)

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

# Create a Dense model
inputs = keras.Input(shape=(1,), dtype="string")
x = text_vectorizer(inputs)
outputs = layers.Dense(1)(x)
model = keras.Model(inputs, outputs)

# Call the model on test data (which includes unknown tokens)
test_data = tf.constant(["The Brain is deeper than the sea"])
test_output = model(test_data)
print("Model output:", test_output)
Encoded text:
 [[8.04719   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.       ]] 

Model output: tf.Tensor([[1.8902668]], shape=(1, 1), dtype=float32)