上个世纪,科学家们发现了视神经的几个特征。视神经有局部感觉眼。整张图片的识别是由多个局部识别点组成的;不同的神经元有不同的形状。识别能力,视神经具有叠加能力,可以用低级的简单线条组成高级的复杂图案。后来发现,经过结论运算,可以很好地反映视神经处理和计算的过程。典型的例子是LeCun在1998年发明的LeNet-5,可以大大提高识别效果。本文主要关注卷积层、池化层和CNN整体结构。1.ConvolutionalLayer卷积层1.原理和参数可以模拟局部感受眼的属性。与上一层同一层,不是全连接,而是小面积连接。这个小区域就是局部感受野(receptivefield)。并且通过构建特定的卷积神经元,可以模拟不同神经元对不同形状刺激的不同反应的性质。如下图所示,一个神经元处理一层会形成一个featuremap,多层叠加,层数会逐渐加深。感觉眼(kernelorfilter)的大小可以看做是fh*fw。由于感觉眼本身是有尺寸的,featuremap会不断缩小。为了方便处理,每一层的大小保持不变,所以我们给每一层添加0条边(零填充),以保证处理后的featuremap与上一层大小相同。多层之间的卷积运算相当于将原始像素的对应位置相乘。如下左图所示,加边后可以保证上下层的尺寸一致。右图显示了每层之间的卷积操作(如果没有添加零填充)。但上图只是一个简单的例子。一般扫描的是三维图像(RGB),不是矩阵,而是立方体。我们用一个三维块来扫描它,原理和上图是一样的。有时在扫描的时候,不是顺序扫描,而是跳过扫描,每次移动2-3个像素值(stride),但没有完全分开,不会造成信息丢失。这样形成的featuremap比原图小,实现了信息聚合的效果。如下图灰度图(2d)所示,左边只提取了垂直线(verticalfilter),右边只提取了水平线(horizo??ntalfilter)。可以看到beam部分变亮了,大量不同的此类filter(比如可以识别角点的叠加,polylinefilters)可以形成多个featuremap,前一层filter的卷积可以形成一个pixel下一层的点,如下图,可以表示i行j列k深度的一个输出像素值,k'表示第k个filter,w表示filter中的值,x表示输入,b是偏见。2.TensorFlow实现下面是使用TensorFlow实现的代码,主要使用conv2d函数importnumpyasnpfromsklearn.datasetsimportload_sample_images#Loadsampleimagesdataset=np.array(load_sample_images().images,dtype=np.float32)#一共4个维度,channel表示通道数,RGB是3batch_size,height,width,channels=dataset.shape#Create2filters#一般感觉眼睛大小是7*7,5*5,3*3,设置2个kernels,输出2层featuremapfilters_test=np.zeros(shape=(7,7,channels,2),dtype=np.float32)#第(0)个filter的设置,在7*7的矩阵中,3为中间的filters_test[:,3,:,0]=1#verticalline#前两(1)个过滤器设置filters_test[3,:,:,1]=1#horizo??ntalline#agraphwithinputXplusaconvolutionallayerapplyingthe2filtersX=tf.placeholder(tf.float32,shape=(None,height,width,channels))#虽然输入是一张四维图像,但是由于batch_size和channel是固定的,所以使用conv2d#strides设置,第一个第四个是1,表示batch_size和channel不能跳过。两个2表示横向和纵向都缩小2,相当于把整张图片缩小到原来的四分之一,做了75%的缩小convolution=tf.nn.conv2d(X,filters,strides=[1,2,2,1],padding="SAME")和tf.Session()assess:output=sess.run(convolution,feed_dict={X:dataset})下面是padding值SAME和VALID的区别(filter的宽度为6,stride为5),SAME确实是确保所有图像信息都通过卷积添加零填充,而VALID仅添加包含的像素。3.内存消耗与传统的全连接层相比,卷积层只是部分连接,节省了大量内存。例如:一个5*5大小的filter的Convolutionallayer,输出200个150*100大小的featuremaps,stride为1(即不跳跃),padding为SAME。输入为150*100大小的RGB图像(channel=3),参数总数为200*(5*5*3+1)=15200,其中+1为bias;如果输出用32位浮点数(np.float32)表示,那么每张图片将占用200*150*100*32=9600000bits(11.4MB),如果一个训练batch包含100张图片(mini-batch=100),那么这一层卷积层会占用1GB的RAM。可以看出,训练一个卷积神经网络会消耗大量的内存,但是在使用的时候,只用到最后一层的输出。2.PoolingLayer1.原理及参数当图片尺寸较大时,内存消耗巨大,PoolingLayer的作用是集中效果,缓解内存压力。即选取一定大小的区域,用具有代表性的元素来表示该区域。具体的Pooling有两种,平均值(mean)和最大值(max)。如下图所示,是一个取最大值的池化层。kernel的大小为2*2,stride的大小取决于kernel的大小。这里取2,这是一个刚好让所有的核不重叠的值,这样可以达到高效的信息压缩。原图横向和纵向都进行了一半压缩,如右图,特征基本完整保留。pooling操作不影响通道数,一般不对featuremap进行操作(即z轴一般不变),只改变水平和垂直尺寸。2.TensorFlow实现#CreateagraphwithinputXplusamaxpoolinglayerX=tf.placeholder(tf.float32,shape=(None,height,width,channels))#选择max_pool方式取最大值#如果是平均值,这里就是mean_pool#ksize是kernelsize,featuremap和channel都是1,horizo??ntal和vertical是2max_pool=tf.nn.max_pool(X,ksize=[1,2,2,1],strides=[1,2,2,1],padding="VALID")withtf.Session()assess:output=sess.run(max_pool,feed_dict={X:dataset})3.整体CNN框架典型CNN架构著名CNN架构:LeNet(onMISIT)-1998:输入32*32(为28*28图像添加零填充)。第一层kernel使用6个神经元,kernel的大小为5*5,stride为1,输出为28*28;第二层有averagepooling,2*2的kernel,stride为2,输出变成原来的halffeaturemap,不改变featuremap的个数;第三层放16个神经元,其他同理;第五层使用120个神经元,5*5的核对5*5的输入进行卷积。再次滑动,输出为1*1;F6用120个1*1输出全连接84个神经元,Out全连接10个神经元,对应手写识别输出的10个数字。激活函数前面使用的tanh是传统CNN常用的。输出层使用RBF,比较特殊。它是一种计算距离的方式来判断目标输出和做丢失之间的距离。.AlexNet-2012:首次用于比赛,准确率提升近10%。输入一张224*224的彩色图像,C1是一个11*11的大滤波器,stride=4。.最后做3层卷积。.最后输出1000个类的分类结果。激活函数用的是现在很流行的ReLU。输出层使用的softmaxAlexNet使用了一个叫做LocalResponseNormalization(LRN局部响应归一化)的小trick。此操作可以为传统输出添加偏差,同时考虑到一些邻居输出影响。也就是说,如果一个输出旁边有一个非常强大的输出,那么它的输出就会被劝阻和压制。可以看出,包括β在内的整个项都在分母中。但是后来发现这种技术对分类器的提升不是很明显,有些是没用的。GoogleLeNet-2014:大量使用Inception模块,一个输入进来,直接四步处理。这四个步骤处理完后,深度就直接叠加了。对不同比例的图像进行操作。大量使用1*1卷积可以灵活控制输出维度,减少参数数量。如右图,输入为192,使用了一个9层的inception模块。如果直接用3*3、5*5的参数,就可以计算出来。之后inception参数的数量非常多,深度可以调整。你可以指定任意数量的特征图,通过增加深度来降低维度。inception模块的6个参数对应6个卷积,上面4个参数对应上面4个卷积。添加maxpool不会改变featuremap的数量(比如480=128+192+96+64)。将正确率提高到95-96%,超过了人类的分辨率,因为imagenet中的狗种类繁多,人类无法一一完全区分。ReSNetResidualNetwork-2015:不再直接学习一个目标函数,输入直接跳过中间层直接连接输出,要学习的是残差f(x),输入跳过中间层直接相加到输出。优点是:深度模型的路径依赖的限制,即梯度向前传递时必须经过所有层。如果中间的一层死亡,则无法训练前一层。残差网络不断跳动,即使中间的一些层死了,信息仍然可以有效流动,从而使训练信号有效传回。
