diff options
| author | ziejd2 | 2017-09-28 15:04:40 -0500 |
|---|---|---|
| committer | ziejd2 | 2017-09-28 15:04:40 -0500 |
| commit | 8070dc963753142bb86c4ed698d91fd623ed28e7 (patch) | |
| tree | d0f6dd8fc46a49b819aa55c1a90faa14d8448883 /sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD | |
| parent | 7cc31810d53176e805532b2789955f4eedbce6bb (diff) | |
| download | BNW-8070dc963753142bb86c4ed698d91fd623ed28e7.tar.gz | |
BNW using Octave instead of Matlab.
This version of BNW should perform the same as the original version. The only difference is that it uses Octave instead of Matlab when running BayesNet Toolbox during parameter learning. I am calling this BNW_1.02. It can be accessed at: compbio.uthsc.edu/BNW_1.02
Diffstat (limited to 'sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD')
8 files changed, 404 insertions, 0 deletions
diff --git a/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/CVS/Entries b/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/CVS/Entries new file mode 100644 index 00000000..1cef220a --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/CVS/Entries @@ -0,0 +1,6 @@ +/convert_to_table.m/1.1.1.1/Wed May 29 15:59:54 2002// +/maximize_params.m/1.1.1.1/Wed May 29 15:59:54 2002// +/mlp_CPD.m/1.1.1.1/Wed May 29 15:59:54 2002// +/reset_ess.m/1.1.1.1/Wed May 29 15:59:54 2002// +/update_ess.m/1.1.1.1/Wed May 29 15:59:54 2002// +D diff --git a/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/CVS/Repository b/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/CVS/Repository new file mode 100644 index 00000000..9fadd7fe --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/CVS/Repository @@ -0,0 +1 @@ +FullBNT/BNT/CPDs/@mlp_CPD diff --git a/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/CVS/Root b/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/convert_to_table.m b/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/convert_to_table.m new file mode 100644 index 00000000..7e25d072 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/convert_to_table.m @@ -0,0 +1,80 @@ +function T = convert_to_table(CPD, domain, evidence) +% CONVERT_TO_TABLE Convert a mlp CPD to a table, incorporating any evidence +% T = convert_to_table(CPD, domain, evidence) + +self = domain(end); +ps = domain(1:end-1); % self' parents +%cps = myintersect(ps, cnodes); % self' continous parents +cnodes = domain(CPD.cpndx); +cps = myintersect(ps, cnodes); +odom = domain(~isemptycell(evidence(domain))); % obs nodes in the net +assert(myismember(cps, odom)); % !ALL the CTS parents must be observed! +ns(cps)=1; +dps = mysetdiff(ps, cps); % self' discrete parents +dobs = myintersect(dps, odom); % discrete obs parents + +% Extract the params compatible with the observations (if any) on the discrete parents (if any) + +if ~isempty(dobs), + dvals = cat(1, evidence{dobs}); + ns_eff= CPD.sizes; % effective node sizes + ens=ns_eff; + ens(dobs) = 1; + S=prod(ens(dps)); + subs = ind2subv(ens(dps), 1:S); + mask = find_equiv_posns(dobs, dps); + for i=1:length(mask), + subs(:,mask(i)) = dvals(i); + end + support = subv2ind(ns_eff(dps), subs)'; +else + ns_eff= CPD.sizes; + support=[1:prod(ns_eff(dps))]; +end + +W1=[]; b1=[]; W2=[]; b2=[]; + +W1 = CPD.W1(:,:,support); +b1= CPD.b1(support,:); +W2 = CPD.W2(:,:,support); +b2= CPD.b2(support,:); +ns(odom) = 1; +dpsize = prod(ns(dps)); % overall size of the self' discrete parents + +x = cat(1, evidence{cps}); +ndata=size(x,2); + +if ~isempty(evidence{self}) % + app=struct(CPD); % + ns(self)=app.mlp{1}.nout; % pump up self to the original dimension if observed + clear app; % +end % + +T =zeros(dpsize, ns(self)); % +for i=1:dpsize % + W1app = W1(:,:,i); % + b1app = b1(i,:); % + W2app = W2(:,:,i); % + b2app = b2(i,:); % for each of the dpsize combinations of self'parents values + z = tanh(x(:)'*W1app + ones(ndata, 1)*b1app); % we tabulate the corrisponding glm model + a = z*W2app + ones(ndata, 1)*b2app; % (element of the cell array CPD.glim) + appoggio = normalise(exp(a)); % + T(i,:)=appoggio; % + W1app=[]; W2app=[]; b1app=[]; b2app=[]; % + z=[]; a=[]; appoggio=[]; % +end % + +if ~isempty(evidence{self}) + appoggio=[]; % + appoggio=zeros(1,ns(self)); % + r = evidence{self}; %...if self is observed => in output there's only the probability of the 'true' class + for i=1:dpsize % + appoggio(i)=T(i,r); % + end + T=zeros(dpsize,1); + for i=1:dpsize + T(i,1)=appoggio(i); + end + clear appoggio; + ns(self) = 1; +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/maximize_params.m b/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/maximize_params.m new file mode 100644 index 00000000..19d0a1be --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/maximize_params.m @@ -0,0 +1,34 @@ +function CPD = maximize_params(CPD, temp) +% MAXIMIZE_PARAMS Find ML params of an MLP using Scaled Conjugated Gradient (SCG) +% CPD = maximize_params(CPD, temperature) +% temperature parameter is ignored + +if ~adjustable_CPD(CPD), return; end +options = foptions; + +% options(1) >= 0 means print an annoying message when the max. num. iter. is reached +if CPD.verbose + options(1) = 1; +else + options(1) = -1; +end +%options(1) = CPD.verbose; + +options(2) = CPD.wthresh; +options(3) = CPD.llthresh; +options(14) = CPD.max_iter; + +dpsz=length(CPD.mlp); + +for i=1:dpsz + mask=[]; + mask=find(CPD.eso_weights(:,:,i)>0); % for adapting the parameters we use only positive weighted example + if ~isempty(mask), + CPD.mlp{i} = netopt_weighted(CPD.mlp{i}, options, CPD.parent_vals(mask',:), CPD.self_vals(mask',:,i), CPD.eso_weights(mask',:,i), 'scg'); + + CPD.W1(:,:,i)=CPD.mlp{i}.w1; % update the parameters matrix + CPD.b1(i,:)=CPD.mlp{i}.b1; % + CPD.W2(:,:,i)=CPD.mlp{i}.w2; % update the parameters matrix + CPD.b2(i,:)=CPD.mlp{i}.b2; % + end +end diff --git a/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/mlp_CPD.m b/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/mlp_CPD.m new file mode 100644 index 00000000..7e9d3f6b --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/mlp_CPD.m @@ -0,0 +1,139 @@ +function CPD = mlp_CPD(bnet, self, nhidden, w1, b1, w2, b2, clamped, max_iter, verbose, wthresh, llthresh) +% MLP_CPD Make a CPD from a Multi Layer Perceptron (i.e., feedforward neural network) +% +% We use a different MLP for each discrete parent combination (if there are any discrete parents). +% We currently assume this node (the child) is discrete. +% +% CPD = mlp_CPD(bnet, self, nhidden) +% will create a CPD with random parameters, where self is the number of this node and nhidden the number of the hidden nodes. +% The params are drawn from N(0, s*I), where s = 1/sqrt(n+1), n = length(X). +% +% CPD = mlp_CPD(bnet, self, nhidden, w1, b1, w2, b2) allows you to specify the params, where +% w1 = first-layer weight matrix +% b1 = first-layer bias vector +% w2 = second-layer weight matrix +% b2 = second-layer bias vector +% These are assumed to be the same for each discrete parent combination. +% If any of these are [], random values will be created. +% +% CPD = mlp_CPD(bnet, self, nhidden, w1, b1, w2, b2, clamped) allows you to prevent the params from being +% updated during learning (if clamped = 1). Default: clamped = 0. +% +% CPD = mlp_CPD(bnet, self, nhidden, w1, b1, w2, b2, clamped, max_iter, verbose, wthresh, llthresh) +% alllows you to specify params that control the M step: +% max_iter - the maximum number of steps to take (default: 10) +% verbose - controls whether to print (default: 0 means silent). +% wthresh - a measure of the precision required for the value of +% the weights W at the solution. Default: 1e-2. +% llthresh - a measure of the precision required of the objective +% function (log-likelihood) at the solution. Both this and the previous condition must +% be satisfied for termination. Default: 1e-2. +% +% For learning, we use a weighted version of scaled conjugated gradient in the M step. + +if nargin==0 + % This occurs if we are trying to load an object from a file. + CPD = init_fields; + CPD = class(CPD, 'mlp_CPD', discrete_CPD(0,[])); + return; +elseif isa(bnet, 'mlp_CPD') + % This might occur if we are copying an object. + CPD = bnet; + return; +end +CPD = init_fields; + +assert(myismember(self, bnet.dnodes)); +ns = bnet.node_sizes; + +ps = parents(bnet.dag, self); +dnodes = mysetdiff(1:length(bnet.dag), bnet.cnodes); +dps = myintersect(ps, dnodes); +cps = myintersect(ps, bnet.cnodes); +dpsz = prod(ns(dps)); +cpsz = sum(ns(cps)); +self_size = ns(self); + +% discrete/cts parent index - which ones of my parents are discrete/cts? +CPD.dpndx = find_equiv_posns(dps, ps); +CPD.cpndx = find_equiv_posns(cps, ps); + +CPD.mlp = cell(1,dpsz); +for i=1:dpsz + CPD.mlp{i} = mlp(cpsz, nhidden, self_size, 'softmax'); + if nargin >=4 & ~isempty(w1) + CPD.mlp{i}.w1 = w1; + end + if nargin >=5 & ~isempty(b1) + CPD.mlp{i}.b1 = b1; + end + if nargin >=6 & ~isempty(w2) + CPD.mlp{i}.w2 = w2; + end + if nargin >=7 & ~isempty(b2) + CPD.mlp{i}.b2 = b2; + end + W1app(:,:,i)=CPD.mlp{i}.w1; + W2app(:,:,i)=CPD.mlp{i}.w2; + b1app(i,:)=CPD.mlp{i}.b1; + b2app(i,:)=CPD.mlp{i}.b2; +end +if nargin < 8, clamped = 0; end +if nargin < 9, max_iter = 10; end +if nargin < 10, verbose = 0; end +if nargin < 11, wthresh = 1e-2; end +if nargin < 12, llthresh = 1e-2; end + +CPD.self = self; +CPD.max_iter = max_iter; +CPD.verbose = verbose; +CPD.wthresh = wthresh; +CPD.llthresh = llthresh; + +% sufficient statistics +% Since MLP is not in the exponential family, we must store all the raw data. +% +CPD.W1=W1app; % Extract all the parameters of the node for handling discrete obs parents +CPD.W2=W2app; % +nparaW=[size(W1app) size(W2app)]; % +CPD.b1=b1app; % +CPD.b2=b2app; % +nparab=[size(b1app) size(b2app)]; % + +CPD.sizes=bnet.node_sizes(:); % used in CPD_to_table to pump up the node sizes + +CPD.parent_vals = []; % X(l,:) = value of cts parents in l'th example + +CPD.eso_weights=[]; % weights used by the SCG algorithm + +CPD.self_vals = []; % Y(l,:) = value of self in l'th example + +% For BIC +CPD.nsamples = 0; +CPD.nparams=prod(nparaW)+prod(nparab); +CPD = class(CPD, 'mlp_CPD', discrete_CPD(clamped, ns([ps self]))); + +%%%%%%%%%%% + +function CPD = init_fields() +% This ensures we define the fields in the same order +% no matter whether we load an object from a file, +% or create it from scratch. (Matlab requires this.) + +CPD.mlp = {}; +CPD.self = []; +CPD.max_iter = []; +CPD.verbose = []; +CPD.wthresh = []; +CPD.llthresh = []; +CPD.approx_hess = []; +CPD.W1 = []; +CPD.W2 = []; +CPD.b1 = []; +CPD.b2 = []; +CPD.sizes = []; +CPD.parent_vals = []; +CPD.eso_weights=[]; +CPD.self_vals = []; +CPD.nsamples = []; +CPD.nparams = []; diff --git a/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/reset_ess.m b/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/reset_ess.m new file mode 100644 index 00000000..ba7a7101 --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/reset_ess.m @@ -0,0 +1,12 @@ +function CPD = reset_ess(CPD) +% RESET_ESS Reset the Expected Sufficient Statistics for a CPD (mlp) +% CPD = reset_ess(CPD) + +CPD.W1 = []; +CPD.W2 = []; +CPD.b1 = []; +CPD.b2 = []; +CPD.parent_vals = []; +CPD.eso_weights=[]; +CPD.self_vals = []; +CPD.nsamples = 0; \ No newline at end of file diff --git a/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/update_ess.m b/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/update_ess.m new file mode 100644 index 00000000..353a0b5c --- /dev/null +++ b/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/update_ess.m @@ -0,0 +1,131 @@ +function CPD = update_ess(CPD, fmarginal, evidence, ns, cnodes, hidden_bitv) +% UPDATE_ESS Update the Expected Sufficient Statistics of a CPD (MLP) +% CPD = update_ess(CPD, family_marginal, evidence, node_sizes, cnodes, hidden_bitv) +% +% fmarginal = overall posterior distribution of self and its parents +% fmarginal(i1,i2...,ik,s)=prob(Pa1=i1,...,Pak=ik, self=s| X) +% +% => 1) prob(self|Pa1,...,Pak)=fmarginal/prob(Pa1,...,Pak) with prob(Pa1,...,Pak)=sum{s,fmarginal} +% [self estimation -> CPD.self_vals] +% 2) prob(Pa1,...,Pak) [SCG weights -> CPD.eso_weights] +% +% Hidden_bitv is ignored + +% Written by Pierpaolo Brutti + +if ~adjustable_CPD(CPD), return; end + +dom = fmarginal.domain; +cdom = myintersect(dom, cnodes); +assert(~any(isemptycell(evidence(cdom)))); +ns(cdom)=1; + +self = dom(end); +ps=dom(1:end-1); +dpdom=mysetdiff(ps,cdom); + +dnodes = mysetdiff(1:length(ns), cnodes); + +ddom = myintersect(ps, dnodes); % +if isempty(evidence{self}), % if self is hidden in what follow we must + ddom = myintersect(dom, dnodes); % consider its dimension +end % + +odom = dom(~isemptycell(evidence(dom))); +hdom = dom(isemptycell(evidence(dom))); % hidden parents in domain + +dobs = myintersect(ddom, odom); +dvals = cat(1, evidence{dobs}); +ens = ns; % effective node sizes +ens(dobs) = 1; + +dpsz=prod(ns(dpdom)); +S=prod(ens(ddom)); +subs = ind2subv(ens(ddom), 1:S); +mask = find_equiv_posns(dobs, ddom); +for i=1:length(mask), + subs(:,mask(i)) = dvals(i); +end +supportedQs = subv2ind(ns(ddom), subs); + +Qarity = prod(ns(ddom)); +if isempty(ddom), + Qarity = 1; +end +fullm.T = zeros(Qarity, 1); +fullm.T(supportedQs) = fmarginal.T(:); + +% For dynamic (recurrent) net------------------------------------------------------------- +% ---------------------------------------------------------------------------------------- +high=size(evidence,1); % slice height +ss_ns=ns(1:high); % single slice nodes sizes +pos=self; % +slice_num=0; % +while pos>high, % + slice_num=slice_num+1; % find active slice + pos=pos-high; % pos=self posistion into a single slice +end % + +last_dim=pos-1; % +if isempty(evidence{self}), % + last_dim=pos; % +end % last_dim=last reshaping dimension +reg=dom-slice_num*high; +dex=myintersect(reg(find(reg>=0)), [1:last_dim]); % +rs_dim=ss_ns(dex); % reshaping dimensions + +if slice_num>0, + act_slice=[]; past_ancest=[]; % + act_slice=slice_num*high+[1:high]; % recover the active slice nodes + % past_ancest=mysetdiff(ddom, act_slice); + past_ancest=mysetdiff(ps, act_slice); % recover ancestors contained into past slices + app=ns(past_ancest); + rs_dim=[app(:)' rs_dim(:)']; % +end % +if length(rs_dim)==1, rs_dim=[1 rs_dim]; end % +if size(rs_dim,1)~=1, rs_dim=rs_dim'; end % + +fullm.T=reshape(fullm.T, rs_dim); % reshaping the marginal + +% ---------------------------------------------------------------------------------------- +% ---------------------------------------------------------------------------------------- + +% X = cts parent, R = discrete self + +% 1) observations vector -> CPD.parents_vals ------------------------------------------------- +x = cat(1, evidence{cdom}); + +% 2) weights vector -> CPD.eso_weights ------------------------------------------------------- +if isempty(evidence{self}) % R is hidden + sum_over=length(rs_dim); + app=sum(fullm.T, sum_over); + pesi=reshape(app,[dpsz,1]); + clear app; +else + pesi=reshape(fullm.T,[dpsz,1]); +end + +assert(approxeq(sum(pesi),1)); + +% 3) estimate (if R is hidden) or recover (if R is obs) self'value---------------------------- +if isempty(evidence{self}) % R is hidden + app=mk_stochastic(fullm.T); % P(self|Pa1,...,Pak)=fmarginal/prob(Pa1,...,Pak) + app=reshape(app,[dpsz ns(self)]); % matrix size: prod{j,ns(Paj)} x ns(self) + r=app; + clear app; +else + r = zeros(dpsz,ns(self)); + for i=1:dpsz + if pesi(i)~=0, r(i,evidence{self}) = 1; end + end +end +for i=1:dpsz + if pesi(i) ~=0, assert(approxeq(sum(r(i,:)),1)); end +end + +CPD.nsamples = CPD.nsamples + 1; +CPD.parent_vals(CPD.nsamples,:) = x(:)'; +for i=1:dpsz + CPD.eso_weights(CPD.nsamples,:,i)=pesi(i); + CPD.self_vals(CPD.nsamples,:,i) = r(i,:); +end |
