Ejemplo n.º 1
0
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)
Ejemplo n.º 2
0
def distribution(dist, name='distribution', step=None, description=None):
    assert dist.shape.rank == 1

    summary_metadata = create_summary_metadata(display_name=name,
                                               description=description)

    with tf.summary.experimental.summary_scope(name,
                                               'distribution',
                                               values=[dist]) as (tag, scope):

        def entry(i, x):
            return tf.stack([
                tf.constant(i - 0.5, shape=(), dtype=tf.float32),
                tf.constant(i + 0.5, shape=(), dtype=tf.float32),
                tf.cast(x, dtype=tf.float32)
            ])

        dist_entries = tf.stack(
            [entry(i, p) for i, p in enumerate(tf.unstack(dist))])

        return tf.summary.write(tag=tag,
                                tensor=dist_entries,
                                step=step,
                                metadata=summary_metadata,
                                name=scope)
Ejemplo n.º 3
0
def histogram(name, data, step=None, 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: Explicit `int64`-castable monotonic step value for this summary. If
      omitted, this defaults to `tf.summary.experimental.get_step()`, which must
      not be None.
    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.

  Raises:
    ValueError: if a default writer exists, but no step was provided and
      `tf.summary.experimental.get_step()` is None.
  """
  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)
Ejemplo n.º 4
0
def histogram_continuous(name,
                         data,
                         bucket_min=None,
                         bucket_max=None,
                         bucket_count=DEFAULT_BUCKET_COUNT,
                         step=None,
                         description=None):
    """histogram for continuous data .

    Args:
        name (str): name for this summary
        data (Tensor): A `Tensor` of any shape.
        bucket_min (float|None): represent bucket min value,
            if None value of tf.reduce_min(data) will be used
        bucket_max (float|None): represent bucket max value,
            if None value tf.reduce_max(data) will be used
        bucket_count (int):  positive `int`. The output will have this many buckets.
        step (None|tf.Variable):  step value for this summary. this defaults to
            `tf.summary.experimental.get_step()`
        description (str): Optional long-form description for this summary
    """
    summary_metadata = metadata.create_summary_metadata(
        display_name=None, description=description)
    summary_scope = (getattr(tf.summary.experimental, 'summary_scope', None)
                     or tf.summary.summary_scope)
    with summary_scope(
            name,
            'histogram_summary',
            values=[data, bucket_min, bucket_max, bucket_count,
                    step]) as (tag, _):
        with tf.name_scope('buckets'):
            data = tf.cast(tf.reshape(data, shape=[-1]), tf.float64)
            if bucket_min is None:
                bucket_min = tf.reduce_min(data)
            if bucket_max is None:
                bucket_max = tf.reduce_min(data)
            range_ = bucket_max - bucket_min
            bucket_width = range_ / tf.cast(bucket_count, tf.float64)
            offsets = data - bucket_min
            bucket_indices = tf.cast(tf.floor(offsets / bucket_width),
                                     dtype=tf.int32)
            clamped_indices = tf.clip_by_value(bucket_indices, 0,
                                               bucket_count - 1)
            one_hots = tf.one_hot(clamped_indices, depth=bucket_count)
            bucket_counts = tf.cast(tf.reduce_sum(input_tensor=one_hots,
                                                  axis=0),
                                    dtype=tf.float64)
            edges = tf.linspace(bucket_min, bucket_max, bucket_count + 1)
            edges = tf.concat([edges[:-1], [tf.cast(bucket_max, tf.float64)]],
                              0)
            edges = tf.cast(edges, tf.float64)
            left_edges = edges[:-1]
            right_edges = edges[1:]
            tensor = tf.transpose(
                a=tf.stack([left_edges, right_edges, bucket_counts]))
        return tf.summary.write(tag=tag,
                                tensor=tensor,
                                step=step,
                                metadata=summary_metadata)
Ejemplo n.º 5
0
def pb(name, data, bucket_count=None, display_name=None, description=None):
  """Create a histogram summary protobuf.

  Arguments:
    name: A unique name for the generated summary, including any desired
      name scopes.
    data: A `np.array` or array-like form of any shape. Must have type
      castable to `float`.
    bucket_count: 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.
    display_name: Optional name for this summary in TensorBoard, as a
      `str`. Defaults to `name`.
    description: Optional long-form description for this summary, as a
      `str`. Markdown is supported. Defaults to empty.

  Returns:
    A `tf.Summary` protobuf object.
  """
  if bucket_count is None:
    bucket_count = DEFAULT_BUCKET_COUNT
  data = np.array(data).flatten().astype(float)
  if data.size == 0:
    buckets = np.array([]).reshape((0, 3))
  else:
    min_ = np.min(data)
    max_ = np.max(data)
    range_ = max_ - min_
    if range_ == 0:
      center = min_
      buckets = np.array([[center - 0.5, center + 0.5, float(data.size)]])
    else:
      bucket_width = range_ / bucket_count
      offsets = data - min_
      bucket_indices = np.floor(offsets / bucket_width).astype(int)
      clamped_indices = np.minimum(bucket_indices, bucket_count - 1)
      one_hots = (np.array([clamped_indices]).transpose()
                  == np.arange(0, bucket_count))  # broadcast
      assert one_hots.shape == (data.size, bucket_count), (
          one_hots.shape, (data.size, bucket_count))
      bucket_counts = np.sum(one_hots, axis=0)
      edges = np.linspace(min_, max_, bucket_count + 1)
      left_edges = edges[:-1]
      right_edges = edges[1:]
      buckets = np.array([left_edges, right_edges, bucket_counts]).transpose()
  tensor = tf.make_tensor_proto(buckets, dtype=tf.float64)

  if display_name is None:
    display_name = name
  summary_metadata = metadata.create_summary_metadata(
      display_name=display_name, description=description)

  summary = tf.Summary()
  summary.value.add(tag='%s/histogram_summary' % name,
                    metadata=summary_metadata,
                    tensor=tensor)
  return summary
Ejemplo n.º 6
0
def pb(name, data, bucket_count=None, display_name=None, description=None):
  """Create a histogram summary protobuf.

  Arguments:
    name: A unique name for the generated summary, including any desired
      name scopes.
    data: A `np.array` or array-like form of any shape. Must have type
      castable to `float`.
    bucket_count: 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.
    display_name: Optional name for this summary in TensorBoard, as a
      `str`. Defaults to `name`.
    description: Optional long-form description for this summary, as a
      `str`. Markdown is supported. Defaults to empty.

  Returns:
    A `tf.Summary` protobuf object.
  """
  if bucket_count is None:
    bucket_count = DEFAULT_BUCKET_COUNT
  data = np.array(data).flatten().astype(float)
  if data.size == 0:
    buckets = np.array([]).reshape((0, 3))
  else:
    min_ = np.min(data)
    max_ = np.max(data)
    range_ = max_ - min_
    if range_ == 0:
      center = min_
      buckets = np.array([[center - 0.5, center + 0.5, float(data.size)]])
    else:
      bucket_width = range_ / bucket_count
      offsets = data - min_
      bucket_indices = np.floor(offsets / bucket_width).astype(int)
      clamped_indices = np.minimum(bucket_indices, bucket_count - 1)
      one_hots = (np.array([clamped_indices]).transpose()
                  == np.arange(0, bucket_count))  # broadcast
      assert one_hots.shape == (data.size, bucket_count), (
          one_hots.shape, (data.size, bucket_count))
      bucket_counts = np.sum(one_hots, axis=0)
      edges = np.linspace(min_, max_, bucket_count + 1)
      left_edges = edges[:-1]
      right_edges = edges[1:]
      buckets = np.array([left_edges, right_edges, bucket_counts]).transpose()
  tensor = tensor_util.make_tensor_proto(buckets, dtype=tf.float64)

  if display_name is None:
    display_name = name
  summary_metadata = metadata.create_summary_metadata(
      display_name=display_name, description=description)

  summary = tf.Summary()
  summary.value.add(tag='%s/histogram_summary' % name,
                    metadata=summary_metadata,
                    tensor=tensor)
  return summary
Ejemplo n.º 7
0
def histogram_pb(tag, data, buckets=None, description=None):
    """Create a histogram summary protobuf.

  Arguments:
    tag: String tag for the summary.
    data: A `np.array` or array-like form of any shape. Must have type
      castable to `float`.
    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
      `str`. Markdown is supported. Defaults to empty.

  Returns:
    A `summary_pb2.Summary` protobuf object.
  """
    # TODO(nickfelt): remove on-demand imports once dep situation is fixed.
    from tensorboard.compat import tf
    bucket_count = DEFAULT_BUCKET_COUNT if buckets is None else buckets
    data = np.array(data).flatten().astype(float)
    if data.size == 0:
        buckets = np.array([]).reshape((0, 3))
    else:
        min_ = np.min(data)
        max_ = np.max(data)
        range_ = max_ - min_
        if range_ == 0:
            center = min_
            buckets = np.array([[center - 0.5, center + 0.5,
                                 float(data.size)]])
        else:
            bucket_width = range_ / bucket_count
            offsets = data - min_
            bucket_indices = np.floor(offsets / bucket_width).astype(int)
            clamped_indices = np.minimum(bucket_indices, bucket_count - 1)
            one_hots = (np.array([clamped_indices
                                  ]).transpose() == np.arange(0, bucket_count)
                        )  # broadcast
            assert one_hots.shape == (data.size,
                                      bucket_count), (one_hots.shape,
                                                      (data.size,
                                                       bucket_count))
            bucket_counts = np.sum(one_hots, axis=0)
            edges = np.linspace(min_, max_, bucket_count + 1)
            left_edges = edges[:-1]
            right_edges = edges[1:]
            buckets = np.array([left_edges, right_edges,
                                bucket_counts]).transpose()
    tensor = tensor_util.make_tensor_proto(buckets, dtype=tf.float64)

    summary_metadata = metadata.create_summary_metadata(
        display_name=None, description=description)
    summary = summary_pb2.Summary()
    summary.value.add(tag=tag, metadata=summary_metadata, tensor=tensor)
    return summary
Ejemplo n.º 8
0
def histogram_pb(tag, data, buckets=None, description=None):
    """Create a histogram summary protobuf.

    Arguments:
      tag: String tag for the summary.
      data: A `np.array` or array-like form of any shape. Must have type
        castable to `float`.
      buckets: Optional positive `int`. The output shape will always be
        [buckets, 3]. If there is no data, then an all-zero array of shape
        [buckets, 3] will be returned. If there is data but all points have
        the same value, then all buckets' left and right endpoints are the
        same and only the last bucket has nonzero count.
      description: Optional long-form description for this summary, as a
        `str`. Markdown is supported. Defaults to empty.

    Returns:
      A `summary_pb2.Summary` protobuf object.
    """
    bucket_count = DEFAULT_BUCKET_COUNT if buckets is None else buckets
    data = np.array(data).flatten().astype(float)
    if bucket_count == 0 or data.size == 0:
        histogram_buckets = np.zeros((bucket_count, 3))
    else:
        min_ = np.min(data)
        max_ = np.max(data)
        range_ = max_ - min_
        if range_ == 0:
            left_edges = right_edges = np.array([min_] * bucket_count)
            bucket_counts = np.array([0] * (bucket_count - 1) + [data.size])
            histogram_buckets = np.array(
                [left_edges, right_edges, bucket_counts]).transpose()
        else:
            bucket_width = range_ / bucket_count
            offsets = data - min_
            bucket_indices = np.floor(offsets / bucket_width).astype(int)
            clamped_indices = np.minimum(bucket_indices, bucket_count - 1)
            one_hots = np.array([clamped_indices]).transpose() == np.arange(
                0, bucket_count)  # broadcast
            assert one_hots.shape == (data.size, bucket_count), (
                one_hots.shape,
                (data.size, bucket_count),
            )
            bucket_counts = np.sum(one_hots, axis=0)
            edges = np.linspace(min_, max_, bucket_count + 1)
            left_edges = edges[:-1]
            right_edges = edges[1:]
            histogram_buckets = np.array(
                [left_edges, right_edges, bucket_counts]).transpose()
    tensor = tensor_util.make_tensor_proto(histogram_buckets, dtype=np.float64)

    summary_metadata = metadata.create_summary_metadata(
        display_name=None, description=description)
    summary = summary_pb2.Summary()
    summary.value.add(tag=tag, metadata=summary_metadata, tensor=tensor)
    return summary
Ejemplo n.º 9
0
def _migrate_histogram_value(value):
  histogram_value = value.histo
  bucket_lefts = [histogram_value.min] + histogram_value.bucket_limit[:-1]
  bucket_rights = histogram_value.bucket_limit[:-1] + [histogram_value.max]
  bucket_counts = histogram_value.bucket
  buckets = np.array([bucket_lefts, bucket_rights, bucket_counts], dtype=np.float32).transpose()

  summary_metadata = histogram_metadata.create_summary_metadata(
      display_name=value.metadata.display_name or value.tag,
      description=value.metadata.summary_description)

  return make_summary(value.tag, summary_metadata, buckets)
Ejemplo n.º 10
0
def histogram(name, data, step=None, 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: Explicit `int64`-castable monotonic step value for this summary. If
      omitted, this defaults to `tf.summary.experimental.get_step()`, which must
      not be None.
    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.

  Raises:
    ValueError: if a default writer exists, but no step was provided and
      `tf.summary.experimental.get_step()` is None.
  """
    summary_metadata = metadata.create_summary_metadata(
        display_name=None, description=description)
    # TODO(https://github.com/tensorflow/tensorboard/issues/2109): remove fallback
    summary_scope = (getattr(tf.summary.experimental, 'summary_scope', None)
                     or tf.summary.summary_scope)

    def histogram_summary(data, buckets, histogram_metadata, step):
        with 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=histogram_metadata)

    # `_buckets()` has dynamic output shapes which is not supported on TPU's. As so, place
    # the bucketing ops on outside compilation cluster so that the function in executed on CPU.
    # TODO(https://github.com/tensorflow/tensorboard/issues/2885): Remove this special
    # handling once dynamic shapes are supported on TPU's.
    if isinstance(tf.distribute.get_strategy(),
                  tf.distribute.experimental.TPUStrategy):
        return tf.compat.v1.tpu.outside_compilation(histogram_summary, data,
                                                    buckets, summary_metadata,
                                                    step)
    return histogram_summary(data, buckets, summary_metadata, step)
