卷积神经网络
揭开卷积神经网络神秘的面纱,发现CNN也不过如此,就像对普通NN一样,第一步了解网络结构,第二步了解节点计算方法,第三步反向调节误差。就可以完全认识这个模型了。从网上看的大部分资料感觉很少有能够说清楚的,CNN确实原本也是一个比较难说明白的模型,所以从大牛的代码来看CNN会更清晰。
一、 CNN结构
图一
这个网络结构举了这样一个列子,输入的map是一个28*28的矩阵,输入层有1个map,到了第一层的时候可以设定有多少个map,图中第一层有6个map。从输入层到第一层的每一个map是通过一种类似卷积的运算求出来的(这种运算在下面小节卷积神经网络节点的计算方法会介绍),因而第一层一般就叫做C1。在第一层到第二层的时候是通过一种可以称为采样的运算得来的(这种运算方式也在后面讲述),故第二层一般称为s2。如上面图所示C1层有6个map,而后面的s2层也会有6个map,并且C1层和s1层的map是一一对应的关系,也就是s1的每个map只与C1层的一个map有关。然后从s2层到第三层又是通过类似卷积的运算得出来的,所以第三层就可以称为C3层。就这样网络一遍一遍的重复卷积运算和采样运算前向的发展。在上面的例子中看到最后一层变成了12个4*4的map。在最后一层这12个4*4的map到输出层还有一个权值。因为做的是模式识别,输出层每个节点输出的结果就是一个数值了而不是矩阵。
二、CNN每层计算方式
1、卷积运算
前面说到输入层到C1层和S2层到C3层都用到了类似于卷积的运算。这种运算方式如下
图二
这个地方的卷积核就类似于普通NN中的权值,而上一层的一个输出map Mj就是普通NN中的第j个节点的输出。看图一中举的例子,因为输入层只有一个map,而C1层有6个map,所以就要有1*6个卷积核了,而在s2层到C3层,s2有6个map,C3有12个map,所以这两层中间就要有6*12个卷积核。而C3层中第j个map到底应该怎么算的,就是s2中的每一个map与对应的卷积核做卷积运算,然后累加求和,再加上偏置,在带入激励函数sigmord函数得出来的。下面看一下工具箱的代码是不是这样子
图三
因为卷积运算和采样运算方式不同,所以第一行首先判断l层是卷积层还是采样层,如果是卷积层的话就计算l层net.layer{l}.outputmaps个map的值,所以就有了第二行代码(不算注释)的for循环,在第四行到第六行就是通过for循环计算前一层(即l-1层)inputmaps个输入map——net.Layers{l-1}.a{i}与对应的卷积核net.Layers{l}.k{i}{j}做卷积运算,然后累加求和。在第七行的部分,将累加求和所得的z与对应的偏置net.Layers{l}.b{j}相加然后带入sigmord函数,结果付给l层的map——net.Layers{l}.a{i}。有点说一下,在第五行中出现的convn函数就是卷积函数,做的运算就是图二中所示的运算。
2、重采样层的采样
重采样层的计算方式非常简单,通过看图三中倒数第四行、倒数第三行的代码可以看到,如果要求采样曾第j个map,只需要拿着前一层的map——net.Layers{l-1}.a{i}与一个卷积核做卷积运算,这个卷积核是ones(net.layers{l}.scale)/( net.layers{l}.scale),在图一举的例子中采样层scale都为2,那么这个矩阵就是一个[1,1;1,1]/4=[1/4,1/4;1/4,1/4]这样的一个矩阵了。net.Layers{l-1}.a{i}和ones(net.layers{l}.scale)/( net.layers{l}.scale)做完卷积运算后赋给z,再把z中的一部分提取出来,通过z(1 : net.layers{l}.scale : end, 1 :net.layers{l}.scale : end, :)可以看出把1 : net.layers{l}.scale: end的行和列提取了出来。这样整个一个过程可以通过图四来表示:
图四
在这里就知道了网络每层中的map到底应该怎样计算了。但是在第一部分CNN结构中说到,最后一层是有12个4*4的map,而我们最后的输出是一个行向量,比如要识别0-9十个手写字符,输出的结果应该是一个10维的向量,从12个4*4的map怎么到一个10维的向量呢?看下面的代码:
在最后一层有12个map,所以numel(net.layers{n}.a)为12,net.fv = [net.fv; X];