CNN的相关知识总结和实践

这篇博客记录一下自己学习实践CNN的一些知识。可能东西会比较碎。

关于CNN的基本原理,请参看《深度学习(四):卷积神经网络(CNN)模型结构,前向传播算法和反向传播算法介绍。》

一、卷积操作和池化操作

卷积操作和池化操作是CNN的核心操作。卷积操作在局部相关的数据中通过权重共享获得更好的表示,池化的基本作用是假设了图像的平移不变性,提高了网络的统计效率。

我们尝试讨论以下2个问题:

  • 在参数数量不变的前提下,使用卷积能否提高图像识别的准确率?

  • 卷积一定要配合池化使用么?

我们使用经典的数据集MNIST来说明这2个问题。

1.1 卷积提高图片识别性能

对于第1个问题,我们使用两种模型,即仅使用全连接层的传统神经网络和卷积神经网络。为了公平对比,需要保证两种模型的可训练参数大致相同,如果需要证明卷积真的有效,最好是卷积模型的参数小于普通模型的参数,但是性能还会比普通模型更优

1.2 池化操作的用处

对于第2个问题,我们同样使用两种模型,即带最大池化的卷积模型和平均池化的卷积模型。我们的问题是,加入池化后,会不会使得CNN表现更加优异呢?另外,平均池化和最大池化效果是否会相同?

1.3 代码

下面的代码完整地说明了问题1和问题2。一些细节,我在代码中加了注释。代码来自参考文献【2】。

from keras.layers import Input
from keras.datasets import mnist

import matplotlib.pyplot as plt
from keras.models import Model

from keras.layers import Conv2D,MaxPooling2D,AveragePooling2D
from keras.layers import Dense,Activation,Flatten
from keras.utils import to_categorical
from keras.layers import BatchNormalization as BN



# 这里为了快速说明问题,将测试集的10000个样本作为训练样本了。
# 数据集链接:https://keras-cn.readthedocs.io/en/latest/other/datasets/
(_,_),(X_train,y_train)=mnist.load_data()

train_labels = to_categorical(y_train)

X_train_normal = X_train.reshape(10000,28*28)
X_train_normal = X_train_normal.astype('float32') / 255

X_train_conv = X_train.reshape(10000,28,28,1)
X_train_conv = X_train_conv.astype('float32') / 255


# filters卷积核的个数,(length, width)卷积核的大小。
def Conv2D_bn(x,filters,length,width,padding='same',strides=(1, 1),dilation_rate=1):
    x = Conv2D(filters, (length, width),strides=strides,padding=padding)(x)
    # Batch Normalization
    x = BN()(x)
    x = Activation('relu')(x)
    return x

def Dense_bn(x,units):
    # units输出后的维度
    x = Dense(units)(x)
    x =BN()(x)
    x =Activation('relu')(x)
    return x

def normal_model():
    x= Input(shape=(28*28,))
    x2= Dense_bn(x,1000)
    x3=Dense_bn(x2,512)
    x4=Dense_bn(x3,256)
    y=Dense(10,activation='softmax')(x4)
    model = Model(inputs=x, outputs=y)
    model.compile(optimizer='SGD',loss='categorical_crossentropy',\
						metrics=['accuracy'])
    return(model)

def conv_model():
    x= Input(shape=(28,28,1))
    x2= Conv2D_bn(x,64,3,3)
    x3= Conv2D_bn(x2,32,3,3)
    x4=Flatten()(x3)
    x5=Dense_bn(x4,32)
    y=Dense(10,activation='softmax')(x5)
    model = Model(inputs=x, outputs=y)
    model.compile(optimizer='SGD',\
                 loss='categorical_crossentropy',\
                 metrics=['accuracy'])
    return(model)
              
def conv_model_max():
    x= Input(shape=(28,28,1))
    x2= Conv2D_bn(x,64,3,3)
    x3=MaxPooling2D(2)(x2)
    x4= Conv2D_bn(x3,32,3,3)
    x5=Flatten()(x4)
    x6=Dense_bn(x5,32)
    y=Dense(10,activation='softmax')(x6)
    model = Model(inputs=x, outputs=y)
    model.compile(optimizer='SGD',loss='categorical_crossentropy',metrics=['accuracy'])
    return(model)

def conv_model_mean():
    x= Input(shape=(28,28,1))
    x2= Conv2D_bn(x,64,3,3)
    x3=AveragePooling2D(2)(x2)
    x4= Conv2D_bn(x3,32,3,3)
    x5=Flatten()(x4)
    x6=Dense_bn(x5,32)
    y=Dense(10,activation='softmax')(x6)
    model = Model(inputs=x, outputs=y)
    model.compile(optimizer='SGD',loss='categorical_crossentropy',metrics=['accuracy'])
    return(model)

# ①只有全连接层的传统网络
model_1=normal_model()
model_1.summary()
his1=model_1.fit(X_train_normal,train_labels,batch_size=128,validation_split=0.3,verbose=1,epochs=10)
'''
Total params: 1,438,482
Trainable params: 1,434,946
Non-trainable params: 3,536
'''