Ejemplo n.º 11
0
def pb(name, data, bucket_count=None, display_name=None, description=None):
  """Create a legacy histogram summary protobuf.

  Arguments:
    name: A unique name for the generated summary, including any desired
      name scopes.
    data: A `np.array` or array-like form of any shape. Must have type
      castable to `float`.
    bucket_count: 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.
    display_name: Optional name for this summary in TensorBoard, as a
      `str`. Defaults to `name`.
    description: Optional long-form description for this summary, as a
      `str`. Markdown is supported. Defaults to empty.

  Returns:
    A `tf.Summary` protobuf object.
  """
  # TODO(nickfelt): remove on-demand imports once dep situation is fixed.
  import tensorflow.compat.v1 as tf
  print('in summary pb')
  if bucket_count is None:
    bucket_count = summary_v2.DEFAULT_BUCKET_COUNT
  data = np.array(data).flatten().astype(float)
  if data.size == 0:
    buckets = np.array([]).reshape((0, 3))
  else:
    min_ = np.min(data)
    max_ = np.max(data)
    range_ = max_ - min_

    bucket_num = tf.minimum(bucket_count,data.size()[0])
    left_edges = tf.linspace(0,bucket_num-1,bucket_num)
    right_edges = tf.linspace(1,bucket_num,bucket_num)
    buckets = np.array([left_edges, right_edges, data[0:bucket_num]]).transpose()
  tensor = tf.make_tensor_proto(buckets, dtype=tf.float64)#dtype是float,那么需要查询bucketcounts怎么绘制,是否需要int

  if display_name is None:
    display_name = name
  summary_metadata = metadata.create_summary_metadata(
      display_name=display_name, description=description)
  tf_summary_metadata = tf.SummaryMetadata.FromString(
      summary_metadata.SerializeToString())

  summary = tf.Summary()
  summary.value.add(tag='%s/bar_summary' % name,
                    metadata=tf_summary_metadata,
                    tensor=tensor)
  return summary
