[英]TensorFlow 2.0 Keras: How to write image summaries for TensorBoard
I'm trying to setup an image recognition CNN with TensorFlow 2.0.我正在尝试使用 TensorFlow 2.0 设置图像识别 CNN。 To be able to analyze my image augmentation I'd like to see the images I feed into the network in tensorboard.为了能够分析我的图像增强,我想在张量板中查看我输入网络的图像。
Unfortunately, I cannot figure out, how to do this with TensorFlow 2.0 and Keras.不幸的是,我不知道如何使用 TensorFlow 2.0 和 Keras 来做到这一点。 I also didn't really find documentation on this.我也没有真正找到这方面的文档。
For simplicity, I'm showing the code of an MNIST example.为简单起见,我展示了一个 MNIST 示例的代码。 How would I add the image summary here?我将如何在此处添加图像摘要?
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')])
Except providing an answer to your question I will make the code more TF2.0
-like.除了为您的问题提供答案外,我将使代码更像TF2.0
。 If you have any questions/need clarification, please post a comment down below.如果您有任何问题/需要澄清,请在下方发表评论。
I would advise to use Tensorflow Datasets library.我建议使用Tensorflow Datasets库。 There is absolutely no need to load data in numpy
and transform it to tf.data.Dataset
if one can do it in a single line:如果可以在一行中完成,绝对不需要在numpy
加载数据并将其转换为tf.data.Dataset
:
import tensorflow_datasets as tfds
dataset = tfds.load("mnist", as_supervised=True, split=tfds.Split.TRAIN)
Line above will only return TRAIN
split (read more about those here ).上面的行只会返回TRAIN
split(在此处阅读有关这些内容的更多信息)。
In order to save images, one has to keep tf.summary.SummaryWriter object throughout each pass.为了保存图像,必须在每次传递中保留tf.summary.SummaryWriter对象。
I have created a convenient wrapping class with __call__
method for easy usage with tf.data.Dataset
's map
capabilities:我用__call__
方法创建了一个方便的包装类,以便与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
will be the name under which each part of images will be saved. name
将是图像的每个部分将被保存的名称。 Which part you may ask - the part defined by max_outputs
.您可能会问哪个部分 - max_outputs
定义的部分。
Say image
in __call__
will have shape (32, 28, 28, 1)
, where the first dimension is batch, second width, third height and last channels (in case of MNIST only onel but this dimension is needed in tf.image
augmentations).假设__call__
image
将具有形状(32, 28, 28, 1)
,其中第一个维度是批次、第二个宽度、第三个高度和最后一个通道(在 MNIST 的情况下只有一个,但在tf.image
增强中需要这个维度) . Furthermore, let's say max_outputs
is specified as 4
.此外,假设max_outputs
被指定为4
。 In this case, only 4 first images from batch will be saved.在这种情况下,将只保存批次中的 4 张第一张图像。 Default value is 3
, so you may set it as BATCH_SIZE
to save every image.默认值为3
,因此您可以将其设置为BATCH_SIZE
以保存每个图像。
In Tensorboard
, each image will be a separate sample over which you can iterate at the end.在Tensorboard
,每个图像都是一个单独的样本,您可以在最后对其进行迭代。
_counter
is needed so the images will not be overwritten (I think, not really sure, clarification from someone else would be nice). _counter
是必需的,因此图像不会被覆盖(我认为,不太确定,其他人的澄清会很好)。
Important: You may want to rename this class to something like ImageSaver
when doing more serious buisness and move augmentation to separate functors/lambda functions.重要提示:在进行更严肃的ImageSaver
时,您可能希望将此类重命名为ImageSaver
类的名称,并将扩充移至单独的函子/lambda 函数。 It suffices for presentation purposes I guess.我想这足以用于演示目的。
Please do not mix function declaration, global variables, data loading and others (like loading data and creating function afterwards).请不要混合使用函数声明、全局变量、数据加载等(如加载数据和创建函数)。 I know TF1.0
encouraged this type of programming but they are trying to get away from it and you might want to follow the trend.我知道TF1.0
鼓励这种类型的编程,但他们正试图摆脱它,你可能想跟上潮流。
Below I have defined some global variables which will be used throughout next parts, pretty self-explanatory I guess:下面我定义了一些全局变量,这些变量将在接下来的部分中使用,我想这是不言自明的:
BATCH_SIZE = 32
DATASET_SIZE = 60000
EPOCHS = 5
LOG_DIR = "/logs/images"
AUGMENTATION = ExampleAugmentation(LOG_DIR, max_images=4, name="Images")
Similar to yours but with a little twist:类似于你的,但有一点点扭曲:
dataset = (
dataset.map(
lambda image, label: (
tf.image.convert_image_dtype(image, dtype=tf.float32),
label,
)
)
.batch(BATCH_SIZE)
.map(AUGMENTATION)
.repeat(EPOCHS)
)
repeat
is needed as the loaded dataset is a generator需要repeat
,因为加载的数据集是一个生成器tf.image.convert_image_dtype
- better and more readable option than explicit tf.cast
mixed with division by 255
(and ensures proper image format) tf.image.convert_image_dtype
- 比显式tf.cast
与除以255
混合更好、更易读的选项(并确保正确的图像格式)Almost as you did in your example, but I have provided additional steps_per_epoch
, so fit
knows how many batches constitute an epoch:几乎就像你在你的例子中所做的那样,但我提供了额外的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)],
)
Not much to explain other than that I think.除了我认为的之外,没有太多解释。
Since TF2.0
one can do it inside colab using %tensorboard --logdir /logs/images
, just wanted to add this for others who may visit this issue.由于TF2.0
可以在 colab 中使用%tensorboard --logdir /logs/images
,因此只想为可能访问此问题的其他人添加此内容。 Do it however you like, anyways you know how to do it for sure.随心所欲地做,反正你肯定知道怎么做。
Images should be inside IMAGES
and each sample named by name
provided to AUGMENTATION
object.图像应该在IMAGES
并且每个以name
命名的样本都提供给AUGMENTATION
对象。
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)],
)
You could do something like this to add input image to 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)])
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.