示例#1
0
 def test_tails_and_offset_are_in_order(self):
   dist = self.dist_cls(loc=10.3, scale=1.5)
   offset = helpers.quantization_offset(dist)
   lower_tail = helpers.lower_tail(dist, 2**-8)
   upper_tail = helpers.upper_tail(dist, 2**-8)
   self.assertGreater(upper_tail, lower_tail)
   self.assertAllClose(offset, 0.3)
 def test_tails_and_offset_are_in_order(self):
     dist = self.dist_cls(loc=10, scale=[1.5, 2], weight=[.5, .5])
     offset = helpers.quantization_offset(dist)
     lower_tail = helpers.lower_tail(dist, 2**-8)
     upper_tail = helpers.upper_tail(dist, 2**-8)
     self.assertGreater(upper_tail, offset)
     self.assertGreater(offset, lower_tail)
示例#3
0
 def test_tails_and_offset_are_in_order(self):
   dist = self.dist_cls(loc=[5.4, 8.6], scale=[1.4, 2], weight=[.6, .4])
   offset = helpers.quantization_offset(dist)
   lower_tail = helpers.lower_tail(dist, 2**-8)
   upper_tail = helpers.upper_tail(dist, 2**-8)
   self.assertGreater(upper_tail, lower_tail)
   self.assertAllClose(offset, 0.4)  # Decimal part of the peakiest mode (5.4).
示例#4
0
 def test_tails_and_offset_are_in_order(self):
     df = deep_factorized.NoisyDeepFactorized()
     offset = helpers.quantization_offset(df)
     lower_tail = helpers.lower_tail(df, 2**-8)
     upper_tail = helpers.upper_tail(df, 2**-8)
     self.assertGreater(upper_tail, offset)
     self.assertGreater(offset, lower_tail)
示例#5
0
  def _build_tables(self, prior):
    """Computes integer-valued probability tables used by the range coder.

    These tables must not be re-generated independently on the sending and
    receiving side, since small numerical discrepancies between both sides can
    occur in this process. If the tables differ slightly, this in turn would
    very likely cause catastrophic error propagation during range decoding. For
    a more in-depth discussion of this, see:

    > "Integer Networks for Data Compression with Latent-Variable Models"<br />
    > J. Ballé, N. Johnston, D. Minnen<br />
    > https://openreview.net/forum?id=S1zz2i0cY7

    The tables are stored in `tf.Variable`s as attributes of this object. The
    recommended way is to train the model, instantiate an entropy model with
    `compression=True`, and then distribute the model to a sender and a
    receiver.

    Args:
      prior: The `tfp.distributions.Distribution` object (see initializer).
    """
    # TODO(jonycgn, relational): Consider not using offset when soft quantization
    # is used.
    offset = self._offset_from_prior(prior)
    lower_tail = helpers.lower_tail(prior, self.tail_mass)
    upper_tail = helpers.upper_tail(prior, self.tail_mass)
    # Integers such that:
    # minima + offset < lower_tail
    # maxima + offset > upper_tail
    minima = tf.cast(tf.math.floor(lower_tail - offset), tf.int32)
    maxima = tf.cast(tf.math.ceil(upper_tail - offset), tf.int32)

    # PMF starting positions and lengths.
    pmf_start = tf.cast(minima, self.dtype) + offset
    pmf_length = maxima - minima + 1

    # Sample the densities in the computed ranges, possibly computing more
    # samples than necessary at the upper end.
    max_length = tf.math.reduce_max(pmf_length)
    if tf.executing_eagerly() and max_length > 2048:
      logging.warning(
          "Very wide PMF with %d elements may lead to out of memory issues. "
          "Consider priors with smaller dispersion or increasing `tail_mass` "
          "parameter.", int(max_length))
    samples = tf.range(tf.cast(max_length, self.dtype), dtype=self.dtype)
    samples = tf.reshape(samples, [-1] + len(self.context_shape) * [1])
    samples += pmf_start
    pmf = prior.prob(samples)

    # Collapse batch dimensions of distribution.
    pmf = tf.reshape(pmf, [max_length, -1])
    pmf = tf.transpose(pmf)

    pmf_length = tf.broadcast_to(pmf_length, self.context_shape_tensor)
    pmf_length = tf.reshape(pmf_length, [-1])
    cdf_length = pmf_length + 2
    cdf_offset = tf.broadcast_to(minima, self.context_shape_tensor)
    cdf_offset = tf.reshape(cdf_offset, [-1])

    # Prevent tensors from bouncing back and forth between host and GPU.
    with tf.device("/cpu:0"):
      def loop_body(args):
        prob, length = args
        prob = prob[:length]
        overflow = tf.math.maximum(1 - tf.reduce_sum(prob, keepdims=True), 0.)
        prob = tf.concat([prob, overflow], axis=0)
        cdf = range_coding_ops.pmf_to_quantized_cdf(
            prob, precision=self.range_coder_precision)
        return tf.pad(
            cdf, [[0, max_length - length]], mode="CONSTANT", constant_values=0)

      # TODO(jonycgn,ssjhv): Consider switching to Python control flow.
      cdf = tf.map_fn(
          loop_body, (pmf, pmf_length), dtype=tf.int32, name="pmf_to_cdf")

    if self.no_variables:
      self._cdf = cdf
      self._cdf_offset = cdf_offset
      self._cdf_length = cdf_length
    else:
      self._cdf = tf.Variable(cdf, trainable=False, name="cdf")
      self._cdf_offset = tf.Variable(
          cdf_offset, trainable=False, name="cdf_offset")
      self._cdf_length = tf.Variable(
          cdf_length, trainable=False, name="cdf_length")
 def test_tails_are_in_order(self):
     df = deep_factorized.NoisyDeepFactorized()
     lower_tail = helpers.lower_tail(df, 2**-8)
     upper_tail = helpers.upper_tail(df, 2**-8)
     self.assertGreater(upper_tail, lower_tail)
