wolffd@0: function CPD = tabular_CPD(bnet, self, varargin) wolffd@0: % TABULAR_CPD Make a multinomial conditional prob. distrib. (CPT) wolffd@0: % wolffd@0: % CPD = tabular_CPD(bnet, node) creates a random CPT. wolffd@0: % wolffd@0: % The following arguments can be specified [default in brackets] wolffd@0: % wolffd@0: % CPT - specifies the params ['rnd'] wolffd@0: % - T means use table T; it will be reshaped to the size of node's family. wolffd@0: % - 'rnd' creates rnd params (drawn from uniform) wolffd@0: % - 'unif' creates a uniform distribution wolffd@0: % adjustable - 0 means don't adjust the parameters during learning [1] wolffd@0: % prior_type - defines type of prior ['none'] wolffd@0: % - 'none' means do ML estimation wolffd@0: % - 'dirichlet' means add pseudo-counts to every cell wolffd@0: % - 'entropic' means use a prior P(theta) propto exp(-H(theta)) (see Brand) wolffd@0: % dirichlet_weight - equivalent sample size (ess) of the dirichlet prior [1] wolffd@0: % dirichlet_type - defines the type of Dirichlet prior ['BDeu'] wolffd@0: % - 'unif' means put dirichlet_weight in every cell wolffd@0: % - 'BDeu' means we put 'dirichlet_weight/(r q)' in every cell wolffd@0: % where r = self_sz and q = prod(parent_sz) (see Heckerman) wolffd@0: % trim - 1 means trim redundant params (rows in CPT) when using entropic prior [0] wolffd@0: % entropic_pcases - list of assignments to the parents nodes when we should use wolffd@0: % the entropic prior; all other cases will be estimated using ML [1:psz] wolffd@0: % sparse - 1 means use 1D sparse array to represent CPT [0] wolffd@0: % wolffd@0: % e.g., tabular_CPD(bnet, i, 'CPT', T) wolffd@0: % e.g., tabular_CPD(bnet, i, 'CPT', 'unif', 'dirichlet_weight', 2, 'dirichlet_type', 'unif') wolffd@0: % wolffd@0: % REFERENCES wolffd@0: % M. Brand - "Structure learning in conditional probability models via an entropic prior wolffd@0: % and parameter extinction", Neural Computation 11 (1999): 1155--1182 wolffd@0: % M. Brand - "Pattern discovery via entropy minimization" [covers annealing] wolffd@0: % AI & Statistics 1999. Equation numbers refer to this paper, which is available from wolffd@0: % www.merl.com/reports/docs/TR98-21.pdf wolffd@0: % D. Heckerman, D. Geiger and M. Chickering, wolffd@0: % "Learning Bayesian networks: the combination of knowledge and statistical data", wolffd@0: % Microsoft Research Tech Report, 1994 wolffd@0: wolffd@0: wolffd@0: if nargin==0 wolffd@0: % This occurs if we are trying to load an object from a file. wolffd@0: CPD = init_fields; wolffd@0: CPD = class(CPD, 'tabular_CPD', discrete_CPD(0, [])); wolffd@0: return; wolffd@0: elseif isa(bnet, 'tabular_CPD') wolffd@0: % This might occur if we are copying an object. wolffd@0: CPD = bnet; wolffd@0: return; wolffd@0: end wolffd@0: CPD = init_fields; wolffd@0: wolffd@0: ns = bnet.node_sizes; wolffd@0: ps = parents(bnet.dag, self); wolffd@0: fam_sz = ns([ps self]); wolffd@0: psz = prod(ns(ps)); wolffd@0: CPD.sizes = fam_sz; wolffd@0: CPD.leftright = 0; wolffd@0: CPD.sparse = 0; wolffd@0: wolffd@0: % set defaults wolffd@0: CPD.CPT = mk_stochastic(myrand(fam_sz)); wolffd@0: CPD.adjustable = 1; wolffd@0: CPD.prior_type = 'none'; wolffd@0: dirichlet_type = 'BDeu'; wolffd@0: dirichlet_weight = 1; wolffd@0: CPD.trim = 0; wolffd@0: selfprob = 0.1; wolffd@0: CPD.entropic_pcases = 1:psz; wolffd@0: wolffd@0: % extract optional args wolffd@0: args = varargin; wolffd@0: % check for old syntax CPD(bnet, i, CPT) as opposed to CPD(bnet, i, 'CPT', CPT) wolffd@0: if ~isempty(args) & ~isstr(args{1}) wolffd@0: CPD.CPT = myreshape(args{1}, fam_sz); wolffd@0: args = []; wolffd@0: end wolffd@0: wolffd@0: for i=1:2:length(args) wolffd@0: switch args{i}, wolffd@0: case 'CPT', wolffd@0: T = args{i+1}; wolffd@0: if ischar(T) wolffd@0: switch T wolffd@0: case 'unif', CPD.CPT = mk_stochastic(myones(fam_sz)); wolffd@0: case 'rnd', CPD.CPT = mk_stochastic(myrand(fam_sz)); wolffd@0: otherwise, error(['invalid CPT ' T]); wolffd@0: end wolffd@0: else wolffd@0: CPD.CPT = myreshape(T, fam_sz); wolffd@0: end wolffd@0: case 'prior_type', CPD.prior_type = args{i+1}; wolffd@0: case 'dirichlet_type', dirichlet_type = args{i+1}; wolffd@0: case 'dirichlet_weight', dirichlet_weight = args{i+1}; wolffd@0: case 'adjustable', CPD.adjustable = args{i+1}; wolffd@0: case 'clamped', CPD.adjustable = ~args{i+1}; wolffd@0: case 'trim', CPD.trim = args{i+1}; wolffd@0: case 'entropic_pcases', CPD.entropic_pcases = args{i+1}; wolffd@0: case 'sparse', CPD.sparse = args{i+1}; wolffd@0: otherwise, error(['invalid argument name: ' args{i}]); wolffd@0: end wolffd@0: end wolffd@0: wolffd@0: switch CPD.prior_type wolffd@0: case 'dirichlet', wolffd@0: switch dirichlet_type wolffd@0: case 'unif', CPD.dirichlet = dirichlet_weight * myones(fam_sz); wolffd@0: case 'BDeu', CPD.dirichlet = (dirichlet_weight/psz) * mk_stochastic(myones(fam_sz)); wolffd@0: otherwise, error(['invalid dirichlet_type ' dirichlet_type]) wolffd@0: end wolffd@0: case {'entropic', 'none'} wolffd@0: CPD.dirichlet = []; wolffd@0: otherwise, error(['invalid prior_type ' prior_type]) wolffd@0: end wolffd@0: wolffd@0: wolffd@0: wolffd@0: % fields to do with learning wolffd@0: if ~CPD.adjustable wolffd@0: CPD.counts = []; wolffd@0: CPD.nparams = 0; wolffd@0: CPD.nsamples = []; wolffd@0: else wolffd@0: %CPD.counts = zeros(size(CPD.CPT)); wolffd@0: CPD.counts = zeros(prod(size(CPD.CPT)), 1); wolffd@0: psz = fam_sz(1:end-1); wolffd@0: ss = fam_sz(end); wolffd@0: if CPD.leftright wolffd@0: % For each of the Qps contexts, we specify Q elements on the diagoanl wolffd@0: CPD.nparams = Qps * Q; wolffd@0: else wolffd@0: % sum-to-1 constraint reduces the effective arity of the node by 1 wolffd@0: CPD.nparams = prod([psz ss-1]); wolffd@0: end wolffd@0: CPD.nsamples = 0; wolffd@0: end wolffd@0: wolffd@0: CPD.trimmed_trans = []; wolffd@0: fam_sz = CPD.sizes; wolffd@0: wolffd@0: %psz = prod(fam_sz(1:end-1)); wolffd@0: %ssz = fam_sz(end); wolffd@0: %CPD.trimmed_trans = zeros(psz, ssz); % must declare before reading wolffd@0: wolffd@0: %sparse CPT wolffd@0: if CPD.sparse wolffd@0: CPD.CPT = sparse(CPD.CPT(:)); wolffd@0: end wolffd@0: wolffd@0: CPD = class(CPD, 'tabular_CPD', discrete_CPD(~CPD.adjustable, fam_sz)); wolffd@0: wolffd@0: wolffd@0: %%%%%%%%%%% wolffd@0: wolffd@0: function CPD = init_fields() wolffd@0: % This ensures we define the fields in the same order wolffd@0: % no matter whether we load an object from a file, wolffd@0: % or create it from scratch. (Matlab requires this.) wolffd@0: wolffd@0: CPD.CPT = []; wolffd@0: CPD.sizes = []; wolffd@0: CPD.prior_type = []; wolffd@0: CPD.dirichlet = []; wolffd@0: CPD.adjustable = []; wolffd@0: CPD.counts = []; wolffd@0: CPD.nparams = []; wolffd@0: CPD.nsamples = []; wolffd@0: CPD.trim = []; wolffd@0: CPD.trimmed_trans = []; wolffd@0: CPD.leftright = []; wolffd@0: CPD.entropic_pcases = []; wolffd@0: CPD.sparse = []; wolffd@0: