1# Copyright 2016 The TensorFlow Authors. All Rights Reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14# ==============================================================================
15
16"""Operations for writing summary data, for use in analysis and visualization.
17
18See the [Summaries and
19TensorBoard](https://www.tensorflow.org/guide/summaries_and_tensorboard) guide.
20"""
21
22from __future__ import absolute_import
23from __future__ import division
24from __future__ import print_function
25
26from google.protobuf import json_format as _json_format
27
28# exports Summary, SummaryDescription, Event, TaggedRunMetadata, SessionLog
29# pylint: disable=unused-import
30from tensorflow.core.framework.summary_pb2 import Summary
31from tensorflow.core.framework.summary_pb2 import SummaryDescription
32from tensorflow.core.framework.summary_pb2 import SummaryMetadata as _SummaryMetadata  # pylint: enable=unused-import
33from tensorflow.core.util.event_pb2 import Event
34from tensorflow.core.util.event_pb2 import SessionLog
35from tensorflow.core.util.event_pb2 import TaggedRunMetadata
36# pylint: enable=unused-import
37
38from tensorflow.python.distribute import summary_op_util as _distribute_summary_op_util
39from tensorflow.python.eager import context as _context
40from tensorflow.python.framework import constant_op as _constant_op
41from tensorflow.python.framework import dtypes as _dtypes
42from tensorflow.python.framework import ops as _ops
43from tensorflow.python.ops import gen_logging_ops as _gen_logging_ops
44from tensorflow.python.ops import gen_summary_ops as _gen_summary_ops  # pylint: disable=unused-import
45from tensorflow.python.ops import summary_op_util as _summary_op_util
46
47# exports FileWriter, FileWriterCache
48# pylint: disable=unused-import
49from tensorflow.python.summary.writer.writer import FileWriter
50from tensorflow.python.summary.writer.writer_cache import FileWriterCache
51# pylint: enable=unused-import
52
53from tensorflow.python.util import compat as _compat
54from tensorflow.python.util.tf_export import tf_export
55
56
57@tf_export(v1=['summary.scalar'])
58def scalar(name, tensor, collections=None, family=None):
59  """Outputs a `Summary` protocol buffer containing a single scalar value.
60
61  The generated Summary has a Tensor.proto containing the input Tensor.
62
63  Args:
64    name: A name for the generated node. Will also serve as the series name in
65      TensorBoard.
66    tensor: A real numeric Tensor containing a single value.
67    collections: Optional list of graph collections keys. The new summary op is
68      added to these collections. Defaults to `[GraphKeys.SUMMARIES]`.
69    family: Optional; if provided, used as the prefix of the summary tag name,
70      which controls the tab name used for display on Tensorboard.
71
72  Returns:
73    A scalar `Tensor` of type `string`. Which contains a `Summary` protobuf.
74
75  Raises:
76    ValueError: If tensor has the wrong shape or type.
77  """
78  if _distribute_summary_op_util.skip_summary():
79    return _constant_op.constant('')
80  with _summary_op_util.summary_scope(
81      name, family, values=[tensor]) as (tag, scope):
82    val = _gen_logging_ops.scalar_summary(tags=tag, values=tensor, name=scope)
83    _summary_op_util.collect(val, collections, [_ops.GraphKeys.SUMMARIES])
84  return val
85
86
87@tf_export(v1=['summary.image'])
88def image(name, tensor, max_outputs=3, collections=None, family=None):
89  """Outputs a `Summary` protocol buffer with images.
90
91  The summary has up to `max_outputs` summary values containing images. The
92  images are built from `tensor` which must be 4-D with shape `[batch_size,
93  height, width, channels]` and where `channels` can be:
94
95  *  1: `tensor` is interpreted as Grayscale.
96  *  3: `tensor` is interpreted as RGB.
97  *  4: `tensor` is interpreted as RGBA.
98
99  The images have the same number of channels as the input tensor. For float
100  input, the values are normalized one image at a time to fit in the range
101  `[0, 255]`.  `uint8` values are unchanged.  The op uses two different
102  normalization algorithms:
103
104  *  If the input values are all positive, they are rescaled so the largest one
105     is 255.
106
107  *  If any input value is negative, the values are shifted so input value 0.0
108     is at 127.  They are then rescaled so that either the smallest value is 0,
109     or the largest one is 255.
110
111  The `tag` in the outputted Summary.Value protobufs is generated based on the
112  name, with a suffix depending on the max_outputs setting:
113
114  *  If `max_outputs` is 1, the summary value tag is '*name*/image'.
115  *  If `max_outputs` is greater than 1, the summary value tags are
116     generated sequentially as '*name*/image/0', '*name*/image/1', etc.
117
118  Args:
119    name: A name for the generated node. Will also serve as a series name in
120      TensorBoard.
121    tensor: A 4-D `uint8` or `float32` `Tensor` of shape `[batch_size, height,
122      width, channels]` where `channels` is 1, 3, or 4.
123    max_outputs: Max number of batch elements to generate images for.
124    collections: Optional list of ops.GraphKeys.  The collections to add the
125      summary to.  Defaults to [_ops.GraphKeys.SUMMARIES]
126    family: Optional; if provided, used as the prefix of the summary tag name,
127      which controls the tab name used for display on Tensorboard.
128
129  Returns:
130    A scalar `Tensor` of type `string`. The serialized `Summary` protocol
131    buffer.
132  """
133  if _distribute_summary_op_util.skip_summary():
134    return _constant_op.constant('')
135  with _summary_op_util.summary_scope(
136      name, family, values=[tensor]) as (tag, scope):
137    val = _gen_logging_ops.image_summary(
138        tag=tag, tensor=tensor, max_images=max_outputs, name=scope)
139    _summary_op_util.collect(val, collections, [_ops.GraphKeys.SUMMARIES])
140  return val
141
142
143@tf_export(v1=['summary.histogram'])
144def histogram(name, values, collections=None, family=None):
145  # pylint: disable=line-too-long
146  """Outputs a `Summary` protocol buffer with a histogram.
147
148  Adding a histogram summary makes it possible to visualize your data's
149  distribution in TensorBoard. You can see a detailed explanation of the
150  TensorBoard histogram dashboard
151  [here](https://www.tensorflow.org/get_started/tensorboard_histograms).
152
153  The generated
154  [`Summary`](https://www.tensorflow.org/code/tensorflow/core/framework/summary.proto)
155  has one summary value containing a histogram for `values`.
156
157  This op reports an `InvalidArgument` error if any value is not finite.
158
159  Args:
160    name: A name for the generated node. Will also serve as a series name in
161      TensorBoard.
162    values: A real numeric `Tensor`. Any shape. Values to use to
163      build the histogram.
164    collections: Optional list of graph collections keys. The new summary op is
165      added to these collections. Defaults to `[GraphKeys.SUMMARIES]`.
166    family: Optional; if provided, used as the prefix of the summary tag name,
167      which controls the tab name used for display on Tensorboard.
168
169  Returns:
170    A scalar `Tensor` of type `string`. The serialized `Summary` protocol
171    buffer.
172  """
173  if _distribute_summary_op_util.skip_summary():
174    return _constant_op.constant('')
175  with _summary_op_util.summary_scope(
176      name, family, values=[values],
177      default_name='HistogramSummary') as (tag, scope):
178    val = _gen_logging_ops.histogram_summary(
179        tag=tag, values=values, name=scope)
180    _summary_op_util.collect(val, collections, [_ops.GraphKeys.SUMMARIES])
181  return val
182
183
184@tf_export(v1=['summary.audio'])
185def audio(name, tensor, sample_rate, max_outputs=3, collections=None,
186          family=None):
187  # pylint: disable=line-too-long
188  """Outputs a `Summary` protocol buffer with audio.
189
190  The summary has up to `max_outputs` summary values containing audio. The
191  audio is built from `tensor` which must be 3-D with shape `[batch_size,
192  frames, channels]` or 2-D with shape `[batch_size, frames]`. The values are
193  assumed to be in the range of `[-1.0, 1.0]` with a sample rate of
194  `sample_rate`.
195
196  The `tag` in the outputted Summary.Value protobufs is generated based on the
197  name, with a suffix depending on the max_outputs setting:
198
199  *  If `max_outputs` is 1, the summary value tag is '*name*/audio'.
200  *  If `max_outputs` is greater than 1, the summary value tags are
201     generated sequentially as '*name*/audio/0', '*name*/audio/1', etc
202
203  Args:
204    name: A name for the generated node. Will also serve as a series name in
205      TensorBoard.
206    tensor: A 3-D `float32` `Tensor` of shape `[batch_size, frames, channels]`
207      or a 2-D `float32` `Tensor` of shape `[batch_size, frames]`.
208    sample_rate: A Scalar `float32` `Tensor` indicating the sample rate of the
209      signal in hertz.
210    max_outputs: Max number of batch elements to generate audio for.
211    collections: Optional list of ops.GraphKeys.  The collections to add the
212      summary to.  Defaults to [_ops.GraphKeys.SUMMARIES]
213    family: Optional; if provided, used as the prefix of the summary tag name,
214      which controls the tab name used for display on Tensorboard.
215
216  Returns:
217    A scalar `Tensor` of type `string`. The serialized `Summary` protocol
218    buffer.
219  """
220  if _distribute_summary_op_util.skip_summary():
221    return _constant_op.constant('')
222  with _summary_op_util.summary_scope(
223      name, family=family, values=[tensor]) as (tag, scope):
224    sample_rate = _ops.convert_to_tensor(
225        sample_rate, dtype=_dtypes.float32, name='sample_rate')
226    val = _gen_logging_ops.audio_summary_v2(
227        tag=tag, tensor=tensor, max_outputs=max_outputs,
228        sample_rate=sample_rate, name=scope)
229    _summary_op_util.collect(val, collections, [_ops.GraphKeys.SUMMARIES])
230  return val
231
232
233@tf_export(v1=['summary.text'])
234def text(name, tensor, collections=None):
235  """Summarizes textual data.
236
237  Text data summarized via this plugin will be visible in the Text Dashboard
238  in TensorBoard. The standard TensorBoard Text Dashboard will render markdown
239  in the strings, and will automatically organize 1d and 2d tensors into tables.
240  If a tensor with more than 2 dimensions is provided, a 2d subarray will be
241  displayed along with a warning message. (Note that this behavior is not
242  intrinsic to the text summary api, but rather to the default TensorBoard text
243  plugin.)
244
245  Args:
246    name: A name for the generated node. Will also serve as a series name in
247      TensorBoard.
248    tensor: a string-type Tensor to summarize.
249    collections: Optional list of ops.GraphKeys.  The collections to add the
250      summary to.  Defaults to [_ops.GraphKeys.SUMMARIES]
251
252  Returns:
253    A TensorSummary op that is configured so that TensorBoard will recognize
254    that it contains textual data. The TensorSummary is a scalar `Tensor` of
255    type `string` which contains `Summary` protobufs.
256
257  Raises:
258    ValueError: If tensor has the wrong type.
259  """
260  if tensor.dtype != _dtypes.string:
261    raise ValueError('Expected tensor %s to have dtype string, got %s' %
262                     (tensor.name, tensor.dtype))
263
264  summary_metadata = _SummaryMetadata(
265      plugin_data=_SummaryMetadata.PluginData(plugin_name='text'))
266  t_summary = tensor_summary(
267      name=name,
268      tensor=tensor,
269      summary_metadata=summary_metadata,
270      collections=collections)
271  return t_summary
272
273
274@tf_export(v1=['summary.tensor_summary'])
275def tensor_summary(name,
276                   tensor,
277                   summary_description=None,
278                   collections=None,
279                   summary_metadata=None,
280                   family=None,
281                   display_name=None):
282  """Outputs a `Summary` protocol buffer with a serialized tensor.proto.
283
284  Args:
285    name: A name for the generated node. If display_name is not set, it will
286      also serve as the tag name in TensorBoard. (In that case, the tag
287      name will inherit tf name scopes.)
288    tensor: A tensor of any type and shape to serialize.
289    summary_description: A long description of the summary sequence. Markdown
290      is supported.
291    collections: Optional list of graph collections keys. The new summary op is
292      added to these collections. Defaults to `[GraphKeys.SUMMARIES]`.
293    summary_metadata: Optional SummaryMetadata proto (which describes which
294      plugins may use the summary value).
295    family: Optional; if provided, used as the prefix of the summary tag,
296      which controls the name used for display on TensorBoard when
297      display_name is not set.
298    display_name: A string used to name this data in TensorBoard. If this is
299      not set, then the node name will be used instead.
300
301  Returns:
302    A scalar `Tensor` of type `string`. The serialized `Summary` protocol
303    buffer.
304  """
305
306  if summary_metadata is None:
307    summary_metadata = _SummaryMetadata()
308
309  if summary_description is not None:
310    summary_metadata.summary_description = summary_description
311
312  if display_name is not None:
313    summary_metadata.display_name = display_name
314
315  serialized_summary_metadata = summary_metadata.SerializeToString()
316
317  if _distribute_summary_op_util.skip_summary():
318    return _constant_op.constant('')
319  with _summary_op_util.summary_scope(
320      name, family, values=[tensor]) as (tag, scope):
321    val = _gen_logging_ops.tensor_summary_v2(
322        tensor=tensor,
323        tag=tag,
324        name=scope,
325        serialized_summary_metadata=serialized_summary_metadata)
326    _summary_op_util.collect(val, collections, [_ops.GraphKeys.SUMMARIES])
327  return val
328
329
330@tf_export(v1=['summary.merge'])
331def merge(inputs, collections=None, name=None):
332  # pylint: disable=line-too-long
333  """Merges summaries.
334
335  This op creates a
336  [`Summary`](https://www.tensorflow.org/code/tensorflow/core/framework/summary.proto)
337  protocol buffer that contains the union of all the values in the input
338  summaries.
339
340  When the Op is run, it reports an `InvalidArgument` error if multiple values
341  in the summaries to merge use the same tag.
342
343  Args:
344    inputs: A list of `string` `Tensor` objects containing serialized `Summary`
345      protocol buffers.
346    collections: Optional list of graph collections keys. The new summary op is
347      added to these collections. Defaults to `[]`.
348    name: A name for the operation (optional).
349
350  Returns:
351    A scalar `Tensor` of type `string`. The serialized `Summary` protocol
352    buffer resulting from the merging.
353
354  Raises:
355    RuntimeError: If called with eager mode enabled.
356
357  @compatibility(eager)
358  Not compatible with eager execution. To write TensorBoard
359  summaries under eager execution, use `tf.contrib.summary` instead.
360  @end_compatibility
361  """
362  # pylint: enable=line-too-long
363  if _context.executing_eagerly():
364    raise RuntimeError(
365        'Merging tf.summary.* ops is not compatible with eager execution. '
366        'Use tf.contrib.summary instead.')
367  if _distribute_summary_op_util.skip_summary():
368    return _constant_op.constant('')
369  name = _summary_op_util.clean_tag(name)
370  with _ops.name_scope(name, 'Merge', inputs):
371    val = _gen_logging_ops.merge_summary(inputs=inputs, name=name)
372    _summary_op_util.collect(val, collections, [])
373  return val
374
375
376@tf_export(v1=['summary.merge_all'])
377def merge_all(key=_ops.GraphKeys.SUMMARIES, scope=None, name=None):
378  """Merges all summaries collected in the default graph.
379
380  Args:
381    key: `GraphKey` used to collect the summaries.  Defaults to
382      `GraphKeys.SUMMARIES`.
383    scope: Optional scope used to filter the summary ops, using `re.match`
384
385  Returns:
386    If no summaries were collected, returns None.  Otherwise returns a scalar
387    `Tensor` of type `string` containing the serialized `Summary` protocol
388    buffer resulting from the merging.
389
390  Raises:
391    RuntimeError: If called with eager execution enabled.
392
393  @compatibility(eager)
394  Not compatible with eager execution. To write TensorBoard
395  summaries under eager execution, use `tf.contrib.summary` instead.
396  @end_compatibility
397  """
398  if _context.executing_eagerly():
399    raise RuntimeError(
400        'Merging tf.summary.* ops is not compatible with eager execution. '
401        'Use tf.contrib.summary instead.')
402  summary_ops = _ops.get_collection(key, scope=scope)
403  if not summary_ops:
404    return None
405  else:
406    return merge(summary_ops, name=name)
407
408
409@tf_export(v1=['summary.get_summary_description'])
410def get_summary_description(node_def):
411  """Given a TensorSummary node_def, retrieve its SummaryDescription.
412
413  When a Summary op is instantiated, a SummaryDescription of associated
414  metadata is stored in its NodeDef. This method retrieves the description.
415
416  Args:
417    node_def: the node_def_pb2.NodeDef of a TensorSummary op
418
419  Returns:
420    a summary_pb2.SummaryDescription
421
422  Raises:
423    ValueError: if the node is not a summary op.
424
425  @compatibility(eager)
426  Not compatible with eager execution. To write TensorBoard
427  summaries under eager execution, use `tf.contrib.summary` instead.
428  @end_compatibility
429  """
430
431  if node_def.op != 'TensorSummary':
432    raise ValueError("Can't get_summary_description on %s" % node_def.op)
433  description_str = _compat.as_str_any(node_def.attr['description'].s)
434  summary_description = SummaryDescription()
435  _json_format.Parse(description_str, summary_description)
436  return summary_description
437