TensorFlow 2.0 Keras:如何为 TensorBoard 编写图像摘要

and*_*ndy 7 python summary keras tensorflow tensorboard

我正在尝试使用 TensorFlow 2.0 设置图像识别 CNN。为了能够分析我的图像增强,我想在张量板中查看我输入网络的图像。

不幸的是,我不知道如何使用 TensorFlow 2.0 和 Keras 来做到这一点。我也没有真正找到这方面的文档。

为简单起见,我展示了一个 MNIST 示例的代码。我将如何在此处添加图像摘要?

import tensorflow as tf
(x_train, y_train), _ = tf.keras.datasets.mnist.load_data()

def scale(image, label):
    return tf.cast(image, tf.float32) / 255.0, label

def augment(image, label):
    return image, label  # do nothing atm

dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train))
dataset = dataset.map(scale).map(augment).batch(32)

model = tf.keras.models.Sequential([
    tf.keras.layers.Flatten(input_shape=(28, 28)),
    tf.keras.layers.Dense(128, activation='relu'),
    tf.keras.layers.Dropout(0.2),
    tf.keras.layers.Dense(10, activation='softmax')
])

model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
model.fit(dataset, epochs=5, callbacks=[tf.keras.callbacks.TensorBoard(log_dir='D:\\tmp\\test')])
Run Code Online (Sandbox Code Playgroud)

Szy*_*zke 6

除了为您的问题提供答案外,我将使代码更像TF2.0。如果您有任何问题/需要澄清,请在下方发表评论。

1.加载数据

我建议使用Tensorflow Datasets库。如果可以在一行中完成,则绝对不需要加载数据numpy并将其转换为tf.data.Dataset

import tensorflow_datasets as tfds

dataset = tfds.load("mnist", as_supervised=True, split=tfds.Split.TRAIN)
Run Code Online (Sandbox Code Playgroud)

上面的行只会返回TRAINsplit(在此处阅读有关这些内容的更多信息)。

2. 定义增强和总结

为了保存图像,必须在每次传递中保留tf.summary.SummaryWriter对象。

我用__call__方法创建了一个方便的包装类,以便于使用tf.data.Datasetmap功能:

import tensorflow as tf

class ExampleAugmentation:
    def __init__(self, logdir: str, max_images: int, name: str):
        self.file_writer = tf.summary.create_file_writer(logdir)
        self.max_images: int = max_images
        self.name: str = name
        self._counter: int = 0

    def __call__(self, image, label):
        augmented_image = tf.image.random_flip_left_right(
            tf.image.random_flip_up_down(image)
        )
        with self.file_writer.as_default():
            tf.summary.image(
                self.name,
                augmented_image,
                step=self._counter,
                max_outputs=self.max_images,
            )

        self._counter += 1
        return augmented_image, label
Run Code Online (Sandbox Code Playgroud)

name将是图像的每个部分将被保存的名称。您可能会问哪个部分 - 由max_outputs.

imagein __call__will have shape (32, 28, 28, 1),其中第一个维度是批次,第二个宽度,第三个高度和最后一个通道(在 MNIST 的情况下只有一个,但在tf.image增强中需要这个维度)。此外,假设max_outputs被指定为4。在这种情况下,将只保存批次中的 4 张第一张图像。默认值为3,因此您可以将其设置为BATCH_SIZE保存每个图像。

在 中Tensorboard,每个图像将是一个单独的样本,您可以在最后对其进行迭代。

_counter需要这样图像就不会被覆盖(我认为,不太确定,其他人的澄清会很好)。

重要提示:您可能希望将此类重命名为类似ImageSaver在进行更严肃的业务并将扩充移动到单独的函子/lambda 函数时之类的名称。我想这足以用于演示目的。

3. 设置全局变量

请不要混合使用函数声明、全局变量、数据加载等(如加载数据和创建函数)。我知道TF1.0鼓励这种类型的编程,但他们正试图摆脱它,你可能想跟上潮流。

下面我定义了一些全局变量,这些变量将在接下来的部分中使用,我想这是不言自明的:

BATCH_SIZE = 32
DATASET_SIZE = 60000
EPOCHS = 5

LOG_DIR = "/logs/images"
AUGMENTATION = ExampleAugmentation(LOG_DIR, max_images=4, name="Images")
Run Code Online (Sandbox Code Playgroud)

4. 数据集扩充

类似于你的,但有一点点扭曲:

dataset = (
    dataset.map(
        lambda image, label: (
            tf.image.convert_image_dtype(image, dtype=tf.float32),
            label,
        )
    )
    .batch(BATCH_SIZE)
    .map(AUGMENTATION)
    .repeat(EPOCHS)
)
Run Code Online (Sandbox Code Playgroud)
  • repeat 需要,因为加载的数据集是一个生成器
  • tf.image.convert_image_dtype- 比显式tf.cast混合除法更好、更易读的选项255(并确保正确的图像格式)
  • 在增强之前进行批处理只是为了演示

5.定义模型,编译,训练

几乎就像你在你的例子中所做的那样,但我提供了额外的steps_per_epoch,所以fit知道有多少批次构成一个纪元:

model = tf.keras.models.Sequential(
    [
        tf.keras.layers.Flatten(input_shape=(28, 28, 1)),
        tf.keras.layers.Dense(128, activation="relu"),
        tf.keras.layers.Dropout(0.2),
        tf.keras.layers.Dense(10, activation="softmax"),
    ]
)

model.compile(
    optimizer="adam", loss="sparse_categorical_crossentropy", metrics=["accuracy"]
)
model.fit(
    dataset,
    epochs=EPOCHS,
    steps_per_epoch=DATASET_SIZE // BATCH_SIZE,
    callbacks=[tf.keras.callbacks.TensorBoard(log_dir=LOG_DIR)],
)
Run Code Online (Sandbox Code Playgroud)

除了我认为的之外,没有太多解释。

6. 运行 Tensorboard

由于TF2.0可以在 colab 中使用 完成此操作%tensorboard --logdir /logs/images,因此只想为可能访问此问题的其他人添加此内容。随心所欲地做,反正你肯定知道怎么做。

图像应该在里面,IMAGES并且每个由 命名的样本name提供给AUGMENTATION对象。

7. 完整代码(让大家的生活更轻松)

import tensorflow as tf
import tensorflow_datasets as tfds


class ExampleAugmentation:
    def __init__(self, logdir: str, max_images: int, name: str):
        self.file_writer = tf.summary.create_file_writer(logdir)
        self.max_images: int = max_images
        self.name: str = name
        self._counter: int = 0

    def __call__(self, image, label):
        augmented_image = tf.image.random_flip_left_right(
            tf.image.random_flip_up_down(image)
        )
        with self.file_writer.as_default():
            tf.summary.image(
                self.name,
                augmented_image,
                step=self._counter,
                max_outputs=self.max_images,
            )

        self._counter += 1
        return augmented_image, label


if __name__ == "__main__":

    # Global settings

    BATCH_SIZE = 32
    DATASET_SIZE = 60000
    EPOCHS = 5

    LOG_DIR = "/logs/images"
    AUGMENTATION = ExampleAugmentation(LOG_DIR, max_images=4, name="Images")

    # Dataset

    dataset = tfds.load("mnist", as_supervised=True, split=tfds.Split.TRAIN)

    dataset = (
        dataset.map(
            lambda image, label: (
                tf.image.convert_image_dtype(image, dtype=tf.float32),
                label,
            )
        )
        .batch(BATCH_SIZE)
        .map(AUGMENTATION)
        .repeat(EPOCHS)
    )

    # Model and training

    model = tf.keras.models.Sequential(
        [
            tf.keras.layers.Flatten(input_shape=(28, 28, 1)),
            tf.keras.layers.Dense(128, activation="relu"),
            tf.keras.layers.Dropout(0.2),
            tf.keras.layers.Dense(10, activation="softmax"),
        ]
    )

    model.compile(
        optimizer="adam", loss="sparse_categorical_crossentropy", metrics=["accuracy"]
    )
    model.fit(
        dataset,
        epochs=EPOCHS,
        steps_per_epoch=DATASET_SIZE // BATCH_SIZE,
        callbacks=[tf.keras.callbacks.TensorBoard(log_dir=LOG_DIR)],
    )
Run Code Online (Sandbox Code Playgroud)