TensorFlow 2.0 Keras:如何为 TensorBoard 编写图像摘要
TensorFlow 2.0 Keras: How to write image summaries for TensorBoard
我正在尝试使用 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')])
您可以像这样将输入图像添加到 tensorboard
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)])
除了回答您的问题
我会让代码更像 TF2.0
。如果您有任何 questions/need 说明,请 post 在下面发表评论。
1。加载数据
我建议使用 Tensorflow Datasets 库。如果可以在一行中完成,则完全没有必要在 numpy
中加载数据并将其转换为 tf.data.Dataset
:
import tensorflow_datasets as tfds
dataset = tfds.load("mnist", as_supervised=True, split=tfds.Split.TRAIN)
上面的行只会 return TRAIN
拆分(阅读更多关于 here 的信息)。
2。定义增强和总结
为了保存图像,必须在每一遍中保留 tf.summary.SummaryWriter 个对象。
我用 __call__
方法创建了一个方便的包装 class 以便与 tf.data.Dataset
的 map
功能一起使用:
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
是必需的,这样图像 就不会 被覆盖(我想,不太确定,最好能得到其他人的澄清)。
重要提示: 在进行更严肃的业务时,您可能希望将此 class 重命名为类似 ImageSaver
的名称,并将扩充移动到单独的 functors/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. 运行张量板
因为 TF2.0
可以使用 %tensorboard --logdir /logs/images
在 colab 中完成它,只是想为可能访问此问题的其他人添加此内容。随便你怎么弄,反正你肯定知道怎么做。
图像应在 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 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')])
您可以像这样将输入图像添加到 tensorboard
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)])
除了回答您的问题
我会让代码更像 TF2.0
。如果您有任何 questions/need 说明,请 post 在下面发表评论。
1。加载数据
我建议使用 Tensorflow Datasets 库。如果可以在一行中完成,则完全没有必要在 numpy
中加载数据并将其转换为 tf.data.Dataset
:
import tensorflow_datasets as tfds
dataset = tfds.load("mnist", as_supervised=True, split=tfds.Split.TRAIN)
上面的行只会 return TRAIN
拆分(阅读更多关于 here 的信息)。
2。定义增强和总结
为了保存图像,必须在每一遍中保留 tf.summary.SummaryWriter 个对象。
我用 __call__
方法创建了一个方便的包装 class 以便与 tf.data.Dataset
的 map
功能一起使用:
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
是必需的,这样图像 就不会 被覆盖(我想,不太确定,最好能得到其他人的澄清)。
重要提示: 在进行更严肃的业务时,您可能希望将此 class 重命名为类似 ImageSaver
的名称,并将扩充移动到单独的 functors/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. 运行张量板
因为 TF2.0
可以使用 %tensorboard --logdir /logs/images
在 colab 中完成它,只是想为可能访问此问题的其他人添加此内容。随便你怎么弄,反正你肯定知道怎么做。
图像应在 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)],
)