Ejemplo n.º 12
0
def _migrate_histogram_value(value):
  histogram_value = value.histo
  bucket_lefts = [histogram_value.min] + histogram_value.bucket_limit[:-1]
  bucket_rights = histogram_value.bucket_limit[:-1] + [histogram_value.max]
  bucket_counts = histogram_value.bucket
  buckets = np.array([bucket_lefts, bucket_rights, bucket_counts]).transpose()

  tensor_proto = tf.make_tensor_proto(buckets)
  summary_metadata = histogram_metadata.create_summary_metadata(
      display_name=value.metadata.display_name or value.tag,
      description=value.metadata.summary_description)
  return tf.Summary.Value(tag=value.tag,
                          metadata=summary_metadata,
                          tensor=tensor_proto)
Ejemplo n.º 13
0
    def test_empty_histogram(self):
        with tf.compat.v1.Graph().as_default():
            old_op = tf.compat.v1.summary.histogram("empty_yet_important",
                                                    tf.constant([]))
            old_value = self._value_from_op(old_op)
        assert old_value.HasField("histo"), old_value
        new_value = data_compat.migrate_value(old_value)

        self.assertEqual("empty_yet_important", new_value.tag)
        expected_metadata = histogram_metadata.create_summary_metadata(
            display_name="empty_yet_important", description="")
        self.assertEqual(expected_metadata, new_value.metadata)
        self.assertTrue(new_value.HasField("tensor"))
        buckets = tensor_util.make_ndarray(new_value.tensor)
        self.assertEmpty(buckets)