# ②带卷积
model_2=conv_model()
model_2.summary()
his2=model_2.fit(X_train_conv,train_labels,batch_size=128,validation_split=0.3,verbose=1,epochs=10)
'''
Total params: 822,794
Trainable params: 822,538
Non-trainable params: 256
'''

# ③卷积+最大池化
model_3=conv_model_max()
model_3.summary()
his3=model_3.fit(X_train_conv,train_labels,batch_size=128,validation_split=0.3,verbose=1,epochs=10)
'''
Total params: 220,682
Trainable params: 220,426
Non-trainable params: 256
'''

# ④卷积+平均池化
model_4=conv_model_mean()
model_4.summary()
his4=model_4.fit(X_train_conv,train_labels,batch_size=128,validation_split=0.3,verbose=1,epochs=10)
'''
Total params: 220,682
Trainable params: 220,426
Non-trainable params: 256
'''



w1=his1.history
w2=his2.history
w3=his3.history
w4=his4.history

import seaborn as sns
sns.set(style='whitegrid')
plt.plot(range(10),w1['acc'],'b-.',label='Normal train')
plt.plot(range(10),w2['acc'],'r-.',label='CNN train')
plt.plot(range(10),w1['val_acc'],'b-',label='Normal test')
plt.plot(range(10),w2['val_acc'],'r-',label='CNN test')
plt.plot(range(10),w3['acc'],'k-.',label='CNN+Maxpooling train')
plt.plot(range(10),w3['val_acc'],'k-',label='CNN+Maxpooling test')
plt.plot(range(10),w4['acc'],'g-.',label='CNN+Meanpooling train')
plt.plot(range(10),w4['val_acc'],'g-',label='CNN+Meanpooling test')
plt.xlabel('epochs')
plt.ylabel('accuracy')
plt.legend()

结果为:
在这里插入图片描述

结论如下:

  • 对于问题1,我们发现:即便普通全连接模型参数数目远大于CNN,但几乎在每一个epochs上,训练和测试准确率均不如CNN表现优异,平均看来,CNN的准确率要比全连接模型高出三个百分点,我们已经有理由说,CNN是比全连接神经网络在MNIST上更为优异的模型。

  • 对于问题2,我们发现:加入池化操作后的卷积神经网络的参数数目明显小于不加池化的。然而,准确率没有提升。这说明了,池化使得模型的参数数量大大减少,而对于准确率不一定会有提升。
    参数数量的减少主要来源于最后全连接层时的参数减少,卷积操作时参数并不会减少(因为卷积操作的参数是跟卷积核有关的,只要卷积核不变,参数数目就不会变。)

二、CNN的一些知识点Q&A

Q:如果输入图片的尺寸为 H ∗ W ∗ C H*W*C HWC,经过 K K K S ∗ S S*S SS的卷积核,步长(stride)为 L L L,zero-padding为 P P P,那么经过该卷积层输出图像的尺寸是多少?不考虑偏置的话,涉及到多少个参数?

A:经过这样的操作,输出图片的长为 ( H + 2 P − S ) / L + 1 (H+2P-S)/L+1 (H+2PS)/L+1,宽为 ( W + 2 P − S ) / L + 1 (W+2P-S)/L+1 (W+2PS)/L+1,深为K。
参数数量:总共涉及到K个卷积核,每个卷积核的权重参数个数为SSC。

Q: Tensorflow或者Keras中卷积函数Conv2D中padding参数为‘valid’和‘same’,解释一下两者的区别。

A: 这是2个不同的padding方式。VALID是采用丢弃的方式,比如下图中的例子,VALID只允许滑动2次,多余的元素全部丢掉。而SAME的方式,采用的是补全的方式,对于上述的情况,允许滑动3次,但是需要补3个元素,左奇右偶,在左边补一个0,右边补2个0
在这里插入图片描述

整理一下,对于VALID,输出的形状计算如下:
n e w h e i g h t = n e w w i d t h = ⌈ ( W − F + 1 ) / S ⌉ newheight=newwidth=\lceil (W-F+1)/S \rceil newheight=newwidth=(WF+1)/S

对于SAME,输出的形状计算如下:
n e w h e i g h t = n e w w i d t h = ⌈ W / S ⌉ newheight=newwidth=\lceil W/S \rceil newheight=newwidth=W/S

其中, W W W为输入的size, F F F为filter的size, S S S为步长, ⌈ ⌉ \lceil \rceil 为向上取整符号。

Q: Tensorflow或者Keras中卷积函数Conv2D中参数dilation_rate有什么作用?如何理解空洞卷积?
dilation_rate是空洞卷积的参数,控制其其膨胀率。空洞卷积指的是,在不增加参数的前提下,将原有的卷积核扩大。原本的卷积操作是 3 ∗ 3 3*3 33的卷积核作用在 3 ∗ 3 3*3 33的区域内,而设置dilation_rate=2后,空洞卷积则会作用在 5 ∗ 5 5*5 55的区域内,一定程度上扩大了特征范围

下图是个例子,来源

CSDN图标

关于空洞卷积的介绍请看这里

参考文献

【1】 卷积之上的新操作(代码篇)| 机器学习你会遇到的“坑”

【2】卷积之上的新操作(理论篇)| 机器学习你会遇到的“坑”

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值