Example #1
0
    def sample(self,
               n_samples=None,
               group_ndims=0,
               is_reparameterized=None,
               compute_density=None,
               name=None):
        group_ndims = validate_group_ndims_arg(group_ndims)
        if not compute_density and compute_density is not None:
            raise RuntimeError('`FlowDistribution` requires `compute_prob` '
                               'not to be False.')

        with tf.name_scope(name, default_name='FlowDistribution.sample'):
            # sample from the base distribution
            x = self._distribution.sample(
                n_samples=n_samples,
                group_ndims=group_ndims,
                is_reparameterized=is_reparameterized,
                compute_density=True)

            # now do the transformation
            is_reparameterized = x.is_reparameterized
            y, log_det = self._flow.transform(x)  # y, log |dy/dx|
            if not is_reparameterized:
                y = tf.stop_gradient(y)  # important!

            # compose the transformed tensor
            return StochasticTensor(
                distribution=self,
                tensor=y,
                n_samples=n_samples,
                group_ndims=group_ndims,
                is_reparameterized=is_reparameterized,
                # log p(y) = log p(x) - log |dy/dx|
                log_prob=x.log_prob() - log_det)
Example #2
0
    def __init__(self,
                 distribution,
                 tensor,
                 n_samples=None,
                 group_ndims=0,
                 is_reparameterized=None,
                 flow_origin=None,
                 log_prob=None):
        """
        Construct the :class:`StochasticTensor`.

        Args:
            distribution (tfsnippet.distributions.Distribution): The
                distribution of this :class:`StochasticTensor`.
            tensor (tf.Tensor or TensorWrapper): The samples or observations
                of this :class:`StochasticTensor`.
            n_samples (tf.Tensor or int): The number of samples taken in
                :class:`Distribution.sample`.  If not :obj:`None`, the first
                dimension of `tensor` should be the sampling dimension.
            group_ndims (int or tf.Tensor): The number of dimensions to be
                considered as events group in samples. (default 0)
            is_reparameterized (bool): Whether or not the samples are
                re-parameterized?  If not specified, will inherit from
                :attr:`tfsnippet.distributions.Distribution.is_reparameterized`.
            log_prob (Tensor or None): Pre-computed log-density of `tensor`,
                given `group_ndims`.
            flow_origin (StochasticTensor): The original stochastic tensor
                from the base distribution of a
                :class:`tfsnippet.FlowDistribution`.
        """
        from tfsnippet.utils import TensorArgValidator, validate_group_ndims_arg

        if is_reparameterized is None:
            is_reparameterized = distribution.is_reparameterized
        if log_prob is not None and not is_tensor_object(log_prob):
            log_prob = tf.convert_to_tensor(log_prob)

        n_samples = validate_n_samples_arg(n_samples, 'n_samples')
        if n_samples is not None:
            with tf.name_scope('validate_n_samples'):
                validator = TensorArgValidator('n_samples')
                n_samples = validator.require_non_negative(
                    validator.require_int32(n_samples))

        group_ndims = validate_group_ndims_arg(group_ndims)

        super(StochasticTensor, self).__init__()
        self._self_distribution = distribution
        self._self_tensor = tf.convert_to_tensor(tensor)
        self._self_n_samples = n_samples
        self._self_group_ndims = group_ndims
        self._self_is_reparameterized = is_reparameterized
        self._self_flow_origin = flow_origin
        self._self_log_prob = log_prob
        self._self_prob = None
Example #3
0
def reduce_group_ndims(operation, tensor, group_ndims, name=None):
    """
    Reduce the last `group_ndims` dimensions in `tensor`, using `operation`.

    In :class:`~tfsnippet.distributions.Distribution`, when computing the
    (log-)densities of certain `tensor`, the last few dimensions
    may represent a group of events, thus should be accounted together.
    This method can be used to reduce these dimensions, for example:

    .. code-block:: python

         log_prob = reduce_group_ndims(tf.reduce_sum, log_prob, group_ndims)
         prob = reduce_group_ndims(tf.reduce_prod, log_prob, group_ndims)

    Args:
        operation: The operation for reducing the last `group_ndims`
            dimensions. It must receive `tensor` as the 1st argument, and
            `axis` as the 2nd argument.
        tensor: The tensor to be reduced.
        group_ndims: The number of dimensions at the end of `tensor` to be
            reduced.  If it is a constant integer and is zero, then no
            operation will take place.
        name: TensorFlow name scope of the graph nodes. (default
            "reduce_group_ndims")

    Returns:
        tf.Tensor: The reduced tensor.

    Raises:
        ValueError: If `group_ndims` cannot be validated by
            :meth:`validate_group_ndims`.
    """
    group_ndims = validate_group_ndims_arg(group_ndims)
    with tf.name_scope(name, default_name='reduce_group_ndims'):
        if is_tensor_object(group_ndims):
            tensor = tf.cond(
                group_ndims > 0,
                lambda: operation(tensor, tf.range(-group_ndims, 0)),
                lambda: tensor)
        else:
            if group_ndims > 0:
                tensor = operation(tensor, tf.range(-group_ndims, 0))
    return tensor
Example #4
0
    def sample(self,
               n_samples=None,
               group_ndims=0,
               is_reparameterized=None,
               compute_density=None,
               name=None):
        group_ndims = validate_group_ndims_arg(group_ndims)
        if not compute_density and compute_density is not None:
            raise RuntimeError('`FlowDistribution` requires `compute_prob` '
                               'not to be False.')

        with tf.name_scope(name, default_name='FlowDistribution.sample'):
            # x and log p(x)
            ndims_diff = (self.flow.x_value_ndims -
                          self.base_distribution.value_ndims)
            x = self._distribution.sample(
                n_samples=n_samples,
                group_ndims=ndims_diff,
                is_reparameterized=is_reparameterized,
                compute_density=True)
            log_px = x.log_prob()

            # y, log |dy/dx|
            is_reparameterized = x.is_reparameterized
            y, log_det = self._flow.transform(x)
            if not is_reparameterized:
                y = tf.stop_gradient(y)  # important!

            # compute log p(y) = log p(x) - log |dy/dx|
            # and then apply `group_ndims` on log p(y)
            log_py = reduce_group_ndims(tf.reduce_sum, log_px - log_det,
                                        group_ndims)

            # compose the transformed tensor
            return StochasticTensor(distribution=self,
                                    tensor=y,
                                    n_samples=n_samples,
                                    group_ndims=group_ndims,
                                    is_reparameterized=is_reparameterized,
                                    log_prob=FlowDistributionDerivedTensor(
                                        tensor=log_py, flow_origin=x),
                                    flow_origin=x)