上篇文章中我们介绍如何通过TensorFlow2.0 构建CNN网络解决图片识别问题。我们使用Fashion-MNIST数据库中的28x28 的灰阶图片,而现实生活中我们面对的大多数问题是彩色图片问题。那么我们如何处理彩色图片作为输入?彩色图片使用RGB表示,Red 红色、Green绿色、Blue蓝色通道,我们可以将图片表示为:
我们同样使用Filter水平和垂直对图片进行操作,但是现在Filter本身是3D的,也就是说我们设置Filter的形状为 3 x 3 x 3 (宽度 x 高度 x 深度)。现在Filter本身是三维的,就像我们将彩色图片看做三个二维矩阵的堆叠一样,也可以将Filter看做三个二维矩阵的堆叠,彩色图片和Filter都拥有 Red、Green、 Blue 通道。
我们分别对 Red、Green、 Blue 通道做卷积操作,计算方法与上篇文章相同,只是现在的求和项是原来的三倍。Filter 1、Filter2、Filter3(3D Filter)运算结果相加得出一个特征值。依次遍历图片得到如下图的二维特征矩阵。
我们当然可以用多个3D Filter 得到三维特征向量,而深度是由使用多少个3D Filter决定的。例如我们使用3个3D Filter 那么最后得到的特征矩阵的深度就是3, 如下图所示:
那么我们如何在代码中定义呢?
我们可以设置filters为3, kernel_size(也就是Filter)为(3,3)。
目前我们只讨论了创建1层卷积层和池化层的用例,如果需要解决复杂的问题我们需要构建多层卷积层和池化层其实也不难,操作是一样的,但需要注意的是计算好每层的输入和输出矩阵的形状。我来举个例子
model = Sequential()
# 第一层卷积层
model.add(Conv2D(16, 3, padding='same', activation='relu', input_shape=(IMG_SHAPE,IMG_SHAPE, 3)))
model.add(MaxPooling2D(pool_size=(2, 2)))
# 第二层卷积层
model.add(Conv2D(32, 3, padding='same', activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
# 第三层卷积层
model.add(Conv2D(64, 3, padding='same', activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
model.add(Dropout(0.2))
model.add(Dense(512, activation='relu'))
model.add(Dropout(0.2))
model.add(Dense(5, activation='softmax'))
# 编译模型
model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
为什么多层卷积层的效果要好呢?我们可以这样理解第一层卷积层从原图像中发现规律提取特征值,第二层卷积层在第一层卷积层发现的规律中发现规律,然后可以继续这么做,从规律中发现规律,然后继续从规律中发现规律(在未出现梯度消失的情况下,后续我们会再介绍梯度消失),这就是卷积网络的强大之处,大量的减少计算量的同时又能保证发现规律。
在这里我们最后一个层级由一个 Dense 层(具有 5个输出单元)和一个 softmax 激活函数组成。请考虑如果最后一层具有2个输出单元,那么我们处理的是什么问题呢? 是的,在处理二元分类问题,也就是yes or no 问题。在TensorFlow中,我们可以使用两种方法:
- tf.keras.layers.Dense(2, activation='softmax')
- tf.keras.layers.Dense(1, activation='sigmoid')
这两种方法都适合二元分类问题,但是请注意,如果决定使用 sigmoid 激活函数,需要将 model.compile() 方法中的 loss 参数从 'sparse_categorical_crossentropy' 更改为'binary_crossentropy',如下所示:
# 编译模型
model.compile(optimizer='adam',
loss='binary_crossentropy',
metrics=['accuracy'])
以上代码我们还会发现一处以前文章中没有见到的“model.add(Dropout(0.2))”,这到底又是什么呢? 是为了解决机器学习中会出现overfitting的情况。我们先来解释下什么情况是overfitting 。举个例子:“我们用火箭弹打苍蝇就是overfitting的情况”叫做过拟合,也就是说模型不能很好地泛化到训练数据意以外的数据,相对的:“用苍蝇拍打哥斯拉就是underfitting的情况”,我们叫做欠拟合,因为我过于简单化了问题。既然我们了解了这两种情况,那么我们如何在机器学习中尽量避免呢? 在实际中我们很难一次就设计出非常合适的神经网络结构,所以我们要不断地尝试。首先我们想象一下有一条瘦的穿不上的裤子和一条肥的穿上就掉的裤子那么我们会选哪个,我们当然会选择肥的裤子,然后再找条腰带。所以在设计神经网络时,我们尽量要偏向于overfitting的方案,然后再使用防止overfitting的技术应用到模型中。
有哪些防止overfitting的技术呢?
- 收集更多数据(Gather more data)
- 图像增强(Data augmentation & Noise), 通过向训练集中的现有图像应用随机图像转换,人为地增加训练集中的图像数量。
from tensorflow.keras.preprocessing.image import ImageDataGenerator
image_gen_train = ImageDataGenerator(
rescale=1./255,
rotation_range=40, # Rotating the image
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2, # Applying Zoom
horizontal_flip=True, # Flipping the image horizontally
fill_mode='nearest')
train_data_gen = image_gen_train.flow_from_directory(batch_size=BATCH_SIZE,
directory=train_dir,
shuffle=True,
target_size=(IMG_SHAPE,IMG_SHAPE),
class_mode='binary')
- 简化模型(Simplify the model ):如果即使现在有了所有数据,模型仍然设法使训练数据集过拟合,则可能是该模型过于强大。然后,您可以尝试降低模型的复杂性。
- 早停法(Early Termination): 对于此方法,我们会在训练过程中跟踪验证集的损失,并根据该损失判断何时停止训练,使模型很准确,但是不会过拟合。
- 正则化(regularization ):L1 (L1罚则旨在最小化权重的绝对值)and L2 (L2惩罚旨在最小化权重的平方大小)
- 丢弃(Dropout):在训练过程中,从神经网络中随机选择固定数量的神经元并关闭这些神经元。
参考资料:
https://hackernoon.com/memorizing-is-not-learning-6-tricks-to-prevent-overfitting-in-machine-learning-820b091dc42
(声明:本文图片来自Google)