判别模型的玻尔兹曼机论文源码解读

前言

三号要去参加CAD/CG会议,投了一篇关于使用生成模型和判别模型的RBM做运动捕捉数据风格识别的论文。这段时间一直搞卷积RBM了,差点把原来的实验内容都忘记了,这里复习一下判别式玻尔兹曼机的训练流程。

国际惯例,贴几个链接:

论文1——Energy Based Learning Classification task using Restricted Boltzmann Machines

链接:http://pan.baidu.com/s/1i5foeEx 密码:flq7

论文2——Classification using Discriminative Restricted Boltzmann Machines

链接:http://pan.baidu.com/s/1qYGT9z2 密码:hebj

代码——RBM-on-Classification

github:https://github.com/atzenigor/RBM-on-Classification

链接:http://pan.baidu.com/s/1gf41MKz 密码:z3jq

数据——mnist手写数字数据库

链接:http://pan.baidu.com/s/1boGxPxX 密码:0l25

本文主要是结合论文1及其代码对判别式限制玻尔兹曼机进行解读,代码主要使用在手写数字识别mnist数据库中。

先画一下代码对应的模型结构图


第一步

从主函数入手:trainingClassRBM.m

首先是一系列的初始化,就不说了,主要包含学习率、动量项、权重衰减、分批大小、训练次数,随机初始化权重和偏置等。这里主要关注两行程序:

  discrim = 1;    % 1 to activate the discriminative learning, 0 otherwisefreeenerg = 1;  % if discrim is zero but one want to compute the free energy

对于这两句话,可以翻开论文1的第五章(第11页)说过这样一句话:


大概意思就是,为了解决特定的分类问题,我们可以简单地定义一个目标函数去进行最小化,进而得到RBM的模型参数。而目标函数可以选择三种:

1、生成模型的训练目标函数


2、判别模型的训练目标函数


3、混合模型的训练目标函数


显然,根据代码,可以发现程序使用了变量discrim来控制是否使用第三种方法,如果discrim=1则使用第三种目标函数,如果discrim=0则使用第一种目标函数。

此后也进行了数据的分批处理和验证集的选取

data_valid = data(end - valid_size + 1 : end, : );
label_valid = labels(end - valid_size + 1 : end, : );%convert y in the notetion one out of number of classes
lab = boolean(set_y(labels(1:end - valid_size,:), num_class));
%convert the dataset in batches
[batch_data, batch_labels , batch_size] = ...createBatches(data(1:end - valid_size,:), lab, num_batch);

第二步

正式进入第一层for循环,用于控制训练次数,每次训练完毕都会输出相应的几个信息,包含重构误差,验证集误差,自由能变化。

  sum_rec_error = 0;sum_class_error = 0;sum_tot_free_energ = 0;
下一句就是在进行第五次迭代时候,将动量项改变

 if  i_epoch == 5 momentum = final_momentum;end;
并且使用模拟退火(simulated annealing)算法降低学习率

%simulated annealinggamma = (init_gamma) / (1 + (i_epoch - 1) / tau);

第三步

正式进入内层循环对数据的分配处理,每次只取一批数据,大小为100(最后一批大小根据情况而定)

  x = batch_data{iBatch}';y = batch_labels{iBatch}';batch_size=size(x,2);
进入生成模型的计算,即计算第一步提到的第一个目标函数对应的模型参数更新。

首先计算隐藏层的激活概率值P(h|x,y),这就是在RBM中常说的positive phase根据文章可以得到

而相应的程序就是

 hprob = prob_h_given_xy(x, y, w, u, b_h);       
其中函数prob_h_given_xy为:

function [ prob ] = prob_h_given_xy( x, y, w, u, b_h )
<span style="white-space:pre">	</span>prob = sigmf(bsxfun(@plus, w * x + u * y, b_h), [1 0]);
end
随后进行隐单元的随机激活,这是RBM中经常出现的一步骤,我们称为激活状态:

h = sampling(hprob);
激活函数实现如下:

function [ samp ] = sampling( p )
<span style="white-space:pre">	</span>samp = p > rand(size(p));
end
然后便是对应的negative phase,使用得到的隐单元状态反向计算x和y单元数据

根据论文介绍的激活函数

可以找到对应的激活函数程序写法:

计算P(x|h),很简单,直接计算权重乘以隐单元的激活状态(并非激活概率值)然后加上输入层偏置即可。

