簡體   English   中英

TensorFlow 2.0 Keras:如何為 TensorBoard 編寫圖像摘要

[英]TensorFlow 2.0 Keras: How to write image summaries for 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')])

除了為您的問題提供答案外,我將使代碼更像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)

上面的行只會返回TRAIN split(在此處閱讀有關這些內容的更多信息)。

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時,您可能希望將此類重命名為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)],
    )

你可以做這樣的事情來將輸入圖像添加到張量板

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM