Работа со слоями предварительной обработки

Посмотреть на TensorFlow.org Запустить в Google Colab Посмотреть исходный код на GitHub Скачать блокнот

Слои предварительной обработки Keras

API слоев предварительной обработки Keras позволяет разработчикам создавать собственные конвейеры обработки ввода для Keras. Эти конвейеры обработки ввода могут использоваться в качестве независимого кода предварительной обработки в рабочих процессах, отличных от Keras, в сочетании непосредственно с моделями Keras и экспортироваться как часть Keras SavedModel.

С помощью слоев предварительной обработки Keras вы можете создавать и экспортировать модели, которые действительно являются сквозными: модели, которые принимают необработанные изображения или необработанные структурированные данные в качестве входных данных; модели, которые самостоятельно обрабатывают нормализацию признаков или индексацию значений признаков.

Доступные слои предварительной обработки

Основные слои предварительной обработки

  • TextVectorization слой: превращает необработанные строки в кодированное представление , которые могут быть прочитаны с помощью Embedding слоя или Dense слоя.
  • Normalization слой: выполняет функцию-накрест нормализует входные функции.

Уровни предварительной обработки структурированных данных

Эти уровни предназначены для кодирования структурированных данных и проектирования функций.

  • CategoryEncoding слоя: целые очереди категориальных функций в одной горячие, мульти-горячие, или TF-IDF плотных представлений.
  • Hashing слой: выполняет категоричны функция хэширования, также известный как «хеширования трюком».
  • Discretization слой: включает непрерывные числовые функции в целом числе категориальных особенностей.
  • StringLookup слой: включает значения категориальных строк в индексы целых чисел.
  • IntegerLookup слой: витки целое категориальных значений в индексы целых чисел.
  • CategoryCrossing слой: сочетает в себе категорические черты и в особенности смежности. Например, если у вас есть значения признаков «a» и «b», он может предоставить комбинированный признак «a и b присутствуют одновременно».

Слои предварительной обработки изображений

Эти слои предназначены для стандартизации входных данных модели изображения.

  • Resizing слоя: изменяет размер партии изображений до заданного размера.
  • Rescaling слой: перемасштабирует и смещают значения партии изображения (например , перейти от входов в [0, 255] диапазоне к входам в [0, 1] диапазоне.
  • CenterCrop слой: возвращает центр урожай партии изображений.

Слои увеличения данных изображения

Эти слои применяют произвольные преобразования увеличения к пакету изображений. Они активны только во время тренировок.

  • RandomCrop слой
  • RandomFlip слой
  • RandomTranslation слой
  • RandomRotation слой
  • RandomZoom слой
  • RandomHeight слой
  • RandomWidth слой

adapt() метод

Некоторые уровни предварительной обработки имеют внутреннее состояние, которое должно быть вычислено на основе выборки обучающих данных. Список уровней предварительной обработки с отслеживанием состояния:

  • TextVectorization : содержит отображение между строк токенов и целыми индексами
  • Normalization : содержит среднее значение и стандартное отклонение функций
  • StringLookup и IntegerLookup : провести сопоставление между входными и выходными значениями индексов.
  • CategoryEncoding : имеет индекс входных значений.
  • Discretization : содержит информацию о границах ковшовых стоимости.

Важно отметить , что эти слои не являются обучаемыми. Их состояние не устанавливается во время обучения; он должен быть установлен перед тренировкой, шаг под названием «Адаптация».

Вы можете установить состояние предварительной обработки слоя, подвергая его обучающим данным, через 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

adapt() метод принимает либо массив Numpy или tf.data.Dataset объект. В случае StringLookup и TextVectorization , вы можете также передать список строк:

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)

Кроме того, адаптируемые слои всегда предоставляют возможность напрямую установить состояние с помощью аргументов конструктора или назначения веса. Если предполагаемые значения состояний известны в слое время строительства, или рассчитываются за пределами adapt() вызова, они могут быть установлены , не полагаясь на внутреннем вычислении слоя. Например, если внешние файлы словаря для TextVectorization , StringLookup или IntegerLookup уже существуют слои, те могут быть загружены непосредственно в справочные таблицы, передавая путь к файлу словаря в аргументах конструктора слоя.

Вот пример , где мы создаем StringLookup слой предвычисленными лексики:

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)

Предварительная обработка данных перед моделью или внутри модели

Есть два способа использовать слои предварительной обработки:

Вариант 1: Сделайте их часть модели, как это:

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

