开局一张图,内容全靠编。
python
上图引用自 【卷积神经网络-进化史】从LeNet到AlexNet. 目前经常使用的卷积神经网络git
深度学习如今是百花齐放,各类网络结构层出不穷,计划梳理下各个经常使用的卷积神经网络结构。 目前先梳理下用于图像分类的卷积神经网络github
本文是关于卷积神经网络的开山之做LeNet的,以前想着论文较早,一直没有细读,仔细看了一遍收获满满啊。
本文有如下内容:算法
LeNet-5可谓是第一个卷积神经网络,而且在手写数字识别上取得了很好的效果。
对于图像像素做为神经网络的输入数据面临的一些问题:网络
其提出了卷积神经网络的概念,并应用机器学习
来解决上述问题。ide
1998年的诞生的LeNet(LeCun et al. Gradient-Based Learning Applied to Document Recognition)可谓是如今各类卷积神经网络的始祖了,其网络结构虽然只有5层,却包含了卷积神经网络的基本组件(卷积层,池化层,全链接层)
函数
参数个数:按照神经网络的全链接设计,每一个像素间都有链接的话,输入层和卷积层之间就要有122304个参数。可是在卷积神经网络中,引入了权值共享,就大大的减小了卷积层的参数的个数。对\(32\times32\)的输入,使用\(5\times5\)的核进行卷积,步长为1,卷积核每移动一步,就获得一个输出,也就须要一个权值参数(有一个链接),这就须要\((5 \times 5 + 1) \times 28 \times 28\)个参数。引入权值共享,也就是一个卷积核在卷积的产生输出的过程当中,每移动一步产生输出,都使用相同的权值参数,这时候参数的个数为\((5 \times 5 + 1) \times 1\)。共有6个卷积核,\((5 \times 5 + 1) \times 6 = 156\)个参数。学习
设输入尺寸为\(W \times W\),卷积核尺寸为\(F \times F\),卷积步长为\(S\),填充宽度为\(P\),则卷积后输出的尺寸为\((W - F + 2P) / S + 1\)测试
第二个卷积层 CONV2
该层的输入为\(14 \times 14 \times 6\),使用16个核为\(5\times5\),步长为1。则其输出尺寸为\(14 - 5 + 1 = 10\)。整个层的输出为\(10 \times 10 \times 16\)。 这里要注意的本层的输入有6个Feature Map,而输出有16个Feature Map。 输入的FeatureMap和输出的FeatureMap之间并非全链接的,而是局部链接的,具体规则以下:
上图所示,输出的6个FeatureMap只和输入的3个FeatureMap相链接;输出的9个FeatureMap和输入的4个FeatureMap相链接;而后1个和6个链接。
- 参数个数: 因为权值共享,每一个卷积核产生的输出共享同一个权值。例如,对于和3个Feature Map相链接的输出的FeatureMap,在卷积的时候,使用3个不一样的卷积核分别对三个输入的FeatureMap进行卷积,而后将卷积的结果进行求和,因此其权值参数的个数是\(5 \times 5 \times + 1\)。因此,总的参数个数是\(6 \times (5 \times 5 \times 3 + 1) + 9 \times ( 5 \times 5 \times 4 + 1) + 1 \times (5 \times 5 \times 6 + 1) = 1516\)。
- 链接个数:输出的Feature Map的尺寸是\(10 \times 10\),则链接个数是: \(1516 \times 10 \times = 151600\)
第二个池化层 POOL2
输入是\(10 \times 10 \times 16\),池化参数和第一个池化层同样采用尺寸\(2 \times 2\)的池化单元,池化时的沿长和宽的步长均为2,其输出为\(5 \times 5 \times 16\),参数个数是\((1 + 1) \times 16 = 32\),链接数\((2 \times 2 + 1) \times 5 \times 5 \times 16 = 2000\)
第三个卷积层 CONV3
120个\(5\times5\)的卷积核,输入的是\(5 \times 5 \times 16\),输入的Feature Map和输出的Feature Map是全链接,输出的尺寸为\(1\times1 \times 120\)(卷积核和输入尺寸相同,且没有边缘填充)。其卷积核的参数是\(5\times5 \times 16 + 1\)。 而输出的120个神经元每一个都和上一层的相连,则链接和参数的个数相同为\((5\times5 \times 16 + 1) \times 120 = 48120\)
全链接层
输入是120维的向量,本层有84个神经元,参数个数是\((120 + 1) \times 84 = 10164\)
输出层
全链接层,10个神经元,表明10个不一样的数字。参数/链接个数为\(84 \times 10 = 840\)
LeNet是在1998年提出的,用于手写体数字的识别, 首次提出了卷积神经网络的基本组成:卷积层,池化层和全链接层以及权值共享,感觉野等概念。 虽然时间比较久i,可是做为卷积神经网络的开山之做,仍是值得入门者研读一番的。
论文提出了:
可是将像素独自的输入到神经元中则有如下问题:
有了以上的问题,LeCun就提出了大名鼎鼎的卷积神经网络(CNN)
局部感觉野
在卷积神经网络的卷积层中,神经元的输入是上一层中一个像素邻域(也就是一个卷积核卷积后的结果,称为局部感觉野)。 使用局部感觉野,在浅层网络中神经元能够提取到图像的边缘,角点等视觉特征,这些特征在后面的网络中进行结合,组成更高层的特征。(在人工设计的特征提取器中,则很难提取图像的高层特征)。
感觉野的定义: 感觉野是卷积神经网络(CNN)每一层输出的特征图(feature map)上的像素点在原始输入图像上映射的区域大小。
一个局部感觉野能够看做是一个卷积核进行一次卷积的结果,一个\(5\times5\)的卷积核对输入图像的\(5\times5\)邻域进行卷积获得一个输出\(P\),输入到神经元中。 在当前的卷积层中,这个\(P\)就能够表明上一层的\(5\times5\)邻域。 我的理解。
权值共享
引入权值共享的一个缘由是为了解决图像的形变和平移致使的图像显著特征位置的变化。将同一个卷积核获得的结果设为相同的权值,能够有效的下降其位置不一样带来的影响。权值共享的另外一个依据是,在一个位置可以提取到有效的特征,在另外的位置也能提取到(特别是基础的点线特征)。另外,使用权值共享也大大的下降的网络的参数。
一个卷积核就至关于一个特征提取器,每一个卷积和和输入图像进行卷积获得输出称为Feature Map,一个FeatureMap中全部的像素点和上一层的链接,使用相同的权值参数,即为权值共享。
每一层中全部的神经元造成一个平面,这个平面中全部神经元共享权值。神经元(unit)的全部输出构成特征图,特征图中全部单元在图像的不一样位置执行相同的操做(同一个特征图是使用赞成给卷积核获得),这样他们能够在输入图像的不一样位置检测到一样的特征,一个完整的卷积层由多个特征图组成(使用不一样的权值向量),这样每一个位置能够提取多种特征。
一个具体的示例就是图2 LeNet-5中的第一层,第一层隐藏层中的全部单元造成6个平面,每一个是一个特征图。一个特征图中的一个单元对应有25个输入,这25个输入链接到输入层的5x5区域,这个区域就是局部感觉野。每一个单元有25个输入,所以有25个可训练的参数加上一个偏置。因为特征图中相邻单元之前一层中连续的单元为中心,因此相邻单元的局部感觉野是重叠的。好比,LeNet-5中,水平方向连续的单元的感觉野存在5行4列的重叠,以前提到过,一个特征图中全部单元共享25个权值和一个偏置,因此他们在输入图像的不一样位置检测相同的特征,每一层的其余特征图使用不一样的一组权值和偏置,提取不一样类型的局部特征。LeNet中,每一个输入位置会提取6个不一样的特征。特征图的一种实现方式就是使用一个带有感觉野的单元,扫面整个图像,而且将每一个对应的位置的状态保持在特征图中,这种操做等价于卷积,后面加入一个偏置和一个函数,所以,取名为卷积网络,卷积核就是链接的权重。卷积层的核就是特征图中全部单元使用的一组链接权重。卷积层的一个重要特性是若是输入图像发生了位移,特征图会发生相应的位移,不然特征图保持不变。这个特性是CNN对位移和形变保持鲁棒的基础。
一个卷积核提取输入数据的某种特征输出一个Feature Map。 既然提取的是同一种特征,那么使用同一个权值也是应该的。
下采样,池化
在图像分类中,起主导做用的是图像特征的相对位置,如图像中的数字7从左上角移到右下角,仍然是数字7,重要的是直线-点-直线之间的相对位置。由于图像的平移,形变是很常见的,因此图像特征的精确的位置信息,在分类识别中甚至是有害的。 经过下降图像分辨率的方式来下降图像特征位置的精度,使用池化函数(均值或者最大)对图像进行下采样,下降网络的输出对图像形变和平移的敏感程度。若是对图像作平移,那么对应于高层特征的平移(由于权值共享);若是对图像作局部旋转,小范围旋转/扭曲会被局部感觉野消除,大范围扭曲会由于降采样而模糊掉其影响。
并无,精确的实现论文中描述的LeNet-5的网络结构,只是照着实现了一个简单的卷积神经网络,网络结构以下:
_________________________________________________________________ Layer (type) Output Shape Param # ================================================================= conv2d_1 (Conv2D) (None, 32, 32, 20) 1520 _________________________________________________________________ activation_1 (Activation) (None, 32, 32, 20) 0 _________________________________________________________________ max_pooling2d_1 (MaxPooling2 (None, 16, 16, 20) 0 _________________________________________________________________ conv2d_2 (Conv2D) (None, 16, 16, 50) 25050 _________________________________________________________________ activation_2 (Activation) (None, 16, 16, 50) 0 _________________________________________________________________ max_pooling2d_2 (MaxPooling2 (None, 8, 8, 50) 0 _________________________________________________________________ flatten_1 (Flatten) (None, 3200) 0 _________________________________________________________________ dense_1 (Dense) (None, 500) 1600500 _________________________________________________________________ activation_3 (Activation) (None, 500) 0 _________________________________________________________________ dense_2 (Dense) (None, 10) 5010 _________________________________________________________________ activation_4 (Activation) (None, 10) 0 ================================================================= Total params: 1,632,080 Trainable params: 1,632,080 Non-trainable params: 0 _________________________________________________________________
调用Keras API能够很容易的实现上述的结构
# -*- coding:utf-8 -*- from keras.models import Sequential from keras.layers.convolutional import Conv2D from keras.layers.convolutional import MaxPooling2D from keras.layers.core import Activation from keras.layers.core import Flatten from keras.layers.core import Dense from keras import backend as K class LeNet: @staticmethod def build(width,height,depth,classes): model = Sequential() inputShape = (height,width,depth) if K.image_data_format() == "channels_first": inputShape = (depth,height,width) # first set of CONV => RELU => POOL model.add(Conv2D(20,(5,5),padding="same",input_shape=inputShape)) model.add(Activation("relu")) model.add(MaxPooling2D(pool_size=(2,2),strides=(2,2))) # second set of CONV => RELU => POOL_layers model.add(Conv2D(50,(5,5),padding="same")) model.add(Activation("relu")) model.add(MaxPooling2D(pool_size=(2,2),strides=(2,2))) # set of FC => RELU layers model.add(Flatten()) model.add(Dense(500)) model.add(Activation("relu")) # softmax classifier model.add(Dense(classes)) model.add(Activation("softmax")) return model
将上述网络应用于CIFAR10数据集进行分类
测试代码,首先使用scikit-learn
加载CIFAR10数据,并进行归一化
print("[INFO] loading CIFAR-10 data...") ((trainX,trainY),(testX,testY)) = cifar10.load_data() trainX = trainX.astype("float") / 255.0 testX = testX.astype("float") / 255.0
对CIFAR10的类别进行编码
lb = LabelBinarizer() trainY = lb.fit_transform(trainY) testY = lb.fit_transform(testY)
处理完数据后,调用上面的LeNet
创建LeNet网络结构并使用trainX
数据集进行训练
print("[INFO] compiling model...") opt = SGD(lr=0.05) model = lenet.LeNet.build(width=width,height=height,depth=depth,classes=classes) model.compile(loss="categorical_crossentropy",optimizer=opt,metrics=["accuracy"]) # train print("[INFO] training network...") H = model.fit(trainX,trainY,validation_data=(testX,testY),batch_size=32,epochs=epochs,verbose=1)
最后使用testX
数据集进行评估
# evaluate the network print("[INFO] evaluating networking...") predictions = model.predict(testX,batch_size=32) print(classification_report(testY.argmax(axis=1),predictions.argmax(axis=1), target_names=labelNames))
很明显的过拟合了,这里就不关注这个精度了,只是简单的测试下。
更详细的测试代码,能够Start/Fork GitHub上https://github.com/brookicv/machineLearningSample 以及 https://github.com/brookicv/imageClassification