RBM(受限玻尔兹曼机)

时间:2022-12-14 18:35:41

基于能量模型 (EBM)

基于能量模型将关联感兴趣变量每个配置标量能量学习修改能量函数使他形状具有最好性能例如我们想的得到最好的参量拥有较低能量
EBM的概率模型定义通过能量函数概率分布如下所示
        RBM(受限玻尔兹曼机)
规则化系数 Z 称为分区函数物理系统的能量模型相似
        RBM(受限玻尔兹曼机)
一种基于能量模型可以学习通过随机梯度下降的方法处理对数似然训练数据至于 logistic 回归分析我们首次作为负对数似然定义对数似然然后损失函数
        RBM(受限玻尔兹曼机)
使用随机梯度 RBM(受限玻尔兹曼机) 更新参数权值, RBM(受限玻尔兹曼机) 是模型中的各种参数 .
 

EBMs 的隐藏神经元

在很多情况下, 我们看不到部分的隐藏单元 RBM(受限玻尔兹曼机) , 或者我们要引入一些不可见的参量来增强模型的能力.所以我们考虑一些可见的神经元(依然表示为 RBM(受限玻尔兹曼机)) 和 隐藏的部分 RBM(受限玻尔兹曼机). 我们可以这样写我们的表达式:

                                                 RBM(受限玻尔兹曼机)                                                                                         (2)

在这种情况下,公式类似于 (1), 我们引入符号, *能量, 定义如下:

                                                      RBM(受限玻尔兹曼机)                                                                                                   (3)

也可以这么写,

RBM(受限玻尔兹曼机)

数据的负极大似然梯度表示为.

                                                                      RBM(受限玻尔兹曼机)                                                           (4)

注意上面的梯度表示为两个部分,涉及到正面的部分和负面的部分.正面和负面的不表示等式中每部分的符号,而是表示对模型中概率密度的影响. 第一部分增加训练数据的概率 (通过降低相应的*能量), 第二部分降低模型确定下降梯度通常是很困难的, 因为他涉及到计算RBM(受限玻尔兹曼机). 这无非在所有配置下RBM(受限玻尔兹曼机)的期望 (符合由模型生成的概率分布 RBM(受限玻尔兹曼机)) !

 

第一步是计算估计固定数量的模型样本的期望. 用来表示负面部分梯度的表示为负粒子, 表示为 RBM(受限玻尔兹曼机). 梯度可以谢伟:

                                                                                     RBM(受限玻尔兹曼机)                             (5)

我们想 根据 RBM(受限玻尔兹曼机)取样元素RBM(受限玻尔兹曼机) of RBM(受限玻尔兹曼机)  (例如. 我们可以做 蒙特卡罗方法). 通过上面的公式, 我们几乎可以使用随机粒子算法来学习EBM模型. 唯一缺少的就是如何提取这些负粒子T RBM(受限玻尔兹曼机). 统计学上有许多抽样方法, 马尔可夫链蒙特卡罗方法特别适合用于模型如受限玻尔兹曼机 (RBM), 一种特殊的 EBM.

 

受限玻尔兹曼机 (RBM)

玻尔兹曼机(BMS)是一种特殊的对数线性马尔可夫随机场(MRF)的形式,即,其能量函数在其*参数的线性空间里。使他们强大到足以代表复杂的分布,我们考虑到一些变量是没有观察到(他们称为隐藏)。通过更多的隐藏变量(也称为隐藏的单位),我们可以增加的玻尔兹曼机的建模能力(BM)。受限玻尔兹曼机进一步限制BMS中那些可见-可见和隐藏-隐藏的连接。下面是一个RBM的图形描述

RBM(受限玻尔兹曼机)

RBM能量方程 RBM(受限玻尔兹曼机) 定义为 :

   RBM(受限玻尔兹曼机)                               (6)

RBM(受限玻尔兹曼机) 表示隐藏单元和可见单元连接的权重, RBM(受限玻尔兹曼机)RBM(受限玻尔兹曼机) 是可见层和隐藏层的偏置.

转化成下面的*能量公式:

RBM(受限玻尔兹曼机)

由于RBM的特殊结构, 可见和隐藏单元 是条件独立的. 利用这个特性, 我们可以得出:

RBM(受限玻尔兹曼机)

 

二值化的RBMs 

在通常的情况下使用二值化单元 (RBM(受限玻尔兹曼机)RBM(受限玻尔兹曼机)), 我们从公式. (6) and (2)得到, 概率版本的常用神经元激活函数:

 RBM(受限玻尔兹曼机)                                       (7)

 RBM(受限玻尔兹曼机)                                      (8)

二值RBM的*能量可以更简单的表示为:

 RBM(受限玻尔兹曼机)                          (9)

 

二值RBM的参数更新方程

结合等式 (5) 和 (9), 我们得到下面的对数似然梯度方程:

                                RBM(受限玻尔兹曼机)                      (10)

 

RBM中的采样

样本 RBM(受限玻尔兹曼机) 可以通过运行Markov chain收敛得到,使用gibbs采样作为转移操作.

