about summary refs log tree commit diff
path: root/sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD
diff options
context:
space:
mode:
Diffstat (limited to 'sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD')
-rw-r--r--sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/CVS/Entries6
-rw-r--r--sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/CVS/Repository1
-rw-r--r--sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/CVS/Root1
-rw-r--r--sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/convert_to_table.m80
-rw-r--r--sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/maximize_params.m34
-rw-r--r--sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/mlp_CPD.m139
-rw-r--r--sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/reset_ess.m12
-rw-r--r--sourcecodes/bnt-master/BNT/CPDs/@mlp_CPD/update_ess.m131
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