def text(name, data, step, description=None): """Write a text summary. Arguments: name: A name for this summary. The summary tag used for TensorBoard will be this name prefixed by any active name scopes. data: A UTF-8 string tensor value. step: Required `int64`-castable monotonic step value. description: Optional long-form description for this summary, as a constant `str`. Markdown is supported. Defaults to empty. Returns: True on success, or false if no summary was emitted because no default summary writer was available. """ # TODO(nickfelt): remove on-demand imports once dep situation is fixed. from tensorboard import compat tf = compat.import_tf_v2() summary_metadata = metadata.create_summary_metadata( display_name=None, description=description) with tf.summary.summary_scope(name, 'text_summary', values=[data, step]) as (tag, _): tf.debugging.assert_type(data, tf.string) return tf.summary.write(tag=tag, tensor=data, step=step, metadata=summary_metadata)
def histogram(name, data, step, buckets=None, description=None): """Write a histogram summary. Arguments: name: A name for this summary. The summary tag used for TensorBoard will be this name prefixed by any active name scopes. data: A `Tensor` of any shape. Must be castable to `float64`. step: Required `int64`-castable monotonic step value. buckets: Optional positive `int`. The output will have this many buckets, except in two edge cases. If there is no data, then there are no buckets. If there is data but all points have the same value, then there is one bucket whose left and right endpoints are the same. description: Optional long-form description for this summary, as a constant `str`. Markdown is supported. Defaults to empty. Returns: True on success, or false if no summary was emitted because no default summary writer was available. """ # TODO(nickfelt): remove on-demand imports once dep situation is fixed. from tensorboard import compat tf = compat.import_tf_v2() summary_metadata = metadata.create_summary_metadata( display_name=None, description=description) with tf.summary.summary_scope(name, 'histogram_summary', values=[data, buckets, step]) as (tag, _): tensor = _buckets(data, bucket_count=buckets) return tf.summary.write(tag=tag, tensor=tensor, step=step, metadata=summary_metadata)
def image(name, data, step, max_outputs=3, description=None): """Write an image summary. Arguments: name: A name for this summary. The summary tag used for TensorBoard will be this name prefixed by any active name scopes. data: A `Tensor` representing pixel data with shape `[k, h, w, c]`, where `k` is the number of images, `h` and `w` are the height and width of the images, and `c` is the number of channels, which should be 1, 2, 3, or 4 (grayscale, grayscale with alpha, RGB, RGBA). Any of the dimensions may be statically unknown (i.e., `None`). Floating point data will be clipped to the range [0,1). step: Required `int64`-castable monotonic step value. max_outputs: Optional `int` or rank-0 integer `Tensor`. At most this many images will be emitted at each step. When more than `max_outputs` many images are provided, the first `max_outputs` many images will be used and the rest silently discarded. description: Optional long-form description for this summary, as a constant `str`. Markdown is supported. Defaults to empty. Returns: True on success, or false if no summary was emitted because no default summary writer was available. """ # TODO(nickfelt): remove on-demand imports once dep situation is fixed. from tensorboard import compat tf = compat.import_tf_v2() summary_metadata = metadata.create_summary_metadata( display_name=None, description=description) with tf.summary.summary_scope(name, 'image_summary', values=[data, max_outputs, step]) as (tag, _): tf.debugging.assert_rank(data, 4) tf.debugging.assert_non_negative(max_outputs) images = tf.image.convert_image_dtype(data, tf.uint8, saturate=True) limited_images = images[:max_outputs] encoded_images = tf.map_fn(tf.image.encode_png, limited_images, dtype=tf.string, name='encode_each_image') # Workaround for map_fn returning float dtype for an empty elems input. encoded_images = tf.cond( tf.shape(input=encoded_images)[0] > 0, lambda: encoded_images, lambda: tf.constant([], tf.string)) image_shape = tf.shape(input=images) dimensions = tf.stack([ tf.as_string(image_shape[2], name='width'), tf.as_string(image_shape[1], name='height') ], name='dimensions') tensor = tf.concat([dimensions, encoded_images], axis=0) return tf.summary.write(tag=tag, tensor=tensor, step=step, metadata=summary_metadata)
from __future__ import division from __future__ import print_function import glob import os import numpy as np import tensorflow as tf from tensorboard.plugins.histogram import metadata from tensorboard.plugins.histogram import summary from tensorboard.util import tensor_util try: from tensorboard import compat tf_v2 = compat.import_tf_v2() except ImportError: tf_v2 = None try: tf.enable_eager_execution() except AttributeError: # TF 2.0 doesn't have this symbol because eager is the default. pass class SummaryBaseTest(object): def setUp(self): super(SummaryBaseTest, self).setUp() np.random.seed(0) self.gaussian = np.random.normal(size=[100])
def audio(name, data, sample_rate, step, max_outputs=3, encoding=None, description=None): """Write an audio summary. Arguments: name: A name for this summary. The summary tag used for TensorBoard will be this name prefixed by any active name scopes. data: A `Tensor` representing audio data with shape `[k, t, c]`, where `k` is the number of audio clips, `t` is the number of frames, and `c` is the number of channels. Elements should be floating-point values in `[-1.0, 1.0]`. Any of the dimensions may be statically unknown (i.e., `None`). sample_rate: An `int` or rank-0 `int32` `Tensor` that represents the sample rate, in Hz. Must be positive. step: Required `int64`-castable monotonic step value. max_outputs: Optional `int` or rank-0 integer `Tensor`. At most this many audio clips will be emitted at each step. When more than `max_outputs` many clips are provided, the first `max_outputs` many clips will be used and the rest silently discarded. encoding: Optional constant `str` for the desired encoding. Only "wav" is currently supported, but this is not guaranteed to remain the default, so if you want "wav" in particular, set this explicitly. description: Optional long-form description for this summary, as a constant `str`. Markdown is supported. Defaults to empty. Returns: True on success, or false if no summary was emitted because no default summary writer was available. """ # TODO(nickfelt): remove on-demand imports once dep situation is fixed. from tensorboard import compat tf = compat.import_tf_v2() # TODO(nickfelt): get encode_wav() exported in the public API. from tensorflow.python.ops import gen_audio_ops if encoding is None: encoding = 'wav' if encoding != 'wav': raise ValueError('Unknown encoding: %r' % encoding) summary_metadata = metadata.create_summary_metadata( display_name=None, description=description, encoding=metadata.Encoding.Value('WAV')) inputs = [data, sample_rate, max_outputs, step] with tf.summary.summary_scope(name, 'audio_summary', values=inputs) as (tag, _): tf.debugging.assert_rank(data, 3) tf.debugging.assert_non_negative(max_outputs) limited_audio = data[:max_outputs] encode_fn = functools.partial(gen_audio_ops.encode_wav, sample_rate=sample_rate) encoded_audio = tf.map_fn(encode_fn, limited_audio, dtype=tf.string, name='encode_each_audio') # Workaround for map_fn returning float dtype for an empty elems input. encoded_audio = tf.cond( tf.shape(input=encoded_audio)[0] > 0, lambda: encoded_audio, lambda: tf.constant([], tf.string)) limited_labels = tf.tile([''], tf.shape(input=limited_audio)[:1]) tensor = tf.transpose(a=tf.stack([encoded_audio, limited_labels])) return tf.summary.write(tag=tag, tensor=tensor, step=step, metadata=summary_metadata)
def _buckets(data, bucket_count=None): """Create a TensorFlow op to group data into histogram buckets. Arguments: data: A `Tensor` of any shape. Must be castable to `float64`. bucket_count: Optional positive `int` or scalar `int32` `Tensor`. Returns: A `Tensor` of shape `[k, 3]` and type `float64`. The `i`th row is a triple `[left_edge, right_edge, count]` for a single bucket. The value of `k` is either `bucket_count` or `1` or `0`. """ # TODO(nickfelt): remove on-demand imports once dep situation is fixed. from tensorboard import compat tf = compat.import_tf_v2() if bucket_count is None: bucket_count = DEFAULT_BUCKET_COUNT with tf.name_scope('buckets', values=[data, bucket_count]): tf.debugging.assert_scalar(bucket_count) tf.debugging.assert_type(bucket_count, tf.int32) data = tf.reshape(data, shape=[-1]) # flatten data = tf.cast(data, tf.float64) is_empty = tf.equal(tf.size(data), 0) def when_empty(): return tf.constant([], shape=(0, 3), dtype=tf.float64) def when_nonempty(): min_ = tf.reduce_min(data) max_ = tf.reduce_max(data) range_ = max_ - min_ is_singular = tf.equal(range_, 0) def when_nonsingular(): bucket_width = range_ / tf.cast(bucket_count, tf.float64) offsets = data - min_ bucket_indices = tf.cast(tf.floor(offsets / bucket_width), dtype=tf.int32) clamped_indices = tf.minimum(bucket_indices, bucket_count - 1) one_hots = tf.one_hot(clamped_indices, depth=bucket_count) bucket_counts = tf.cast(tf.reduce_sum(one_hots, axis=0), dtype=tf.float64) edges = tf.linspace(min_, max_, bucket_count + 1) # Ensure edges[-1] == max_, which TF's linspace implementation does not # do, leaving it subject to the whim of floating point rounding error. edges = tf.concat([edges[:-1], [max_]], 0) left_edges = edges[:-1] right_edges = edges[1:] return tf.transpose( tf.stack([left_edges, right_edges, bucket_counts])) def when_singular(): center = min_ bucket_starts = tf.stack([center - 0.5]) bucket_ends = tf.stack([center + 0.5]) bucket_counts = tf.stack([tf.cast(tf.size(data), tf.float64)]) return tf.transpose( tf.stack([bucket_starts, bucket_ends, bucket_counts])) return tf.cond(is_singular, when_singular, when_nonsingular) return tf.cond(is_empty, when_empty, when_nonempty)