前言
此次竞赛为《形状识别2:方圆之外》,是一起图像的多分类问题。因为是初次接触CNN模型,主要分析标杆模型并进行调参。
提交的结果为每行的预测标签,也就是0、1、2。评价方法为准确率。本文最终处理结果为0.98767,排名2
1 CNN模型概述
其实卷积神经网络(CNN)依旧是层级网络,只是层的功能和形式做了变化,主要是面对图像输入
1.1 卷积神经网络的层级结构
1.1.1 数据输入层/ Input layer
该层要做的处理主要是对原始图像数据进行预处理
1.1.2 卷积计算层/ CONV layer
这一层就是卷积神经网络最重要的一个层次,也是“卷积神经网络”的名字来源。在这个卷积层,有两个关键操作:
局部关联——每个神经元看做一个滤波器(filter)——相当于一个filter提取图像的一个特征图
窗口(receptive field)滑动——filter对局部数据计算——矩阵内积
一般要设置的超参数包括filters的数量、大小、步长,以及填充值(padding)
这里filters的数量为两个filter(w0,w1)、大小为(3×3)、步长为2
这里的蓝色矩阵就是输入的图像,灰色边框是填充值
粉色矩阵就是卷积层的filter
移动的蓝色矩阵就是窗口滑动,将窗口内的局部数据与粉色矩阵filter计算内积
绿色矩阵就是经过卷积运算后的输出矩阵
1.1.3 ReLU激励层 / ReLU layer
把卷积层输出结果做非线性映射,不过一般使用ReLU函数,它的特点是收敛快,求梯度简单,但较脆弱,图像如下:
1.1.4 池化层 / Pooling layer
池化层夹在连续的卷积层中间, 用于在特征不变的基础上压缩数据和参数的量,减小过拟合
这里里面没有参数需要我们学习,因为这里里面的参数都是我们设置好了,要么是Maxpooling,要么是Averagepooling
需要指定的超参数,包括是Max还是average,窗口大小以及步长
通常,我们使用的比较多的是Maxpooling,而且一般取大小为(2,2)步长为2的filter,这样,经过pooling之后,输入的长宽都会缩小2倍,特征不变
1.1.5 全连接层 / FC layer
和传统的神经网络一样,这一层是每一个单元都和前一层的每一个单元相连接,所以称之为“全连接”
这里要指定的超参数,无非就是神经元的数量,以及激活函数,一般最后一个全连接层才使用softmax函数(前面用relu是为了收敛快,最后softmax是为了输出结果)
1.2 CNN的特点
CNN相较于传统的神经网络,无非就是把FC改成了CONV和POOL,就是把传统的由一个个神经元组成的layer,变成了由filters组成的layer
优点
• 共享卷积核(一个filter对应一个参数),对高维数据处理无压力
• 无需手动选取特征,训练好权重,即得特征分类效果好
缺点
• 需要调参,需要大样本量,训练最好要GPU
• 物理含义不明确(也就说,我们并不知道没个卷积层到底提取到的是什么特征,而且神经网络本身就是一种难以解释的“黑箱模型”)
1.3 CNN结构演化历史
以上结构可查看《深度学习----CNN几种常见网络结构及区别》
2 标杆模型分析
标杆模型是基于Keras所构造的一个CNN卷积网络。
Keras是一个高级的Python神经网络框架,已被添加到TensorFlow中,成为其默认的框架,为TensorFlow提供更高级的API。
keras系列︱Sequential与Model模型、keras基本结构功能(一)
2.1 加载数据
将数据特征转换为矩阵,以便卷积计算
2.2 数据处理
①图像预处理:
利用中值滤波(median filter)进行降噪
——中值滤波的基本原理是把数字图像或数字序列中一点的值用该点的一个邻域中各点值的中值代替,让周围的像素值接近的真实值,从而消除孤立的噪声点。
利用阈值分割法(threshold segmentation)生成掩膜(binary mask)
——图像阈值化的目的是要按照灰度级,对像素集合进行一个划分,得到的每个子集形成一个与现实景物相对应的区域,各个区域内部具有一致的属性,而相邻区域不具有这种一致属性。
利用形态闭合(morphology closing)来填充图中的小洞
——先膨胀(一般对二值图像进行操作。找到像素值为1的点,将它的邻近像素点都设置成这个值。1值表示白,0值表示黑,因此膨胀操作可以扩大白色值范围,压缩黑色值范围)再腐蚀(和膨胀相反的操作,将0值扩充到邻近像素),可用来填充孔洞。
②挑选异形样本并过采样:
因为训练集中没有异形样本,人工挑选5个异形样本加入训练
2.3 构造卷积神经网络
def built_model():
n_filter=32;
model = Sequential()
model.add(Convolution2D(filters=n_filter, kernel_size=(5, 5), input_shape=(40, 40, 1), activation='relu'))
model.add(Convolution2D(filters=n_filter, kernel_size=(5,5), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Convolution2D(filters=n_filter, kernel_size=(5,5), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Convolution2D(filters=n_filter, kernel_size=(3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.5))
model.add(Flatten())
model.add(Dense(units=128, activation='relu'))
model.add(Dense(3, activation='softmax')) # Final Layer using Softmax
model.compile(loss='categorical_crossentropy',
optimizer=Adam(lr=0.0003),
metrics=['accuracy'])
model.summary()
return model
流程:创建Sequential()对象(简单线性堆叠网络),逐层堆叠网络
[Convolution2D-> RELU]卷积层—— 【[Convolution2D-> RELU]卷积层——MaxPooling2D池化层】×3
——Dropout防止过拟合——Flatten压平,多维转为一维——[FC -> RELU]全连接层——[FC -> Softmax]全连接层
n_filter就是滤波器(filter)的个数,一般个数从少到多,本文统一为32个
model.compile()定义损失函数
model.summary()输出模型各层的参数状况,具体参数计算可参考《详细解释CNN卷积神经网络各层的参数和链接个数的计算》
2.4 训练卷积神经网络
①训练模型的同时进行数据增广:
datagen = ImageDataGenerator( rotation_range=180, width_shift_range=0.1, height_shift_range=0.1, horizontal_flip=True )
# 训练模型的同时进行数据增广
history=model.fit_generator(datagen.flow(x_train, y_train, batch_size=batch_size),
steps_per_epoch=len(x_train) / batch_size, epochs=epochs,
class_weight=class_weight,
validation_data=datagen.flow(x_train, y_train, batch_size=batch_size), validation_steps=1)
ImageDataGenerator()是keras.preprocessing.image模块中的图片生成器,同时也可以在batch中对数据进行增强,扩充数据集大小,增强模型的泛化能力。比如进行旋转,变形,归一化等等。
因为异形样本较少,还设置class_weight损失权重,使得损失函数对异形样本更加关注。
②画epoch损失图:
2.5 提交结果
试着按照原代码,每一个epoch时间为150s(使用的是CPU所以计算较慢),经过100个epoch,最终提交成绩为0.95242
3 模型处理
3.1 模型调参和选择结构
3.1.1 调整卷积层和池化层数量
【[Convolution2D-> RELU]卷积层——MaxPooling2D池化层】
——Dropout防止过拟合——Flatten压平,多维转为一维——[FC -> RELU]全连接层——[FC -> Softmax]全连接层
【[Convolution2D-> RELU]卷积层——MaxPooling2D池化层】×2
——Dropout防止过拟合——Flatten压平,多维转为一维——[FC -> RELU]全连接层——[FC -> Softmax]全连接层
【[Convolution2D-> RELU]卷积层——MaxPooling2D池化层】×3
——Dropout防止过拟合——Flatten压平,多维转为一维——[FC -> RELU]全连接层——[FC -> Softmax]全连接层
由上往下,随着卷积层和池化层得增多,时耗增多,这是因为每一层卷积层都会增加参数数量。但发现图2表现最好,即下降得快且又平滑,
3.1.2 调整n_filter个数
在上面图1的基础上更改n_filter个数(速度较快)
n_filter 越多,时耗越多,也是因为参数增多。但n_filter=36 时模型表现最好。
3.2 再增异形样本
以上损失图中最后epoch显示训练集和测试集的损失都接近0了,可提交上去仍未能前几。从竞赛前身《形状识别:是方还是圆》中仅对正方形和圆形识别,排行榜多为1.0,猜测是因为此竞赛因为多了异形判断,所以难达成100%的准确率。
尝试再增加不同的异形样本加入训练集中,刚好另一位博主的竞赛文章也加入了异形样本且成绩有所提高,引用该博主寻找的异形。
最终模型决定是:
def built_model():
n_filter=36;
model = Sequential()
model.add(Convolution2D(filters=n_filter, kernel_size=(5, 5), input_shape=(40, 40, 1), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Convolution2D(filters=n_filter, kernel_size=(3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.5))
model.add(Flatten())
model.add(Dense(units=128, activation='relu'))
model.add(Dense(3, activation='softmax')) # Final Layer using Softmax
model.compile(loss='categorical_crossentropy',
optimizer=Adam(lr=0.0003),
metrics=['accuracy'])
model.summary()
return model
num_anno=10
anno_idx=np.array([0,1, 4, 10, 13,4949, 4956, 4973, 4974, 4988])
3.3 提交结果
每一个epoch时间为60s,经过100个epoch,最终提交成绩为0.98767
小结
①了解了CNN模型,但未尝试更为复杂的结构(VGG、ResNet等)
②神经网络这种大量参数需要大量运算的,最好还是使用GPU版本(我安装失败就没用)
③本次并未对模型多大处理,因为标杆模型做的足够好,主要仅是消化CNN模型原理和简单运用