(EBM)

        RBM(受限玻尔兹曼机)
        RBM(受限玻尔兹曼机)
        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  (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);
}

 

 

相关文章: