您的位置:首页 > 编程语言

CNN原理和代码解读【未完待续】

2015-01-09 10:30 323 查看

CNN 原理和代码解读

原理参见论文Notes on Convolutional Neural Networks
代码来自githup的Deep
Learning的toolbox,

(一)卷积和pooling





[b]包含了卷积,求和,加入偏置,经过非线性激励函数等过程[/b]





上图说明了获得一个输出maps的过程。蓝色的maps是输入;绿色的maps,是卷积后的结果;黄色的maps是输出。

先对输入层分别做卷积,注意对不同的输出map,卷积核不同,即不同的j,kij不同(不同的输入map,卷积核也不相同,即不同的i,kij不同。
权值共享,仅在对一个maps求卷积的时候(滑动卷积核时,卷积核相对于该maps位置变,但是权值未变)。注意这里的卷积层还加入了偏置,并经过一个而非线性激励函数输出(非线性激励函数不可以少,卷积运算实际也是一种线性运算,没有非线性激励函数,该网络是无法模拟一个非线性复杂函数的)。看一下代码是如何实现这一过程的。

<span style="background-color: rgb(204, 204, 255);">for j = 1 : net.layers{l}.outputmaps   %  for each output map
%  create temp output map
z = zeros(size(net.layers{l - 1}.a{l-1}) - [net.layers{l}.kernelsize - 1 net.layers{l}.kernelsize - 1 0]);
%z = zeros(size(net.layers{l - 1}.a{1}) - [net.layers{l}.kernelsize - 1 net.layers{l}.kernelsize - 1 0]); %本层输出的map大小[x y num]x*y是map大小,num是样本的个数
for i = 1 : inputmaps   %  for each input map
%  convolve with corresponding kernel and add to temp output map
z = z + convn(net.layers{l - 1}.a{i}, net.layers{l}.k{i}{j}, 'valid');
end
%  add bias, pass through nonlinearity
net.layers{l}.a{j} = sigm(z + net.layers{l}.b{j});
%上两句的含义是:第l层有第j个输出,是所有i个输出maps卷积后的和,再加上偏置量,最后通过sigm非线性激活函数
end</span>

pooling比较好理解,重点看一下代码中如何实现的(pooling即是对一个maps划分成n*n个patches,然后对每个patches求均值,用这个均值代替这个patch)。pooling层不改变maps的个数,但是会改变maps的大小(因为一般划分的patches,都是非重叠的,即便重叠,也只是部分重叠)。因为改变maps的大小,所以pooling层有认为是sum-sampling层。用patch的均值代替这个patch,使得对形变不那么敏感,实现一定程度的平移和旋转不变性。再看代码:

%  downsample
for j = 1 : inputmaps
% 用卷积实现降采样 先是均值滤波 然后降采样
z = convn(net.layers{l - 1}.a{j}, ones(net.layers{l}.scale) / (net.layers{l}.scale ^ 2), 'valid');   
%  !! replace with variable
net.layers{l}.a{j} = z(1 : net.layers{l}.scale : end, 1 : net.layers{l}.scale : end, :);
end
代码巧妙的运用了均值滤波,然后再降采样,获得非重叠patch的pooling结果

(二)计算梯度

2.1,卷积层的梯度

在cnn中卷积层和pooling是交替的,所以一个卷积层的下一层肯定是pooling层(除了最后一层,最后一层的下一层是一个全连接层,即普通的一维神经网络)。由BP算法得知,计算梯度关键是计算本层的残差(delta),而本层的残差是由上层的残差决定的。残差的后向传播公式:












再来分析一下残差计算:






本层的残差是由该层对激励函数对下层线性加权和的导数,乘上上层残差对上层对下层的连接权值的加权和。计算卷积层的残差时,上层的残差已知,那么pooling层对卷积层的权值Wji是多少呢?以及下层的输入加权和

又是什么?(本层的激励函数是sigmoid函数,求导公式简单)


pooling层的一个点对应的是卷积层的一个patch,为了计算方便,我们先把pooling层的残差矩阵的大小扩充为卷积层maps的维数(up-sampling)。现在可以想象,卷积层的一个patch里的神经元对应pooling层中一个patch(这个patch就是由原来的一个均值扩充的),神经元之间的连接权值一般都是1,也可以取非1的数,但是都是相等的,所以残差公式中的

对所有的j都是相等的,从而可以移出求和项。卷积层的残差计算公式可以写为:







其中

就是pooling层对卷积层的神经元直接的连接权值,为常数。up表示up-sampling,圆圈是element
wise multiplication。


相比于简单的神经网络,这里很难理解,因为pooling是一种对patch的求均值操作,无法像神经网络中神经元之间连接那样子想象。在神经网络中,每个节点都有一个残差值。在CNN中国,没有单个节点,都是maps,maps可以看成由多个神经节点组成,所以求出的残差

也是一个maps,且该maps的大小与该层的输出maps相同。注意,

里的下标j,在简单的神经网络中,j表示第l层的第j个神经节点;在CNN中,下标j表示第l的第j个maps。


在神经网络中,残差和目标函数对参数梯度的关系:





每个上层节点,对应一个偏置值,目标函数对偏置b的偏导就是上层节点的残差;本层节点j和上层节点i之间的连接权值Wij是本层节点j的激活值

和上层节点i的残差

乘积。


CNN如何计算目标函数对参数的偏导呢?我们如果把maps拉成向量,可以想象这些个神经节点的参数值有很多是相同的。比如,对于偏置b,一个maps中个点的偏置b是相同的;对于卷积核参数,虽然不同的maps[b]Wij不同,但是在一个maps内,由于卷积核滑动,很多点的参数W也是相同的。[/b]

[b]论文中给的办法就是把具有相同参数的节点的梯度相加起来。[/b]

[b]


[/b]

[b]


[/b]

其中,

是本层的输入(l-1层的输出)与

相乘的输入点的集合。观察上两个公式,又有新的问题啦,不是本层的梯度由上层的残差决定么?为什么公式里都是本层的残差呢?这里要注意,我们对第l层计算残差,实际上计算的是该层输出maps的残差。卷积是发生在该层的输入层和输出层之间(卷积核参数连接的也是这两层),与接下来的pooling无关系的。所以对于这个卷积操作,该层的输出层就相当于神经网络的第l+1层,而该层的输入层相当于神经网络的第l层。


卷积层的梯度结束了,笔者觉得很难理解,特别把相同参数的梯度加起来作为目标函数对改参数的偏导,不知道是有理论依据的,还是发明者认为这么做简单。难理解也主要因为相对于一维的神经网络,这里是二维的(神经网络中参数Wij是二维的,CNN的卷积核参数是三维的(自身map是二维,还有一维是卷积核的种类)),同时网络的连接比神经网络复杂太多,还有不同类型网络的交替,它们之间的残差传播就很难理解。

2.2,pooling层的梯度





论文的pooling层在降采样后,还用参数相乘,并加上一个偏置量,再经过非线性激励函数。要学习的参数就是



。pooling层比较麻烦的就是计算残差。传统神经网络的残差:





由于上层是卷积层,所以上层对下层的权值就是卷积核的参数。但是卷积核的每个节点对应了下层的一个maps,这里并不是所有的节点,所以要注意上式中的

,实际上为卷积核大小i*j。



这里通过一个卷积操作实现了加权和。注意,如果下层是神经网络连接层,则就可以按照神经网络的残差反向传播公式计算。接下来,求解梯度,还是把具有相同参数的节点的梯度相加起来





其中

是降采样的结果,为什么这层的输入加权和是降采样的结果呢?再看一下该层的公式

,其实就是参数

和一个

的普通神经网络。输入就是降采样,且一个输入节点对应一个输出节点。

2.3 代码理解

注意代码中的pooling层没有通过激励函数

这里直接附上各个函数的理解



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: