コード例 #1
0
class MPEState:
    """Assembles TF operations computing MPE state for an SPN.

    Args:
        mpe_path (MPEPath): Pre-computed MPE_path.
        value_inference_type (InferenceType): The inference type used during the
            upwards pass through the SPN. Ignored if ``mpe_path`` is given.
        log (bool): If ``True``, calculate the value in the log space. Ignored
                    if ``mpe_path`` is given.
    """
    def __init__(self,
                 mpe_path=None,
                 log=True,
                 value_inference_type=None,
                 matmul_or_conv=False):
        # Create internal MPE path generator
        if mpe_path is None:
            self._mpe_path = MPEPath(log=log,
                                     value_inference_type=value_inference_type,
                                     use_unweighted=False,
                                     matmul_or_conv=matmul_or_conv)
        else:
            self._mpe_path = mpe_path

    @property
    def mpe_path(self):
        """MPEPath: Computed MPE path."""
        return self._mpe_path

    def get_state(self, root, *var_nodes):
        """Assemble TF operations computing the MPE state of the given SPN
        variables for the SPN rooted in ``root``.

        Args:
            root (Node): The root node of the SPN graph.
            *var_nodes (VarNode): Variable nodes for which the state should
                                  be computed.

        Returns:
            list of Tensor: A list of tensors containing the MPE state for the
            variable nodes.
        """
        # Generate path if not yet generated
        if not self._mpe_path.counts:
            self._mpe_path.get_mpe_path(root)

        with tf.name_scope("MPEState"):
            return tuple(
                var_node._compute_mpe_state(self._mpe_path.counts[var_node])
                for var_node in var_nodes)
コード例 #2
0
ファイル: em.py プロジェクト: hrstoyanov/libspn
class EMLearning:
    """Assembles TF operations performing EM learning of an SPN.

    Args:
        mpe_path (MPEPath): Pre-computed MPE_path.
        value_inference_type (InferenceType): The inference type used during the
            upwards pass through the SPN. Ignored if ``mpe_path`` is given.
        log (bool): If ``True``, calculate the value in the log space. Ignored
                    if ``mpe_path`` is given.
    """

    ParamNode = namedtuple("ParamNode", ["node", "name_scope", "accum"])
    GaussianLeafNode = namedtuple(
        "GaussianLeafNode", ["node", "name_scope", "accum", "sum_data", "sum_data_squared"])

    def __init__(self, root, mpe_path=None, log=True, value_inference_type=None,
                 additive_smoothing=None, add_random=None, initial_accum_value=None,
                 use_unweighted=False, sample=False, sample_prob=None,
                 dropconnect_keep_prob=None):
        self._root = root
        self._log = log
        self._additive_smoothing = additive_smoothing
        self._initial_accum_value = initial_accum_value
        self._sample = sample
        # Create internal MPE path generator
        if mpe_path is None:
            self._mpe_path = MPEPath(
                log=log, value_inference_type=value_inference_type, add_random=add_random,
                use_unweighted=use_unweighted, sample=sample, sample_prob=sample_prob,
                dropconnect_keep_prob=dropconnect_keep_prob)
        else:
            self._mpe_path = mpe_path
        # Create a name scope
        with tf.name_scope("EMLearning") as self._name_scope:
            pass
        # Create accumulators
        self._create_accumulators()

    @property
    def mpe_path(self):
        """MPEPath: Computed MPE path."""
        return self._mpe_path

    @property
    def value(self):
        """Value or LogValue: Computed SPN values."""
        return self._mpe_path.value

    # TODO: For testing only
    def root_accum(self):
        for pn in self._param_nodes:
            if pn.node == self._root.weights.node:
                return pn.accum
        return None

    @utils.lru_cache
    def reset_accumulators(self):
        with tf.name_scope(self._name_scope):
            return tf.group(*(
                    [pn.accum.initializer for pn in self._param_nodes] +
                    [dn.accum.initializer for dn in self._gaussian_leaf_nodes] +
                    [dn.sum_data.initializer for dn in self._gaussian_leaf_nodes] +
                    [dn.sum_data_squared.initializer for dn in self._gaussian_leaf_nodes] +
                    [dn.node._total_count_variable.initializer
                     for dn in self._gaussian_leaf_nodes]),
                            name="reset_accumulators")

    def init_accumulators(self):
        return self.reset_accumulators()

    def accumulate_and_update_weights(self):
        accumulate_updates = self.accumulate_updates()
        with tf.control_dependencies([accumulate_updates]):
            return self.update_spn()

    def accumulate_updates(self):
        # Generate path if not yet generated
        if not self._mpe_path.counts:
            self._mpe_path.get_mpe_path(self._root)

        # Generate all accumulate operations
        with tf.name_scope(self._name_scope):
            assign_ops = []
            for pn in self._param_nodes:
                with tf.name_scope(pn.name_scope):
                    # counts = self._mpe_path.counts[pn.node]
                    # update_value = pn.node._compute_hard_em_update(counts)
                    # with tf.control_dependencies([update_value]):
                    # op = tf.assign_add(pn.accum, self._mpe_path.counts[pn.node])
                    counts_summed_batch = pn.node._compute_hard_em_update(
                        self._mpe_path.counts[pn.node])
                    assign_ops.append(tf.assign_add(pn.accum, counts_summed_batch))

            for dn in self._gaussian_leaf_nodes:
                with tf.name_scope(dn.name_scope):
                    counts = self._mpe_path.counts[dn.node]
                    update_value = dn.node._compute_hard_em_update(counts)
                    with tf.control_dependencies(update_value.values()):
                        assign_ops.append(tf.assign_add(dn.accum, update_value['accum']))
                        assign_ops.append(tf.assign_add(dn.sum_data, update_value['sum_data']))
                        assign_ops.append(tf.assign_add(
                            dn.sum_data_squared, update_value['sum_data_squared']))

            return tf.group(*assign_ops, name="accumulate_updates")

    def update_spn(self):
        # Generate all update operations
        with tf.name_scope(self._name_scope):
            assign_ops = []
            for pn in self._param_nodes:
                with tf.name_scope(pn.name_scope):
                    accum = pn.accum
                    if self._additive_smoothing is not None:
                        accum = tf.add(accum, self._additive_smoothing)
                    if pn.node.log:
                        assign_ops.append(pn.node.assign_log(tf.log(accum)))
                    else:
                        assign_ops.append(pn.node.assign(accum))

            for dn in self._gaussian_leaf_nodes:
                with tf.name_scope(dn.name_scope):
                    assign_ops.extend(dn.node.assign(dn.accum, dn.sum_data, dn.sum_data_squared))

            return tf.group(*assign_ops, name="update_spn")

    def learn(self):
        """Assemble TF operations performing EM learning of the SPN."""
        return None

    def _create_accumulators(self):
        def fun(node):
            if node.is_param:
                with tf.name_scope(node.name) as scope:
                    if self._initial_accum_value is not None:
                        if node.mask and not all(node.mask):
                            accum = tf.Variable(tf.cast(tf.reshape(node.mask,
                                                node.variable.shape),
                                                dtype=conf.dtype) *
                                                self._initial_accum_value,
                                                dtype=conf.dtype,
                                                collections=['em_accumulators'])
                        else:
                            accum = tf.Variable(tf.ones_like(node.variable,
                                                             dtype=conf.dtype) *
                                                self._initial_accum_value,
                                                dtype=conf.dtype,
                                                collections=['em_accumulators'])
                    else:
                        accum = tf.Variable(tf.zeros_like(node.variable,
                                                          dtype=conf.dtype),
                                            dtype=conf.dtype,
                                            collections=['em_accumulators'])
                    param_node = EMLearning.ParamNode(node=node, accum=accum,
                                                      name_scope=scope)
                    self._param_nodes.append(param_node)
            if isinstance(node, GaussianLeaf) and node.learn_distribution_parameters:
                with tf.name_scope(node.name) as scope:
                    if self._initial_accum_value is not None:
                        accum = tf.Variable(tf.ones_like(node.loc_variable, dtype=conf.dtype) *
                                            self._initial_accum_value,
                                            dtype=conf.dtype,
                                            collections=['em_accumulators'])
                        sum_x = tf.Variable(node.loc_variable * self._initial_accum_value,
                                            dtype=conf.dtype, collections=['em_accumulators'])
                        sum_x2 = tf.Variable(tf.square(node.loc_variable) *
                                             self._initial_accum_value,
                                             dtype=conf.dtype, collections=['em_accumulators'])
                    else:
                        accum = tf.Variable(tf.zeros_like(node.loc_variable, dtype=conf.dtype),
                                            dtype=conf.dtype,
                                            collections=['em_accumulators'])
                        sum_x = tf.Variable(tf.zeros_like(node.loc_variable), dtype=conf.dtype,
                                            collections=['em_accumulators'])
                        sum_x2 = tf.Variable(tf.zeros_like(node.loc_variable), dtype=conf.dtype,
                                             collections=['em_accumulators'])
                    gaussian_node = EMLearning.GaussianLeafNode(
                        node=node, accum=accum, sum_data=sum_x, sum_data_squared=sum_x2,
                        name_scope=scope)
                    self._gaussian_leaf_nodes.append(gaussian_node)

        self._gaussian_leaf_nodes = []
        self._param_nodes = []
        with tf.name_scope(self._name_scope):
            traverse_graph(self._root, fun=fun)
コード例 #3
0
ファイル: em.py プロジェクト: jostosh/libspn
class EMLearning():
    """Assembles TF operations performing EM learning of an SPN.

    Args:
        mpe_path (MPEPath): Pre-computed MPE_path.
        value_inference_type (InferenceType): The inference type used during the
            upwards pass through the SPN. Ignored if ``mpe_path`` is given.
        log (bool): If ``True``, calculate the value in the log space. Ignored
                    if ``mpe_path`` is given.
    """

    ParamNode = namedtuple("ParamNode", ["node", "name_scope", "accum"])

    def __init__(self, root, mpe_path=None, log=True, value_inference_type=None,
                 additive_smoothing=None, add_random=None, initial_accum_value=None,
                 use_unweighted=False):
        self._root = root
        self._additive_smoothing = additive_smoothing
        self._initial_accum_value = initial_accum_value
        # Create internal MPE path generator
        if mpe_path is None:
            self._mpe_path = MPEPath(log=log,
                                     value_inference_type=value_inference_type,
                                     add_random=add_random, use_unweighted=use_unweighted)
        else:
            self._mpe_path = mpe_path
        # Create a name scope
        with tf.name_scope("EMLearning") as self._name_scope:
            pass
        # Create accumulators
        self._create_accumulators()

    @property
    def mpe_path(self):
        """MPEPath: Computed MPE path."""
        return self._mpe_path

    @property
    def value(self):
        """Value or LogValue: Computed SPN values."""
        return self._mpe_path.value

    # TODO: For testing only
    def root_accum(self):
        for pn in self._param_nodes:
            if pn.node == self._root.weights.node:
                return pn.accum
        return None

    def reset_accumulators(self):
        with tf.name_scope(self._name_scope):
            return tf.group(*[pn.accum.initializer for pn in self._param_nodes],
                            name="reset_accumulators")

    def accumulate_updates(self):
        # Generate path if not yet generated
        if not self._mpe_path.counts:
            self._mpe_path.get_mpe_path(self._root)

        # Generate all accumulate operations
        with tf.name_scope(self._name_scope):
            assign_ops = []
            for pn in self._param_nodes:
                with tf.name_scope(pn.name_scope):
                    counts = self._mpe_path.counts[pn.node]
                    update_value = pn.node._compute_hard_em_update(counts)
                    op = tf.assign_add(pn.accum, update_value)
                    assign_ops.append(op)

            return tf.group(*assign_ops, name="accumulate_updates")

    def update_spn(self):
        # Generate all update operations
        with tf.name_scope(self._name_scope):
            assign_ops = []
            for pn in self._param_nodes:
                with tf.name_scope(pn.name_scope):
                    accum = pn.accum
                    if self._additive_smoothing is not None:
                        accum = tf.add(accum, self._additive_smoothing)
                    assign_ops.append(pn.node.assign(accum))
            return tf.group(*assign_ops, name="update_spn")

    def learn(self):
        """Assemble TF operations performing EM learning of the SPN."""
        return None

    def _create_accumulators(self):
        def fun(node):
            if node.is_param:
                with tf.name_scope(node.name) as scope:
                    if self._initial_accum_value is not None:
                        accum = tf.Variable(tf.ones_like(node.variable,
                                                         dtype=conf.dtype) *
                                            self._initial_accum_value,
                                            dtype=conf.dtype,
                                            collections=['em_accumulators'])
                    else:
                        accum = tf.Variable(tf.zeros_like(node.variable,
                                                          dtype=conf.dtype),
                                            dtype=conf.dtype,
                                            collections=['em_accumulators'])
                    param_node = EMLearning.ParamNode(node=node, accum=accum,
                                                      name_scope=scope)
                    self._param_nodes.append(param_node)

        self._param_nodes = []
        with tf.name_scope(self._name_scope):
            traverse_graph(self._root, fun=fun)
