def to_layers(spn, sparse=True, copy=True): with elapsed_timer() as e: if copy: spn = Copy(spn) print('copy', e()) spn = Prune(spn, contract_single_parents=False) print('prune', e()) complete_layers([spn], type(spn)) print('complete layers', e()) node_layers = get_topological_order_layers(spn) print('topo search', e()) print('nr layers', len(node_layers)) layers = [LeafLayer(node_layers[0])] for i in tqdm(range(1, len(node_layers))): cur_layer = node_layers[i] prev_layer = node_layers[i - 1] scope = get_scope(cur_layer, prev_layer, sparse) if isinstance(cur_layer[0], Sum): weights = np.concatenate( list(map(lambda x: x.weights, cur_layer))) layers.append(SumLayer(cur_layer, scope, weights)) else: layers.append(ProductLayer(cur_layer, scope)) print('to layer objects', e()) return layers
def test_eval_parametric(self): data = np.array([1, 1, 1, 1, 1, 1, 1], dtype=np.float32).reshape( (1, 7)) spn = (Gaussian(mean=1.0, stdev=1.0, scope=[0]) * Exponential(l=1.0, scope=[1]) * Gamma(alpha=1.0, beta=1.0, scope=[2]) * LogNormal(mean=1.0, stdev=1.0, scope=[3]) * Poisson(mean=1.0, scope=[4]) * Bernoulli(p=0.6, scope=[5]) * Categorical(p=[0.1, 0.2, 0.7], scope=[6])) ll = log_likelihood(spn, data) tf_ll = eval_tf(spn, data) self.assertTrue(np.all(np.isclose(ll, tf_ll))) spn_copy = Copy(spn) tf_graph, data_placeholder, variable_dict = spn_to_tf_graph( spn_copy, data, 1) with tf.Session() as sess: sess.run(tf.global_variables_initializer()) tf_graph_to_spn(variable_dict) str_val = spn_to_str_equation(spn) str_val2 = spn_to_str_equation(spn_copy) self.assertEqual(str_val, str_val2)
def optimize_tf(spn, data, epochs=1000, optimizer=None): spn_copy = Copy(spn) tf_graph, data_placeholder, variable_dict = spn_to_tf_graph(spn_copy, data) optimize_tf_graph(tf_graph, variable_dict, data_placeholder, data, epochs=epochs, optimizer=optimizer) return spn_copy
def prod_condition(node, children, input_vals=None, scope=None): if not scope.intersection(node.scope): return Copy(node), 0 new_node = Product() new_node.scope = list(set(node.scope) - scope) probability = 0 for c in children: if c[0]: new_node.children.append(c[0]) probability += float(c[1]) return new_node, probability
def get_node_description(spn, parent_node, size): # parent_node.validate() parent_type = type(parent_node).__name__ node_descriptions = dict() node_descriptions['num'] = len(parent_node.children) nodes = list() for i, node in enumerate(parent_node.children): node_spn = Copy(node) assign_ids(node_spn) node_dir = dict() node_dir[ 'weight'] = parent_node.weights[i] if parent_type == 'Sum' else 1 node_dir['size'] = get_number_of_nodes(node) - 1 node_dir['num_children'] = len( node.children) if not isinstance(node, Leaf) else 0 node_dir['leaf'] = isinstance(node, Leaf) node_dir['type'] = type(node).__name__ + ' Node' node_dir['split_features'] = [ list(c.scope) for c in node.children ] if not isinstance(node, Leaf) else node.scope node_dir['split_features'].sort(key=lambda x: len(x)) node_dir['depth'] = get_depth(node) node_dir['child_depths'] = [get_depth(c) for c in node.children] descriptor = node_dir['type'] if all((d == 0 for d in node_dir['child_depths'])): descriptor = 'shallow ' + descriptor node_dir['quick'] = 'shallow' elif len([d for d in node_dir['child_depths'] if d == 0]) == 1: node_dir['quick'] = 'split_one' descriptor += ', which separates one feature' else: node_dir['quick'] = 'deep' descriptor = 'deep ' + descriptor descriptor = 'a ' + descriptor node_dir['descriptor'] = descriptor node_dir['short_descriptor'] = descriptor node_dir['representative'] = mpe(node_spn, np.array([[np.nan] * size])) nodes.append(node_dir) node_descriptions['shallow'] = len( [d for d in nodes if d['quick'] == 'shallow']) node_descriptions['split_one'] = len( [d for d in nodes if d['quick'] == 'split_one']) node_descriptions['deep'] = len([d for d in nodes if d['quick'] == 'deep']) nodes.sort(key=lambda x: x['weight']) nodes.reverse() node_descriptions['nodes'] = nodes return node_descriptions
def sum_condition(node, children, input_vals=None, scope=None): if not scope.intersection(node.scope): return Copy(node), 0 new_node = Sum() new_node.scope = list(set(node.scope) - scope) new_weights = [] probs = [] for i, c in enumerate(children): if c[0]: new_node.children.append(c[0]) new_weights.append(node.weights[i] * np.exp(c[1])) else: probs.append(node.weights[i] * np.exp(c[1])) new_node.weights = [w / sum(new_weights) for w in new_weights] assert np.all(np.logical_not(np.isnan( new_node.weights))), 'Found nan weights' if not new_node.scope: return None, np.log(sum(probs)) return new_node, np.log(sum(new_weights))
def optimize_tf( spn: Node, data: np.ndarray, epochs=1000, batch_size: int = None, optimizer: tf.train.Optimizer = None, return_loss=False, ) -> Union[Tuple[Node, List[float]], Node]: """ Optimize weights of an SPN with a tensorflow stochastic gradient descent optimizer, maximizing the likelihood function. :param spn: SPN which is to be optimized :param data: Input data :param epochs: Number of epochs :param batch_size: Size of each minibatch for SGD :param optimizer: Optimizer procedure :param return_loss: Whether to also return the list of losses for each epoch or not :return: If `return_loss` is true, a copy of the optimized SPN and the list of the losses for each epoch is returned, else only a copy of the optimized SPN is returned """ # Make sure, that the passed SPN is not modified spn_copy = Copy(spn) # Compile the SPN to a static tensorflow graph tf_graph, data_placeholder, variable_dict = spn_to_tf_graph( spn_copy, data, batch_size) # Optimize the tensorflow graph loss_list = optimize_tf_graph(tf_graph, variable_dict, data_placeholder, data, epochs=epochs, batch_size=batch_size, optimizer=optimizer) # Return loss as well if flag is set if return_loss: return spn_copy, loss_list return spn_copy
def to_compressed_layers(spn): with elapsed_timer() as e: spn = Copy(spn) print('copy', e()) spn = Prune(spn, contract_single_parents=False) print('prune', e()) complete_layers([spn], type(spn)) print('complete layers', e()) node_layers = get_topological_order_layers(spn) print('topo search', e()) print('nr layers', len(node_layers)) layers = [LeafLayer(node_layers[0])] for i in range(1, len(node_layers)): cur_layer = node_layers[i] prev_layer = node_layers[i - 1] cur_is_sum = isinstance(cur_layer[0], Sum) prev_is_prod = isinstance(prev_layer[0], Product) # print(i, cur_is_sum, prev_is_prod) if cur_is_sum: weights = list(map(lambda x: x.weights, cur_layer)) if cur_is_sum and prev_is_prod: # build sp layer # remove prod from previous layer layers.pop() scopes = get_two_layer_scopes(cur_layer, node_layers[i - 2], True) layers.append(SumProductLayer(cur_layer, scopes, weights)) else: scope = get_scope(cur_layer, prev_layer, True) if cur_is_sum: layers.append(SumLayer(cur_layer, scope, weights)) else: layers.append(ProductLayer(cur_layer, scope)) print('to layer objects', e()) return layers
# for _ in range(0, 20): rg.random_split(2, 2) rg_layers = rg.make_layers() print("random graph built in ", (time.perf_counter() - start)) start = time.perf_counter() vector_list, root = Make_SPN_from_RegionGraph(rg_layers, np.random.RandomState(100), num_classes=1, num_gauss=20, num_sums=20) print("Make_SPN_from_RegionGraph in ", (time.perf_counter() - start)) start = time.perf_counter() print(get_structure_stats(root)) print("get_structure_stats in ", (time.perf_counter() - start)) old_root = Copy(root) start = time.perf_counter() root = Prune(root) print("Prune in ", (time.perf_counter() - start)) start = time.perf_counter() root = SPN_Reshape(root, 2) print("SPN_Reshape in ", (time.perf_counter() - start)) start = time.perf_counter() print(get_structure_stats(root)) print("get_structure_stats in ", (time.perf_counter() - start)) start = time.perf_counter() layers, layer_types = get_execution_layers(root)
def leaf_condition(node, input_vals=None, scope=None): if not scope.intersection(node.scope): return Copy(node), 0 _likelihood = log_likelihood(node, input_vals) return None, _likelihood
def describe_misclassified(spn, dictionary, misclassified, data_dict, numerical_data): context = dictionary['context'] categoricals = get_categoricals(spn, context) empty = np.array([[np.nan] * len(spn.scope)]) for i in categoricals: if use_shapley: raise NotImplementedError else: if misclassified_explanations == 'all': show_misclassified = misclassified[i] elif isinstance(misclassified_explanations, int): num_choices = min(misclassified_explanations, len(misclassified[i])) show_misclassified = random.sample(misclassified[i].tolist(), k=num_choices) else: show_misclassified = misclassified_explanations for inst_num in show_misclassified: instance = data_dict[i][inst_num:inst_num + 1] evidence = instance.copy() evidence[:, i] = np.nan prior = log_likelihood(spn, evidence) posterior = log_likelihood(spn, instance) total = 0 all_nodes = [] for j, node in enumerate(spn.children): node_prob = np.exp( np.log(spn.weights[j]) + log_likelihood(spn, instance) - posterior) total += node_prob all_nodes.append((node_prob, j)) all_nodes.sort() all_nodes.reverse() needed_nodes = [] all_reps = [] total_prob = 0 for prob, idx in all_nodes: node = Copy(spn.children[idx]) assign_ids(node) total_prob += prob needed_nodes.append(idx) all_reps.append(mpe(node, empty)[0]) if total_prob > 0.9: break real_value = dictionary['features'][i][ 'encoder'].inverse_transform( [int(numerical_data[inst_num, i])]) pred_value = dictionary['features'][i][ 'encoder'].inverse_transform( [int(data_dict[i][inst_num, i])]) printmd( 'Instance {} was predicted as "{}", even though it is "{}", because it was most similar to the following clusters: {}' .format(inst_num, pred_value, real_value, ', '.join(map(str, needed_nodes)))) all_reps = np.array(all_reps).reshape(len(needed_nodes), len(spn.scope)) table = np.round(np.concatenate([instance, all_reps], axis=0), 2) node_nums = np.array(['instance'] + needed_nodes).reshape( -1, 1) table = np.append(node_nums, table, axis=1) iplot( p.plot_table([''] + context.feature_names, table.transpose()))