Denoising Autoencod

Denoising Autoencod(去噪自编码)(DA)是一个经典的autoencode算法的扩展,在[vincent08 ]中作为深网络的构建块使用。我们将开始本教程对autoencoders简短的讨论。
 
  • 一个autoencode 将输入   \mathbf{x} \in [0,1]^d  通过 一个确定的投影方法(encode ) 投影到一个隐层空间\mathbf{y} \in [0,1]^{d'}。 

\mathbf{y} = s(\mathbf{W}\mathbf{x} + \mathbf{b})                  W是投影矩阵, b为隐层的偏置 s是一个非线性函数如 sigmoid。然后我们通过 y重建 x。得到z。投影方法如下式表示

\mathbf{z} = s(\mathbf{W'}\mathbf{y} + \mathbf{b'})                 z作为x(输入的预测)        \mathbf{W'} = \mathbf{W}^T

那么这个模型的参数列表为\mathbf{W}\mathbf{b}\mathbf{b'}

 

传统的平方误差为                      

                                     L(\mathbf{x} \mathbf{z}) = || \mathbf{x} -
\mathbf{z} ||^2

重建的交叉熵为

                            L_{H} (\mathbf{x}, \mathbf{z}) = - \sum^d_{k=1}[\mathbf{x}_k \log
        \mathbf{z}_k + (1 - \mathbf{x}_k)\log(1 - \mathbf{z}_k)]

 

 

下面我们来用C语言实现这个模型。

 

首先需要定义

typedef struct {
  int N;
  int n_visible;   //输入神经元个数
  int n_hidden;  //隐藏神经元个数
  double **W;    //参数w
  double *hbias; // 参数  输入的偏置
  double *vbias; //隐藏神经元的偏置
} dA;

 

 

定义encode 函数    \mathbf{y} = s(\mathbf{W}\mathbf{x} + \mathbf{b})

void dA_get_hidden_values(dA* this, int *x, double *y) {
            int i,j;
            for(i=0; i<this->n_hidden; i++) {
                 y[i] = 0;
                 for(j=0; j<this->n_visible; j++) {
                        y[i] += this->W[i][j] * x[j];
                 }
                 y[i] += this->hbias[i];
                 y[i] = sigmoid(y[i]);
             }
       }

 

定义decode 函数 \mathbf{z} = s(\mathbf{W'}\mathbf{y} + \mathbf{b'})

void dA_get_reconstructed_input(dA* this, double *y, double *z) {
                int i, j;
                for(i=0; i<this->n_visible; i++) {
                     z[i] = 0;
                     for(j=0; j<this->n_hidden; j++) {
                            z[i] += this->W[j][i] * y[j];
                      }
                      z[i] += this->vbias[i];
                      z[i] = sigmoid(z[i]);
                  }
              }

 

 

随机损坏输入。这样能更好的使我们的模型学习更多。(but, I now can not understand why we should do this, just for Denoising  or for others)

建议读者查看gibbs采样或者其他的文章。

void dA_get_corrupted_input(dA* this, int *x, int *tilde_x, double p) {
         int i;
         for(i=0; i<this->n_visible; i++) {
              if(x[i] == 0) {
               tilde_x[i] = 0;
          } else {
          tilde_x[i] = binomial(1, p);   //按照p的概率生成1.该函数总是将 (1-p)概率的1转化为0,如果p = 1,将不会发生任何转化
       }
    }
}

 

 

初始化我们的模型:

void dA__construct(dA* 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;
  }
}

 

有了encode和decode函数 我们就可以计算 lost了并且更新我们的权值。

权值的更新采用偏导数算出。大家可以自己计算。

void dA_train(dA* this, int *x, double lr, double corruption_level) {
  int i, j;
  int *tilde_x = (int *)malloc(sizeof(int) * this->n_visible);
  double *y = (double *)malloc(sizeof(double) * this->n_hidden);
  double *z = (double *)malloc(sizeof(double) * this->n_visible);

  double *L_vbias = (double *)malloc(sizeof(double) * this->n_visible);
  double *L_hbias = (double *)malloc(sizeof(double) * this->n_hidden);

  double p = 1 - corruption_level;

  dA_get_corrupted_input(this, x, tilde_x, p);
  dA_get_hidden_values(this, tilde_x, y);
  dA_get_reconstructed_input(this, y, z);

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

  // hbias
  for(i=0; i<this->n_hidden; i++) {
    L_hbias[i] = 0;
    for(j=0; j<this->n_visible; j++) {
      L_hbias[i] += this->W[i][j] * L_vbias[j];
    }
    L_hbias[i] *= y[i] * (1 - y[i]);

    this->hbias[i] += lr * L_hbias[i] / this->N;
  }

  // W
  for(i=0; i<this->n_hidden; i++) {
    for(j=0; j<this->n_visible; j++) {
      this->W[i][j] += lr * (L_hbias[i] * tilde_x[j] + L_vbias[j] * y[i]) / this->N;
    }
  }

在上面的代码中我们可以发现,如果我们去除随机将输入1变为0的话。那么就是一个标准的autoencode。

posted @ 2015-05-10 15:54  独立寒风  阅读(607)  评论(0编辑  收藏  举报