コード例 #4
0
class HardEMLearning:
    """Assembles TF operations performing EM learning of an SPN.

    Args:
        mpe_path (MPEPath): Pre-computed MPE_path.
        value_inference_type (InferenceType): The inference type used during the
            upwards pass through the SPN. Ignored if ``mpe_path`` is given.
        log (bool): If ``True``, calculate the value in the log space. Ignored
                    if ``mpe_path`` is given.
    """

    ParamNode = namedtuple("ParamNode", ["node", "name_scope", "accum"])
    LocationScaleLeafNode = namedtuple(
        "LocationScaleLeafNode", ["node", "name_scope", "accum", "sum_data", "sum_data_squared"])

    def __init__(self, root, mpe_path=None, log=True, value_inference_type=None,
                 additive_smoothing=None, initial_accum_value=1.0,
                 use_unweighted=False, sample_winner=False, sample_prob=None,
                 matmul_or_conv=False):
        self._root = root
        self._log = log
        self._additive_smoothing = additive_smoothing
        self._initial_accum_value = initial_accum_value
        self._sample_winner = sample_winner
        # Create internal MPE path generator
        if mpe_path is None:
            self._mpe_path = MPEPath(
                log=log, value_inference_type=value_inference_type,
                use_unweighted=use_unweighted, sample=sample_winner, sample_prob=sample_prob,
                matmul_or_conv=matmul_or_conv)
        else:
            self._mpe_path = mpe_path
        # Create a name scope
        with tf.name_scope("HardEMLearning") as self._name_scope:
            pass
        # Create accumulators
        self._create_accumulators()

    @property
    def mpe_path(self):
        """MPEPath: Computed MPE path."""
        return self._mpe_path

    @property
    def value(self):
        """Value or LogValue: Computed SPN values."""
        return self._mpe_path.value

    @utils.lru_cache
    def reset_accumulators(self):
        with tf.name_scope(self._name_scope):
            return tf.group(*(
                [pn.accum.initializer for pn in self._param_nodes] +
                [dn.accum.initializer for dn in self._loc_scale_leaf_nodes] +
                [dn.sum_data.initializer for dn in self._loc_scale_leaf_nodes] +
                [dn.sum_data_squared.initializer for dn in self._loc_scale_leaf_nodes] +
                [dn.node._total_count_variable.initializer
                 for dn in self._loc_scale_leaf_nodes]),
                            name="reset_accumulators")

    def init_accumulators(self):
        return self.reset_accumulators()

    def accumulate_and_update_weights(self):
        accumulate_updates = self.accumulate_updates()
        with tf.control_dependencies([accumulate_updates]):
            return self.update_spn()

    def accumulate_updates(self):
        # Generate path if not yet generated
        if not self._mpe_path.counts:
            self._mpe_path.get_mpe_path(self._root)

        # Generate all accumulate operations
        with tf.name_scope(self._name_scope):
            assign_ops = []
            for pn in self._param_nodes:
                with tf.name_scope(pn.name_scope):
                    counts_summed_batch = pn.node._compute_hard_em_update(
                        self._mpe_path.counts[pn.node])
                    assign_ops.append(tf.assign_add(pn.accum, counts_summed_batch))

            for dn in self._loc_scale_leaf_nodes:
                with tf.name_scope(dn.name_scope):
                    counts = self._mpe_path.counts[dn.node]
                    update_value = dn.node._compute_hard_em_update(counts)
                    with tf.control_dependencies(update_value.values()):
                        if dn.node.dimensionality > 1:
                            accum = tf.squeeze(update_value['accum'], axis=-1)
                        else:
                            accum = update_value['accum']
                        assign_ops.extend(
                            [tf.assign_add(dn.accum, accum),
                             tf.assign_add(dn.sum_data, update_value['sum_data']),
                             tf.assign_add(
                                 dn.sum_data_squared, update_value['sum_data_squared'])])

            return tf.group(*assign_ops, name="accumulate_updates")

    def update_spn(self):
        # Generate all update operations
        with tf.name_scope(self._name_scope):
            assign_ops = []
            for pn in self._param_nodes:
                with tf.name_scope(pn.name_scope):
                    accum = pn.accum
                    if self._additive_smoothing is not None:
                        accum = tf.add(accum, self._additive_smoothing)
                    if pn.node.log:
                        assign_ops.append(pn.node.assign_log(tf.log(accum)))
                    else:
                        assign_ops.append(pn.node.assign(accum))

            for dn in self._loc_scale_leaf_nodes:
                with tf.name_scope(dn.name_scope):
                    assign_ops.extend(dn.node.assign(dn.accum, dn.sum_data, dn.sum_data_squared))

            return tf.group(*assign_ops, name="update_spn")

    def learn(self):
        """Assemble TF operations performing EM learning of the SPN."""
        return None

    def _create_accumulators(self):
        def fun(node):
            if node.is_param:
                with tf.name_scope(node.name) as scope:
                    if self._initial_accum_value is not None:
                        if node.mask and not all(node.mask):
                            accum = tf.Variable(tf.cast(tf.reshape(node.mask,
                                                                   node.variable.shape),
                                                        dtype=conf.dtype) *
                                                self._initial_accum_value,
                                                dtype=conf.dtype)
                        else:
                            accum = tf.Variable(tf.ones_like(node.variable,
                                                             dtype=conf.dtype) *
                                                self._initial_accum_value,
                                                dtype=conf.dtype)
                    else:
                        accum = tf.Variable(tf.zeros_like(node.variable,
                                                          dtype=conf.dtype),
                                            dtype=conf.dtype)
                    param_node = HardEMLearning.ParamNode(node=node, accum=accum,
                                                      name_scope=scope)
                    self._param_nodes.append(param_node)
            if isinstance(node, LocationScaleLeaf) and (node.trainable_scale or node.trainable_loc):
                with tf.name_scope(node.name) as scope:
                    if self._initial_accum_value is not None:
                        accum = tf.Variable(tf.ones_like(node.loc_variable, dtype=conf.dtype) *
                                            self._initial_accum_value,
                                            dtype=conf.dtype)
                        sum_x = tf.Variable(node.loc_variable * self._initial_accum_value,
                                            dtype=conf.dtype)
                        sum_x2 = tf.Variable(tf.square(node.loc_variable) *
                                             self._initial_accum_value,
                                             dtype=conf.dtype)
                    else:
                        accum = tf.Variable(tf.zeros_like(node.loc_variable, dtype=conf.dtype),
                                            dtype=conf.dtype)
                        sum_x = tf.Variable(tf.zeros_like(node.loc_variable), dtype=conf.dtype)
                        sum_x2 = tf.Variable(tf.zeros_like(node.loc_variable), dtype=conf.dtype)
                    loc_scale_node = HardEMLearning.LocationScaleLeafNode(
                        node=node, accum=accum, sum_data=sum_x, sum_data_squared=sum_x2,
                        name_scope=scope)
                    self._loc_scale_leaf_nodes.append(loc_scale_node)

        self._loc_scale_leaf_nodes = []
        self._param_nodes = []
        with tf.name_scope(self._name_scope):
            traverse_graph(self._root, fun=fun)