xref: /aosp_15_r20/external/tensorflow/tensorflow/python/summary/summary.py (revision b6fb3261f9314811a0f4371741dbb8839866f948)
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
22import contextlib
23import warnings
24
25from google.protobuf import json_format as _json_format
26
27# exports Summary, SummaryDescription, Event, TaggedRunMetadata, SessionLog
28# pylint: disable=unused-import, g-importing-member
29from tensorflow.core.framework.summary_pb2 import Summary
30from tensorflow.core.framework.summary_pb2 import SummaryDescription
31from tensorflow.core.framework.summary_pb2 import SummaryMetadata as _SummaryMetadata  # pylint: enable=unused-import
32from tensorflow.core.util.event_pb2 import Event
33from tensorflow.core.util.event_pb2 import SessionLog
34from tensorflow.core.util.event_pb2 import TaggedRunMetadata
35# pylint: enable=unused-import
36
37from tensorflow.python.distribute import summary_op_util as _distribute_summary_op_util
38from tensorflow.python.eager import context as _context
39from tensorflow.python.framework import constant_op as _constant_op
40from tensorflow.python.framework import dtypes as _dtypes
41from tensorflow.python.framework import ops as _ops
42from tensorflow.python.ops import array_ops as _array_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
46from tensorflow.python.ops import summary_ops_v2 as _summary_ops_v2
47
48# exports FileWriter, FileWriterCache
49# pylint: disable=unused-import
50from tensorflow.python.summary.writer.writer import FileWriter
51from tensorflow.python.summary.writer.writer_cache import FileWriterCache
52# pylint: enable=unused-import
53
54from tensorflow.python.training import training_util as _training_util
55from tensorflow.python.util import compat as _compat
56from tensorflow.python.util.tf_export import tf_export
57
58
59@tf_export(v1=['summary.scalar'])
60def scalar(name, tensor, collections=None, family=None):
61  """Outputs a `Summary` protocol buffer containing a single scalar value.
62
63  The generated Summary has a Tensor.proto containing the input Tensor.
64
65  Args:
66    name: A name for the generated node. Will also serve as the series name in
67      TensorBoard.
68    tensor: A real numeric Tensor containing a single value.
69    collections: Optional list of graph collections keys. The new summary op is
70      added to these collections. Defaults to `[GraphKeys.SUMMARIES]`.
71    family: Optional; if provided, used as the prefix of the summary tag name,
72      which controls the tab name used for display on Tensorboard.
73
74  Returns:
75    A scalar `Tensor` of type `string`. Which contains a `Summary` protobuf.
76
77  Raises:
78    ValueError: If tensor has the wrong shape or type.
79
80  @compatibility(TF2)
81  For compatibility purposes, when invoked in TF2 where the outermost context is
82  eager mode, this API will check if there is a suitable TF2 summary writer
83  context available, and if so will forward this call to that writer instead. A
84  "suitable" writer context means that the writer is set as the default writer,
85  and there is an associated non-empty value for `step` (see
86  `tf.summary.SummaryWriter.as_default`, `tf.summary.experimental.set_step` or
87  alternatively `tf.compat.v1.train.create_global_step`). For the forwarded
88  call, the arguments here will be passed to the TF2 implementation of
89  `tf.summary.scalar`, and the return value will be an empty bytestring tensor,
90  to avoid duplicate summary writing. This forwarding is best-effort and not all
91  arguments will be preserved.
92
93  To migrate to TF2, please use `tf.summary.scalar` instead. Please check
94  [Migrating tf.summary usage to
95  TF 2.0](https://www.tensorflow.org/tensorboard/migrate#in_tf_1x) for concrete
96  steps for migration. `tf.summary.scalar` can also log training metrics in
97  Keras, you can check [Logging training metrics in
98  Keras](https://www.tensorflow.org/tensorboard/scalars_and_keras) for detials.
99
100  #### How to Map Arguments
101
102  | TF1 Arg Name  | TF2 Arg Name    | Note                                   |
103  | :------------ | :-------------- | :------------------------------------- |
104  | `name`        | `name`          | -                                      |
105  | `tensor`      | `data`          | -                                      |
106  | -             | `step`          | Explicit int64-castable monotonic step |
107  :               :                 : value. If omitted, this defaults to    :
108  :               :                 : `tf.summary.experimental.get_step()`.  :
109  | `collections` | Not Supported   | -                                      |
110  | `family`      | Removed         | Please use `tf.name_scope` instead to  |
111  :               :                 : manage summary name prefix.            :
112  | -             | `description`   | Optional long-form `str` description   |
113  :               :                 : for the summary. Markdown is supported.:
114  :               :                 : Defaults to empty.                     :
115
116  @end_compatibility
117  """
118  if _distribute_summary_op_util.skip_summary():
119    return _constant_op.constant('')
120
121  # Special case: invoke v2 op for TF2 users who have a v2 writer.
122  if _should_invoke_v2_op():
123    # Defer the import to happen inside the symbol to prevent breakage due to
124    # missing dependency.
125    from tensorboard.summary.v2 import scalar as scalar_v2  # pylint: disable=g-import-not-at-top
126    with _compat_summary_scope(name, family) as tag:
127      scalar_v2(name=tag, data=tensor, step=_get_step_for_v2())
128    # Return an empty Tensor, which will be acceptable as an input to the
129    # `tf.compat.v1.summary.merge()` API.
130    return _constant_op.constant(b'')
131
132  # Fall back to legacy v1 scalar implementation.
133  with _summary_op_util.summary_scope(
134      name, family, values=[tensor]) as (tag, scope):
135    val = _gen_logging_ops.scalar_summary(tags=tag, values=tensor, name=scope)
136    _summary_op_util.collect(val, collections, [_ops.GraphKeys.SUMMARIES])
137  return val
138
139
140@tf_export(v1=['summary.image'])
141def image(name, tensor, max_outputs=3, collections=None, family=None):
142  """Outputs a `Summary` protocol buffer with images.
143
144  The summary has up to `max_outputs` summary values containing images. The
145  images are built from `tensor` which must be 4-D with shape `[batch_size,
146  height, width, channels]` and where `channels` can be:
147
148  *  1: `tensor` is interpreted as Grayscale.
149  *  3: `tensor` is interpreted as RGB.
150  *  4: `tensor` is interpreted as RGBA.
151
152  The images have the same number of channels as the input tensor. For float
153  input, the values are normalized one image at a time to fit in the range
154  `[0, 255]`.  `uint8` values are unchanged.  The op uses two different
155  normalization algorithms:
156
157  *  If the input values are all positive, they are rescaled so the largest one
158     is 255.
159
160  *  If any input value is negative, the values are shifted so input value 0.0
161     is at 127.  They are then rescaled so that either the smallest value is 0,
162     or the largest one is 255.
163
164  The `tag` in the outputted Summary.Value protobufs is generated based on the
165  name, with a suffix depending on the max_outputs setting:
166
167  *  If `max_outputs` is 1, the summary value tag is '*name*/image'.
168  *  If `max_outputs` is greater than 1, the summary value tags are
169     generated sequentially as '*name*/image/0', '*name*/image/1', etc.
170
171  Args:
172    name: A name for the generated node. Will also serve as a series name in
173      TensorBoard.
174    tensor: A 4-D `uint8` or `float32` `Tensor` of shape `[batch_size, height,
175      width, channels]` where `channels` is 1, 3, or 4.
176    max_outputs: Max number of batch elements to generate images for.
177    collections: Optional list of ops.GraphKeys.  The collections to add the
178      summary to.  Defaults to [_ops.GraphKeys.SUMMARIES]
179    family: Optional; if provided, used as the prefix of the summary tag name,
180      which controls the tab name used for display on Tensorboard.
181
182  Returns:
183    A scalar `Tensor` of type `string`. The serialized `Summary` protocol
184    buffer.
185
186  @compatibility(TF2)
187  For compatibility purposes, when invoked in TF2 where the outermost context is
188  eager mode, this API will check if there is a suitable TF2 summary writer
189  context available, and if so will forward this call to that writer instead. A
190  "suitable" writer context means that the writer is set as the default writer,
191  and there is an associated non-empty value for `step` (see
192  `tf.summary.SummaryWriter.as_default`, `tf.summary.experimental.set_step` or
193  alternatively `tf.compat.v1.train.create_global_step`). For the forwarded
194  call, the arguments here will be passed to the TF2 implementation of
195  `tf.summary.image`, and the return value will be an empty bytestring tensor,
196  to avoid duplicate summary writing. This forwarding is best-effort and not all
197  arguments will be preserved. Additionally:
198
199  *  The TF2 op does not do any of the normalization steps described above.
200     Rather than rescaling data that's outside the expected range, it simply
201     clips it.
202  *  The TF2 op just outputs the data under a single tag that contains multiple
203     samples, rather than multiple tags (i.e. no "/0" or "/1" suffixes).
204
205  To migrate to TF2, please use `tf.summary.image` instead. Please check
206  [Migrating tf.summary usage to
207  TF 2.0](https://www.tensorflow.org/tensorboard/migrate#in_tf_1x) for concrete
208  steps for migration.
209
210  #### How to Map Arguments
211
212  | TF1 Arg Name  | TF2 Arg Name    | Note                                   |
213  | :------------ | :-------------- | :------------------------------------- |
214  | `name`        | `name`          | -                                      |
215  | `tensor`      | `data`          | -                                      |
216  | -             | `step`          | Explicit int64-castable monotonic step |
217  :               :                 : value. If omitted, this defaults to    :
218  :               :                 : `tf.summary.experimental.get_step()`.  :
219  | `max_outputs` | `max_outputs`   | -                                      |
220  | `collections` | Not Supported   | -                                      |
221  | `family`      | Removed         | Please use `tf.name_scope` instead     |
222  :               :                 : to manage summary name prefix.         :
223  | -             | `description`   | Optional long-form `str` description   |
224  :               :                 : for the summary. Markdown is supported.:
225  :               :                 : Defaults to empty.                     :
226
227  @end_compatibility
228  """
229  if _distribute_summary_op_util.skip_summary():
230    return _constant_op.constant('')
231
232  # Special case: invoke v2 op for TF2 users who have a v2 writer.
233  if _should_invoke_v2_op():
234    # Defer the import to happen inside the symbol to prevent breakage due to
235    # missing dependency.
236    from tensorboard.summary.v2 import image as image_v2  # pylint: disable=g-import-not-at-top
237    with _compat_summary_scope(name, family) as tag:
238      image_v2(
239          name=tag,
240          data=tensor,
241          step=_get_step_for_v2(),
242          max_outputs=max_outputs)
243    # Return an empty Tensor, which will be acceptable as an input to the
244    # `tf.compat.v1.summary.merge()` API.
245    return _constant_op.constant(b'')
246
247  # Fall back to legacy v1 image implementation.
248  with _summary_op_util.summary_scope(
249      name, family, values=[tensor]) as (tag, scope):
250    val = _gen_logging_ops.image_summary(
251        tag=tag, tensor=tensor, max_images=max_outputs, name=scope)
252    _summary_op_util.collect(val, collections, [_ops.GraphKeys.SUMMARIES])
253  return val
254
255
256@tf_export(v1=['summary.histogram'])
257def histogram(name, values, collections=None, family=None):
258  # pylint: disable=line-too-long
259  """Outputs a `Summary` protocol buffer with a histogram.
260
261  Adding a histogram summary makes it possible to visualize your data's
262  distribution in TensorBoard. You can see a detailed explanation of the
263  TensorBoard histogram dashboard
264  [here](https://www.tensorflow.org/get_started/tensorboard_histograms).
265
266  The generated
267  [`Summary`](https://www.tensorflow.org/code/tensorflow/core/framework/summary.proto)
268  has one summary value containing a histogram for `values`.
269
270  This op reports an `InvalidArgument` error if any value is not finite.
271
272  Args:
273    name: A name for the generated node. Will also serve as a series name in
274      TensorBoard.
275    values: A real numeric `Tensor`. Any shape. Values to use to
276      build the histogram.
277    collections: Optional list of graph collections keys. The new summary op is
278      added to these collections. Defaults to `[GraphKeys.SUMMARIES]`.
279    family: Optional; if provided, used as the prefix of the summary tag name,
280      which controls the tab name used for display on Tensorboard.
281
282  Returns:
283    A scalar `Tensor` of type `string`. The serialized `Summary` protocol
284    buffer.
285
286  @compatibility(TF2)
287  For compatibility purposes, when invoked in TF2 where the outermost context is
288  eager mode, this API will check if there is a suitable TF2 summary writer
289  context available, and if so will forward this call to that writer instead. A
290  "suitable" writer context means that the writer is set as the default writer,
291  and there is an associated non-empty value for `step` (see
292  `tf.summary.SummaryWriter.as_default`, `tf.summary.experimental.set_step` or
293  alternatively `tf.compat.v1.train.create_global_step`). For the forwarded
294  call, the arguments here will be passed to the TF2 implementation of
295  `tf.summary.histogram`, and the return value will be an empty bytestring
296  tensor, to avoid duplicate summary writing. This forwarding is best-effort and
297  not all arguments will be preserved.
298
299  To migrate to TF2, please use `tf.summary.histogram` instead. Please check
300  [Migrating tf.summary usage to
301  TF 2.0](https://www.tensorflow.org/tensorboard/migrate#in_tf_1x) for concrete
302  steps for migration.
303
304  #### How to Map Arguments
305
306  | TF1 Arg Name  | TF2 Arg Name    | Note                                   |
307  | :------------ | :-------------- | :------------------------------------- |
308  | `name`        | `name`          | -                                      |
309  | `values`      | `data`          | -                                      |
310  | -             | `step`          | Explicit int64-castable monotonic step |
311  :               :                 : value. If omitted, this defaults to    :
312  :               :                 : `tf.summary.experimental.get_step()`   :
313  | -             | `buckets`       | Optional positive `int` specifying     |
314  :               :                 : the histogram bucket number.           :
315  | `collections` | Not Supported   | -                                      |
316  | `family`      | Removed         | Please use `tf.name_scope` instead     |
317  :               :                 : to manage summary name prefix.         :
318  | -             | `description`   | Optional long-form `str` description   |
319  :               :                 : for the summary. Markdown is supported.:
320  :               :                 : Defaults to empty.                     :
321
322  @end_compatibility
323  """
324  if _distribute_summary_op_util.skip_summary():
325    return _constant_op.constant('')
326
327  # Special case: invoke v2 op for TF2 users who have a v2 writer.
328  if _should_invoke_v2_op():
329    # Defer the import to happen inside the symbol to prevent breakage due to
330    # missing dependency.
331    from tensorboard.summary.v2 import histogram as histogram_v2  # pylint: disable=g-import-not-at-top
332    with _compat_summary_scope(name, family) as tag:
333      histogram_v2(name=tag, data=values, step=_get_step_for_v2())
334    # Return an empty Tensor, which will be acceptable as an input to the
335    # `tf.compat.v1.summary.merge()` API.
336    return _constant_op.constant(b'')
337
338  # Fall back to legacy v1 histogram implementation.
339  with _summary_op_util.summary_scope(
340      name, family, values=[values],
341      default_name='HistogramSummary') as (tag, scope):
342    val = _gen_logging_ops.histogram_summary(
343        tag=tag, values=values, name=scope)
344    _summary_op_util.collect(val, collections, [_ops.GraphKeys.SUMMARIES])
345  return val
346
347
348@tf_export(v1=['summary.audio'])
349def audio(name, tensor, sample_rate, max_outputs=3, collections=None,
350          family=None):
351  # pylint: disable=line-too-long
352  """Outputs a `Summary` protocol buffer with audio.
353
354  The summary has up to `max_outputs` summary values containing audio. The
355  audio is built from `tensor` which must be 3-D with shape `[batch_size,
356  frames, channels]` or 2-D with shape `[batch_size, frames]`. The values are
357  assumed to be in the range of `[-1.0, 1.0]` with a sample rate of
358  `sample_rate`.
359
360  The `tag` in the outputted Summary.Value protobufs is generated based on the
361  name, with a suffix depending on the max_outputs setting:
362
363  *  If `max_outputs` is 1, the summary value tag is '*name*/audio'.
364  *  If `max_outputs` is greater than 1, the summary value tags are
365     generated sequentially as '*name*/audio/0', '*name*/audio/1', etc
366
367  Args:
368    name: A name for the generated node. Will also serve as a series name in
369      TensorBoard.
370    tensor: A 3-D `float32` `Tensor` of shape `[batch_size, frames, channels]`
371      or a 2-D `float32` `Tensor` of shape `[batch_size, frames]`.
372    sample_rate: A Scalar `float32` `Tensor` indicating the sample rate of the
373      signal in hertz.
374    max_outputs: Max number of batch elements to generate audio for.
375    collections: Optional list of ops.GraphKeys.  The collections to add the
376      summary to.  Defaults to [_ops.GraphKeys.SUMMARIES]
377    family: Optional; if provided, used as the prefix of the summary tag name,
378      which controls the tab name used for display on Tensorboard.
379
380  Returns:
381    A scalar `Tensor` of type `string`. The serialized `Summary` protocol
382    buffer.
383
384  @compatibility(TF2)
385  For compatibility purposes, when invoked in TF2 where the outermost context is
386  eager mode, this API will check if there is a suitable TF2 summary writer
387  context available, and if so will forward this call to that writer instead. A
388  "suitable" writer context means that the writer is set as the default writer,
389  and there is an associated non-empty value for `step` (see
390  `tf.summary.SummaryWriter.as_default`, `tf.summary.experimental.set_step` or
391  alternatively `tf.compat.v1.train.create_global_step`). For the forwarded
392  call, the arguments here will be passed to the TF2 implementation of
393  `tf.summary.audio`, and the return value will be an empty bytestring tensor,
394  to avoid duplicate summary writing. This forwarding is best-effort and not all
395  arguments will be preserved. Additionally:
396
397  * The TF2 op just outputs the data under a single tag that contains multiple
398    samples, rather than multiple tags (i.e. no "/0" or "/1" suffixes).
399
400  To migrate to TF2, please use `tf.summary.audio` instead. Please check
401  [Migrating tf.summary usage to
402  TF 2.0](https://www.tensorflow.org/tensorboard/migrate#in_tf_1x) for concrete
403  steps for migration.
404
405  #### How to Map Arguments
406
407  | TF1 Arg Name  | TF2 Arg Name    | Note                                   |
408  | :------------ | :-------------- | :------------------------------------- |
409  | `name`        | `name`          | -                                      |
410  | `tensor`      | `data`          | Input for this argument now must be    |
411  :               :                 : three-dimensional `[k, t, c]`, where   :
412  :               :                 : `k` is the number of audio clips, `t`  :
413  :               :                 : is the number of frames, and `c` is    :
414  :               :                 : the number of channels. Two-dimensional:
415  :               :                 : input is no longer supported.          :
416  | `sample_rate` | `sample_rate`   | -                                      |
417  | -             | `step`          | Explicit int64-castable monotonic step |
418  :               :                 : value. If omitted, this defaults to    :
419  :               :                 : `tf.summary.experimental.get_step()`.  :
420  | `max_outputs` | `max_outputs`   | -                                      |
421  | `collections` | Not Supported   | -                                      |
422  | `family`      | Removed         | Please use `tf.name_scope` instead to  |
423  :               :                 : manage summary name prefix.            :
424  | -             | `encoding`      | Optional constant str for the desired  |
425  :               :                 : encoding. Check the docs for           :
426  :               :                 : `tf.summary.audio` for latest supported:
427  :               :                 : audio formats.                         :
428  | -             | `description`   | Optional long-form `str` description   |
429  :               :                 : for the summary. Markdown is supported.:
430  :               :                 : Defaults to empty.                     :
431
432  @end_compatibility
433  """
434  if _distribute_summary_op_util.skip_summary():
435    return _constant_op.constant('')
436
437  # Special case: invoke v2 op for TF2 users who have a v2 writer.
438  if _should_invoke_v2_op():
439    # Defer the import to happen inside the symbol to prevent breakage due to
440    # missing dependency.
441    from tensorboard.summary.v2 import audio as audio_v2  # pylint: disable=g-import-not-at-top
442    if tensor.shape.rank == 2:
443      # TF2 op requires 3-D tensor, add the `channels` dimension.
444      tensor = _array_ops.expand_dims_v2(tensor, axis=2)
445    with _compat_summary_scope(name, family) as tag:
446      audio_v2(
447          name=tag,
448          data=tensor,
449          sample_rate=sample_rate,
450          step=_get_step_for_v2(),
451          max_outputs=max_outputs,
452      )
453    # Return an empty Tensor, which will be acceptable as an input to the
454    # `tf.compat.v1.summary.merge()` API.
455    return _constant_op.constant(b'')
456
457  # Fall back to legacy v1 audio implementation.
458  with _summary_op_util.summary_scope(
459      name, family=family, values=[tensor]) as (tag, scope):
460    sample_rate = _ops.convert_to_tensor(
461        sample_rate, dtype=_dtypes.float32, name='sample_rate')
462    val = _gen_logging_ops.audio_summary_v2(
463        tag=tag, tensor=tensor, max_outputs=max_outputs,
464        sample_rate=sample_rate, name=scope)
465    _summary_op_util.collect(val, collections, [_ops.GraphKeys.SUMMARIES])
466  return val
467
468
469@tf_export(v1=['summary.text'])
470def text(name, tensor, collections=None):
471  """Summarizes textual data.
472
473  Text data summarized via this plugin will be visible in the Text Dashboard
474  in TensorBoard. The standard TensorBoard Text Dashboard will render markdown
475  in the strings, and will automatically organize 1d and 2d tensors into tables.
476  If a tensor with more than 2 dimensions is provided, a 2d subarray will be
477  displayed along with a warning message. (Note that this behavior is not
478  intrinsic to the text summary api, but rather to the default TensorBoard text
479  plugin.)
480
481  Args:
482    name: A name for the generated node. Will also serve as a series name in
483      TensorBoard.
484    tensor: a string-type Tensor to summarize.
485    collections: Optional list of ops.GraphKeys.  The collections to add the
486      summary to.  Defaults to [_ops.GraphKeys.SUMMARIES]
487
488  Returns:
489    A TensorSummary op that is configured so that TensorBoard will recognize
490    that it contains textual data. The TensorSummary is a scalar `Tensor` of
491    type `string` which contains `Summary` protobufs.
492
493  Raises:
494    ValueError: If tensor has the wrong type.
495
496  @compatibility(TF2)
497  For compatibility purposes, when invoked in TF2 where the outermost context is
498  eager mode, this API will check if there is a suitable TF2 summary writer
499  context available, and if so will forward this call to that writer instead. A
500  "suitable" writer context means that the writer is set as the default writer,
501  and there is an associated non-empty value for `step` (see
502  `tf.summary.SummaryWriter.as_default`, `tf.summary.experimental.set_step` or
503  alternatively `tf.compat.v1.train.create_global_step`). For the forwarded
504  call, the arguments here will be passed to the TF2 implementation of
505  `tf.summary.text`, and the return value will be an empty bytestring tensor, to
506  avoid duplicate summary writing. This forwarding is best-effort and not all
507  arguments will be preserved.
508
509  To migrate to TF2, please use `tf.summary.text` instead. Please check
510  [Migrating tf.summary usage to
511  TF 2.0](https://www.tensorflow.org/tensorboard/migrate#in_tf_1x) for concrete
512  steps for migration.
513
514  #### How to Map Arguments
515
516  | TF1 Arg Name  | TF2 Arg Name    | Note                                   |
517  | :------------ | :-------------- | :------------------------------------- |
518  | `name`        | `name`          | -                                      |
519  | `tensor`      | `data`          | -                                      |
520  | -             | `step`          | Explicit int64-castable monotonic step |
521  :               :                 : value. If omitted, this defaults to    :
522  :               :                 : `tf.summary.experimental.get_step()`.  :
523  | `collections` | Not Supported   | -                                      |
524  | -             | `description`   | Optional long-form `str` description   |
525  :               :                 : for the summary. Markdown is supported.:
526  :               :                 : Defaults to empty.                     :
527
528  @end_compatibility
529  """
530  if tensor.dtype != _dtypes.string:
531    raise ValueError('Expected tensor %s to have dtype string, got %s' %
532                     (tensor.name, tensor.dtype))
533
534  # Special case: invoke v2 op for TF2 users who have a v2 writer.
535  if _should_invoke_v2_op():
536    # `skip_summary` check for v1 op case is done in `tensor_summary`.
537    if _distribute_summary_op_util.skip_summary():
538      return _constant_op.constant('')
539    # Defer the import to happen inside the symbol to prevent breakage due to
540    # missing dependency.
541    from tensorboard.summary.v2 import text as text_v2  # pylint: disable=g-import-not-at-top
542    text_v2(name=name, data=tensor, step=_get_step_for_v2())
543    # Return an empty Tensor, which will be acceptable as an input to the
544    # `tf.compat.v1.summary.merge()` API.
545    return _constant_op.constant(b'')
546
547  # Fall back to legacy v1 text implementation.
548  summary_metadata = _SummaryMetadata(
549      plugin_data=_SummaryMetadata.PluginData(plugin_name='text'))
550  t_summary = tensor_summary(
551      name=name,
552      tensor=tensor,
553      summary_metadata=summary_metadata,
554      collections=collections)
555  return t_summary
556
557
558@tf_export(v1=['summary.tensor_summary'])
559def tensor_summary(name,
560                   tensor,
561                   summary_description=None,
562                   collections=None,
563                   summary_metadata=None,
564                   family=None,
565                   display_name=None):
566  """Outputs a `Summary` protocol buffer with a serialized tensor.proto.
567
568  Args:
569    name: A name for the generated node. If display_name is not set, it will
570      also serve as the tag name in TensorBoard. (In that case, the tag
571      name will inherit tf name scopes.)
572    tensor: A tensor of any type and shape to serialize.
573    summary_description: A long description of the summary sequence. Markdown
574      is supported.
575    collections: Optional list of graph collections keys. The new summary op is
576      added to these collections. Defaults to `[GraphKeys.SUMMARIES]`.
577    summary_metadata: Optional SummaryMetadata proto (which describes which
578      plugins may use the summary value).
579    family: Optional; if provided, used as the prefix of the summary tag,
580      which controls the name used for display on TensorBoard when
581      display_name is not set.
582    display_name: A string used to name this data in TensorBoard. If this is
583      not set, then the node name will be used instead.
584
585  Returns:
586    A scalar `Tensor` of type `string`. The serialized `Summary` protocol
587    buffer.
588  """
589
590  if summary_metadata is None:
591    summary_metadata = _SummaryMetadata()
592
593  if summary_description is not None:
594    summary_metadata.summary_description = summary_description
595
596  if display_name is not None:
597    summary_metadata.display_name = display_name
598
599  serialized_summary_metadata = summary_metadata.SerializeToString()
600
601  if _distribute_summary_op_util.skip_summary():
602    return _constant_op.constant('')
603  with _summary_op_util.summary_scope(
604      name, family, values=[tensor]) as (tag, scope):
605    val = _gen_logging_ops.tensor_summary_v2(
606        tensor=tensor,
607        tag=tag,
608        name=scope,
609        serialized_summary_metadata=serialized_summary_metadata)
610    _summary_op_util.collect(val, collections, [_ops.GraphKeys.SUMMARIES])
611  return val
612
613
614@tf_export(v1=['summary.merge'])
615def merge(inputs, collections=None, name=None):
616  # pylint: disable=line-too-long
617  """Merges summaries.
618
619  This op creates a
620  [`Summary`](https://www.tensorflow.org/code/tensorflow/core/framework/summary.proto)
621  protocol buffer that contains the union of all the values in the input
622  summaries.
623
624  When the Op is run, it reports an `InvalidArgument` error if multiple values
625  in the summaries to merge use the same tag.
626
627  Args:
628    inputs: A list of `string` `Tensor` objects containing serialized `Summary`
629      protocol buffers.
630    collections: Optional list of graph collections keys. The new summary op is
631      added to these collections. Defaults to `[]`.
632    name: A name for the operation (optional).
633
634  Returns:
635    A scalar `Tensor` of type `string`. The serialized `Summary` protocol
636    buffer resulting from the merging.
637
638  Raises:
639    RuntimeError: If called with eager mode enabled.
640
641  @compatibility(TF2)
642  This API is not compatible with eager execution or `tf.function`. To migrate
643  to TF2, this API can be omitted entirely, because in TF2 individual summary
644  ops, like `tf.summary.scalar()`, write directly to the default summary writer
645  if one is active. Thus, it's not necessary to merge summaries or to manually
646  add the resulting merged summary output to the writer. See the usage example
647  shown below.
648
649  For a comprehensive `tf.summary` migration guide, please follow
650  [Migrating tf.summary usage to
651  TF 2.0](https://www.tensorflow.org/tensorboard/migrate#in_tf_1x).
652
653  #### TF1 & TF2 Usage Example
654
655  TF1:
656
657  ```python
658  dist = tf.compat.v1.placeholder(tf.float32, [100])
659  tf.compat.v1.summary.histogram(name="distribution", values=dist)
660  writer = tf.compat.v1.summary.FileWriter("/tmp/tf1_summary_example")
661  summaries = tf.compat.v1.summary.merge_all()
662
663  sess = tf.compat.v1.Session()
664  for step in range(100):
665    mean_moving_normal = np.random.normal(loc=step, scale=1, size=[100])
666    summ = sess.run(summaries, feed_dict={dist: mean_moving_normal})
667    writer.add_summary(summ, global_step=step)
668  ```
669
670  TF2:
671
672  ```python
673  writer = tf.summary.create_file_writer("/tmp/tf2_summary_example")
674  for step in range(100):
675    mean_moving_normal = np.random.normal(loc=step, scale=1, size=[100])
676    with writer.as_default(step=step):
677      tf.summary.histogram(name='distribution', data=mean_moving_normal)
678  ```
679
680  @end_compatibility
681  """
682  # pylint: enable=line-too-long
683  if _context.executing_eagerly():
684    raise RuntimeError(
685        'Merging tf.summary.* ops is not compatible with eager execution. '
686        'Use tf.contrib.summary instead.')
687  if _distribute_summary_op_util.skip_summary():
688    return _constant_op.constant('')
689  name = _summary_op_util.clean_tag(name)
690  with _ops.name_scope(name, 'Merge', inputs):
691    val = _gen_logging_ops.merge_summary(inputs=inputs, name=name)
692    _summary_op_util.collect(val, collections, [])
693  return val
694
695
696@tf_export(v1=['summary.merge_all'])
697def merge_all(key=_ops.GraphKeys.SUMMARIES, scope=None, name=None):
698  """Merges all summaries collected in the default graph.
699
700  Args:
701    key: `GraphKey` used to collect the summaries.  Defaults to
702      `GraphKeys.SUMMARIES`.
703    scope: Optional scope used to filter the summary ops, using `re.match`.
704    name: A name for the operation (optional).
705
706  Returns:
707    If no summaries were collected, returns None.  Otherwise returns a scalar
708    `Tensor` of type `string` containing the serialized `Summary` protocol
709    buffer resulting from the merging.
710
711  Raises:
712    RuntimeError: If called with eager execution enabled.
713
714  @compatibility(TF2)
715  This API is not compatible with eager execution or `tf.function`. To migrate
716  to TF2, this API can be omitted entirely, because in TF2 individual summary
717  ops, like `tf.summary.scalar()`, write directly to the default summary writer
718  if one is active. Thus, it's not necessary to merge summaries or to manually
719  add the resulting merged summary output to the writer. See the usage example
720  shown below.
721
722  For a comprehensive `tf.summary` migration guide, please follow
723  [Migrating tf.summary usage to
724  TF 2.0](https://www.tensorflow.org/tensorboard/migrate#in_tf_1x).
725
726  #### TF1 & TF2 Usage Example
727
728  TF1:
729
730  ```python
731  dist = tf.compat.v1.placeholder(tf.float32, [100])
732  tf.compat.v1.summary.histogram(name="distribution", values=dist)
733  writer = tf.compat.v1.summary.FileWriter("/tmp/tf1_summary_example")
734  summaries = tf.compat.v1.summary.merge_all()
735
736  sess = tf.compat.v1.Session()
737  for step in range(100):
738    mean_moving_normal = np.random.normal(loc=step, scale=1, size=[100])
739    summ = sess.run(summaries, feed_dict={dist: mean_moving_normal})
740    writer.add_summary(summ, global_step=step)
741  ```
742
743  TF2:
744
745  ```python
746  writer = tf.summary.create_file_writer("/tmp/tf2_summary_example")
747  for step in range(100):
748    mean_moving_normal = np.random.normal(loc=step, scale=1, size=[100])
749    with writer.as_default(step=step):
750      tf.summary.histogram(name='distribution', data=mean_moving_normal)
751  ```
752
753  @end_compatibility
754  """
755  if _context.executing_eagerly():
756    raise RuntimeError(
757        'Merging tf.summary.* ops is not compatible with eager execution. '
758        'Use tf.contrib.summary instead.')
759  summary_ops = _ops.get_collection(key, scope=scope)
760  if not summary_ops:
761    return None
762  else:
763    return merge(summary_ops, name=name)
764
765
766@tf_export(v1=['summary.get_summary_description'])
767def get_summary_description(node_def):
768  """Given a TensorSummary node_def, retrieve its SummaryDescription.
769
770  When a Summary op is instantiated, a SummaryDescription of associated
771  metadata is stored in its NodeDef. This method retrieves the description.
772
773  Args:
774    node_def: the node_def_pb2.NodeDef of a TensorSummary op
775
776  Returns:
777    a summary_pb2.SummaryDescription
778
779  Raises:
780    ValueError: if the node is not a summary op.
781
782  @compatibility(eager)
783  Not compatible with eager execution. To write TensorBoard
784  summaries under eager execution, use `tf.contrib.summary` instead.
785  @end_compatibility
786  """
787
788  if node_def.op != 'TensorSummary':
789    raise ValueError("Can't get_summary_description on %s" % node_def.op)
790  description_str = _compat.as_str_any(node_def.attr['description'].s)
791  summary_description = SummaryDescription()
792  _json_format.Parse(description_str, summary_description)
793  return summary_description
794
795
796def _get_step_for_v2():
797  """Get step for v2 summary invocation in v1.
798
799  In order to invoke v2 op in `tf.compat.v1.summary`, global step needs to be
800  set for the v2 summary writer.
801
802  Returns:
803    The step set by `tf.summary.experimental.set_step` or
804    `tf.compat.v1.train.create_global_step`, or None is no step has been
805    set.
806  """
807  step = _summary_ops_v2.get_step()
808  if step is not None:
809    return step
810  return _training_util.get_global_step()
811
812
813def _should_invoke_v2_op():
814  """Check if v2 op can be invoked.
815
816  When calling TF1 summary op in eager mode, if the following conditions are
817  met, v2 op will be invoked:
818  - The outermost context is eager mode.
819  - A default TF2 summary writer is present.
820  - A step is set for the writer (using `tf.summary.SummaryWriter.as_default`,
821    `tf.summary.experimental.set_step` or
822    `tf.compat.v1.train.create_global_step`).
823
824  Returns:
825    A boolean indicating whether v2 summary op should be invoked.
826  """
827  # Check if in eager mode.
828  if not _ops.executing_eagerly_outside_functions():
829    return False
830  # Check if a default summary writer is present.
831  if not _summary_ops_v2.has_default_writer():
832    warnings.warn(
833        'Cannot activate TF2 compatibility support for TF1 summary ops: '
834        'default summary writer not found.')
835    return False
836  # Check if a step is set for the writer.
837  if _get_step_for_v2() is None:
838    warnings.warn(
839        'Cannot activate TF2 compatibility support for TF1 summary ops: '
840        'global step not set. To set step for summary writer, '
841        'use `tf.summary.SummaryWriter.as_default(step=_)`, '
842        '`tf.summary.experimental.set_step()` or '
843        '`tf.compat.v1.train.create_global_step()`.')
844    return False
845  return True
846
847
848@contextlib.contextmanager
849def _compat_summary_scope(name, family):
850  """Handles `family` argument for v2 op invocation in v1."""
851  # Get a new summary tag name with the `family` arg.
852  with _summary_op_util.summary_scope(name, family) as (tag, _):
853    # Reset the root name scope with an empty summary_scope.
854    with _summary_op_util.summary_scope(name='', family=None):
855      yield tag
856