N随机变量的Gibbs采样的联合概率RBM(受限玻尔兹曼机)可以通过一系列的采样得到RBM(受限玻尔兹曼机) ,其中RBM(受限玻尔兹曼机) 包含 RBM(受限玻尔兹曼机) 个 RBM(受限玻尔兹曼机)中其他的随机参数但不包括RBM(受限玻尔兹曼机).

对于RBM,RBM(受限玻尔兹曼机)包含一组可见或者不可见单元,由于他们是条件独立的你可以执行块Gibbs采样。在这种背景下对可见单元同时采样来得到隐藏单元的值. 同样的可以对隐藏单元同时采样,得到可见单元的值。 一步Markov chainA 由下面公式得到:

RBM(受限玻尔兹曼机)

其中RBM(受限玻尔兹曼机) 表示是指在Markov chain第n步RBM(受限玻尔兹曼机) 的所有隐藏单元. 意思是, 例如, RBM(受限玻尔兹曼机) 是根据概率RBM(受限玻尔兹曼机)随机选择为1(或者0), 相似的, RBM(受限玻尔兹曼机) 是根据概率RBM(受限玻尔兹曼机)随机选择为1(或者0).

这可以用下图说明

RBM(受限玻尔兹曼机)

 

当 RBM(受限玻尔兹曼机), 样本RBM(受限玻尔兹曼机) 保证处于真实样本下 RBM(受限玻尔兹曼机).

理论下,每个参数的获取都必须运行这样一个链知道收敛,不用说这样做代价是十分昂贵的。因此,大家为RBM设计了不少算法,为了在学习过程中得到在概率分布RBM(受限玻尔兹曼机)下的有效样本。

对比差异(不知道是否如此翻译) (CD-k)

CD使用两个技巧来加速采样过程 :

  • 由于我们最终想要得到 RBM(受限玻尔兹曼机) (真实的处于数据分布的样本), 我们把马尔科夫链初始化为一个训练样本(即,我们要使一个分布靠近p,那么我们的链应该已经收敛到最后的分布p)
  • CD不需要等待链收敛,样本结果k步gibbs采样得到,在实践中,k=1工作的出奇的好。

Persistent CD

Persistent CD [Tieleman08] 使用另一种方法近似RBM(受限玻尔兹曼机)下的样本,他依赖于一个拥有持续性的马尔科夫链, (i.e.,不为每个可见样本重新计算链 ). 对于每一个参量更新,我们简单的运行k-步链生成新的样本。 链的状态需要保存用来计算以后步骤的参量更新.

一般的直觉是,如果参量更新对于链的混合速率足够小,马尔科夫链应该可以察觉到模型的改变。

 

 

下面我们需要用代码来实现RBM(使用c语言)

 

 

  定义RBM结构体。

typedef struct {
int N;
int n_visible;
int n_hidden;
double **W;
double *hbias;
double *vbias;
} RBM;

  其中,n_visible是可见单元个数,n_hidden为隐藏单元个数。w为隐藏单元和可见单元的权值,bias为偏置。N为训练样本数量。

构建RBM结构:

  

void RBM__construct(RBM* this, int N, int n_visible, int n_hidden, \
double **W, double *hbias, double *vbias) {
int i, j;
double a = 1.0 / n_visible;

this->N = N;
this->n_visible = n_visible;
this->n_hidden = n_hidden;

if(W == NULL) {
this->W = (double **)malloc(sizeof(double*) * n_hidden);
this->W[0] = (double *)malloc(sizeof(double) * n_visible * n_hidden);
for(i=0; i<n_hidden; i++) this->W[i] = this->W[0] + i * n_visible;

for(i=0; i<n_hidden; i++) {
for(j=0; j<n_visible; j++) {
this->W[i][j] = uniform(-a, a);
}
}
} else {
this->W = W;
}

if(hbias == NULL) {
this->hbias = (double *)malloc(sizeof(double) * n_hidden);
for(i=0; i<n_hidden; i++) this->hbias[i] = 0;
} else {
this->hbias = hbias;
}

if(vbias == NULL) {
this->vbias = (double *)malloc(sizeof(double) * n_visible);
for(i=0; i<n_visible; i++) this->vbias[i] = 0;
} else {
this->vbias = vbias;
}
}

  初始化一些结构体,和参量等等。

 

由可见层得到隐藏样本:

double RBM_propdown(RBM* this, int *h, int i, double b) {
int j;
double pre_sigmoid_activation = 0.0;

for(j=0; j<this->n_hidden; j++) {
pre_sigmoid_activation
+= this->W[j][i] * h[j];
}
pre_sigmoid_activation
+= b;
return sigmoid(pre_sigmoid_activation);
}

void RBM_sample_v_given_h(RBM* this, int *h0_sample, double *mean, int *sample) {
int i;
for(i=0; i<this->n_visible; i++) {
mean[i]
= RBM_propdown(this, h0_sample, i, this->vbias[i]);
sample[i]
= binomial(1, mean[i]);
}
}

由隐藏样本得到可见样本:

  