Ejemplo n.º 14
0
def _migrate_histogram_value(value):
    histogram_value = value.histo
    bucket_lefts = [histogram_value.min] + histogram_value.bucket_limit[:-1]
    bucket_rights = histogram_value.bucket_limit[:-1] + [histogram_value.max]
    bucket_counts = histogram_value.bucket
    buckets = np.array([bucket_lefts, bucket_rights,
                        bucket_counts]).transpose()

    tensor_proto = tf.make_tensor_proto(buckets)
    summary_metadata = histogram_metadata.create_summary_metadata(
        display_name=value.metadata.display_name or value.tag,
        description=value.metadata.summary_description)
    return tf.Summary.Value(tag=value.tag,
                            metadata=summary_metadata,
                            tensor=tensor_proto)
    def test_histogram(self):
        old_op = tf.summary.histogram('important_data',
                                      tf.random_normal(shape=[23, 45]))
        old_value = self._value_from_op(old_op)
        assert old_value.HasField('histo'), old_value
        new_value = data_compat.migrate_value(old_value)

        self.assertEqual('important_data', new_value.tag)
        expected_metadata = histogram_metadata.create_summary_metadata(
            display_name='important_data', description='')
        self.assertEqual(expected_metadata, new_value.metadata)
        self.assertTrue(new_value.HasField('tensor'))
        buckets = tf.make_ndarray(new_value.tensor)
        self.assertEqual(old_value.histo.min, buckets[0][0])
        self.assertEqual(old_value.histo.max, buckets[-1][1])
        self.assertEqual(23 * 45, buckets[:, 2].astype(int).sum())
Ejemplo n.º 16
0
  def test_histogram(self):
    old_op = tf.summary.histogram('important_data',
                                  tf.random_normal(shape=[23, 45]))
    old_value = self._value_from_op(old_op)
    assert old_value.HasField('histo'), old_value
    new_value = data_compat.migrate_value(old_value)

    self.assertEqual('important_data', new_value.tag)
    expected_metadata = histogram_metadata.create_summary_metadata(
        display_name='important_data', description='')
    self.assertEqual(expected_metadata, new_value.metadata)
    self.assertTrue(new_value.HasField('tensor'))
    buckets = tf.make_ndarray(new_value.tensor)
    self.assertEqual(old_value.histo.min, buckets[0][0])
    self.assertEqual(old_value.histo.max, buckets[-1][1])
    self.assertEqual(23 * 45, buckets[:, 2].astype(int).sum())
Ejemplo n.º 17
0
    def test_histogram(self):
        with tf.compat.v1.Graph().as_default():
            old_op = tf.compat.v1.summary.histogram(
                "important_data", tf.random.normal(shape=[23, 45]))
            old_value = self._value_from_op(old_op)
        assert old_value.HasField("histo"), old_value
        new_value = data_compat.migrate_value(old_value)

        self.assertEqual("important_data", new_value.tag)
        expected_metadata = histogram_metadata.create_summary_metadata(
            display_name="important_data", description="")
        self.assertEqual(expected_metadata, new_value.metadata)
        self.assertTrue(new_value.HasField("tensor"))
        buckets = tensor_util.make_ndarray(new_value.tensor)
        self.assertEqual(old_value.histo.min, buckets[0][0])
        self.assertEqual(old_value.histo.max, buckets[-1][1])
        self.assertEqual(23 * 45, buckets[:, 2].astype(int).sum())
Ejemplo n.º 18
0
def histogram_discrete(name,
                       data,
                       bucket_min,
                       bucket_max,
                       step=None,
                       description=None):
    """histogram for discrete data.

    Args:
        name (str): name for this summary
        data (Tensor): A `Tensor` integers of any shape.
        bucket_min (int): represent bucket min value
        bucket_max (int): represent bucket max value
            bucket count is calculate as `bucket_max - bucket_min + 1`
            and output will have this many buckets.
        step (None|tf.Variable):  step value for this summary. this defaults to
            `tf.summary.experimental.get_step()`
        description (str): Optional long-form description for this summary
    """
    summary_metadata = metadata.create_summary_metadata(
        display_name=None, description=description)
    summary_scope = (getattr(tf.summary.experimental, 'summary_scope', None)
                     or tf.summary.summary_scope)
    with summary_scope(name,
                       'histogram_summary',
                       values=[data, bucket_min, bucket_max,
                               step]) as (tag, _):
        with tf.name_scope('buckets'):
            bucket_count = bucket_max - bucket_min + 1
            data = data - bucket_min
            one_hots = tf.one_hot(tf.reshape(data, shape=[-1]),
                                  depth=bucket_count)
            bucket_counts = tf.cast(
                tf.reduce_sum(input_tensor=one_hots, axis=0), tf.float64)
            edge = tf.cast(tf.range(bucket_count), tf.float64)
            # histogram can not draw when left_edge == right_edge
            left_edge = edge - 1e-12
            right_edge = edge + 1e-12
            tensor = tf.transpose(
                a=tf.stack([left_edge, right_edge, bucket_counts]))

        return tf.summary.write(tag=tag,
                                tensor=tensor,
                                step=step,
                                metadata=summary_metadata)
Ejemplo n.º 19
0
def op(
    name,
    data,
    bucket_count=None,
    display_name=None,
    description=None,
    collections=None,
):
    """Create a legacy histogram summary op.

    Arguments:
      name: A unique name for the generated summary node.
      data: A `Tensor` of any shape. Must be castable to `float64`.
      bucket_count: 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.
      display_name: Optional name for this summary in TensorBoard, as a
        constant `str`. Defaults to `name`.
      description: Optional long-form description for this summary, as a
        constant `str`. Markdown is supported. Defaults to empty.
      collections: Optional list of graph collections keys. The new
        summary op is added to these collections. Defaults to
        `[Graph Keys.SUMMARIES]`.

    Returns:
      A TensorFlow summary op.
    """
    # TODO(nickfelt): remove on-demand imports once dep situation is fixed.
    import tensorflow.compat.v1 as tf

    if display_name is None:
        display_name = name
    summary_metadata = metadata.create_summary_metadata(
        display_name=display_name, description=description)
    with tf.name_scope(name):
        tensor = _buckets(data, bucket_count=bucket_count)
        return tf.summary.tensor_summary(
            name="histogram_summary",
            tensor=tensor,
            collections=collections,
            summary_metadata=summary_metadata,
        )
