Увеличение данных

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

Обзор

В этом руководстве демонстрируется увеличение данных: метод увеличения разнообразия вашего обучающего набора путем применения случайных (но реалистичных) преобразований, таких как поворот изображения. Вы узнаете, как применять увеличение данных двумя способами. Во- первых, вы будете использовать Keras Preprocessing Layers . Далее, вы будете использовать tf.image .

Настраивать

import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
import tensorflow_datasets as tfds

from tensorflow.keras import layers
2021-07-31 01:20:29.398577: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcudart.so.11.0

Скачать набор данных

В этом руководстве используется tf_flowers набор данных. Для удобства скачивания набора данных с помощью TensorFlow наборов данных . Если вы хотели бы узнать о других способах импорта данных см загрузки изображений учебника.

(train_ds, val_ds, test_ds), metadata = tfds.load(
    'tf_flowers',
    split=['train[:80%]', 'train[80%:90%]', 'train[90%:]'],
    with_info=True,
    as_supervised=True,
)
2021-07-31 01:20:32.658409: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcuda.so.1
2021-07-31 01:20:33.245494: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-07-31 01:20:33.246357: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1733] Found device 0 with properties: 
pciBusID: 0000:00:05.0 name: Tesla V100-SXM2-16GB computeCapability: 7.0
coreClock: 1.53GHz coreCount: 80 deviceMemorySize: 15.78GiB deviceMemoryBandwidth: 836.37GiB/s
2021-07-31 01:20:33.246390: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcudart.so.11.0
2021-07-31 01:20:33.249453: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcublas.so.11
2021-07-31 01:20:33.249541: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcublasLt.so.11
2021-07-31 01:20:33.250610: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcufft.so.10
2021-07-31 01:20:33.250929: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcurand.so.10
2021-07-31 01:20:33.251983: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcusolver.so.11
2021-07-31 01:20:33.252852: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcusparse.so.11
2021-07-31 01:20:33.253031: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcudnn.so.8
2021-07-31 01:20:33.253121: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-07-31 01:20:33.253980: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-07-31 01:20:33.254770: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1871] Adding visible gpu devices: 0
2021-07-31 01:20:33.255502: I tensorflow/core/platform/cpu_feature_guard.cc:142] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations:  AVX2 AVX512F FMA
To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags.
2021-07-31 01:20:33.256126: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-07-31 01:20:33.256945: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1733] Found device 0 with properties: 
pciBusID: 0000:00:05.0 name: Tesla V100-SXM2-16GB computeCapability: 7.0
coreClock: 1.53GHz coreCount: 80 deviceMemorySize: 15.78GiB deviceMemoryBandwidth: 836.37GiB/s
2021-07-31 01:20:33.257035: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-07-31 01:20:33.257857: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-07-31 01:20:33.258642: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1871] Adding visible gpu devices: 0
2021-07-31 01:20:33.258683: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcudart.so.11.0
2021-07-31 01:20:33.827497: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1258] Device interconnect StreamExecutor with strength 1 edge matrix:
2021-07-31 01:20:33.827531: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1264]      0 
2021-07-31 01:20:33.827538: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1277] 0:   N 
2021-07-31 01:20:33.827734: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-07-31 01:20:33.828649: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-07-31 01:20:33.829480: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-07-31 01:20:33.830357: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1418] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 14646 MB memory) -> physical GPU (device: 0, name: Tesla V100-SXM2-16GB, pci bus id: 0000:00:05.0, compute capability: 7.0)

Набор данных цветов состоит из пяти классов.

num_classes = metadata.features['label'].num_classes
print(num_classes)
5

Давайте извлечем изображение из набора данных и используем его для демонстрации увеличения данных.

get_label_name = metadata.features['label'].int2str

image, label = next(iter(train_ds))
_ = plt.imshow(image)
_ = plt.title(get_label_name(label))
2021-07-31 01:20:33.935374: I tensorflow/compiler/mlir/mlir_graph_optimization_pass.cc:176] None of the MLIR Optimization Passes are enabled (registered 2)
2021-07-31 01:20:33.935908: I tensorflow/core/platform/profile_utils/cpu_utils.cc:114] CPU Frequency: 2000160000 Hz
2021-07-31 01:20:34.470092: W tensorflow/core/kernels/data/cache_dataset_ops.cc:768] The calling iterator did not fully read the dataset being cached. In order to avoid unexpected truncation of the dataset, the partially cached contents of the dataset  will be discarded. This can happen if you have an input pipeline similar to `dataset.cache().take(k).repeat()`. You should use `dataset.take(k).cache().repeat()` instead.