double RBM_propup(RBM* this, int *v, double *w, double b) {
int j;
double pre_sigmoid_activation = 0.0;
for(j=0; j<this->n_visible; j++) {
pre_sigmoid_activation
+= w[j] * v[j];
}
pre_sigmoid_activation
+= b;
return sigmoid(pre_sigmoid_activation);
}

void RBM_sample_h_given_v(RBM* this, int *v0_sample, double *mean, int *sample) {
int i;
for(i=0; i<this->n_hidden; i++) {
mean[i]
= RBM_propup(this, v0_sample, this->W[i], this->hbias[i]);
sample[i]
= binomial(1, mean[i]);
}
}

Gibbs采样:

  

void RBM_gibbs_hvh(RBM* this, int *h0_sample, double *nv_means, int *nv_samples, \
double *nh_means, int *nh_samples) {
RBM_sample_v_given_h(
this, h0_sample, nv_means, nv_samples);
RBM_sample_h_given_v(
this, nv_samples, nh_means, nh_samples);
}

运行CD-K并且更新权值:

  

void RBM_contrastive_divergence(RBM* this, int *input, double lr, int k) {
int i, j, step;

double *ph_mean = (double *)malloc(sizeof(double) * this->n_hidden);
int *ph_sample = (int *)malloc(sizeof(int) * this->n_hidden);
double *nv_means = (double *)malloc(sizeof(double) * this->n_visible);
int *nv_samples = (int *)malloc(sizeof(int) * this->n_visible);
double *nh_means = (double *)malloc(sizeof(double) * this->n_hidden);
int *nh_samples = (int *)malloc(sizeof(int) * this->n_hidden);

/* CD-k */
RBM_sample_h_given_v(
this, input, ph_mean, ph_sample);

for(step=0; step<k; step++) {
if(step == 0) {
RBM_gibbs_hvh(
this, ph_sample, nv_means, nv_samples, nh_means, nh_samples);
}
else {
RBM_gibbs_hvh(
this, nh_samples, nv_means, nv_samples, nh_means, nh_samples);
}
}

for(i=0; i<this->n_hidden; i++) {
for(j=0; j<this->n_visible; j++) {
// this->W[i][j] += lr * (ph_sample[i] * input[j] - nh_means[i] * nv_samples[j]) / this->N;
this->W[i][j] += lr * (ph_mean[i] * input[j] - nh_means[i] * nv_samples[j]) / this->N;
}
this->hbias[i] += lr * (ph_sample[i] - nh_means[i]) / this->N;
}

for(i=0; i<this->n_visible; i++) {
this->vbias[i] += lr * (input[i] - nv_samples[i]) / this->N;
}


free(ph_mean);
free(ph_sample);
free(nv_means);
free(nv_samples);
free(nh_means);
free(nh_samples);
}

 

 

下面的代码是如何重建样本:就是  v->h->v

void RBM_reconstruct(RBM* this, int *v, double *reconstructed_v) {
int i, j;
double *h = (double *)malloc(sizeof(double) * this->n_hidden);
double pre_sigmoid_activation;

for(i=0; i<this->n_hidden; i++) {
h[i]
= RBM_propup(this, v, this->W[i], this->hbias[i]);
}

for(i=0; i<this->n_visible; i++) {
pre_sigmoid_activation
= 0.0;
for(j=0; j<this->n_hidden; j++) {
pre_sigmoid_activation
+= this->W[j][i] * h[j];
}
pre_sigmoid_activation
+= this->vbias[i];

reconstructed_v[i]
= sigmoid(pre_sigmoid_activation);
}

free(h);
}

 

 

最后检测RBM代码:

  

void test_rbm(void) {
srand(
0);

int i, j, epoch;

double learning_rate = 0.1;
int training_epochs = 1000;
int k = 1;

int train_N = 6;
int test_N = 2;
int n_visible = 6;
int n_hidden = 3;

// training data
int train_X[6][6] = {
{
1, 1, 1, 0, 0, 0},
{
1, 0, 1, 0, 0, 0},
{
1, 1, 1, 0, 0, 0},
{
0, 0, 1, 1, 1, 0},
{
0, 0, 1, 0, 1, 0},
{
0, 0, 1, 1, 1, 0}
};

// construct RBM
RBM rbm;
RBM__construct(
&rbm, train_N, n_visible, n_hidden, NULL, NULL, NULL);

// train
for(epoch=0; epoch<training_epochs; epoch++) {
for(i=0; i<train_N; i++) {
RBM_contrastive_divergence(
&rbm, train_X[i], learning_rate, k);
}
}


// test data
int test_X[2][6] = {
{
1, 1, 0, 0, 0, 0},
{
0, 0, 0, 1, 1, 0}
};
double reconstructed_X[2][6];

// test
for(i=0; i<test_N; i++) {
RBM_reconstruct(
&rbm, test_X[i], reconstructed_X[i]);
for(j=0; j<n_visible; j++) {
printf(
"%.5f ", reconstructed_X[i][j]);
}
printf(
"\n");
}


// destruct RBM
RBM__destruct(&rbm);
}