Ejemplo n.º 20
0
    def test_single_value_histogram(self):
        with tf.compat.v1.Graph().as_default():
            old_op = tf.compat.v1.summary.histogram("single_value_data",
                                                    tf.constant([1] * 1024))
            old_value = self._value_from_op(old_op)
        assert old_value.HasField("histo"), old_value
        new_value = data_compat.migrate_value(old_value)

        self.assertEqual("single_value_data", new_value.tag)
        expected_metadata = histogram_metadata.create_summary_metadata(
            display_name="single_value_data", description="")
        self.assertEqual(expected_metadata, new_value.metadata)
        self.assertTrue(new_value.HasField("tensor"))
        buckets = tensor_util.make_ndarray(new_value.tensor)
        # Only one bucket is kept.
        self.assertEqual((1, 3), buckets.shape)
        self.assertEqual(1, buckets[0][0])
        self.assertEqual(1, buckets[-1][1])
        self.assertEqual(1024, buckets[0][2])
Ejemplo n.º 21
0
def distribution(name, dist):
    dist = dist / tf.reduce_sum(dist)

    def entry(i, x):
        return tf.stack([
            tf.constant(i - 0.5, shape=(), dtype=tf.float32),
            tf.constant(i + 0.5, shape=(), dtype=tf.float32),
            tf.cast(x, dtype=tf.float32)
        ])

    dist_entries = tf.stack(
        [entry(i, p) for i, p in enumerate(tf.unstack(dist))])

    metadata = create_summary_metadata(display_name=name, description=None)

    dist_summary = tf.summary.tensor_summary(name,
                                             dist_entries,
                                             summary_metadata=metadata)

    return dist_summary
Ejemplo n.º 22
0
    def test_histogram_with_extremal_values(self):
        with tf.compat.v1.Graph().as_default():
            old_op = tf.compat.v1.summary.histogram("extremal_values",
                                                    tf.constant([-1e20, 1e20]))
            old_value = self._value_from_op(old_op)
        assert old_value.HasField("histo"), old_value
        new_value = data_compat.migrate_value(old_value)

        self.assertEqual("extremal_values", new_value.tag)
        expected_metadata = histogram_metadata.create_summary_metadata(
            display_name="extremal_values", description="")
        self.assertEqual(expected_metadata, new_value.metadata)
        self.assertTrue(new_value.HasField("tensor"))
        buckets = tensor_util.make_ndarray(new_value.tensor)
        for bucket in buckets:
            # No `backwards` buckets.
            self.assertLessEqual(bucket[0], bucket[1])
        self.assertEqual(old_value.histo.min, buckets[0][0])
        self.assertEqual(old_value.histo.max, buckets[-1][1])
        self.assertEqual(2, buckets[:, 2].astype(int).sum())
Ejemplo n.º 23
0
def _migrate_histogram_value(value):
    """Convert `old-style` histogram value to `new-style`.

    The "old-style" format can have outermost bucket limits of -DBL_MAX and
    DBL_MAX, which are problematic for visualization. We replace those here
    with the actual min and max values seen in the input data, but then in
    order to avoid introducing "backwards" buckets (where left edge > right
    edge), we first must drop all empty buckets on the left and right ends.
    """
    histogram_value = value.histo
    bucket_counts = histogram_value.bucket
    # Find the indices of the leftmost and rightmost non-empty buckets.
    n = len(bucket_counts)
    start = next((i for i in range(n) if bucket_counts[i] > 0), n)
    end = next((i for i in reversed(range(n)) if bucket_counts[i] > 0), -1)
    if start > end:
        # If all input buckets were empty, treat it as a zero-bucket
        # new-style histogram.
        buckets = np.zeros([0, 3], dtype=np.float32)
    else:
        # Discard empty buckets on both ends, and keep only the "inner"
        # edges from the remaining buckets. Note that bucket indices range
        # from `start` to `end` inclusive, but bucket_limit indices are
        # exclusive of `end` - this is because bucket_limit[i] is the
        # right-hand edge for bucket[i].
        bucket_counts = bucket_counts[start:end + 1]
        inner_edges = histogram_value.bucket_limit[start:end]
        # Use min as the left-hand limit for the first non-empty bucket.
        bucket_lefts = [histogram_value.min] + inner_edges
        # Use max as the right-hand limit for the last non-empty bucket.
        bucket_rights = inner_edges + [histogram_value.max]
        buckets = np.array([bucket_lefts, bucket_rights, bucket_counts],
                           dtype=np.float32).transpose()

    summary_metadata = histogram_metadata.create_summary_metadata(
        display_name=value.metadata.display_name or value.tag,
        description=value.metadata.summary_description,
    )

    return make_summary(value.tag, summary_metadata, buckets)
Ejemplo n.º 24
0
def op(name,
       data,
       bucket_count=None,
       display_name=None,
       description=None,
       collections=None):
  """Create a histogram summary op.

  Arguments:
    name: A unique name for the generated summary node.
    data: A `Tensor` of any shape. Must be castable to `float64`.
    bucket_count: 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.
    display_name: Optional name for this summary in TensorBoard, as a
      constant `str`. Defaults to `name`.
    description: Optional long-form description for this summary, as a
      constant `str`. Markdown is supported. Defaults to empty.
    collections: Optional list of graph collections keys. The new
      summary op is added to these collections. Defaults to
      `[Graph Keys.SUMMARIES]`.

  Returns:
    A TensorFlow summary op.
  """
  if display_name is None:
    display_name = name
  summary_metadata = metadata.create_summary_metadata(
      display_name=display_name, description=description)
  with tf.name_scope(name):
    tensor = _buckets(data, bucket_count=bucket_count)
    return tf.summary.tensor_summary(name='histogram_summary',
                                     tensor=tensor,
                                     collections=collections,
                                     summary_metadata=summary_metadata)
