"""Simple example on how to log scalars and images to tensorboard without tensor ops. | |
License: BSD License 2.0 | |
""" | |
__author__ = "Michael Gygli" | |
import tensorflow as tf | |
from StringIO import StringIO | |
import matplotlib.pyplot as plt | |
import numpy as np | |
class Logger(object): | |
"""Logging in tensorboard without tensorflow ops.""" | |
def __init__(self, log_dir): | |
"""Creates a summary writer logging to log_dir.""" | |
self.writer = tf.summary.FileWriter(log_dir) | |
def log_scalar(self, tag, value, step): | |
"""Log a scalar variable. | |
Parameter | |
---------- | |
tag : basestring | |
Name of the scalar | |
value | |
step : int | |
training iteration | |
""" | |
summary = tf.Summary(value=[tf.Summary.Value(tag=tag, | |
simple_value=value)]) | |
self.writer.add_summary(summary, step) | |
def log_images(self, tag, images, step): | |
"""Logs a list of images.""" | |
im_summaries = [] | |
for nr, img in enumerate(images): | |
# Write the image to a string | |
s = StringIO() | |
plt.imsave(s, img, format='png') | |
# Create an Image object | |
img_sum = tf.Summary.Image(encoded_image_string=s.getvalue(), | |
height=img.shape[0], | |
width=img.shape[1]) | |
# Create a Summary value | |
im_summaries.append(tf.Summary.Value(tag='%s/%d' % (tag, nr), | |
image=img_sum)) | |
# Create and write Summary | |
summary = tf.Summary(value=im_summaries) | |
self.writer.add_summary(summary, step) | |
def log_histogram(self, tag, values, step, bins=1000): | |
"""Logs the histogram of a list/vector of values.""" | |
# Convert to a numpy array | |
values = np.array(values) | |
# Create histogram using numpy | |
counts, bin_edges = np.histogram(values, bins=bins) | |
# Fill fields of histogram proto | |
hist = tf.HistogramProto() | |
hist.min = float(np.min(values)) | |
hist.max = float(np.max(values)) | |
hist.num = int(np.prod(values.shape)) | |
hist.sum = float(np.sum(values)) | |
hist.sum_squares = float(np.sum(values**2)) | |
# Requires equal number as bins, where the first goes from -DBL_MAX to bin_edges[1] | |
# See https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/framework/summary.proto#L30 | |
# Thus, we drop the start of the first bin | |
bin_edges = bin_edges[1:] | |
# Add bin edges and counts | |
for edge in bin_edges: | |
hist.bucket_limit.append(edge) | |
for c in counts: | |
hist.bucket.append(c) | |
# Create and write Summary | |
summary = tf.Summary(value=[tf.Summary.Value(tag=tag, histo=hist)]) | |
self.writer.add_summary(summary, step) | |
self.writer.flush() |
Could you include a license?
Thanks or the code it works great, found a small bug though...
If values
is a list in log_histogram
then because list has no attribute shape the program crashes you should write values=np.array(values)
in the beginning of the function to avoid these issue.
@nasimrahaman: Added copyleft
@MiguelMonteiro fixed
Thank you for such a helpful tool ! I've been confused about how to write what I want into tensorboard
Thanks!
s = StringIO()
plt.imsave(s, img, format='png')
doesn't work because pyplot.imsave() expects a file path
doesn't work because pyplot.imsave() expects a file path
That's not true, at least for more recent versions, where it can be a "file-like" object, see:
https://matplotlib.org/api/_as_gen/matplotlib.pyplot.imsave.html
@adizhol: Maybe your version is outdated?
Thanks!
It works, thx!
Great code, thanks
excellent work, and I have to use BytesIO
instead of StringIO
for python 3.5.5, matplotlib 2.2.2
Cool! Thanks
@gyglim Maybe it is worth to add flush
method? I think for newcomers to tensorboard it would be a good hint that they should consider flushing.
Thanks a lot, perfect!
Thanks a lot Michael, this helped me today :).
BTW: I had to use a Bytes-like object (BytesIO) instead of a StringIO for it to work (TF 1.13, Matplotlib 3.0.3).
Cheers !
Are you sure this works for multiple scalars? Is flush needed? I see points showing up in tboard but they do not seem to update often enough?
Also anyone know what to put in a step to get the scalars to look similar to the keras callback scalars?
text summary can be a new feature
Anyone knows how to construct text summary?
What does 'License: Copyleft' mean?
Can you provide more detailed license information, such as GPL, LGPL or MPL?
Thanks!!
Added BSD License 2.0
@gyglim Thanks!! :)
To add text summary
from tensorboard.plugins.text import metadata
class Logger(object):
"""Logging in tensorboard without tensorflow ops."""
def __init__(self, log_dir):
"""Creates a summary writer logging to log_dir."""
self.writer = tf.summary.FileWriter(log_dir)
def log_text(self, tag, value, step):
"""Log string or 2D string tables. """
summary_metadata = metadata.create_summary_metadata(
display_name="text",
description="Text Summary")
summary_metadata = tf.SummaryMetadata.FromString(
summary_metadata.SerializeToString())
tensor = tf.make_tensor_proto(value, dtype=tf.string)
summary = tf.Summary(value=[tf.Summary.Value(
tag=tag,
metadata=summary_metadata,
tensor=tensor
)])
self.writer.add_summary(summary, step)
self.writer.flush()
Example usage:
logger = Logger('/tmp/test')
logger.log_text(tag="string", value="test", step=0)
logger.log_text(tag="table", value=[["r0c0", "r0c1"], ["r1c0", "r1c1"]], step=0)
I get some incompatibility errors when running on tensorflow 2.0. Any plans to update this -very useful- code?
Wonderful solution:) Why flush
is only in log_histogram
?
Very minor tweaks for xrz000's text summary for TF2 Compatibility:
class Logger(object):
"""Logging in tensorboard without tensorflow ops."""
def __init__(self, log_dir):
"""Creates a summary writer logging to log_dir."""
self.writer = tf.compat.v1.summary.FileWriter(log_dir)
def log_text(self, tag, value, step):
"""Log string or 2D string tables. """
summary_metadata = metadata.create_summary_metadata(
display_name="text",
description="Text Summary")
summary_metadata = tf.compat.v1.SummaryMetadata.FromString(
summary_metadata.SerializeToString())
tensor = tf.make_tensor_proto(value, dtype=tf.string)
summary = tf.compat.v1.Summary(value=[tf.compat.v1.Summary.Value(
tag=tag,
metadata=summary_metadata,
tensor=tensor
)])
self.writer.add_summary(summary, step)
self.writer.flush()
Example Usage:
with tf.compat.v1.Graph().as_default():
logger.log_text(tag="string", value="test", step=0)
logger.log_text(tag="table", value=[["r0c0", "r0c1"], ["r1c0", "r1c1"]], step=0)
在张力流 2.0 上运行时,我遇到一些不兼容的错误。有什么计划来更新这个 - 非常有用的代码?
Hello, has your problem been solved? Which version should be configured to be compatible
tested it and every things worked great with tf 2.4
Any chance you can add a function for text?