VGG16 预处理数据集生成器到数据集映射

VGG16 preprocessing dataset generator to dataset mapping

我有一个使用 Keras/tensorflow 实现的 VGG16 模型。

当我调用 model.fit 时,我传入了一个数据生成器。生成器执行 VGGNet 所需的转换:

  1. 使用 vgg16.preprocess_input
  2. 预处理图像
  3. 通过to_categorical
  4. 将标签转换为单热向量

发电机可以在下面看到并且可以工作。不幸的是,由于有多个纪元,我必须设置 dataset.repeat(-1)(无限重复),这样生成器就不会 运行 了。这反过来又需要一个人通过 steps_per_epoch 以便完成给定的训练迭代。正如您可能在想的那样,这很脆弱,(取决于已知的数据集基数)!

我决定最好预先使用 Dataset.map 对训练 Dataset 进行一次预处理。但是,我正在努力构建映射函数,似乎 to_categorical 不适用于 tf.Tensor。下面是我现在拥有的,但我不确定是否存在潜在错误。

如何正确地将下面的 Dataset 生成器转换为 Dataset.map 函数?


当前Dataset发电机

这是通过 Python 3.8 和 tensorflow==2.4.4 实现的(并且已知可以工作)。

from typing import Iterable, Tuple

import numpy as np
import tensorflow as tf


def make_vgg_preprocessing_generator(
    dataset: tf.data.Dataset, num_repeat: int = -1
) -> Iterable[Tuple[tf.Tensor, np.ndarray]]:
    num_classes = len(dataset.class_names)
    for batch_images, batch_labels in dataset.repeat(num_repeat):
        pre_images = tf.keras.applications.vgg16.preprocess_input(batch_images)
        pre_labels = tf.keras.utils.to_categorical(batch_labels, num_classes)
        yield pre_images, pre_labels


train_ds: tf.data.Dataset  # Not provided in this sample
model.fit(
    make_vgg_preprocessing_generator(train_ds)
    epochs=10,
    steps_per_epoch=10,  # Required since default num_repeat is indefinitely
)

Dataset.map函数

这是我希望改进的当前翻译。

def vgg_preprocess_dataset(dataset: tf.data.Dataset) -> tf.data.Dataset:
    num_classes = len(dataset.class_names)

    def _preprocess(x: tf.Tensor, y: tf.Tensor) -> Tuple[tf.Tensor, tf.Tensor]:
        pre_x = tf.keras.applications.vgg16.preprocess_input(x)
        pre_y = tf.one_hot(y, depth=num_classes)
        return pre_x, pre_y

    return dataset.map(_preprocess)

是的,您走对了!您需要将 to_categorical 替换为 tf.one_hot,就像您所做的那样,因为 tf.one_hot 专门用于张量,并且专为这种情况而设计。接下来,您可能想试用其他一些 tf.data.Dataset 方法 here 并将它们添加到您的管道中。现在,您的批量大小将是一个样本,un-shuffled。您可能会执行的一些其他处理的示例:

def vgg_preprocess_dataset(dataset: tf.data.Dataset, batch_size=32, shuffle_buffer=1000) -> tf.data.Dataset:
    num_classes = len(dataset.class_names)

    def _preprocess(x: tf.Tensor, y: tf.Tensor):
        pre_x = tf.keras.applications.vgg16.preprocess_input(x)
        pre_y = tf.one_hot(y, depth=num_classes)
        # pre_y = to_categorical(y, num_classes)
        return pre_x, pre_y

    # bigger buffer is better but slower
    dataset = dataset.shuffle(shuffle_buffer) 

    # do your mapping after shuffle
    dataset = dataset.map(_preprocess)

    # next batch it
    dataset = dataset.batch(batch_size)
    
    # this allows your CPU to fetch the next batch (do the above shuffling, mapping, etc) during the 
    # current GPU pass, so that the GPU has minimal downtime
    dataset = dataset.prefetch(2)

    return dataset

ds = vgg_preprocess_dataset(ds)

# and you just pass it right to fit!
model.fit(ds)