Ejemplo n.º 25
0
def histogram(name, data, step=None, buckets=None, description=None):
    """Write a histogram summary.

    See also `tf.summary.scalar`, `tf.summary.SummaryWriter`.

    Writes a histogram to the current default summary writer, for later analysis
    in TensorBoard's 'Histograms' and 'Distributions' dashboards (data written
    using this API will appear in both places). Like `tf.summary.scalar` points,
    each histogram is associated with a `step` and a `name`. All the histograms
    with the same `name` constitute a time series of histograms.

    The histogram is calculated over all the elements of the given `Tensor`
    without regard to its shape or rank.

    This example writes 2 histograms:

    ```python
    w = tf.summary.create_file_writer('test/logs')
    with w.as_default():
        tf.summary.histogram("activations", tf.random.uniform([100, 50]), step=0)
        tf.summary.histogram("initial_weights", tf.random.normal([1000]), step=0)
    ```

    A common use case is to examine the changing activation patterns (or lack
    thereof) at specific layers in a neural network, over time.

    ```python
    w = tf.summary.create_file_writer('test/logs')
    with w.as_default():
    for step in range(100):
        # Generate fake "activations".
        activations = [
            tf.random.normal([1000], mean=step, stddev=1),
            tf.random.normal([1000], mean=step, stddev=10),
            tf.random.normal([1000], mean=step, stddev=100),
        ]

        tf.summary.histogram("layer1/activate", activations[0], step=step)
        tf.summary.histogram("layer2/activate", activations[1], step=step)
        tf.summary.histogram("layer3/activate", activations[2], step=step)
    ```

    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. The histogram is computed over its elements,
        which must be castable to `float64`.
      step: Explicit `int64`-castable monotonic step value for this summary. If
        omitted, this defaults to `tf.summary.experimental.get_step()`, which must
        not be None.
      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 all buckets' left and right endpoints are the same
        and only the last bucket has nonzero count.
      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.

    Raises:
      ValueError: if a default writer exists, but no step was provided and
        `tf.summary.experimental.get_step()` is None.
    """
    # Avoid building unused gradient graphs for conds below. This works around
    # an error building second-order gradient graphs when XlaDynamicUpdateSlice
    # is used, and will generally speed up graph building slightly.
    data = tf.stop_gradient(data)
    summary_metadata = metadata.create_summary_metadata(
        display_name=None, description=description)
    # TODO(https://github.com/tensorflow/tensorboard/issues/2109): remove fallback
    summary_scope = (getattr(tf.summary.experimental, "summary_scope", None)
                     or tf.summary.summary_scope)

    # TODO(ytjing): add special case handling.
    with summary_scope(name, "histogram_summary", values=[data, buckets,
                                                          step]) as (tag, _):
        # Defer histogram bucketing logic by passing it as a callable to
        # write(), wrapped in a LazyTensorCreator for backwards
        # compatibility, so that we only do this work when summaries are
        # actually written.
        @lazy_tensor_creator.LazyTensorCreator
        def lazy_tensor():
            return _buckets(data, buckets)

        return tf.summary.write(
            tag=tag,
            tensor=lazy_tensor,
            step=step,
            metadata=summary_metadata,
        )
Ejemplo n.º 26
0
def layers(name, layers=None, gradients=None, activations=None):
    def entry(i, x):
        return tf.stack([
            tf.constant(i - 0.5, shape=(), dtype=tf.float32),
            tf.constant(i + 0.5, shape=(), dtype=tf.float32),
            tf.cast(x, dtype=tf.float32)
        ])

    with tf.name_scope(name):
        summary_list = []

        # weight size summary
        if layers is not None:
            sizes_entries = []
            for i, l in enumerate(layers):
                size = sum([tf.reduce_prod(tf.shape(v)) for v in l.variables])
                sizes_entries.append(entry(i, size))

            sizes_tensor = tf.stack(sizes_entries)
            total_size = np.sum(
                [np.prod(v.shape) for l in layers for v in l.variables])
            metadata = create_summary_metadata(
                display_name='sizes',
                description='total: {}'.format(total_size))
            sizes_summary = tf.summary.tensor_summary(
                'sizes', sizes_tensor, summary_metadata=metadata)
            summary_list.append(sizes_summary)

        # weight L2 norm summary
        if layers is not None:
            vars_entries = []
            for i, l in enumerate(layers):
                vnorm = sum([tf.nn.l2_loss(v) for v in l.variables])
                vars_entries.append(entry(i, vnorm))

            vars_tensor = tf.stack(vars_entries)
            metadata = create_summary_metadata(display_name='weights',
                                               description=None)
            vars_summary = tf.summary.tensor_summary('weights',
                                                     vars_tensor,
                                                     summary_metadata=metadata)
            summary_list.append(vars_summary)

        # activation L2 norm summary
        if activations is not None:
            act_entries = []
            for i, x in enumerate(activations):
                xnorm = tf.nn.l2_loss(x)
                act_entries.append(entry(i, xnorm))

            act_tensor = tf.stack(act_entries)
            metadata = create_summary_metadata(display_name='activations',
                                               description=None)
            act_summary = tf.summary.tensor_summary('activations',
                                                    act_tensor,
                                                    summary_metadata=metadata)
            summary_list.append(act_summary)

        # gradient L2 norm summary
        if layers is not None and gradients is not None:
            grads_entries = []
            for i, l in enumerate(layers):
                glist = [g for g, v in gradients if v in set(l.variables)]

                gnorm = sum([tf.nn.l2_loss(g) for g in glist])
                grads_entries.append(entry(i, gnorm))

            grads_tensor = tf.stack(grads_entries)
            metadata = create_summary_metadata(display_name='gradients',
                                               description=None)
            grads_summary = tf.summary.tensor_summary(
                'gradients', grads_tensor, summary_metadata=metadata)
            summary_list.append(grads_summary)

        merged_summary = tf.summary.merge(summary_list)

    return merged_summary
