Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Logging to tensorboard without tensorflow operations. Uses manually generated summaries instead of summary ops
"""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()
@gyglim

This comment has been minimized.

Copy link
Owner Author

@gyglim gyglim commented Jan 4, 2017

Example usage:

    import numpy as np
    logger = Logger('/tmp/test')
    for i in range(1000):
        l.log_histogram('test_hist',np.random.rand(50)*(i+1),i)

Then simpy run tensorboard:

    tensorboard --logdir /tmp/test

Gives the following histogram on tensorboard (and the corresponding distribution)
image

@MadcowD

This comment has been minimized.

Copy link

@MadcowD MadcowD commented Jan 11, 2017

This is great!

@el3ment

This comment has been minimized.

Copy link

@el3ment el3ment commented May 20, 2017

Any chance you can add a function for text?

@nasimrahaman

This comment has been minimized.

Copy link

@nasimrahaman nasimrahaman commented Jun 27, 2017

Could you include a license?

@MiguelMonteiro

This comment has been minimized.

Copy link

@MiguelMonteiro MiguelMonteiro commented Jul 18, 2017

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.

@gyglim

This comment has been minimized.

Copy link
Owner Author

@gyglim gyglim commented Oct 3, 2017

@nasimrahaman: Added copyleft
@MiguelMonteiro fixed

@Yikhan

This comment has been minimized.

Copy link

@Yikhan Yikhan commented Oct 27, 2017

Thank you for such a helpful tool ! I've been confused about how to write what I want into tensorboard

@dc0d32

This comment has been minimized.

Copy link

@dc0d32 dc0d32 commented Dec 21, 2017

Thanks!

@adizhol

This comment has been minimized.

Copy link

@adizhol adizhol commented Apr 3, 2018

            s = StringIO()
            plt.imsave(s, img, format='png')

doesn't work because pyplot.imsave() expects a file path

@gyglim

This comment has been minimized.

Copy link
Owner Author

@gyglim gyglim commented Apr 12, 2018

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?

@yongjun823

This comment has been minimized.

Copy link

@yongjun823 yongjun823 commented Apr 13, 2018

Thanks!

@yongzhengqi

This comment has been minimized.

Copy link

@yongzhengqi yongzhengqi commented Jul 26, 2018

It works, thx!

@kukuruza

This comment has been minimized.

Copy link

@kukuruza kukuruza commented Aug 3, 2018

Great code, thanks

@raytroop

This comment has been minimized.

Copy link

@raytroop raytroop commented Sep 12, 2018

excellent work, and I have to use BytesIO instead of StringIO for python 3.5.5, matplotlib 2.2.2

@gavr97

This comment has been minimized.

Copy link

@gavr97 gavr97 commented Oct 10, 2018

Cool! Thanks

@gavr97

This comment has been minimized.

Copy link

@gavr97 gavr97 commented Oct 11, 2018

@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.

@jensdebruijn

This comment has been minimized.

Copy link

@jensdebruijn jensdebruijn commented Jan 29, 2019

Thanks a lot, perfect!

@kvanhoey

This comment has been minimized.

Copy link

@kvanhoey kvanhoey commented Mar 12, 2019

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 !

@cottrell

This comment has been minimized.

Copy link

@cottrell cottrell commented Mar 18, 2019

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?

@cottrell

This comment has been minimized.

Copy link

@cottrell cottrell commented Mar 19, 2019

Also anyone know what to put in a step to get the scalars to look similar to the keras callback scalars?

@shawnthu

This comment has been minimized.

Copy link

@shawnthu shawnthu commented Apr 3, 2019

text summary can be a new feature

@shawnthu

This comment has been minimized.

Copy link

@shawnthu shawnthu commented Apr 3, 2019

Anyone knows how to construct text summary?

@beanmilk

This comment has been minimized.

Copy link

@beanmilk beanmilk commented May 27, 2019

What does 'License: Copyleft' mean?
Can you provide more detailed license information, such as GPL, LGPL or MPL?

Thanks!!

@gyglim

This comment has been minimized.

Copy link
Owner Author

@gyglim gyglim commented Jul 22, 2019

Added BSD License 2.0

@beanmilk

This comment has been minimized.

Copy link

@beanmilk beanmilk commented Jul 23, 2019

@gyglim Thanks!! :)

@xrz000

This comment has been minimized.

Copy link

@xrz000 xrz000 commented Aug 28, 2019

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)

Tensorboard:
text

@hipoglucido

This comment has been minimized.

Copy link

@hipoglucido hipoglucido commented Apr 15, 2020

I get some incompatibility errors when running on tensorflow 2.0. Any plans to update this -very useful- code?

@pfk-beta

This comment has been minimized.

Copy link

@pfk-beta pfk-beta commented Aug 20, 2020

Wonderful solution:) Why flush is only in log_histogram?

@Zlo7

This comment has been minimized.

Copy link

@Zlo7 Zlo7 commented Oct 11, 2020

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)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment