Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/tensorboard/plugins/histogram/summary.py: 11%
80 statements
« prev ^ index » next coverage.py v7.4.0, created at 2024-01-03 07:57 +0000
« prev ^ index » next coverage.py v7.4.0, created at 2024-01-03 07:57 +0000
1# Copyright 2017 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"""Histogram summaries and TensorFlow operations to create them.
17A histogram summary stores a list of buckets. Each bucket is encoded as
18a triple `[left_edge, right_edge, count]`. Thus, a full histogram is
19encoded as a tensor of dimension `[k, 3]`.
21In general, the value of `k` (the number of buckets) will be a constant,
22like 30. There are two edge cases: if there is no data, then there are
23no buckets (the shape is `[0, 3]`); and if there is data but all points
24have the same value, then there is one bucket whose left and right
25endpoints are the same (the shape is `[1, 3]`).
27NOTE: This module is in beta, and its API is subject to change, but the
28data that it stores to disk will be supported forever.
29"""
32import numpy as np
34from tensorboard.plugins.histogram import metadata
35from tensorboard.plugins.histogram import summary_v2
38# Export V3 versions.
39histogram = summary_v2.histogram
40histogram_pb = summary_v2.histogram_pb
43def _buckets(data, bucket_count=None):
44 """Create a TensorFlow op to group data into histogram buckets.
46 Arguments:
47 data: A `Tensor` of any shape. Must be castable to `float64`.
48 bucket_count: Optional positive `int` or scalar `int32` `Tensor`.
49 Returns:
50 A `Tensor` of shape `[k, 3]` and type `float64`. The `i`th row is
51 a triple `[left_edge, right_edge, count]` for a single bucket.
52 The value of `k` is either `bucket_count` or `1` or `0`.
53 """
54 # TODO(nickfelt): remove on-demand imports once dep situation is fixed.
55 import tensorflow.compat.v1 as tf
57 if bucket_count is None:
58 bucket_count = summary_v2.DEFAULT_BUCKET_COUNT
59 with tf.name_scope(
60 "buckets", values=[data, bucket_count]
61 ), tf.control_dependencies(
62 [tf.assert_scalar(bucket_count), tf.assert_type(bucket_count, tf.int32)]
63 ):
64 data = tf.reshape(data, shape=[-1]) # flatten
65 data = tf.cast(data, tf.float64)
66 is_empty = tf.equal(tf.size(input=data), 0)
68 def when_empty():
69 return tf.constant([], shape=(0, 3), dtype=tf.float64)
71 def when_nonempty():
72 min_ = tf.reduce_min(input_tensor=data)
73 max_ = tf.reduce_max(input_tensor=data)
74 range_ = max_ - min_
75 is_singular = tf.equal(range_, 0)
77 def when_nonsingular():
78 bucket_width = range_ / tf.cast(bucket_count, tf.float64)
79 offsets = data - min_
80 bucket_indices = tf.cast(
81 tf.floor(offsets / bucket_width), dtype=tf.int32
82 )
83 clamped_indices = tf.minimum(bucket_indices, bucket_count - 1)
84 # Use float64 instead of float32 to avoid accumulating floating point error
85 # later in tf.reduce_sum when summing more than 2^24 individual `1.0` values.
86 # See https://github.com/tensorflow/tensorflow/issues/51419 for details.
87 one_hots = tf.one_hot(
88 clamped_indices, depth=bucket_count, dtype=tf.float64
89 )
90 bucket_counts = tf.cast(
91 tf.reduce_sum(input_tensor=one_hots, axis=0),
92 dtype=tf.float64,
93 )
94 edges = tf.linspace(min_, max_, bucket_count + 1)
95 left_edges = edges[:-1]
96 right_edges = edges[1:]
97 return tf.transpose(
98 a=tf.stack([left_edges, right_edges, bucket_counts])
99 )
101 def when_singular():
102 center = min_
103 bucket_starts = tf.stack([center - 0.5])
104 bucket_ends = tf.stack([center + 0.5])
105 bucket_counts = tf.stack(
106 [tf.cast(tf.size(input=data), tf.float64)]
107 )
108 return tf.transpose(
109 a=tf.stack([bucket_starts, bucket_ends, bucket_counts])
110 )
112 return tf.cond(is_singular, when_singular, when_nonsingular)
114 return tf.cond(is_empty, when_empty, when_nonempty)
117def op(
118 name,
119 data,
120 bucket_count=None,
121 display_name=None,
122 description=None,
123 collections=None,
124):
125 """Create a legacy histogram summary op.
127 Arguments:
128 name: A unique name for the generated summary node.
129 data: A `Tensor` of any shape. Must be castable to `float64`.
130 bucket_count: Optional positive `int`. The output will have this
131 many buckets, except in two edge cases. If there is no data, then
132 there are no buckets. If there is data but all points have the
133 same value, then there is one bucket whose left and right
134 endpoints are the same.
135 display_name: Optional name for this summary in TensorBoard, as a
136 constant `str`. Defaults to `name`.
137 description: Optional long-form description for this summary, as a
138 constant `str`. Markdown is supported. Defaults to empty.
139 collections: Optional list of graph collections keys. The new
140 summary op is added to these collections. Defaults to
141 `[Graph Keys.SUMMARIES]`.
143 Returns:
144 A TensorFlow summary op.
145 """
146 # TODO(nickfelt): remove on-demand imports once dep situation is fixed.
147 import tensorflow.compat.v1 as tf
149 if display_name is None:
150 display_name = name
151 summary_metadata = metadata.create_summary_metadata(
152 display_name=display_name, description=description
153 )
154 with tf.name_scope(name):
155 tensor = _buckets(data, bucket_count=bucket_count)
156 return tf.summary.tensor_summary(
157 name="histogram_summary",
158 tensor=tensor,
159 collections=collections,
160 summary_metadata=summary_metadata,
161 )
164def pb(name, data, bucket_count=None, display_name=None, description=None):
165 """Create a legacy histogram summary protobuf.
167 Arguments:
168 name: A unique name for the generated summary, including any desired
169 name scopes.
170 data: A `np.array` or array-like form of any shape. Must have type
171 castable to `float`.
172 bucket_count: Optional positive `int`. The output will have this
173 many buckets, except in two edge cases. If there is no data, then
174 there are no buckets. If there is data but all points have the
175 same value, then there is one bucket whose left and right
176 endpoints are the same.
177 display_name: Optional name for this summary in TensorBoard, as a
178 `str`. Defaults to `name`.
179 description: Optional long-form description for this summary, as a
180 `str`. Markdown is supported. Defaults to empty.
182 Returns:
183 A `tf.Summary` protobuf object.
184 """
185 # TODO(nickfelt): remove on-demand imports once dep situation is fixed.
186 import tensorflow.compat.v1 as tf
188 if bucket_count is None:
189 bucket_count = summary_v2.DEFAULT_BUCKET_COUNT
190 data = np.array(data).flatten().astype(float)
191 if data.size == 0:
192 buckets = np.array([]).reshape((0, 3))
193 else:
194 min_ = np.min(data)
195 max_ = np.max(data)
196 range_ = max_ - min_
197 if range_ == 0:
198 center = min_
199 buckets = np.array([[center - 0.5, center + 0.5, float(data.size)]])
200 else:
201 bucket_width = range_ / bucket_count
202 offsets = data - min_
203 bucket_indices = np.floor(offsets / bucket_width).astype(int)
204 clamped_indices = np.minimum(bucket_indices, bucket_count - 1)
205 one_hots = np.array([clamped_indices]).transpose() == np.arange(
206 0, bucket_count
207 ) # broadcast
208 assert one_hots.shape == (data.size, bucket_count), (
209 one_hots.shape,
210 (data.size, bucket_count),
211 )
212 bucket_counts = np.sum(one_hots, axis=0)
213 edges = np.linspace(min_, max_, bucket_count + 1)
214 left_edges = edges[:-1]
215 right_edges = edges[1:]
216 buckets = np.array(
217 [left_edges, right_edges, bucket_counts]
218 ).transpose()
219 tensor = tf.make_tensor_proto(buckets, dtype=tf.float64)
221 if display_name is None:
222 display_name = name
223 summary_metadata = metadata.create_summary_metadata(
224 display_name=display_name, description=description
225 )
226 tf_summary_metadata = tf.SummaryMetadata.FromString(
227 summary_metadata.SerializeToString()
228 )
230 summary = tf.Summary()
231 summary.value.add(
232 tag="%s/histogram_summary" % name,
233 metadata=tf_summary_metadata,
234 tensor=tensor,
235 )
236 return summary