Ejemplo n.º 27
0
def histogram(name, data, step=None, buckets=None, description=None):
    """Write a histogram summary.

    See also `tf.summary.scalar`, `tf.summary.SummaryWriter`.

    Writes a histogram to the current default summary writer, for later analysis
    in TensorBoard's 'Histograms' and 'Distributions' dashboards (data written
    using this API will appear in both places). Like `tf.summary.scalar` points,
    each histogram is associated with a `step` and a `name`. All the histograms
    with the same `name` constitute a time series of histograms.

    The histogram is calculated over all the elements of the given `Tensor`
    without regard to its shape or rank.

    This example writes 2 histograms:

    ```python
    w = tf.summary.create_file_writer('test/logs')
    with w.as_default():
        tf.summary.histogram("activations", tf.random.uniform([100, 50]), step=0)
        tf.summary.histogram("initial_weights", tf.random.normal([1000]), step=0)
    ```

    A common use case is to examine the changing activation patterns (or lack
    thereof) at specific layers in a neural network, over time.

    ```python
    w = tf.summary.create_file_writer('test/logs')
    with w.as_default():
    for step in range(100):
        # Generate fake "activations".
        activations = [
            tf.random.normal([1000], mean=step, stddev=1),
            tf.random.normal([1000], mean=step, stddev=10),
            tf.random.normal([1000], mean=step, stddev=100),
        ]

        tf.summary.histogram("layer1/activate", activations[0], step=step)
        tf.summary.histogram("layer2/activate", activations[1], step=step)
        tf.summary.histogram("layer3/activate", activations[2], step=step)
    ```

    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. The histogram is computed over its elements,
        which must be castable to `float64`.
      step: Explicit `int64`-castable monotonic step value for this summary. If
        omitted, this defaults to `tf.summary.experimental.get_step()`, which must
        not be None.
      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.

    Raises:
      ValueError: if a default writer exists, but no step was provided and
        `tf.summary.experimental.get_step()` is None.
    """
    # Avoid building unused gradient graphs for conds below. This works around
    # an error building second-order gradient graphs when XlaDynamicUpdateSlice
    # is used, and will generally speed up graph building slightly.
    data = tf.stop_gradient(data)
    summary_metadata = metadata.create_summary_metadata(
        display_name=None, description=description)
    # TODO(https://github.com/tensorflow/tensorboard/issues/2109): remove fallback
    summary_scope = (getattr(tf.summary.experimental, "summary_scope", None)
                     or tf.summary.summary_scope)

    # Try to capture current name scope so we can re-enter it below within our
    # histogram_summary helper. We do this to avoid having the `tf.cond` below
    # insert an extra `cond` into the tag name.
    # TODO(https://github.com/tensorflow/tensorboard/issues/2885): Remove this
    # special handling once the format no longer requires dynamic output shapes.
    name_scope_cms = []
    if hasattr(tf, "get_current_name_scope"):
        # Coerce None to ""; this API should only return a string but as of TF
        # 2.5 it returns None in contexts that re-enter the empty scope.
        current_scope = tf.get_current_name_scope() or ""
        # Append a "/" to the scope name, which causes that scope to be treated
        # as absolute instead of relative to the current scope, so that we can
        # re-enter it. It also prevents auto-incrementing of the scope name.
        # This is legacy graph mode behavior, undocumented except in comments:
        # https://github.com/tensorflow/tensorflow/blob/v2.5.0/tensorflow/python/framework/ops.py#L6664-L6666
        scope_to_reenter = current_scope + "/" if current_scope else ""
        name_scope_cms.append(tf.name_scope(scope_to_reenter))

    def histogram_summary(data, buckets, histogram_metadata, step):
        with contextlib.ExitStack() as stack:
            for cm in name_scope_cms:
                stack.enter_context(cm)
            with summary_scope(name,
                               "histogram_summary",
                               values=[data, buckets, step]) as (tag, _):
                # Defer histogram bucketing logic by passing it as a callable to
                # write(), wrapped in a LazyTensorCreator for backwards
                # compatibility, so that we only do this work when summaries are
                # actually written.
                @lazy_tensor_creator.LazyTensorCreator
                def lazy_tensor():
                    return _buckets(data, buckets)

                return tf.summary.write(
                    tag=tag,
                    tensor=lazy_tensor,
                    step=step,
                    metadata=summary_metadata,
                )

    # `_buckets()` has dynamic output shapes which is not supported on TPU's.
    # To address this, explicitly mark this logic for outside compilation so it
    # will be executed on the CPU, and skip it entirely if we aren't actually
    # recording summaries to avoid overhead of transferring data.
    # TODO(https://github.com/tensorflow/tensorboard/issues/2885): Remove this
    # special handling once the format no longer requires dynamic output shapes.
    if isinstance(
            tf.distribute.get_strategy(),
        (tf.distribute.experimental.TPUStrategy, tf.distribute.TPUStrategy),
    ):
        return tf.cond(
            tf.summary.should_record_summaries(),
            lambda: tf.compat.v1.tpu.outside_compilation(
                histogram_summary, data, buckets, summary_metadata, step),
            lambda: False,
        )
    return histogram_summary(data, buckets, summary_metadata, step)
