自编码器是什么?
自编码器本身就是一种BP神经网络。
它是一种无监督学习算法。
我们都知道神经网络能够从随意精度逼近随意函数,这里我们让神经网络目标值等于输出值x,也就是模拟一个恒等函数:
太无聊了,是吗?输入等于输出,这网络有什么意义?可是。当我们把自编码神经网络增加某些限制,事情就发生了变化。
如图1所看到的,这就是一个主要的自编码神经网络。能够看到隐含层节点数量要少于输入层节点数量。
图1
举个样例,假设我们输入一张10*10的图像,这样就有100个像素,所以输入层和输出层的节点数量就是100。
而我们取隐藏层节点数量为25。
注意。这样就会迫使隐藏层节点学习得到输入数据的压缩表示方法。逼得隐藏层要用25维数据重构出100维的数据。这样也就完毕了学习过程。
这和我们学习的过程非常像,如果一共同拥有100个考点,可是仅仅同意你用25个知识点概括全部这些考点,这就是学习的过程。
稀疏自编码器又是什么?
更一般的,假设隐藏层节点数量非常大,甚至比输入层节点数量还要多时。我们仍然能够使用自编码算法,可是这时须要增加稀疏性限制。这就是稀疏自编码器。
什么是稀疏性限制?
简单说就是要保证隐藏神经元在大多数情况下是被抑制的状态。详细表现就是sigmoid函数的输出大多数状态是0,tanh函数的输出大多数状态是-1。这样有什么优点?这样可以迫使隐藏神经元发挥最大的潜力,在非常不利的条件下学习到真正的特征。
怎么衡量某个隐藏神经元的激活度?
取平均就好了,如果表示在给定输入x的情况下,隐藏神经元j的激活度,那么自然就有平均激活度
稀疏性惩处项——相对熵
为了保证这个稀疏度小到我们希望的那么多。比方说,即
我们须要在优化目标函数中增加一个额外的惩处因子。这个罚因子基于相对熵(KLdivergence):
因此这个罚因子会有例如以下性质,当时(这里取稀疏性參数为0.2),
等于0,而两者差异越来越大时,相对熵会高速趋近于无穷大,如图2所看到的:
图2
稀疏自编码器训练方式与BP神经网络相对照
稀疏自编码神经网络的代价函数是BP神经网络的代价函数加上一个稀疏性惩处项:
对应地,残差迭代公式也要进行修正
实现一个稀疏自编码器
数据概览
採用的是Andrew著名的UFLDL给出的例子。当中数据文件叫做IMAGES,这是一个512*512*10的三维数组,里面存了10张图片,每张都是262144像素。运行
<span style="font-size:14px;">loadIMAGES;
imagesc(IMAGES(:,:,6))
colormapgray;</span>
能够看看第6张图的样子,如图3所看到的。是一张关于森林和雪山的图像。
图3
数据採样
因为数据的图片挺大,我们总不能一上来搞一个每层都2万多节点的神经网络训练吧。我们先採样,这里从10张图片里面随机採样10000个小patch。每一个小patch是一个8*8像素小碎片。我们定义训练集是64*10000的矩阵。每一列就是把刚刚的小patch拉成列向量的结果。
这里代码例如以下
<span style="font-size:14px;">loadIMAGES; % load images from disk
patchsize= 8; % we'll use 8x8 patches
%numpatches = 10;
numpatches= 10000;
%Initialize patches with zeros. Your codewill fill in this matrix--one
%column per patch, 10000 columns.
patches= zeros(patchsize*patchsize, numpatches);
tic
image_size=size(IMAGES);
i=randi(image_size(1)-patchsize+1,1,numpatches);
j=randi(image_size(2)-patchsize+1,1,numpatches);
k=randi(image_size(3),1,numpatches);
fornum=1:numpatches
patches(:,num)=reshape(IMAGES(i(num):i(num)+patchsize-1,j(num):j(num)+patchsize-1,k(num)),1,[]);
end
toc</span>
这里randi函数。randi(iMax。m,n)在闭区间[1,iMax]生成m*n型随机矩阵。而reshape函数的作用是把每一个小patch拉成一个列向量。
前200个patch的样子如图4所看到的:
图4
sparseAutoencoderCost.m
这部分代码是最核心也是最重要的,正如前面所说。如今优化目标函数有了3个部分:均方差项、权重衰减项、稀疏惩处项。
这里程序须要一部分一部分地调试,否则很easy得到看似正确但实际效果没有正确代码好的错误结果。这里附上我最新改动版本号的代码,去掉了全部显式for循环,使用矢量化编程,简洁了代码,增强执行效率,但也减弱了可读性。
<span style="font-size:14px;">numpatches=size(patches,2);
a2=sigmoid(W1*patches+repmat(b1,1,numpatches));
a3=sigmoid(W2*a2+repmat(b2,1,numpatches));
Rho=sum(a2,2)/numpatches;
Penalty=-sparsityParam./Rho+(1-sparsityParam)./(1-Rho);
Delta3=(a3-patches).*a3.*(1-a3);
Delta2=(W2'*Delta3+beta*repmat(Penalty,1,numpatches)).*a2.*(1-a2);
cost1=sumsqr(a3-patches)/numpatches/2;
cost2=(sumsqr(W1)+sumsqr(W2))*lambda/2;
cost3=beta*sum(sparsityParam*log(sparsityParam./Rho)+(1-sparsityParam)*log((1-sparsityParam)./(1-Rho)));
cost=cost1+cost2+cost3;
W2grad=Delta3*a2'/numpatches+lambda*W2;
b2grad=sum(Delta3,2)/numpatches;
W1grad=Delta2*patches'/numpatches+lambda*W1;
b1grad=sum(Delta2,2)/numpatches;</span>
这里repmat的反复使用是用来复制矩阵的,一定要不遗余力去掉这部分代码中全部的显式for循环。否则运行起来时间会非常长。
梯度校验
梯度校验是代码调试的大杀器,这一部分我用到了for循环,效率确实很低了,所以调试的时候要把校验參数做些调整。令隐藏节点数量为2。採样数量为100,这样就能大大加快校验速度。
否则要等相当久的时间。
<span style="font-size:14px;">EPSILON=0.0001;
thetaspslion=zeros(size(theta));
fori=1:size(theta)
thetaspslion(i)=EPSILON;
numgrad(i)=(J(theta+thetaspslion)-J(theta-thetaspslion))/2/EPSILON;
thetaspslion(i)=0;
end</span>
注意:最后执行时一定要关掉梯度校验,不然就要卡死机了。。
。
这个梯度校验对于目标函数的3部分的调试一定要循序渐进。心急吃不了热豆腐。均方差项调试正确了以后,会得到如图5所看到的的图像
图5
而当,均方差项和权重衰减项都调试通过后,得到的图片是这种
图6
而当全部的目标函数都调试通过以后,接下来就是见证奇迹的时刻!
图7
这就是图像的基。通过稀疏自编码器。我们学习得到了25个8*8的基,这些图像的基就相当于咱们每一个视神经细胞所示东西,当这些细胞组成阵列,垒成层层叠叠,我们就能看到全部的东西了。
稀疏自编码器作为无监督学习的一层基本模块,这就是我们DeepLearning万里长城的第一步:搞基!