Denoising Autoencod
Denoising Autoencod(去噪自编码)(DA)是一个经典的autoencode算法的扩展,在[vincent08 ]中作为深网络的构建块使用。我们将开始本教程对autoencoders简短的讨论。
-
一个autoencode 将输入 通过 一个确定的投影方法(encode ) 投影到一个隐层空间。
W是投影矩阵, b为隐层的偏置 s是一个非线性函数如 sigmoid。然后我们通过 y重建 x。得到z。投影方法如下式表示
z作为x(输入的预测)
那么这个模型的参数列表为,, ,
传统的平方误差为
,
重建的交叉熵为
下面我们来用C语言实现这个模型。
首先需要定义
typedef struct { int N; int n_visible; //输入神经元个数 int n_hidden; //隐藏神经元个数 double **W; //参数w double *hbias; // 参数 输入的偏置 double *vbias; //隐藏神经元的偏置 } dA;
定义encode 函数 :
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 函数 :
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。