虽然推导过二值形式的RBM,但是对于可见层为实值的输入还是半知半解的。最近写个深度学习相关综述,看了一些关于RBM的文献,这里做一下对比总结。但是不倾向于对实值RBM的推导,而是相关代码的实现。
RBM是具有特殊结构的玻尔兹曼机,可见单元到隐单元是全连接,但是层内是无连接的,也就是可见单元不会和可见单元连一起。RBM的这种二分结构保证了当给定可见单元的时候,隐藏层个单元是相互独立的,至于为什么,可以看我前面对概率图模型截图的两篇文章:概率无向图模型、概率有向图模型,这两篇文章不长,而且对理解RBM非常有帮助,毕竟RBM也属于概率无向能量图模型。
【笔者注】有句话要记住:“没有万能的模型,只有不断优化的理论和结构”。个人非常不建议这样的问题:“为什么我把XX结构用到XX数据上效果不好哇”,“XX这个结构用到XX上出现这个问题是什么情况”。多看论文,不都迎刃而解了么。
定义RBM的可见单元为 vi ,对应偏置为 ai ,隐层单元为 hj ,对应偏置为 bj ,那么RBM的可见层与隐藏层的联合概率分布就是
用向量的方法写RBM的联合概率分布,迭代采样中的条件概率分布如下 1 :
找代码的目的主要是为了看理论和代码的一致性,Hinton提供的代码结构分析如下:
可见层 → 隐藏层
利用可见层对隐层采样
data = batchdata(:,:,batch);
poshidprobs = 1./(1 + exp(-data*vishid - repmat(hidbiases,numcases,1)));
batchposhidprobs(:,:,batch)=poshidprobs;
当然,由于隐层是二值形式,所以采用随机阈值激活方法
poshidstates = poshidprobs > rand(numcases,numhid);
记录 <vihj>data;<vi>data;<hj>data
posprods = data' * poshidprobs;
poshidact = sum(poshidprobs);
posvisact = sum(data);
隐藏层 → 可见层
利用隐层二值状态poshidstates
对可见层重构
negdata = 1./(1 + exp(-poshidstates*vishid' - repmat(visbiases,numcases,1)));
记录 <vihj>recon;<vi>recon;<hj>recon
neghidprobs = 1./(1 + exp(-negdata*vishid - repmat(hidbiases,numcases,1)));
negprods = negdata'*neghidprobs;
neghidact = sum(neghidprobs);
negvisact = sum(negdata);
更新模型参数
vishidinc = momentum*vishidinc + ...
epsilonw*( (posprods-negprods)/numcases - weightcost*vishid);
visbiasinc = momentum*visbiasinc + (epsilonvb/numcases)*(posvisact-negvisact);
hidbiasinc = momentum*hidbiasinc + (epsilonhb/numcases)*(poshidact-neghidact);
vishid = vishid + vishidinc;
visbiases = visbiases + visbiasinc;
hidbiases = hidbiases + hidbiasinc;
代码中多出的项分别为动量项momentum
,学习率epsilon
,权重衰减weightcost
由此可见,上述理论和代码可以作为二值RBM的标准写法。
很多情况RBM的输入向量是实值形式的,比如手写数字的处理中,先对数据进行归一化,然后使用二值可见单元的实值概率取代激活情况。注意下面讨论的是可见单元为实值,隐单元为二值的RBM的情况,关于实值-实值的情况以后再讨论,其实有人说,实值-实值的RBM已经退化成PCA了,后续继续研究。
在Hinton的课程和文献 2 中书写方式为
所以在实际中,经常让 σ=1 ,而且在文献 5 中也提到了: 实际中,我们经常把我们的数据缩放到零均值和单位方差,固定式中方差 σi 为1能够学习地更好,即使我们期望模型能够达到更高的精度,因而这就出现了后来的
采样方法依旧是使用k次吉布斯采样,依据是一层的一个单元关于另一层所有单元的条件概率分布函数。需要注意的是,由于我们采用的是实值RBM,可见层单元服从零均值、单位方差的分布,所以依据隐层采样可见层不再是 P(vi=1|h) ,而是利用隐单元对可见单元的均值做一个线性贡献 2 6 ,在高斯分布中进行采样得到的,而高斯分布的参数也是由下式给出的
因为方差 σ=1 ,所以出现了下面两种写法
其中 f(⋅) 是logistic函数(对数函数), N(μ,V) 是高斯分布。
权重和偏置的更新方法如下
保险起见,对于实值RBM,分析的代码应该不止一个,而且读者没必要具体分析代码的各个变量,只需要关注更新的大概方法即可。代码公布下载戳此处:代码1、代码2、代码3 .
关于实值RBM训练阶段的positive过程(可见层 → 隐层)就直接贴了,和二值形式一样,先计算条件概率 P(hj=1|v) ,然后使用随机阈值激活为01二值状态。
eta = w*(data(:,:,1)./gsd)' + ... %bottom-up connections
repmat(bj, 1, numcases) + ... %static biases on unit
bjstar; %dynamic biases
hposteriors = 1./(1 + exp(-eta)); %logistic
%Activate the hidden units
hidstates = double(hposteriors' > rand(numcases,numhid));
%Calculate positive gradients (note w.r.t. neg energy)
wgrad = hidstates'*(data(:,:,1)./gsd);
bigrad = sum(data(:,:,1)' - ...
repmat(bi,1,numcases) - bistar,2)./gsd^2;
bjgrad = sum(hidstates,1)';
可以发现此代码保留了 σ ,由可见层得到隐层状态的公式为
%pass 3-way term + gated biases + hidbiases through sigmoid
poshidprobs = 1./(1 + exp(-yvisfeat*hidfac' ...
-ypastfeatB*hidfacB' - repmat(hidbiases,numcases,1)));
%-data*vishid - repmat(hidbiases,numcases,1)));
%Activate the hidden units
hidstates = single(poshidprobs > rand(numcases,numhid));
此代码未保留 σ 即 σ=1 ,那么更新形式就较为简单
%Term calculated by summing over hiddens
hidinp = data*vishid + effhidbias;
exphidinp = exp(hidinp); %cache this computation
poshidprobsall(:,:,cc) = exphidinp./(1+exphidinp);
poshidprobs(idx,:) = poshidprobsall(idx,:,cc);
posprods(:,:,cc) = (data(idx,:)./gsd)'*poshidprobs(idx,:); %smoothed: probs, not binary
poshidact(cc,:) = sum(poshidprobs(idx,:),1); %col vector; again smoothed
posvisact(cc,:) = sum(data(idx,:),1)./gsd^2; %row vector
这里的激活函数依旧是sigmoid函数,只不过变了一个形式
反向阶段比较模糊的从 N(μ,σ2) 中采样的具体写法,其实比较建议先看我关于吉布斯采样翻译和matlab实现的那一部分代码,基本就知道如何根据均值和方差采样了,而且也有效果展示,可以明白为什么使用这个代码能够得到符合次分布的采样点。
topdown = gsd.*(hidstates*w);
%This is the mean of the Gaussian
%Instead of properly sampling, negdata is just the mean
%If we want to sample from the Gaussian, we would add in
%gsd.*randn(numcases,numdims);
negdata = topdown + ... %top down connections
repmat(bi',numcases,1) + ... %static biases
bistar'; %dynamic biases
可以发现 P(vi|h) 的计算方法很简单
eta = w*(negdata./gsd)' + ... %bottom-up connections
repmat(bj, 1, numcases) + ... %static biases on unit (no change)
bjstar; %dynamic biases (no change)
hposteriors = 1./(1 + exp(-eta)); %logistic
%Calculate negative gradients
negwgrad = hposteriors*(negdata./gsd); %not using activations
negbigrad = sum( negdata' - ...
repmat(bi,1,numcases) - bistar,2)./gsd^2;
negbjgrad = sum(hposteriors,2);
可以发现是先利用一次 P(vi|h) 得到新的隐状态激活概率,然后
negdata = (yfeat.*yhid)*visfac' + ...
(yfeatpastA)*visfacA' + ...
repmat(visbiases,numcases,1);
由于采用三通道,所以会多出几项,但是也很容易看出一般情况的更新方法
negdata(idx,:) = gsd.*(hidstates(idx,:)*vishid(:,:,cc)') + ...
effvisbiases(idx,:,cc);
同样符合上式。结合三个代码可以发现,从高斯分布中采样的方法为
综合上述理论及代码,我们可以得到RBM的条件分布采样方法以及参数更新公式
Reference
[1] The Recurrent Temporal Restricted Boltzmann Machine
[2] Two Distributed-State Models For Generating High-Dimensional Time Series
[3] Robust Generation of Dynamical Patterns in Human Motion by a Deep Belief Nets
[4] Modeling Human-Skeleton Motion Patterns Using Conditional Deep Boltzmann Machine
[5] Modeling Human Motion Using Binary Latent Variables
[6] Temporal Autoencoding Improves Generative Models of Time Series
[7] Factored Conditional Restricted Boltzmann Machines for Modeling Motion Style
[8] Implicit mixtures of Conditional Restricted Boltzmann Machines