function [ prob ] = prob_x_given_h( h, w, b_x )
<span style="white-space:pre">	</span>prob = sig(bsxfun(@plus, w' * h, b_x));
end
计算P(y|h),稍微复杂点,在分母的处理上,需要进行加和,u'*h计算得到的矩阵大小是类别数*批大小,实验中大小是30*716,而sum是将所有行加到第一行,也就是说对每一个样本计算得到的标签层单元数据进行加和,整个过程类似于进行了归一化。

function [ prob ] = prob_y_given_h( h, u , b_y )
<span style="white-space:pre">	</span>prob_not_norm = exp(bsxfun(@plus, u' * h, b_y));
<span style="white-space:pre">	</span>norm_factor = sum(prob_not_norm);
<span style="white-space:pre">	</span>prob = bsxfun(@rdivide, prob_not_norm, norm_factor);
end
计算得到x和y的激活概率值之后同样需要激活状态

xrprob = prob_x_given_h( h, w, b_x );
yrprob = prob_y_given_h( h, u, b_y );
xr = sampling(xrprob);
yr = sampling_y(yrprob);
进行negative phase的最后一步,利用新的x和y的状态值(0或者1)去计算隐层的激活概率值

hrprob = prob_h_given_xy( xr, yr, w, u, b_h);
根据以上计算的positive phase和negative phase相关值计算生成模型各参数的更新梯度,主要包含隐层至输入层的连接权重w,隐层至标签层的连接权重v,输入层偏置b_x,隐藏层偏置b_h,标签层偏置b_y。注意更新的时候用的是激活概率值去更新,而非激活状态值去更新。

g_w_gen = (hprob * x' - hrprob * xrprob') / batch_size;
g_u_gen = (hprob * y' - hrprob * yrprob') / batch_size;
g_b_x_gen = (sum(x,2) - sum(xrprob,2)) / batch_size;
g_b_y_gen = (sum(y,2) - sum(yrprob,2)) / batch_size;
g_b_h_gen = (sum(hprob,2) - sum(hrprob,2)) / batch_size;

第四步

进入判别模型的目标函数计算,上面第一节介绍的第二个目标函数。

当然首先就是判断我们是否选择使用判别模型,以及初始化判别模型计算中的相关参数,包含隐层偏置梯度,两个权重w和u的梯度

    if (discrim || freeenerg)%initialization of the gradientsg_b_h_disc_acc = 0;g_w_disc_acc = 0;g_u_disc_acc = zeros(h_size,num_class);
特别注意,此处是没有输入层偏置的,详细可看论文1中的这样一句话:


意思就是b(文章的b是输入层的偏置)的梯度为0,因为计算P(y|x)的时候并没有用到输入层的偏置。

让我们看看P(y|x)的计算方法



其中F 就代表自由能量(free energy)

还是写一下比较好,后面的代码会单独计算这个自由能量,其中dy是标签层偏置


后面的步骤便是逐步实现这个采样函数

注意这里的softplus也是一个函数,而非简单的加法,具体介绍点击此处


①首先发现式子中始终有w*x这一项,所以提出来计算一下,避免后面的重复计算

      %ausiliary variablesw_times_x = w * x;
②接下来计算自由能量函数

 for iClass= 1:num_classo_t_y(:,:,iClass) = bsxfun(@plus, b_h + u(:,iClass), w_times_x );neg_free_energ(iClass,:) = b_y(iClass) + sum(log(1 + exp(o_t_y(:,:,iClass))));
end;

注意这里的两个变量的维度:

o_t_y维度大小是800*716*30,分别代表隐单元神经元个数、一批样本数、标签总数,就像对隐层求了30个800*716的单元数值,然后后面计算的时候也确实如此,每次计算都用了一个for循环对第三个维度的800*716矩阵分别遍历,应该就是为了计算此样本属于每一个标签的概率。

neg_free_energ大小是30*716,分别代表标签总数、样本总数

计算的时候是依次计算标签层每个神经元所代表的所有样本之和。有点像先用x和y计算得到h,然后将h经过softplus映射,最后将每一个样本的隐层800个单元求和,得到一个样本在对应的标签单元的值。比如o_t_y计算iClass=1时候得到的其实是一个800*716的矩阵,然后经过sum得到1*716的向量,直接作为标签层的第一个单元的所有样本的值即可。

然后记录了一下当前批数据的总共的能量和,用于与下一批的自由能量和相加

sum_tot_free_energ = sum_tot_free_energ + sum(-neg_free_energ(y));
至此“ if (discrim || freeenerg)”中的freeenerg计算完毕。
③接下来就是计算discrim对应的参数更新梯度了

主要就是计算P(y|x)根据上面提到的式子。

程序中首先对求得的neg_free_energ进行了零均值化,并计算出了分子的值

        med = mean2(neg_free_energ);      p_y_given_x = exp(neg_free_energ - med);
利用分子的值,计算分子/分母的值,注意这里的加是将每个样本对应的30个标签层值相加,即sum(p_y_given_x)的结果是将30*716加和成了1*716维度的向量

p_y_given_x = bsxfun(@rdivide, p_y_given_x, sum(p_y_given_x));
④最后就是计算梯度了

首先计算标签层的梯度,按照原文的公式


说白了就是用原始标签减去P(y|x)推理得到的标签值,最后计算一下加和即可,便得到了标签层偏置更新梯度

        dif_y_p_y = y - p_y_given_x;g_b_y_disc_acc = sum(dif_y_p_y,2);

随后便是计算剩下的三个量权重w、v和隐层偏置b_h的梯度,查看论文1的计算方法


其中


相关的计算程序如下:

首先计算第一项和第二项的 O 函数

sig_o_t_y = sig(o_t_y(:,:,iClass)); 
sig_o_t_y_selected = sig_o_t_y(:,y(iClass,:)); 
然后分别套入对b_h、w、u的计算中

g_b_h_disc_acc = g_b_h_disc_acc + sum(sig_o_t_y_selected, 2) - sum(bsxfun(@times, sig_o_t_y, p_y_given_x(iClass,:)),2);
%update the gradient of w fot the class 'iClass'
g_w_disc_acc = g_w_disc_acc + sig_o_t_y_selected * x(:,y(iClass,:))' - bsxfun(@times, sig_o_t_y, p_y_given_x(iClass,:)) * x';
%update the gradient of u fot the class 'iClass'
g_u_disc_acc(:,iClass) = sum(bsxfun(@times, sig_o_t_y, dif_y_p_y(iClass,:)), 2);
注意这两部分代码要用一个for循环包含起来

  for iClass= 1:num_class
主要也就是上面说过的,对o_t_y(维度为隐层单元数*一批样本数*标签类别总数)的第三维进行遍历,每次遍历得到的隐单元数*批样本数作为隐层单元即sig_o_t_y(代码维度是800*716),然后从中抽取出标签为iClass的样本(比如716个样本中,有29个样本标签为当前循环的iClass)得到sig_o_t_y_selected(iClass=1时文章维度为800*29)这样就比较好理解了,就像生成模型的RBM一样:

计算隐层偏置b_h的时候,计算sig_o_t_y_selected与sig_o_t_y*P(y|x)的差值即可;

计算连接权重w的时候,计算sig_o_t_y_selected*x_selected(这个变量代表批样本x中标签为iClass的样本矩阵)与sig_o_t_y*P(y|x)*x对应的差值即可;

计算连接权重u的时候,只需计算sig_o_t_y与dif_y_p_y(iClass,:)的乘积即可,因为在计算dif_y_p_y的时候,已经计算过差值了,所以没有像上面计算b_h和w一样去做差。

⑤全部计算完了,就更新一下判别模型中的几个参数吧

g_b_y_disc = g_b_y_disc_acc / batch_size;
g_b_h_disc = g_b_h_disc_acc / batch_size;
g_w_disc = g_w_disc_acc / batch_size;
g_u_disc = g_u_disc_acc / batch_size;

第五步

利用第一步介绍的目标函数三进行全局梯度更新

 delta_w = delta_w * momentum + ...gamma * (discrim * g_w_disc + alpha * g_w_gen - weight_cost * w);
delta_u = delta_u * momentum + ...gamma * (discrim * g_u_disc + alpha * g_u_gen - weight_cost * u);
delta_b_x = delta_b_x * momentum + ...gamma * alpha * g_b_x_gen;
delta_b_y = delta_b_y * momentum + ...gamma * (discrim * g_b_y_disc + alpha * g_b_y_gen);
delta_b_h = delta_b_h * momentum + ...gamma * (discrim * g_b_h_disc + alpha * g_b_h_gen);
w = w + delta_w;
u = u + delta_u;
b_x = b_x + delta_b_x;
b_y = b_y + delta_b_y;
b_h = b_h + delta_b_h;
后面做了一个平均,表示没太懂是干什么用的,好像是削减梯度?而且在每次迭代输出的验证集误差的时候使用的依旧是上面的b_y, b_h, w, u 而非做平均的b_y_avg, b_h_avg, w_avg, u_avg 。这个还有待探讨。

第六步

获得所有参数以后需要进行识别,很简单,有一个输入testdata,对应的标签testlabels,总类别数目num_class,对应的模型参数b_y, b_h, w, u,然后利用能量函数F的计算得到样本属于每个标签的概率,前面也说了,neg_free_energ的大小是标签数*样本数,这样就能得到每一个样本对应的每一个标签的概率是对少了,取最大的那个概率就是了。

function [ err ] = predict( testdata, testlabels, num_class, b_y, b_h, w, u )numcases= size(testdata, 1);w_times_x = w * testdata';neg_free_energ = zeros(num_class, numcases);for iClass= 1:num_classneg_free_energ(iClass,:) = b_y(iClass) + sum(log(1 + ...exp(bsxfun(@plus, b_h + u(:,iClass), w_times_x ))));end;[~, prediction] = max(neg_free_energ);err = sum(prediction ~= testlabels') / numcases * 100;end

第七步

补充一下,如何看模型的效果好坏呢?

首先重构误差不必多说,肯定是慢慢降低


其次就是自由能量了,根据RBM的描述,系统最稳定的时刻就是能量最少的时候。


当然咯,分类误差肯定也是降低啦


还有,有时候能量会突然升高,这时候不要慌,先等等,看看是否一直升高,有可能在某次迭代完成就下降了呢,类似于这样


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/246674.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Jacobian矩阵和Hessian矩阵

原文转自&#xff1a;http://jacoxu.com/?p146 1. Jacobian 在向量分析中, 雅可比矩阵是一阶偏导数以一定方式排列成的矩阵, 其行列式称为雅可比行列式. 还有, 在代数几何中, 代数曲线的雅可比量表示雅可比簇&#xff1a;伴随该曲线的一个代数群, 曲线可以嵌入其中. 它们全部都…

为什么梯度下降法对于非线性可分数据有效

前言 晚上逛微博看到的&#xff0c;顺便拿过来翻译一下&#xff0c;做做笔记 国际惯例&#xff0c;来个原文链接&#xff1a; 原文地址&#xff1a;Why is gradient descent robust to non-linearly separable data? PDF拷贝&#xff1a;http://download.csdn.net/detail/…

卷积RBM源码解读

前言 卷积RBM相对RBM来说具有很多优势&#xff0c;详细的我也不说了&#xff0c;看文章就行。主要还是为了加深自己对细节部分的理解吧。 国际惯例&#xff0c;贴几个链接 卷积RBM的创始人Honglak Lee&#xff1a;http://web.eecs.umich.edu/~honglak/hl_publications.html#…

c语言:递归法求n的阶乘|练习题

一、题目 输入一个数n&#xff0c;用递归法求n的阶乘 二、思路分析 1、因为n!(n-1)!*n,所以&#xff0c;可以选择用递归法 三、代码截图【带注释】 四、源代码【带注释】 #include <stdio.h> //思路&#xff1a; //因为n!(n-1)!*n,所以&#xff0c;可以选择用递归法 int…

【caffe-Windows】cifar实例编译之model的生成

参考&#xff1a;<span style"font-family: Arial, Helvetica, sans-serif;">http://blog.csdn.net/chengzhongxuyou/article/details/50715455</span> 准备工作 按照之前的教程&#xff0c;成功生成过caffe&#xff0c;并且编译整个caffe.sln项目工程&a…

机器学习性能改善备忘单:32个帮你做出更好预测模型的技巧和窍门

文章来源&#xff1a;大数据文摘 作者 | Jason Brownlee 选文 | Aileen 翻译 | 姜范波 校对 | 寒小阳 机器学习最有价值(实际应用最广)的部分是预测性建模。也就是在历史数据上进行训练&#xff0c;在新数据上做出预测。 而预测性建模的首要问题是&#xff1a; 如何才能得…

【caffe-Windows】新层添加——LSTM

前言 原始的【caffe-Windows】是没有LSTM层的&#xff0c;维护以后的caffe的windows版本也懒得配置了&#xff0c;因为大部分文章的代码还是基于老版caffe。其实大部分的添加层方法都可以参考本博客&#xff0c;仅限Windows。 需要的文件有&#xff1a; 1. 原始的caffe-Wind…

【caffe-Windows】关于LSTM的使用-coco数据集

前言 建议大家使用Linux&#xff0c;因为Linux下的配置就没这么麻烦&#xff0c;各种make就行啦。Linux用户请绕道&#xff0c;因为此博客只针对Windows&#xff0c;可能比Linux麻烦很多倍。 在caffe-Windows新增LSTM层以后&#xff0c;相信很多人和我一样很好奇如何使用这一…

【caffe-Windows】关于LSTM的简单小例子

前言 这里主要是看到了一个简单的LSTM例子&#xff0c;比上一个coco简单很多&#xff0c;所以在这里记录一下&#xff0c;便于后续的分析&#xff0c;参考博客在上一篇文章的末尾提到过&#xff1a;Recurrent neural nets with Caffe 需要说明的是这个例子也并非原原本本的使…

概率有向图模型

1. 前言 主要参考书籍《深度学习导论及案例分析》、维基百科“贝叶斯网络”、老笨妞的博客、PRML中文翻译&#xff0c;重点还是概念的掌握和几个小理论的推导&#xff0c;比较枯燥。加入了自己的一些简单理解。 个人感觉概率有向图模型最大的意义在于&#xff1a;一个特定的有…

概率无向图模型

1. 前言 前面看了概率有向图模型&#xff0c;必然对无向图模型也要研究一下。而且这个概率无向图模型对学习RBM有很大的帮助&#xff0c;它关系到能量函数的来源&#xff0c;所以还是看看吧。参考资料依旧是概率有向图模型中参考的三个资料。 有向图将一组变量上的联合概率分…

softmax理论及代码解读——UFLDL

前言 看了各种softmax以后迷迷糊糊的&#xff0c;还是研究一下UFLDL的教程稳点。当然还是得参考挺多教程的&#xff1a;UFLDL-softmax 、Softmax的理解与应用 、Logistic 分类器与 softmax分类器 、详解softmax函数以及相关求导过程 、Exercise:Softmax Regression 。 【UFLDL…

二值RBM与实值RBM理论及代码解读

1. 前言 虽然推导过二值形式的RBM&#xff0c;但是对于可见层为实值的输入还是半知半解的。最近写个深度学习相关综述&#xff0c;看了一些关于RBM的文献&#xff0c;这里做一下对比总结。但是不倾向于对实值RBM的推导&#xff0c;而是相关代码的实现。 2. RBM回顾 RBM是具有…

【caffe-Windows】添加工程-以classification为例

前言 兴趣所向&#xff0c;研究一下如何在caffe工程之外建立一个属于自己的工程&#xff0c;这里以分类为例&#xff0c;将classification.cpp提取出来&#xff0c;然后调用相应的三方库和libcaffe.lib进行编译。这里比较建议有一丢丢C功底的同志参考学习&#xff0c;主要涉及…

MKL学习——数学运算库安装调试

前言 最近要用C折腾一些东西&#xff0c;涉及到矩阵运算&#xff0c;看了一下网上推荐的数学库&#xff0c;貌似MKL还是蛮不错滴&#xff0c;放到VS2013里面试试 国际惯例&#xff0c;来波地址 blas, cblas, openblas, atlas, lapack, mkl性能对比 Compiling and Linking I…

MKL学习——向量操作

前言 推荐两个比较好的教程: BLAS (Basic Linear Algebra Subprograms) LAPACK for Windows 命名规范 BLAS基本线性代数子程序的函数命令都有一定规范&#xff0c;便于记忆 <character> <name> <mod> () character 定义的是数据类型 s实数域&#…

【caffe-Windows】识别率批量输出——matlab实现

前言 今天看到群里有人问”用matlab输出测试集的精度“&#xff0c;瞎试了一下&#xff0c;好像还成功了。主要还是依据前面所有在matlab中操作caffe的博客。 这里说一下&#xff1a;classification.m是适用单张图片的精度&#xff0c;类似于classification.exe的功能&#x…

相机矩阵(Camera Matrix)

前言 最近翻阅关于从2D视频或者图片中重构3D姿态的文章及其源码&#xff0c;发现都有关于摄像机参数的求解&#xff0c;查找了相关资料&#xff0c;做一下笔记。 国际惯例&#xff0c;来一波参考网址 透视变换、透镜畸变及校正模型、相机校正(Camera Calibration)、Matlab相…

损失函数梯度对比-均方差和交叉熵

前言 我们都知道在机器学习中&#xff0c;希望算法或者网络收敛更快&#xff0c;有些是对数据预处理&#xff0c;尤其是Batch Normalization&#xff0c;有些是采用不同的激活函数&#xff0c;尤其是Relu激活函数取得了巨大的成功&#xff0c;还有一种加速收敛方法是更换损失函…

如何选择深度学习优化器

前言 转载地址&#xff1a;如何选择深度学习优化器 作者&#xff1a;不会停的蜗牛 CSDN AI专栏作家 在很多机器学习和深度学习的应用中&#xff0c;我们发现用的最多的优化器是 Adam&#xff0c;为什么呢&#xff1f; 下面是 TensorFlow 中的优化器&#xff0c; https://w…