Ejemplo n.º 28
0
  def __init__(self,
               input_data,
               targets,
               difficulty,
               target_mask,
               sequence_length,
               params,
               is_training=False):
    """
    input_data: If non-sequence, then batch_size x feature_size
      otherwise batch_size x max_sequence_length x feature_size
    targets: If non-sequence, then batch_size x num_classes
      otherwise batch_size x max_sequence_length x num_classes
    sequence_length: If non-sequence, then None else tensor with shape batch_size
    """
    self.targets = targets
    self.params = params
    self.batch_size = params.batch_size
    self.hidden_size = params.hidden_size
    self.clip_grad_norm = params.clip_grad_norm
    self.use_lstm = params.use_lstm
    self.difficulty = difficulty
    self.max_difficulty = params.max_difficulty
    self.target_mask = target_mask

    # self.input_data has to be a (length max_sequence_length) list of tensors
    # with shape batch_size x feature_size
    self.input_data = tf.cast(input_data, tf.float32)
    if self.input_data.shape.ndims == 2:
      self.input_data = [self.input_data]
      assert sequence_length is None, 'Non-sequential inputs should leave sequence_length=None'
      sequence_length = tf.constant([1] * self.batch_size)
    elif self.input_data.shape.ndims == 3:
      self.input_data = tf.split(
          self.input_data, num_or_size_splits=self.input_data.shape[1], axis=1)
      self.input_data = [tf.squeeze(t, axis=1) for t in self.input_data]
    else:
      raise Exception('Input has to be of rank 2 or 3')

    # Set up ACT cell and inner rnn-type cell for use inside the ACT cell.
    with tf.variable_scope("rnn"):
      if self.use_lstm:
        inner_cell = BasicLSTMCell(self.hidden_size, state_is_tuple=False)
      else:
        inner_cell = GRUCell(self.hidden_size)

    with tf.variable_scope("ACT"):
      act = ACTCell(
          self.hidden_size,
          inner_cell,
          params.epsilon,
          use_new_ponder_cost=params.use_new_ponder_cost,
          max_computation=params.max_computation,
          batch_size=self.batch_size,
          difficulty=difficulty)

    self.outputs, _ = tf.nn.static_rnn(
        cell=act, inputs=self.input_data, dtype=tf.float32)

    output = tf.stack(self.outputs, axis=1)
    self.logits = tf.layers.dense(output, params.num_classes)

    if params.data == "addition":
      # reshape logits and labels to (batch size, sequence, digits, one hot)
      self.logits = tf.reshape(
          self.logits,
          shape=(params.batch_size, params.max_difficulty,
                 params.num_digits + 1, 10))
      self.targets = tf.reshape(
          self.targets,
          shape=(params.batch_size, params.max_difficulty,
                 params.num_digits + 1, 10))

    self.predictions = tf.nn.softmax(self.logits)
    self.target_mask = tf.cast(self.target_mask, tf.float32)

    ce = tf.nn.softmax_cross_entropy_with_logits_v2(
        labels=self.targets, logits=self.logits, dim=-1)
    masked_ce = self.target_mask * ce
    masked_reduced_ce = sparse_mean(masked_ce)

    # Compute the cross entropy based pondering cost multiplier
    avg_ce = tf.Variable(initial_value=0.7, trainable=False)
    avg_ce_decay = 0.85
    avg_ce_update_op = tf.assign(
        avg_ce,
        avg_ce_decay * avg_ce + (1.0 - avg_ce_decay) * masked_reduced_ce)
    with tf.control_dependencies([avg_ce_update_op]):
      inverse_difficulty = safe_div(avg_ce, masked_ce)
      inverse_difficulty /= sparse_mean(inverse_difficulty)
      # ponder_v2 has NaN problem in its backward pass without this
      inverse_difficulty = tf.stop_gradient(inverse_difficulty)

    # Add up loss and retrieve batch-normalised ponder cost: sum N + sum
    # Remainder
    ponder_cost = act.calculate_ponder_cost(
        time_penalty=self.params.ponder_time_penalty,
        inverse_difficulty=inverse_difficulty)

    masked_reduced_ponder_cost = sparse_mean(self.target_mask * ponder_cost)

    self.cost = masked_reduced_ce + masked_reduced_ponder_cost

    if is_training:
      tvars = tf.trainable_variables()
      grads, _ = tf.clip_by_global_norm(
          tf.gradients(self.cost, tvars), self.clip_grad_norm)
      optimizer = tf.contrib.estimator.TowerOptimizer(
          tf.train.AdamOptimizer(self.params.learning_rate))
      apply_gradients = optimizer.apply_gradients(zip(grads, tvars))

      gs = tf.train.get_global_step()
      self.train_op = tf.group(apply_gradients, tf.assign_add(gs, 1))

    # Cost metrics
    tf.summary.scalar("ce", masked_reduced_ce)
    tf.summary.scalar("average_inverse_difficulty",
                      sparse_mean(inverse_difficulty * self.target_mask))

    # Pondering metrics
    pondering = tf.stack(act.iterations, axis=-1) + 1

    if params.data == "addition" and pondering.shape.ndims == 2:
      # expand pondering to 3 dimension with repeated last dimension
      pondering = tf.tile(
          tf.expand_dims(pondering, -1), [1, 1, self.target_mask.shape[-1]])

    masked_pondering = self.target_mask * pondering
    dense_pondering = tf.gather_nd(
        masked_pondering, indices=tf.where(tf.not_equal(masked_pondering, 0)))
    tf.summary.scalar("average_pondering", tf.reduce_mean(dense_pondering))
    tf.summary.histogram("pondering", dense_pondering)

    if params.data == "addition":
      avg_pondering = tf.reduce_sum(masked_pondering, axis=[-1, -2]) / \
                      tf.count_nonzero(masked_pondering, axis=[-1, -2],
                                       dtype=tf.float32)
    else:
      avg_pondering = tf.reduce_sum(masked_pondering, axis=-1) / \
                      tf.count_nonzero(masked_pondering, axis=-1,
                                       dtype=tf.float32)

    summary_ponder_metadata = histogram_metadata.create_summary_metadata(
        "difficulty/pondering", "ponder_steps_difficulty")
    summary_ce_metadata = histogram_metadata.create_summary_metadata(
        "difficulty/ce", "ce_steps_difficulty")
    input_difficulty_steps = tf.cast(self.difficulty, tf.float32)
    ponder_steps = tf.cast(avg_pondering, tf.float32)
    ce_steps = tf.cast(masked_reduced_ce, tf.float32)
    ponder_heights = []
    ce_heights = []
    for i in range(self.max_difficulty):
      mask = tf.to_float(tf.equal(self.difficulty, i))
      ponder_avg_steps = tf.cond(
          tf.equal(tf.reduce_sum(mask), 0), lambda: 0.0,
          lambda: tf.reduce_sum(mask * ponder_steps) / tf.reduce_sum(mask))
      ce_avg_steps = tf.cond(
          tf.equal(tf.reduce_sum(mask), 0), lambda: 0.0,
          lambda: tf.reduce_sum(mask * ce_steps) / tf.reduce_sum(mask))
      ponder_heights.append(ponder_avg_steps)
      ce_heights.append(ce_avg_steps)

    ponder_difficulty_steps = tf.transpose(
        tf.stack([
            tf.range(self.max_difficulty, dtype=tf.float32),
            tf.range(self.max_difficulty, dtype=tf.float32) + 1, ponder_heights
        ]))
    ce_difficulty_steps = tf.transpose(
        tf.stack([
            tf.range(self.max_difficulty, dtype=tf.float32),
            tf.range(self.max_difficulty, dtype=tf.float32) + 1, ce_heights
        ]))

    tf.summary.tensor_summary(
        name='ponder_steps_difficulty',
        tensor=ponder_difficulty_steps,
        collections=None,
        summary_metadata=summary_ponder_metadata)

    tf.summary.tensor_summary(
        name='ce_steps_difficulty',
        tensor=ce_difficulty_steps,
        collections=None,
        summary_metadata=summary_ce_metadata)