http://blog.sina.com.cn/s/blog_593af2a70101endk.html
深度学习有一个重要的概念叫
autoencoder,这是个什么东西呢,本文通过一个例子来普及这个术语。
简单来说autoencoder是一个压缩编码器,也就是对input的一坨东西通过变换,输出和input一样的东西。例如input是一个鸡,ouput也是一个鸡,input是一个鸭,output也是一个鸭。
学术一点说就是找到一个函数能够使得 Function(input) = input,叫做
identity function。如上图所示,即学习Hw,b(x)=x。
但这和深度学习有什么关系呢?这就要说到压缩编码,我们都知道input需要有一种编码形式,如果我们能在函数内部找到一个更简洁的编码形式,那么这个变换过程就等价于学习到了一种压缩表示的函数,能够少量的存储形式来表示原本较复杂的但信息冗余较大的表示形式。
我们下面的代码中举了一个精彩的例子(这个例子是从同学的一次实验中直接受启发,我只是按照自己的理解实现了一把,例子非原创)。在这个例子中,input是4个不同的数字,分别是
(0,0,0,1)可以看作1
(0,0,1,0)可以看作2
(0,1,0,0)可以看作3
(1,0,0,0)可以看作4
因为所有的input只有这4种,因此其实用4个bit是不经济的,存在压缩表示的可能性,比如2个bit就可以表示这4个不同的数。
那么我们设计了输入层是4+1(4个脚接受4bit编码的input,1个脚是常数项,这个用来做先验的);隐藏层2+1(因为我们实现已经知道2个bit就够了,所以2个隐藏层,具有足够的表达能力);输出层4(为了能让输出和输入保持一致)
通过数轮迭代,我们看到如下的情况
(0,0,0,1)->(
0.99,0.09
)->(
0.06,0.00,0.01,0.91)
(0,0,1,0)->(
0.85,
0.99)->(
0.00,0.07,0.90,0.07)
(0,1,0,0)->(
0.01,0.67
)->(
0.06,0.87,0.11,0.00)
(1,0,0,0)->(
0.12,0.00
)->(
0.89,0.10,0.00,0.02)
input_layer hidden_layer output_layer
hidden层的编码恰好可以看作是
(
0.99,0.09
) 1
,
0
(
0.85,
0.99) 1
,
1
(
0.01,0.67
) 0,1
(
0.12,0.00
) 0,0
也就是说输入的(0,0,0,1)可以被1,0 压缩表示,最终4bit的信息,可以用2bit表示,当然还需要保持边的权重,但这些边权重只需要一份,在输入足够复杂的时候,压缩表示是有价值的。
那压缩表示有什么价值呢?比如一组广告,一条新闻,人看了挺好,压缩表示后,人看起来就不爽了,恰恰是人看着不爽了,机器就好处理了,下回再说。。
其他相关可参见美军参考资料:
http://ufldl.stanford.edu/wiki/index.php/Autoencoders_and_Sparsity
以下是实验代码,水平有限,仅供参考。
#include <iostream>
#include "math.h"
using namespace std;
double d = 0.4;
double function_g(double x)
{
double ex = pow(2.718281828,x);
return ex/(1+ex);
}
double input[4][5]={{0,0,0,1,1},
//{0,0,0,1} {1}
{0,0,1,0,1},
{0,1,0,0,1},
{1,0,0,0,1}};
double edge_1[2][5];
double edge_2[4][3];
double first_layer[5];
double hidden_layer[3]={0,0,1};
double output_layer[4];
double diff()
{
double ret = 0.0;
for(int i=0;i<4;++i)
{
ret += (output_layer[i]-first_layer[i])*(output_layer[i]-first_layer[i]);
}
return ret;
}
void count_second_l( )
{
for(int i=0;i<2;++i)
{
hidden_layer[i] = 0;
for(int j=0;j<5;++j)
{
hidden_layer[i] += first_layer[j]*edge_1[i][j];
}
hidden_layer[i] = function_g(hidden_layer[i]);
}
return;
}
void count_last_l()
{
for(int i=0;i<4;++i)
{
output_layer[i] = 0;
for(int j=0;j<3;++j)
{
output_layer[i] += hidden_layer[j]*edge_2[i][j];
}
output_layer[i] = function_g(output_layer[i]);
}
return;
}
double total_diff()
{
double ret = 0.0;
for( int j=0;j<4;++j)
{
first_layer[0]=input[j][0];
first_layer[1]=input[j][1];
first_layer[2]=input[j][2];
first_layer[3]=input[j][3];
first_layer[4]=input[j][4];
count_second_l();
count_last_l();
ret += diff();
}
return ret;
}
int main(void)
{
for(int i=0;i<2;++i)
for(int j=0;j<5;++j)
edge_1[i][j] = ((i+j)%4+0.1)/10;
//initialize each edges between first_layers and hidden layers
for(int i=0;i<4;++i)
for(int j=0;j<3;++j)
edge_2[i][j] = ((i+j)%4+0.1)/10;
//initialize each edges between hidden_layers and output_layers
for(int i=0;i<40000;++i)
//iterate 40000 times
{
double origin_diff=total_diff();
double direction_1[2][5],direction_2[4][3];
for(int i=0;i<2;++i)
for(int j=0;j<5;++j)
{
double tmp = edge_1[i][j] ;
edge_1[i][j] += d * origin_diff;
count_second_l();
count_last_l();
double diff2 = total_diff();
direction_1[i][j] = origin_diff - diff2;
edge_1[i][j] = tmp;
}
for(int i=0;i<4;++i)
for(int j=0;j<3;++j)
{
double tmp = edge_2[i][j] ;
edge_2[i][j] += d * origin_diff;
count_second_l();
count_last_l();
double diff2 = total_diff();
direction_2[i][j] = origin_diff - diff2;
edge_2[i][j] = tmp;
}
for(int i=0;i<2;++i)
for(int j=0;j<5;++j)
{
edge_1[i][j] += d * origin_diff * direction_1[i][j];
}
for(int i=0;i<4;++i)
for(int j=0;j<3;++j)
{
edge_2[i][j] += d * origin_diff * direction_2[i][j];
}
double d = 0.0;
for( int j=0;j<4;++j)
{
first_layer[0]=input[j][0];
first_layer[1]=input[j][1];
first_layer[2]=input[j][2];
first_layer[3]=input[j][3];
first_layer[4]=input[j][4];
count_second_l();
count_last_l();
d += diff();
cout<<"####################"<<endl;
cout<<"input:\t"<<first_layer[0]<<"\t"<<first_layer[1]<<"\t"<<first_layer[2]<<"\t"<<first_layer[3]<<endl;
cout<<"output:\t"<<output_layer[0]<<"\t"<<output_layer[1]<<"\t"<<output_layer[2]<<"\t"<<output_layer[3]<<endl;
cout<<"hidden:\t"<<hidden_layer[0]<<"\t"<<hidden_layer[1]<<endl;
cout<<"####################"<<endl;
}
cout<<"diff_sum:"<<d<<endl;
}
}