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

Posted

技术标签:

【中文标题】TensorFlow 2.0 Keras:如何为 TensorBoard 编写图像摘要【英文标题】:TensorFlow 2.0 Keras: How to write image summaries for TensorBoard 【发布时间】:2019-08-20 14:56:27 【问题描述】:

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

不幸的是,我不知道如何使用 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')])

【问题讨论】:

您发布的代码是否适用于旧版本的 TF ? 是的,代码也运行在 TF 1.13.1 上。但是,图像摘要不在代码中,因为我不知道如何添加它。 您可以尝试将this 和this 放在一起以获得可能的解决方案。但是当我尝试使用 tf 1.x 时,它并不简单。 这里是关于 Tensorboard 与 TF 2.0 一起用于图像数据的文档:Link @TobiasM.:我不知道如何使用回调来创建模型输入图像的摘要。你能说得更详细些吗?文档没有给出这样的例子。 【参考方案1】:

除了回答您的问题 我会让代码更像TF2.0-like。如果您有任何问题/需要澄清,请在下方发表评论。

1。加载数据

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

import tensorflow_datasets as tfds

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

上面的行只会返回TRAIN split(阅读更多关于here的信息)。

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

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

__call__ 中说image 将具有(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")

4。数据集扩充

与您的相似,但略有不同:

dataset = (
    dataset.map(
        lambda image, label: (
            tf.image.convert_image_dtype(image, dtype=tf.float32),
            label,
        )
    )
    .batch(BATCH_SIZE)
    .map(AUGMENTATION)
    .repeat(EPOCHS)
)
需要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)],
)

除了我想的,没什么好解释的。

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)],
    )

【讨论】:

推荐安装tensorflow_datasets还是和使用tensorflow模块提供的tf.keras.datasets.mnist.load_data()一样? tensorflow_datasets 返回tf.data.Dataset 对象,它允许您更轻松地预处理数据。 numpy 不是面向 TensorFlow 的。您不能将mapcache 之类的东西与普通数组一起使用,所以我至少会坚持使用tf.data.Dataset 类。 一个错字:“... as to keep tf.summar(y).SummaryWriter ...” 感谢您的示例,非常有帮助。当我运行它时,所有图像都显示在第 0 步中,并且运行更多的时期不会产生更多的图像记录到张量板上。计数器没有增加吗? @AlexShepard 它应该是并且是我上次检查 IIRC 的时候。如果你知道为什么请评论这个答案,我有空时会检查它。【参考方案2】:

你可以这样做将输入图像添加到张量板

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


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


file_writer = tf.summary.create_file_writer(logdir + "/images")


def plot_to_image(figure):
    buf = io.BytesIO()
    plt.savefig(buf, format='png')
    plt.close(figure)
    buf.seek(0)
    image = tf.image.decode_png(buf.getvalue(), channels=4)
    image = tf.expand_dims(image, 0)
    return image


def image_grid():
    """Return a 5x5 grid of the MNIST images as a matplotlib figure."""
    # Create a figure to contain the plot.
    figure = plt.figure(figsize=(10, 10))
    for i in range(25):
        # Start next subplot.
        plt.subplot(5, 5, i + 1, title=str(y_train[i]))
        plt.xticks([])
        plt.yticks([])
        plt.grid(False)
        image, _ = scale(x_train[i], y_train[i])
        plt.imshow(x_train[i], cmap=plt.cm.binary)

    return figure


# Prepare the plot
figure = image_grid()
# Convert to image and log
with file_writer.as_default():
    tf.summary.image("Training data", plot_to_image(figure), step=0)

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=logdir)])

【讨论】:

对不起,但这根本不能回答我的问题。我问我如何显示输入到我的网络中的输入图像。我已经找到了这个例子,但它只显示了一个任意的其他图像,而不是输入网络的图像。 我已更新代码以在 tensorboard 中添加摘要 感谢您的努力!但我不想简单地将前 25 张图像写入 tensorboard。相反,我想在训练期间编写摘要,这样我就可以看到输入网络的实际图像的示例。例如。如果我应用数据增强,我想跟踪网络在整个训练过程中实际看到的图像。使用标准张量流,这不是问题。我想知道如何使用 keras 和 TF 2.0 做到这一点。

以上是关于TensorFlow 2.0 Keras:如何为 TensorBoard 编写图像摘要的主要内容,如果未能解决你的问题,请参考以下文章

如何为 keras 模型使用 tensorflow 自定义损失?

Keras 2.3.0 发布:支持TensorFlow 2.0!!!!!

Tensorflow 2.0 Keras 的训练速度比 2.0 Estimator 慢 4 倍

翻译: Keras 标准化:TensorFlow 2.0 中高级 API 指南

Keras 中的像素加权损失函数 - TensorFlow 2.0

Keras TensorFlow 2.0 精华资源