PNG

Используйте слои предварительной обработки Keras

Изменение размера и масштабирования

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

IMG_SIZE = 180

resize_and_rescale = tf.keras.Sequential([
  layers.experimental.preprocessing.Resizing(IMG_SIZE, IMG_SIZE),
  layers.experimental.preprocessing.Rescaling(1./255)
])

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

result = resize_and_rescale(image)
_ = plt.imshow(result)

PNG

Вы можете проверить пиксели в [0-1] .

print("Min and max pixel values:", result.numpy().min(), result.numpy().max())
Min and max pixel values: 0.0 1.0

Увеличение данных

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

Давайте создадим несколько слоев предварительной обработки и повторно применим их к одному и тому же изображению.

data_augmentation = tf.keras.Sequential([
  layers.experimental.preprocessing.RandomFlip("horizontal_and_vertical"),
  layers.experimental.preprocessing.RandomRotation(0.2),
])
# Add the image to a batch
image = tf.expand_dims(image, 0)
plt.figure(figsize=(10, 10))
for i in range(9):
  augmented_image = data_augmentation(image)
  ax = plt.subplot(3, 3, i + 1)
  plt.imshow(augmented_image[0])
  plt.axis("off")

PNG

Есть множество препроцессирования слоев , которые можно использовать для увеличения данных , включая layers.RandomContrast , layers.RandomCrop , layers.RandomZoom и другие.

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

Есть два способа использования этих слоев предварительной обработки с важными компромиссами.

Вариант 1. Сделайте слои предварительной обработки частью вашей модели

model = tf.keras.Sequential([
  resize_and_rescale,
  data_augmentation,
  layers.Conv2D(16, 3, padding='same', activation='relu'),
  layers.MaxPooling2D(),
  # Rest of your model
])

В этом случае следует помнить о двух важных моментах:

  • Расширение данных будет выполняться на устройстве синхронно с остальными вашими слоями и получать выгоду от ускорения графического процессора.

  • При экспорте модели с использованием model.save , предобработка слои будут сохранены вместе с остальной частью вашей модели. Если вы позже развернете эту модель, она автоматически стандартизирует изображения (в соответствии с конфигурацией ваших слоев). Это может избавить вас от необходимости заново реализовывать эту логику на стороне сервера.

Вариант 2. Примените слои предварительной обработки к набору данных.

aug_ds = train_ds.map(
  lambda x, y: (resize_and_rescale(x, training=True), y))

При таком подходе вы используете Dataset.map создать набор данных , который дает партии увеличенных изображений. В таком случае:

  • Увеличение данных происходит асинхронно на ЦП и не является блокирующим. Вы можете перекрывать обучение модели на GPU с данными предварительной обработки с использованием Dataset.prefetch , как показано ниже.
  • В этом случае prepreprocessing слои не будут экспортироваться с моделью при вызове model.save . Вам нужно будет прикрепить их к вашей модели перед ее сохранением или повторной реализацией на стороне сервера. После обучения вы можете прикрепить слои предварительной обработки перед экспортом.

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

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

Сконфигурируйте наборы данных для обучения, проверки и тестирования с помощью слоев предварительной обработки, которые вы создали выше. Вы также сконфигурируете наборы данных для повышения производительности, используя параллельное чтение и буферизованную предварительную выборку, чтобы получить пакеты с диска без блокировки ввода-вывода. Вы можете узнать больше набора данных производительности в производительности лучше с tf.data API руководства.

batch_size = 32
AUTOTUNE = tf.data.AUTOTUNE

def prepare(ds, shuffle=False, augment=False):
  # Resize and rescale all datasets
  ds = ds.map(lambda x, y: (resize_and_rescale(x), y), 
              num_parallel_calls=AUTOTUNE)

  if shuffle:
    ds = ds.shuffle(1000)

  # Batch all datasets
  ds = ds.batch(batch_size)

  # Use data augmentation only on the training set
  if augment:
    ds = ds.map(lambda x, y: (data_augmentation(x, training=True), y), 
                num_parallel_calls=AUTOTUNE)

  # Use buffered prefecting on all datasets
  return ds.prefetch(buffer_size=AUTOTUNE)
