项目背景及问题描述
本项目是由 State Farm 在 Kaggle 上发起的一个竞赛项目,目的是能通过算法去识别司机的行为,判断司机是否在安全驾驶状态。这样,当司机做出非安全驾驶的行为时,就可以去提醒司机专心驾驶,从而在一定程度上减少交通事故 。 项目地址:https://www.kaggle.com/c/state-farm-distracted-driver-detection
项目的要求是给定司机驾驶状态的 2 维图像,去识别图像中司机
是处于哪种驾驶状态。这实际上是一个分类问题:用给定的带标签的训练数据(图像)训练模型,然后用测试数据集验证模型。需要识别的驾驶状态一共有如下10 种:
c0: 安全驾驶
c1: 右手打字
c2: 右手打电话
c3: 左手打字
c4: 左手打电话
c5: 调收音机
c6: 喝饮料
c7: 拿后面的东西
c8: 整理头发和化妆
c9: 和其他乘客说话
根据项目要求,模型的评估指标为对数损失函数(multi-class
logarithmic loss):
在测试集上模型的对数损失函数的数值越低,说明模型的表现越好。
在 Kaggle 上,该项目的 Public Leaderboard 是用大约 31%的测试数据去测试模型,Private Leaderboard 则是用大约 69%的测试数据去测试模型,评估指标 logloss 的值越低,说明模型的表现越好,在
Leaderboard 上的排名也就越靠前。该项目的 Leaderboard 有 1440 组参赛者,做本项目的目标是进入 Private Leaderboard 的 top 10%,也就是 Private Leaderboard 上的logloss要低于0.25634。
项目提供分辨率为 640x480、格式为 JPG 的图片,分为训练数据
和测试数据,其中训练数据中的图片按照标签 c0~c9 分别存放在对应的文件夹中。 下面是从数据集中挑选的一些示例图片:
数据集一共包含26个司机共79726 张图片,其中训练集有22424 张图片。训练集中各类别包含的图片数量如下图所示:
在项目提供的 driver_imgs_list.csv 文件中,把每个司机都分配了一个ID,统计了每个司机所有图片的文件名,以及这些图片的类别。
这样,在做数据预处理时我们就可以根据 driver_imgs_list.csv文件提
供的信息,对训练数据集进行划分(分为训练集和验证集)。划分训练数据集时应该按照司机来划分,同一个司机的图片不能同时出现在训练集和验证集中,这是因为同一个类别中包含了同一个司机的若干张照片,这些照片是不能同时出现在训练集和验证集中的。这一点是需要特别注意的。
为了训练泛化能力强的模型,最好的方法是使用大量的数据进行训练,这样模型就能从样本的差异中得到很好的泛化能力,避免过拟合。但是在实际中,训练样本总是有限的,本项目的训练数据集只有2 万多张图片,用来训练模型是远远不够的。为了解决这个问题,一种有效的方法是对数据集进行数据增强操作。常用的图像数据增强操作有:旋转、剪切、缩放、水平移动、垂直移动、通道转换、水平翻转、垂直翻转等。在 Keras 中,可以通过生成器实时生成带数据增强的图像数据,下图是一个本文中所作数据增强操作的示例。
上图中第一张图片是原图,其他8张图片是做了增强操作后的效果。数据增强的基本前提是不会改变样本所属的类别,而且要根据实际情况选择合理的操作,例如在本项目中,垂直翻转就是不合理的操作。
VGG16 模型的结构简单,前面由一系列卷积层和池化层堆叠而成,后面接两个 4096 的全连接层,最后通过 Softmax-1000 层输出结果。在本项目的实现中,我们使用 Keras 自带的 VGG16 模型,只加载前面的5个block,采用 I mage N et预训练 的 权重,后面接两层128全连接层,每个全连接层后接一个 BatchNormalization和 Dropout(0.5)层,分类输出层改为本项目对应的 Softmax-10 。 我们把该模型取名为,VGG16_FC, 整个模型的结构如下 :
训练的时候,首先锁住预训练模型的 5 个 block 的层,用优化器Adam(lr=1e-3)训练后面的全连接层和 Softmax-10 层,这样不致于在训练新加入层的时候,因为大的梯度变化破坏预训练模型的权重。训练几代后,再放开其他的层进行训练。整个训练过程的训练集和验证集的accuracy和loss的曲线如下所示:
另外,我们还用同样的方法在VGG19的基础上训练了模型VGG19_FC。VGG16_FC和VGG19_FC模型的在Softmax 输出层前面接的都是两层全连接层。由于全连接层参数较多,用全连接层容易造成过拟合,所以近几年推出的CNN模型都摈弃了使用全连接层,转而使用全局平均池化层(Global Average Pooling Layer)。全局平均池化层由Lin等人提出,主要思想是分别对每个特征图内部求平均,再把得到的特征向量送入 Softmax 层进行分类。全局平均池化层可以大大
减少参数量,可以有效地防止过拟合。对上文中的VGG16_FC和VGG19_FC 模型进行改进,用全局平均池化层取代两层全连接层,得
到的模型分别用VGG16_GAP和VGG19_GAP指代,两个模型的框图如下:
VGG16_GAP 模型
VGG19_GAP 模型
在上一节中,一共训练了 VGG16_FC、VGG19_FC、VGG16_GAP和 VGG19_GAP 这四个模型。为了得到更好的结果,可以把这四个模型做融合。一种简单的融合方法是把四个模型的输出做平均,作为最终模型的输出,本文采用就是采用这种融合方法。融合后模型的结
构如下图所示:
将训练的模型提交到 Kaggle,得到的结果如下:
集成 4 个 VGG 模型后,在 Kaggle 上的 Private Score 为 0.24568 ,排名 130/1440 ,已经达到本项目的预期目标。本文中只使用了 VGG 模型,其实还有很多优秀的 CNN 模型可以使用,效果应该会更好。