示例#7
0
 def test_deep_factorized_tails_are_in_order(self):
     dist = deep_factorized.DeepFactorized(batch_shape=[10])
     self.assertAllGreater(
         helpers.upper_tail(dist, 2**-8) - helpers.lower_tail(dist, 2**-8),
         0)
示例#8
0
 def test_normal_tails_are_in_order(self):
     dist = tfp.distributions.Normal(loc=3., scale=5.)
     self.assertGreater(helpers.upper_tail(dist, 2**-8),
                        helpers.lower_tail(dist, 2**-8))
示例#9
0
 def test_logistic_tails_are_in_order(self):
     dist = tfp.distributions.Logistic(loc=-3., scale=1.)
     self.assertGreater(helpers.upper_tail(dist, 2**-8),
                        helpers.lower_tail(dist, 2**-8))
示例#10
0
 def test_laplace_tails_are_in_order(self):
     dist = tfp.distributions.Laplace(loc=-2., scale=5.)
     self.assertGreater(helpers.upper_tail(dist, 2**-8),
                        helpers.lower_tail(dist, 2**-8))
示例#11
0
 def test_cauchy_tails_are_in_order(self):
     dist = tfp.distributions.Cauchy(loc=1.5, scale=3.)
     self.assertGreater(helpers.upper_tail(dist, 2**-8),
                        helpers.lower_tail(dist, 2**-8))