train_ds = prepare(train_ds, shuffle=True, augment=True)
val_ds = prepare(val_ds)
test_ds = prepare(test_ds)

Обучить модель

Для полноты картины вы теперь будете обучать модель, используя эти наборы данных. Эта модель не была настроена на точность (цель - показать вам механику).

model = tf.keras.Sequential([
  layers.Conv2D(16, 3, padding='same', activation='relu'),
  layers.MaxPooling2D(),
  layers.Conv2D(32, 3, padding='same', activation='relu'),
  layers.MaxPooling2D(),
  layers.Conv2D(64, 3, padding='same', activation='relu'),
  layers.MaxPooling2D(),
  layers.Flatten(),
  layers.Dense(128, activation='relu'),
  layers.Dense(num_classes)
])
model.compile(optimizer='adam',
              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
              metrics=['accuracy'])
epochs=5
history = model.fit(
  train_ds,
  validation_data=val_ds,
  epochs=epochs
)
Epoch 1/5
2021-07-31 01:20:39.448244: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcudnn.so.8
2021-07-31 01:20:41.475212: I tensorflow/stream_executor/cuda/cuda_dnn.cc:359] Loaded cuDNN version 8100
2021-07-31 01:20:46.496035: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcublas.so.11
2021-07-31 01:20:46.860481: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcublasLt.so.11
92/92 [==============================] - 17s 66ms/step - loss: 1.3434 - accuracy: 0.4271 - val_loss: 1.1534 - val_accuracy: 0.5368
Epoch 2/5
92/92 [==============================] - 3s 27ms/step - loss: 1.0960 - accuracy: 0.5565 - val_loss: 1.0718 - val_accuracy: 0.5695
Epoch 3/5
92/92 [==============================] - 3s 26ms/step - loss: 1.0115 - accuracy: 0.5988 - val_loss: 1.0322 - val_accuracy: 0.6022
Epoch 4/5
92/92 [==============================] - 3s 27ms/step - loss: 0.9503 - accuracy: 0.6202 - val_loss: 0.8811 - val_accuracy: 0.6730
Epoch 5/5
92/92 [==============================] - 3s 27ms/step - loss: 0.8758 - accuracy: 0.6570 - val_loss: 0.8760 - val_accuracy: 0.6485
loss, acc = model.evaluate(test_ds)
print("Accuracy", acc)
12/12 [==============================] - 1s 13ms/step - loss: 0.8319 - accuracy: 0.6812
Accuracy 0.6811988949775696

Расширение пользовательских данных

Вы также можете создавать собственные слои дополнения данных. В этом руководстве показаны два способа сделать это. Во- первых, вы будете создавать layers.Lambda слой. Это хороший способ написать лаконичный код. Далее, вы будете писать новый слой с помощью подклассов , который дает вам больше контроля. Оба слоя с некоторой вероятностью будут случайным образом инвертировать цвета изображения.

def random_invert_img(x, p=0.5):
  if  tf.random.uniform([]) < p:
    x = (255-x)
  else:
    x
  return x
def random_invert(factor=0.5):
  return layers.Lambda(lambda x: random_invert_img(x, factor))

random_invert = random_invert()
plt.figure(figsize=(10, 10))
for i in range(9):
  augmented_image = random_invert(image)
  ax = plt.subplot(3, 3, i + 1)
  plt.imshow(augmented_image[0].numpy().astype("uint8"))
  plt.axis("off")

PNG

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

class RandomInvert(layers.Layer):
  def __init__(self, factor=0.5, **kwargs):
    super().__init__(**kwargs)
    self.factor = factor

  def call(self, x):
    return random_invert_img(x)
_ = plt.imshow(RandomInvert()(image)[0])

PNG

Оба эти слоя можно использовать, как описано в вариантах 1 и 2 выше.

Использование tf.image

Вышеуказанные layers.preprocessing утилиты удобны. Для более точного управления, вы можете написать свои собственные аугментации данные трубопроводы или слои с использованием tf.data и tf.image . Вы также можете проверить TensorFlow Addons Изображения: Операции и TensorFlow I / O: Цветовое пространство Конверсия