С этой опцией предварительная обработка будет происходить на устройстве синхронно с остальной частью выполнения модели, а это означает, что она выиграет от ускорения графического процессора. Если вы тренируетесь на GPU, это лучший вариант для Normalization слоя, а также для всех изображений предварительной обработки и дополнения данных слоев.

Вариант 2: применить его к tf.data.Dataset , таким образом , чтобы получить набор данных , который дает партии предварительно обработанных данных, как это:

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

С этой опцией ваша предварительная обработка будет происходить на ЦП асинхронно и будет буферизована перед переходом в модель.

Это лучший вариант для TextVectorization , и все структурированные данные предварительной обработки слоев. Это также может быть хорошим вариантом, если вы тренируетесь на ЦП и используете слои предварительной обработки изображений.

Преимущества предварительной обработки внутри модели во время вывода

Даже если вы выберете вариант 2, вы можете позже экспортировать сквозную модель только для вывода, которая будет включать слои предварительной обработки. Главное преимущество , чтобы сделать это , что делает вашу модель портативной и это помогает сократить обучение / обслуживающий перекос .

Когда вся предварительная обработка данных является частью модели, другие люди могут загружать и использовать вашу модель, не зная, как ожидается кодирование и нормализация каждой функции. Ваша модель вывода сможет обрабатывать необработанные изображения или необработанные структурированные данные и не требует, чтобы пользователи модели знали детали, например, схему токенизации, используемую для текста, схему индексирования, используемую для категориальных функций, и значения пикселей изображения. нормировано на [-1, +1] или [0, 1] , и т.д. Это особенно мощное , если вы экспортировать модель в другое выполнение, например, TensorFlow.js: вам не придется переопределять вашу предварительную обработку конвейер в JavaScript.

Если вы изначально положить Preprocessing слоев в вашем 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(
    [
        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)

Вы можете увидеть подобную установку в действии в примере классификации изображений с нуля .

Нормализация числовых характеристик

# 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 [==============================] - 3s 1ms/step - loss: 2.1776
<tensorflow.python.keras.callbacks.History at 0x7f58c5f44208>

Кодирование категориальных функций строки с помощью однократного кодирования

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

Обратите внимание , что индекс 0 зарезервирован для пропущенных значений (которые вы должны указать , как пустую строку "" ), а индекс 1 зарезервирован для вне словаря значения (значения , которые не были замечены в процессе adapt() ). Вы можете настроить это с помощью mask_token и oov_token аргументов конструктора StringLookup .

Вы можете увидеть StringLookup и CategoryEncoding слоев в действии в примере структурированы классификации данных с нуля .

Кодирование целочисленных категориальных функций с помощью однократного кодирования

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

Обратите внимание , что индекс 0 зарезервирован для отсутствующих значений (которые следует указать в качестве значения 0), а индекс 1 зарезервирована для вне словаря значения (значения , которые не были замечены в процессе adapt() ). Вы можете настроить это с помощью mask_value и oov_value аргументов конструктора IntegerLookup .

Вы можете увидеть IntegerLookup и CategoryEncoding слоев в действии в примере структурированы классификации данных с нуля .

Применение трюка хеширования к целочисленной категориальной функции

Если у вас есть категориальная функция, которая может принимать много разных значений (порядка 10e3 или выше), где каждое значение появляется в данных только несколько раз, индексирование и однократное кодирование значений функций становится непрактичным и неэффективным. Вместо этого может быть хорошей идеей применить «трюк с хешированием»: хешировать значения в вектор фиксированного размера. Это позволяет управлять размером пространства функций и устраняет необходимость в явном индексировании.

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

Кодирование текста как последовательности индексов токенов

Это, как вы должны препроцессировать текст , который будет передан в 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']

Вы можете увидеть TextVectorization слой в действии, в сочетании с Embedding режимом, в примере классификации текста с нуля .

Обратите внимание , что при подготовке такой модели, для лучшей производительности, вы должны использовать TextVectorization слой как часть входного трубопровода (который является то , что мы делаем в этом примере текст классификации выше).

Кодирование текста как плотной матрицы нграмм с многократным горячим кодированием

Это, как вы должны препроцессировать текст , который будет принят в 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.6381588]], shape=(1, 1), dtype=float32)

Кодирование текста как плотной матрицы нграмм с взвешиванием TF-IDF

Это альтернативный способ предварительной обработки текста перед передачей его в 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.2379041]], shape=(1, 1), dtype=float32)