def init(self, graph): if self.known_cpds is None: self.known_cpds = [] self.alpha = float(self.alpha) known = {cpd.scope[0] for cpd in self.known_cpds} self.unknown = set(self.scope) - known known_cpds = [CPD(cpd.scope, cpd.values) for cpd in self.known_cpds] unknown_cpds = [] self.parents = find_parents(self.scope, graph) for v in self.unknown: pa_v = sorted(self.parents[v]) f = Factor([v] + pa_v) val_pa_v = product(*(range(pa.k) for pa in pa_v)) for assg in val_pa_v: dist = np.random.dirichlet([self.alpha / len(f.values)] * v.k) assg = list(assg) for i in range(v.k): f.values[f.atoi([i] + assg)] = dist[i] unknown_cpds.append(CPD(f.scope, f.values)) self.bn = BayesianNetwork(known_cpds + unknown_cpds)
def optimal_decision_rule(self, scope): cf = self.id.chance_factors uf = Factor([], [0.0]) for f in self.id.utility_factors: uf += f gd = GibbsDistribution(cf + [uf]) ve = VariableElimination(gd) mu = ve.posterior(scope, normalize=False) assg_map = [scope.index(v) for v in mu.scope] ind = mu.scope.index(scope[0]) rule = Factor(scope, np.zeros(np.prod([v.k for v in scope]))) n = int(np.prod([v.k for v in scope[1:]])) for i in range(n): assg = rule.itoa(i) assg_mu = np.array(assg)[assg_map] assg_mu[ind] = -1 assg_max = [mu.argmax(assg_mu)] + list(assg[1:]) rule.values[rule.atoi(assg_max)] = 1.0 return CPD(rule.scope, rule.values)
def maximum_a_posteriori(self, evidence=[]): gd = self.gd.reduce(evidence) elim_variables = set([v for f in gd.factors for v in f.scope]) gm = MinFillElimination(gd.factors) elim_variables = gm.ordering(elim_variables) factors = gd.factors phis = [] for v in elim_variables: factor = reduce(lambda x, y: x * y, [f for f in factors if v in f.scope], Factor([], np.array([1.0]))) phis.append(factor) factor = factor.maximize(v) factors = [f for f in factors if v not in f.scope] + [factor] assgmap = dict() for (i, f) in reversed(list(enumerate(phis))): assg = [0] * len(f.scope) for j, v in enumerate(f.scope): if v == elim_variables[i]: assg[j] = -1 else: assg[j] = assgmap[v] assgmap[elim_variables[i]] = f.argmax(assg) return [(v, assgmap[v]) for v in elim_variables]
def refit(self, graph): parents = find_parents(self.scope, graph) new_scopes = set() for v in self.scope: scope = tuple([v] + sorted(parents[v])) new_scopes.add(scope) old_scopes = set(self.stats.keys()) remove_c = len(old_scopes | new_scopes) - self.maxlen if remove_c > 0: for key in self.stats.keys(): if key not in new_scopes: del self.stats[key] remove_c -= 1 if remove_c <= 0: break self.last_scopes = new_scopes new_scopes = new_scopes - old_scopes for scope in new_scopes: self.stats[scope] = Factor(scope) self.count_occurences(new_scopes) return self
def six_variables(): M = RandomVar('Market', 3) S = RandomVar('Survey', 4) # S = 3 means no survey T = RandomVar('Test', 2) F = RandomVar('Found', 2) uMF = Factor([M, F], [0, -7, 0, 5, 0, 20]) uT = Factor([T], [0, -1]) cM = CPD([M], [0.5, 0.3, 0.2]) cST = CPD([S, M, T], [ 0.0, 0.6, 0.0, 0.3, 0.0, 0.1, 0.0, 0.3, 0.0, 0.4, 0.0, 0.4, 0.0, 0.1, 0.0, 0.3, 0.0, 0.5, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0 ]) # Alternative decision rules for F given S dFS_1 = CPD([F, S], [0, 0, 0, 1, 1, 1, 1, 0]) dFS_2 = CPD([F, S], [1, 0, 0, 0, 0, 1, 1, 1]) # Optimal # Alternative decision rules for T dT_1 = CPD([T], [1.0, 0.0]) dT_2 = CPD([T], [0.0, 1.0]) # Optimal id = InfluenceDiagram([cM, cST], [uMF, uT]) eu = ExpectedUtility(id) print(eu.expected_utility([dFS_1, dT_1])) print(eu.expected_utility([dFS_1, dT_2])) print(eu.expected_utility([dFS_2, dT_1])) print(eu.expected_utility([dFS_2, dT_2])) # New influence diagram with a single decision rule dT = dT_2 id2 = InfluenceDiagram([cM, cST, dT], [uMF, uT]) eu2 = ExpectedUtility(id2) dFS_optimal = eu2.optimal_decision_rule([F, S]) print(eu.expected_utility([dFS_optimal, dT]))
def getFactor(self, conditions): newTable = dict(self.table) for var in conditions: # We need to fix the entry for the var in this factors if var in self.factors: loc = self.factors.index(var) newTable = { key: newTable[key] for key in newTable if key[loc] == conditions[var] } return Factor(self.factors, newTable)
def fit(self, X, graph): self.X = np.array(X) self.stats = OrderedDict() new_scopes = set() parents = find_parents(self.scope, graph) for v in self.scope: scope = tuple([v] + sorted(parents[v])) self.stats[scope] = Factor(scope) new_scopes.add(scope) self.last_scopes = new_scopes self.count_occurences(new_scopes) return self
def posterior(self, hypothesis, evidence=[], normalize=True): gd = self.gd.reduce(evidence) elim_variables = set( [v for f in gd.factors for v in f.scope if v not in hypothesis]) gm = MinFillElimination(gd.factors) elim_variables = gm.ordering(elim_variables) factors = gd.factors for v in elim_variables: factor = reduce(lambda x, y: x * y, [f for f in factors if v in f.scope], Factor([], np.array([1.0]))) factor = factor.marginalize(v) factors = [f for f in factors if v not in f.scope] + [factor] return GibbsDistribution(factors).joint(normalize)
def three_variables(): M = RandomVar('Market', 3) F = RandomVar('Found', 2) uMF = Factor([M, F], [0, -7, 0, 5, 0, 20]) cM = CPD([M], [0.5, 0.3, 0.2]) # Alternative decision rules for F dF_1 = CPD([F], [1.0, 0]) dF_2 = CPD([F], [0, 1.0]) # Optimal id = InfluenceDiagram([cM], [uMF]) eu = ExpectedUtility(id) print(eu.expected_utility([dF_1])) print(eu.expected_utility([dF_2])) print(eu.optimal_decision_rule([F]))
def die(): # Parameters # d1_ = [0.2, 0.0, 0.5, 0.1, 0.1, 0.1] # d2_ = [0.2, 0.3, 0.1, 0.05, 0.05, 0.3] d1_ = [0.1, 0.9] d2_ = [0.6, 0.4] n_samples = 5000 n_iterations = 10 n_restarts = 2 verbose = 2 # Model creation if len(d1_) != len(d2_): raise Exception('The die should have the same cardinality') h = RandomVar('h', 2) o1 = RandomVar('o1', len(d1_)) o2 = RandomVar('o2', len(d2_)) f_h = CPD([h], [0.5, 0.5]) f_o1_h = Factor([o1, h]) f_o2_h = Factor([o2, h]) for i in range(len(f_o1_h.values)): o_, h_ = f_o1_h.itoa(i) f_o1_h.values[i] = d1_[o_] if h_ == 0 else d2_[o_] f_o2_h.values[i] = d2_[o_] if h_ == 0 else d1_[o_] f_o1_h = CPD(f_o1_h.scope, f_o1_h.values) f_o2_h = CPD(f_o2_h.scope, f_o2_h.values) bn = BayesianNetwork([f_h, f_o1_h, f_o2_h]) # Sampling from true model fs = ForwardSampler(bn) fs.sample(n_samples) scope, X = fs.samples_to_matrix() em = ExpectationMaximization(scope, known_cpds=[f_h], n_iterations=n_iterations, n_restarts=n_restarts, alpha=10.0, verbose=verbose) print('True log-likelihood (no missing variables):') print(em.log_likelihood(X, bn)) print('Maximum log-likelihood (no missing variables):') ls = LikelihoodScore(scope) ls.fit(X, bn.graph()) print(ls.score) # Hiding variable X[:, scope.index(h)] = -1 print('True log-likelihood (missing variables):') print(em.log_likelihood(X, bn)) bn_pred = em.fit_predict(X, bn.graph()) print('Best log-likelihood (missing variables)') print(em.log_likelihood(X, bn_pred)) # Estimation results print('Results:') f_o1_h = [f for f in bn_pred.factors if f.scope[0] == o1][0] f_o2_h = [f for f in bn_pred.factors if f.scope[0] == o2][0] d = np.zeros(o1.k) d1 = np.zeros(o1.k) d2 = np.zeros(o1.k) with printoptions(precision=3): print('d1: {0}'.format(d1_)) for i in range(o1.k): d[i] = f_o1_h.values[f_o1_h.atoi([i, 0])] print('d1 according to o1: {0}'.format(d)) d1 += d for i in range(o2.k): d[i] = f_o2_h.values[f_o2_h.atoi([i, 1])] print('d1 according to o2: {0}'.format(d)) d1 += d print('d2: {0}'.format(d2_)) for i in range(o1.k): d[i] = f_o1_h.values[f_o1_h.atoi([i, 1])] print('d2 according to o1: {0}'.format(d)) d2 += d for i in range(o2.k): d[i] = f_o2_h.values[f_o2_h.atoi([i, 0])] print('d2 according to o2: {0}'.format(d)) d2 += d print('Average estimate:') print('d1: {0}'.format(d1/2.)) print('d2: {0}'.format(d2/2.))
def joint(self, normalize=True): f = Factor([], [1.0]) for fi in self.factors: f = f * fi return f.normalize() if normalize else f
def fit(self, X, graph): """Find the parameters for a probabilistic graphical model, given a graph and a data set that possibly contains missing data. After fitting, the model is available as a BayesianNetwork `self.bn`. Parameters ---------- X : two-dimensional np.array or python matrix of integers Matrix representing the observations. The value `X[i, j]` should correspond to the discrete random variable `self.scope[j]` in sample element `i`. The number -1 represents a missing value. graph: dict from RandomVariables to sets of RandomVariables the graph for the probabilistic graphical model """ var_index = {v: i for (i, v) in enumerate(self.scope)} best_ll = float('-inf') best_bn = None for irestart in range(self.n_restarts): if self.verbose > 0: print('Restart {0}.'.format(irestart + 1)) self.init(graph) known_cpds = [ CPD(cpd.scope, cpd.values) for cpd in self.known_cpds ] M_scopes = [] for v in self.unknown: M_scopes.append([v] + sorted(self.parents[v])) for iiteration in range(self.n_iterations): ess = [Factor(M_scope) for M_scope in M_scopes] for x in X: evidence = [] hidden = [] for (i, xi) in enumerate(x): if xi == -1: hidden.append(self.scope[i]) else: evidence.append((self.scope[i], xi)) for M in ess: M_assg = x[[var_index[v] for v in M.scope]] M_h = [] for (i, v) in enumerate(M.scope): if M_assg[i] == -1: M_h.append(v) if M_h: ve = VariableElimination(self.bn) f = ve.posterior(M_h, evidence=evidence) Mh_index = [M.scope.index(v) for v in f.scope] for i in range(len(f.values)): f_assg = f.itoa(i) M_assg[Mh_index] = f_assg M.values[M.atoi(M_assg)] += f.values[i] else: M.values[M.atoi(M_assg)] += 1 self.bn = BayesianNetwork([M.to_cpd() for M in ess] + known_cpds) if self.verbose > 1: print('Iteration {0}. '.format(iiteration + 1)) if self.verbose > 2: ll = self.log_likelihood(X, self.bn) print('Current log-likelihood {0}.'.format(ll)) ll = self.log_likelihood(X, self.bn) print('Final log-likelihood {0}.'.format(ll)) if ll > best_ll: best_ll = ll best_bn = self.bn self.bn = best_bn return self