Поскольку набор данных цветов ранее был настроен с добавлением данных, давайте повторно импортируем его, чтобы начать все заново.

(train_ds, val_ds, test_ds), metadata = tfds.load(
    'tf_flowers',
    split=['train[:80%]', 'train[80%:90%]', 'train[90%:]'],
    with_info=True,
    as_supervised=True,
)

Получить изображение для работы.

image, label = next(iter(train_ds))
_ = plt.imshow(image)
_ = plt.title(get_label_name(label))
2021-07-31 01:21:08.829596: W tensorflow/core/kernels/data/cache_dataset_ops.cc:768] The calling iterator did not fully read the dataset being cached. In order to avoid unexpected truncation of the dataset, the partially cached contents of the dataset  will be discarded. This can happen if you have an input pipeline similar to `dataset.cache().take(k).repeat()`. You should use `dataset.take(k).cache().repeat()` instead.

PNG

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

def visualize(original, augmented):
  fig = plt.figure()
  plt.subplot(1,2,1)
  plt.title('Original image')
  plt.imshow(original)

  plt.subplot(1,2,2)
  plt.title('Augmented image')
  plt.imshow(augmented)

Увеличение данных

Переворачивание изображения

Отразите изображение по вертикали или горизонтали.

flipped = tf.image.flip_left_right(image)
visualize(image, flipped)

PNG

Оттенки серого изображения

Оттенки серого изображения.

grayscaled = tf.image.rgb_to_grayscale(image)
visualize(image, tf.squeeze(grayscaled))
_ = plt.colorbar()

PNG

Насыщите изображение

Насыщите изображение, указав коэффициент насыщенности.

saturated = tf.image.adjust_saturation(image, 3)
visualize(image, saturated)

PNG

Изменить яркость изображения

Измените яркость изображения, указав коэффициент яркости.

bright = tf.image.adjust_brightness(image, 0.4)
visualize(image, bright)

PNG

Обрезать изображение по центру

Обрежьте изображение от центра до желаемой части изображения.

cropped = tf.image.central_crop(image, central_fraction=0.5)
visualize(image,cropped)

PNG

Повернуть изображение

Поверните изображение на 90 градусов.

rotated = tf.image.rot90(image)
visualize(image, rotated)

PNG

Случайные преобразования

Применение случайных преобразований к изображениям может еще больше помочь обобщить и расширить набор данных. Текущий tf.image API обеспечивает 8 таких операций случайного изображения (OPS):

Эти операции случайного изображения являются чисто функциональными: вывод зависит только от ввода. Это упрощает их использование в высокопроизводительных детерминированных конвейерах ввода. Они требуют seed значения вводить каждый шаг. Учитывая то же seed , они возвращают те же результаты , независимо от того , сколько раз они называются.

В следующих разделах вы:

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

Случайное изменение яркости изображения

Случайным образом изменить яркость image , обеспечивая коэффициент яркости и seed . Коэффициент яркости выбирается случайным образом в диапазоне [-max_delta, max_delta) и связан с данной seed .

for i in range(3):
  seed = (i, 0)  # tuple of size (2,)
  stateless_random_brightness = tf.image.stateless_random_brightness(
      image, max_delta=0.95, seed=seed)
  visualize(image, stateless_random_brightness)

PNG

PNG

PNG

Произвольно изменить контраст изображения

Случайным образом изменить контрастность image , обеспечивая диапазон контрастности и seed . Контрастность выбирается случайным образом в интервале [lower, upper] и связана с данной seed .

for i in range(3):
  seed = (i, 0)  # tuple of size (2,)
  stateless_random_contrast = tf.image.stateless_random_contrast(
      image, lower=0.1, upper=0.9, seed=seed)
  visualize(image, stateless_random_contrast)

PNG

PNG

PNG

Произвольно обрезать изображение

Случайный обрезать image , обеспечивая целевой size и seed . Часть , которая получает обрезана из image находится на случайно выбранное смещение и связано с данным seed .

for i in range(3):
  seed = (i, 0)  # tuple of size (2,)
  stateless_random_crop = tf.image.stateless_random_crop(
      image, size=[210, 300, 3], seed=seed)
  visualize(image, stateless_random_crop)

PNG

PNG

PNG

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