示例#12
0
  def _build_tables(self, prior, precision, offset=None):
    """Computes integer-valued probability tables used by the range coder.

    These tables must not be re-generated independently on the sending and
    receiving side, since small numerical discrepancies between both sides can
    occur in this process. If the tables differ slightly, this in turn would
    very likely cause catastrophic error propagation during range decoding. For
    a more in-depth discussion of this, see:

    > "Integer Networks for Data Compression with Latent-Variable Models"<br />
    > J. Ballé, N. Johnston, D. Minnen<br />
    > https://openreview.net/forum?id=S1zz2i0cY7

    Args:
      prior: The `tfp.distributions.Distribution` object (see initializer).
      precision: Integer. Precision for range coder.
      offset: None or float tensor between -.5 and +.5. Sub-integer quantization
        offsets to use for sampling prior probabilities. Defaults to 0.

    Returns:
      CDF table, CDF offsets, CDF lengths.
    """
    precision = int(precision)
    offset = tf.cast(0 if offset is None else offset, prior.dtype)
    # Subclasses should have already caught this, but better be safe.
    assert not prior.event_shape.rank

    lower_tail = helpers.lower_tail(prior, self.tail_mass)
    upper_tail = helpers.upper_tail(prior, self.tail_mass)
    # Integers such that:
    # minima + offset < lower_tail
    # maxima + offset > upper_tail
    minima = tf.cast(tf.math.floor(lower_tail - offset), tf.int32)
    maxima = tf.cast(tf.math.ceil(upper_tail - offset), tf.int32)

    # PMF starting positions and lengths.
    pmf_start = tf.cast(minima, prior.dtype) + offset
    pmf_length = maxima - minima + 1

    # Sample the densities in the computed ranges, possibly computing more
    # samples than necessary at the upper end.
    max_length = tf.math.reduce_max(pmf_length)
    if tf.executing_eagerly() and max_length > 2048:
      logging.warning(
          "Very wide PMF with %d elements may lead to out of memory issues. "
          "Consider priors with smaller variance, or increasing `tail_mass` "
          "parameter.", int(max_length))
    samples = tf.range(tf.cast(max_length, prior.dtype), dtype=prior.dtype)
    samples = tf.reshape(samples, [-1] + pmf_length.shape.rank * [1])
    samples += pmf_start
    pmf = prior.prob(samples)
    pmf_shape = tf.shape(pmf)[1:]
    num_pmfs = tf.reduce_prod(pmf_shape)

    # Collapse batch dimensions of distribution.
    pmf = tf.reshape(pmf, [max_length, num_pmfs])
    pmf = tf.transpose(pmf)

    pmf_length = tf.broadcast_to(pmf_length, pmf_shape)
    pmf_length = tf.reshape(pmf_length, [num_pmfs])
    cdf_offset = tf.broadcast_to(minima, pmf_shape)
    cdf_offset = tf.reshape(cdf_offset, [num_pmfs])
    precision_tensor = tf.constant([-precision], dtype=tf.int32)

    # Prevent tensors from bouncing back and forth between host and GPU.
    with tf.device("/cpu:0"):
      def loop_body(i, cdf):
        p = pmf[i, :pmf_length[i]]
        overflow = tf.math.maximum(1. - tf.reduce_sum(p, keepdims=True), 0.)
        p = tf.cast(tf.concat([p, overflow], 0), tf.float32)
        c = gen_ops.pmf_to_quantized_cdf(p, precision=precision)
        return i + 1, tf.concat([cdf, precision_tensor, c], 0)
      i_0 = tf.constant(0, tf.int32)
      cdf_0 = tf.constant([], tf.int32)
      _, cdf = tf.while_loop(
          lambda i, _: i < num_pmfs, loop_body, (i_0, cdf_0),
          shape_invariants=(i_0.shape, tf.TensorShape([None])),
          name="pmf_to_cdf")

    return cdf, cdf_offset
示例#13
0
 def _lower_tail(self, tail_mass):
     return tf.math.floor(helpers.lower_tail(self.base, tail_mass))
示例#14
0
 def _lower_tail(self, tail_mass):
     # Same logic as for _quantile.
     if not self.invertible:
         raise NotImplementedError()
     return self.transform(helpers.lower_tail(self.base, tail_mass))
示例#15
0
 def lower_tail(self):
     """Approximate lower tail quantile for range coding."""
     return helpers.lower_tail(self.prior, self.tail_mass)
示例#16
0
 def _lower_tail(self, tail_mass):
     return helpers.lower_tail(self.base, tail_mass)