Давайте сначала снова загрузим набор данных изображений, если они были изменены в предыдущих разделах.

(train_datasets, val_ds, test_ds), metadata = tfds.load(
    'tf_flowers',
    split=['train[:80%]', 'train[80%:90%]', 'train[90%:]'],
    with_info=True,
    as_supervised=True,
)

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

def resize_and_rescale(image, label):
  image = tf.cast(image, tf.float32)
  image = tf.image.resize(image, [IMG_SIZE, IMG_SIZE])
  image = (image / 255.0)
  return image, label

Давайте также определить augment функцию , которая может применить случайные преобразования к изображениям. Эта функция будет использована в наборе данных на следующем шаге.

def augment(image_label, seed):
  image, label = image_label
  image, label = resize_and_rescale(image, label)
  image = tf.image.resize_with_crop_or_pad(image, IMG_SIZE + 6, IMG_SIZE + 6)
  # Make a new seed
  new_seed = tf.random.experimental.stateless_split(seed, num=1)[0, :]
  # Random crop back to the original size
  image = tf.image.stateless_random_crop(
      image, size=[IMG_SIZE, IMG_SIZE, 3], seed=seed)
  # Random brightness
  image = tf.image.stateless_random_brightness(
      image, max_delta=0.5, seed=new_seed)
  image = tf.clip_by_value(image, 0, 1)
  return image, label

Вариант 1: Использование tf.data.experimental.Counter()

Создание tf.data.experimental.Counter() объект (назовем его counter ) и zip набор данных с (counter, counter) . Это будет гарантировать , что каждое изображение в наборе данных получает ассоциируется с уникальным значением (формы (2,) ) на основе counter , который впоследствии может получить перешедший в augment функцию в качестве seed значения для случайных преобразований.

# Create counter and zip together with train dataset
counter = tf.data.experimental.Counter()
train_ds = tf.data.Dataset.zip((train_datasets, (counter, counter)))

Сопоставьте augment функцию обучающего набора данных.

train_ds = (
    train_ds
    .shuffle(1000)
    .map(augment, num_parallel_calls=AUTOTUNE)
    .batch(batch_size)
    .prefetch(AUTOTUNE)
)
val_ds = (
    val_ds
    .map(resize_and_rescale, num_parallel_calls=AUTOTUNE)
    .batch(batch_size)
    .prefetch(AUTOTUNE)
)
test_ds = (
    test_ds
    .map(resize_and_rescale, num_parallel_calls=AUTOTUNE)
    .batch(batch_size)
    .prefetch(AUTOTUNE)
)

Вариант 2: Использование tf.random.Generator

Создать tf.random.Generator объект с intial seed значения. Вызывающее make_seeds работать на тот же объект , генератор возвращает новое, уникальное seed значение всегда. Определим функцию - оболочку , что 1) вызывает make_seeds функцию и 2) проходит вновь сгенерированный seed значение в augment функции для случайных преобразований.

# Create a generator
rng = tf.random.Generator.from_seed(123, alg='philox')
# A wrapper function for updating seeds
def f(x, y):
  seed = rng.make_seeds(2)[0]
  image, label = augment((x, y), seed)
  return image, label

Карта функции упаковщика f к обучающему набору данным.

train_ds = (
    train_datasets
    .shuffle(1000)
    .map(f, num_parallel_calls=AUTOTUNE)
    .batch(batch_size)
    .prefetch(AUTOTUNE)
)
val_ds = (
    val_ds
    .map(resize_and_rescale, num_parallel_calls=AUTOTUNE)
    .batch(batch_size)
    .prefetch(AUTOTUNE)
)
test_ds = (
    test_ds
    .map(resize_and_rescale, num_parallel_calls=AUTOTUNE)
    .batch(batch_size)
    .prefetch(AUTOTUNE)
)

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

Следующие шаги

Это руководство продемонстрировало увеличение данных с использованием Keras Препроцессирования слоев и tf.image . Чтобы узнать , как включить Preprocessing слоев внутри вашей модели, см классификации изображений учебника. Вы также можете быть заинтересованы в изучении , как предобработки слои могут помочь классифицировать текст, как показано в Основном тексте классификации учебник. Вы можете узнать больше о tf.data в этом руководстве , и вы можете узнать , как настроить входные трубопроводы для выполнения здесь .