好玩的人工智能
快乐的深度学习

使用深度学习识别交通标志,准确率达98%

这是Udacity自动驾驶汽车工程师Nanodegree第1项的第2 。您可以在github上找到与此项目相关的所有代码。您还可以通过单击链接阅读项目1:使用计算机视觉检测车道线的帖子


交通标志是我们道路基础设施的一个组成部分。它们为道路使用者提供关键信息,有时是引人注目的建议,这反过来又要求他们调整驾驶行为,以确保他们遵守当前实施的任何道路规则。如果没有这些有用的迹象,我们很可能会面临更多的事故,因为司机不会得到关于他们能够安全前进的速度的关键反馈,或者未知道路工程,急转弯或学校过路的情况。在我们这个现代化的时代,每年约有130万人死于道路。没有我们的路标,这个数字会更高。 
当然,自动驾驶汽车也必须遵守道路立法,因此必须认识理解 交通标志。

传统上,采用标准计算机视觉方法来检测和分类交通标志,但这些方法需要相当费时的手工工作来手工绘制图像中的重要特征。相反,通过应用深度学习这个问题,我们创建了可靠的流量进行分类标志,通过学习找出这个问题的最合适的功能模型本身。在这篇文章中,我展示了如何创建一个深度学习架构,可以在测试集上识别出接近98%准确度的交通标志。

项目设置

该数据集包含培训,测试和验证集,具有以下特征:

  • 图像为32(宽)x 32(高)x 3(RGB颜色通道)
  • 训练集由34799个图像组成
  • 验证集由4410个图像组成
  • 测试集由12630个图像组成
  • 共有43个班级(例如限速20公里/小时,没有入场,崎岖不平的道路等)

此外,我们将使用带有Tensorflow的Python 3.5来编写代码。

图像和分布

您可以在下面看到数据集中图像的样本,标签显示在相应图像的行上方。它们中的一些非常暗,所以我们会稍后改善对比度。

上面标签的训练集图像样本

训练集中的各个班级之间也存在显着的不平衡,如下面的直方图所示。有些类的图像少于200个,而其他类的图像超过2000个。这意味着我们的模型可能偏向于过度表示的类,特别是当它的预测不确定时。稍后我们将看到如何使用数据增强来缓解这种差异。

训练集中的图像分布 – 不太平衡

预处理步骤

我们最初对图像应用了两个预处理步骤:

灰度
我们将3通道图像转换为单个灰度图像(我们在项目1中做同样的事情 – 车道线检测 – 你可以在这里阅读我的博客文章)。

灰度训练集图像样本,上面有标签

图像标准化
我们通过用数据集平均值减去每个图像并除以其标准偏差来对图像数据集的分布进行居中。这有助于我们的模型统一处理图像。生成的图像如下所示:

标准化图像 – 我们可以看到“噪声”是如何分布的

模型架构

建议的建筑灵感来自Yann Le Cun 关于交通标志分类的论文。我们添加了一些调整并创建了一个模块化代码库,它允许我们尝试不同的滤波器大小,深度和卷积层数,以及完全连接层的尺寸。为了向Le Cun致敬,并且带着一丝厚颜无耻,我们称这样的网络为EdLeNet  :)。

我们主要尝试了5×5和3×3过滤器(又称内核)大小,并以第一个卷积层的深度32开始。EdLeNet的3×3架构如下所示:

EdLeNet 3×3架构

该网络由3个卷积层组成 – 内核大小为3×3,下一层的深度加倍 – 使用ReLU作为激活函数,每个后跟2×2最大池操作。最后3层完全连接,最后一层产生43个结果(可能的标签总数),使用SoftMax激活功能计算。使用Adam优化器使用小批量随机梯度下降训练网络。我们构建了一个高度模块化的编码基础架构,使我们能够动态创建我们的模型,如下面的代码片段所示:


1
2
3
4
5
mc_3x3 = ModelConfig(EdLeNet, "EdLeNet_Norm_Grayscale_3x3_Dropout_0.50", [32, 32, 1], [3, 32, 3], [120, 84], n_classes, [0.75, 0.5])
mc_5x5 = ModelConfig(EdLeNet, "EdLeNet_Norm_Grayscale_5x5_Dropout_0.50", [32, 32, 1], [5, 32, 2], [120, 84], n_classes, [0.75, 0.5])

me_g_norm_drpt_0_50_3x3 = ModelExecutor(mc_3x3)
me_g_norm_drpt_0_50_5x5 = ModelExecutor(mc_5x5)

1
ModelConfig

包含模型信息,例如:

  • 模型功能(例如
    1
    EdLeNet

  • 型号名称
  • 输入格式(例如[32,32,1]用于灰度),
  • 卷积层配置[过滤器大小,起始深度,层数],
  • 完全连接的层尺寸(例如[120,84])
  • 课程数量
  • 辍学保持百分比值[p-conv,p-fc]

1
ModelExecutor

可用于培训评估预测和生成激活图的可视化。

为了更好地隔离我们的模型并确保它们不是全部存在于相同的Tensorflow图下,我们使用以下有用的构造:


1
2
3
4
5
6
7
self.graph = tf.Graph()
with self.graph.as_default() as g:
    with g.name_scope( self.model_config.name ) as scope:

...

with tf.Session(graph = self.graph) as sess:

这样,我们为每个模型创建单独的图形,确保我们的变量,占位符等没有混合。这给我带来了很多麻烦。

实际上,我们的卷积深度为16,但是得到了更好的结果,因此我们已经确定了这个值。我们还比较了颜色与灰度,标准和标准化图像,并看到灰度倾向于超越颜色。不幸的是,我们在3×3或5×5型号上几乎没有达到93%的测试设置精度,并没有始终如一地达到这个里程碑。此外,我们在给定数量的时期之后观察到验证集上的一些不稳定的损失行为,这实际上意味着我们的模型在训练集上过度拟合而不是一般化。您可以在下面看到我们针对不同型号配置的一些度量图表。

颜色归一化图像的模型性能
灰度标准化图像的模型性能

退出

为了提高模型的可靠性,我们转向衰退,这是一种正则化形式,其中权重以概率p保持:因此,未保持权重被“丢弃”。这可以防止模型过度拟合。辍学是由深度学习领域的先驱Geoffrey Hinton介绍的。他的小组关于这个主题的论文是必须阅读的,以便更好地理解作者背后的动机。还有一个与生物学和进化相关的迷人平行。
在论文中,作者根据层的类型应用不同程度的辍学。因此,我决定采用类似的方法,定义两个级别的丢失,一个用于卷积层,另一个用于完全连接的层:


1
2
p-conv: probability of keeping weight in convolutional layer
p-fc: probability of keeping weight in fully connected layer

此外,作者逐渐采用了更具侵略性(即更低)的辍学价值,因为他们在网络中更深入。因此我也决定:


1
p-conv >= p-fc

也就是说,我们将在卷积中保持权重大于或等于完全连接的层。推理这一点的方法是,我们将网络视为一个漏斗,因此我们希望在我们深入到层次时逐渐收紧它们:我们不希望在开始时丢弃太多信息,因为其中一些信息非常严重有价值。此外,当我们在卷积层中应用MaxPooling时,我们已经失去了一些信息。

我们尝试了不同的paratemers,但最终确定了p-conv = 0.75p-fc = 0.5,这使我们能够在3×3模型的标准化灰度图像上实现97.55%的测试集精度。有趣的是,我们在验证集上达到了98.3%的准确率:


1
2
3
4
5
6
7
8
9
Training EdLeNet_Norm_Grayscale_3x3_Dropout_0.50 [epochs=100, batch_size=512]...

[1] total=5.222s | train: time=3.139s, loss=3.4993, acc=0.1047 | val: time=2.083s, loss=3.5613, acc=0.1007
[10]    total=5.190s | train: time=3.122s, loss=0.2589, acc=0.9360 | val: time=2.067s, loss=0.3260, acc=0.8973
...
[90]    total=5.193s | train: time=3.120s, loss=0.0006, acc=0.9999 | val: time=2.074s, loss=0.0747, acc=0.9841
[100]   total=5.191s | train: time=3.123s, loss=0.0004, acc=1.0000 | val: time=2.068s, loss=0.0849, acc=0.9832
Model ./models/EdLeNet_Norm_Grayscale_3x3_Dropout_0.50.chkpt saved
[EdLeNet_Norm_Grayscale_3x3_Dropout_0.50 - Test Set]    time=0.686s, loss=0.1119, acc=0.9755
辍学引入后灰度标准化图像的模型性能

上面的图表显示模型是平滑的,不像一些图表更高。我们已经达到了在测试集上获得超过93%准确度的目标,但我们能做得更好吗?请记住,有些图像模糊不清,每个图像的图像分布非常不均匀。我们将探讨以下用于解决每个问题的其他技巧。

直方图均衡

直方图均衡是一种用于增加图像对比度的计算机视觉技术。由于我们的一些图像遭受低对比度(模糊,黑暗),我们将通过应用OpenCV的对比度限制自适应直方图均衡(也称为CLAHE)功能来提高可视性。

我们再次尝试各种配置,并使用以下丢失值在3×3模型上找到最佳结果,测试精度为97.75%p-conv = 0.6p-fc = 0.5  。


1
Training EdLeNet_Grayscale_CLAHE_Norm_Take-2_3x3_Dropout_0.50 [epochs=500, batch_size=512]...

1
2
3
4
5
6
7
8
[1] total=5.194s | train: time=3.137s, loss=3.6254, acc=0.0662 | val: time=2.058s, loss=3.6405, acc=0.0655
[10]    total=5.155s | train: time=3.115s, loss=0.8645, acc=0.7121 | val: time=2.040s, loss=0.9159, acc=0.6819
...
[480]   total=5.149s | train: time=3.106s, loss=0.0009, acc=0.9998 | val: time=2.042s, loss=0.0355, acc=0.9884
[490]   total=5.148s | train: time=3.106s, loss=0.0007, acc=0.9998 | val: time=2.042s, loss=0.0390, acc=0.9884
[500]   total=5.148s | train: time=3.104s, loss=0.0006, acc=0.9999 | val: time=2.044s, loss=0.0420, acc=0.9862
Model ./models/EdLeNet_Grayscale_CLAHE_Norm_Take-2_3x3_Dropout_0.50.chkpt saved
[EdLeNet_Grayscale_CLAHE_Norm_Take-2_3x3_Dropout_0.50 - Test Set]   time=0.675s, loss=0.0890, acc=0.9775

我们在下面的图表中显示了以前的运行图,其中我们也测试了5×5模型,超过220个时期。我们可以在这里看到更平滑的曲线,增强了我们的直觉,即我们的模型更稳定。

具有压差的灰度均衡图像的模型性能

我们确定了269个模型无法正确识别的图像。我们在下面显示10个,随机选择,以推测模型错误的原因。

10个图像的样本,其中我们的模型预测错误

尽管我们的直方图均衡,但有些图像非常模糊,而其他图像则显得扭曲。我们的测试集中可能没有足够的此类图像示例来进行模型的改进预测。此外,虽然97.75%的测试精度非常好,但我们仍然还有一个问题:数据增强。

数据扩充

我们之前观察到,这些数据在43个类别中表现出明显的不平衡。然而,它似乎并不是一个严重的问题,因为尽管阶级不平衡,我们仍能达到很高的准确率。我们还注意到测试集中的一些图像是扭曲的。因此,我们将使用数据增强技术来尝试:

  1. 扩展数据集并以不同的照明设置和方向提供其他图片
  2. 提高模型变得更通用的能力
  3. 提高测试和验证的准确性,尤其是在扭曲的图像上

我们使用一个名为imgaug的漂亮的库来创建我们的扩充。我们主要应用仿射变换来增强图像。我们的代码如下:


1
2
3
4
5
6
7
8
9
10
11
12
def augment_imgs(imgs, p):
    """
    Performs a set of augmentations with with a probability p
    """
    augs =  iaa.SomeOf((2, 4),
          [
              iaa.Crop(px=(0, 4)), # crop images from each side by 0 to 4px (randomly chosen)
              iaa.Affine(scale={"x": (0.8, 1.2), "y": (0.8, 1.2)}),
              iaa.Affine(translate_percent={"x": (-0.2, 0.2), "y": (-0.2, 0.2)}),
              iaa.Affine(rotate=(-45, 45)), # rotate by -45 to +45 degrees)
              iaa.Affine(shear=(-10, 10)) # shear by -10 to +10 degrees
          ])

1
2
3
    seq = iaa.Sequential([iaa.Sometimes(p, augs)])
   
    return seq.augment_images(imgs)

虽然类不平衡可能会导致模型出现偏差,但我们决定不在此阶段解决它,因为它会导致我们的数据集显着膨胀并延长我们的培训时间(我们没有太多时间花在培训上在这个阶段)。相反,我们决定将每个班级增加10%。我们的新数据集如下所示。

增强图像的样本

当然,图像的分布不会发生显着变化,但我们会对图像应用灰度,直方图均衡和标准化预处理步骤。我们训练2000个时期的辍学(p-conv = 0.6p-fc = 0.5),并在测试集上达到97.86%的准确度:

[EdLeNet]构建神经网络[conv layers = 3,conv filter size = 3,conv start depth = 32,fc layers = 2] 
训练EdLeNet_Augs_Grayscale_CLAHE_Norm_Take4_Bis_3x3_Dropout_0.50 [epochs = 2000,batch_size = 512] ... 

[1] total = 5.824s | 火车:时间= 3.594s,损失= 3.6283,acc = 0.0797 | val:时间= 2.231s,损失= 3.6463,acc = 0.0687 
... 
[1970]总计= 5.627s | 火车:时间= 3.408s,损失= 0.0525,acc = 0.9870 | val:时间= 2.219s,损失= 0.0315,acc = 0.9914 
[1980]总计= 5.627s | 火车:时间= 3.409s,损失= 0.0530,acc = 0.9862 | val:时间= 2.218s,损失= 0.0309,acc = 0.9902 
[1990]总计= 5.628s | 火车:时间= 3.412s,损失= 0.0521,acc = 0.9869 | val:时间= 2.216s,损失= 0.0302,acc = 0.9900 
[2000]总计= 5.632s | 火车:时间= 3.415s,损失= 0.0521,acc = 0.9869 | val:时间= 2.217s,损失= 0.0311,acc = 0.9902
模型./models/EdLeNet_Augs_Grayscale_CLAHE_Norm_Take4_Bis_3x3_Dropout_0.50.chkpt已保存
[EdLeNet_Augs_Grayscale_CLAHE_Norm_Take4_Bis_3x3_Dropout_0.50  - 测试集]时间= 0.678s,损失= 0.0842,acc = 0.9786

这是我们迄今为止最好的表现!

神经网络庆典

但是 ……如果你看一下训练集上的损失指标,你可以看到在0.0521,我们很可能还有一些摆动空间。我们计划培养更多时代,并将在未来报告我们的新成果。

测试新图像

我们决定在新图像上测试我们的模型,以确保它确实比我们原始数据集中的交通标志更普遍。因此,我们下载了五张新图像并将其提交给我们的模型进行预测。

下载5个新的交通标志 – 颜色

图像的基本事实如下:


1
2
3
4
5
['Speed limit (120km/h)',
 'Priority road',
 'No vehicles',
 'Road work',
 'Vehicles over 3.5 metric tons prohibited']

选择图像的原因如下:

  • 它们代表了我们目前分类的不同交通标志
  • 它们的形状和颜色各不相同
  • 它们处于不同的光照条件下(第4个有阳光反射)
  • 它们处于不同的方向(第三个是倾斜的)
  • 他们有不同的背景
  • 最后一张图片实际上是一个设计,而不是一张真实的图片,我们想要针对它测试模型
  • 其中一些人的代表性不足

我们采取的第一步是将相同的CLAHE应用于这些新图像,从而产生以下结果:

下载5个新的交通标志 – 灰度CLAHE

我们在新图像上实现了100%的完美精度。在原始测试集上,我们达到了97.86%的准确率。我们可以探索模糊/扭曲我们的新图像或修改对比度,以了解模型将来如何处理这些变化。


1
2
3
4
new_img_grayscale_norm_pred_acc = np.sum(new_img_lbs == preds) / len(preds)
print("[Grayscale Normalised] Predictional accuracy on new images: {0}%".format(new_img_grayscale_norm_pred_acc * 100))
...
[Grayscale Normalised] Predictional accuracy on new images: 100.0%

我们还显示了为每个图像计算的前5个SoftMax概率,绿色条显示了基本事实。我们可以清楚地看到我们的模型对其预测非常有信心。在最坏的情况下(最后一幅图像),第二大可能预测的概率约为0.1%。事实上,我们的模型在最后一张图像上最为挣扎,我认为这实际上是一种设计,甚至不是真实的图像。总的来说,我们开发了一个强大的模型!

模型的前5个预测的可视化

可视化我们的激活地图

我们在下面显示每个卷积层产生的结果(在最大池之前),得到3个激活图

第1层

我们可以看到网络正在关注圆圈的边缘以及卡车上的某种方式。背景基本上被忽略了。

第2层

第二卷积层的激活图

很难确定网络在第2层中关注的是什么,但它似乎在圆圈的边缘和卡车出现的中间“激活”。

第3层

这个激活图也难以破译……但似乎网络再次对边缘和中间的刺激做出反应。

结论

我们讨论了如何使用深度学习来高精度地对交通标志进行分类,采用各种预处理和正则化技术(例如,丢失),以及尝试不同的模型架构。我们构建了高度可配置的代码,并开发了一种评估多种体系结构的灵活方法。我们的模型在测试集上达到接近98%的准确度,在验证集上达到99%。

就个人而言,我非常喜欢这个项目,并使用Tensorflow,matplotlib和研究人工神经网络架构获得了实践经验。此外,我深入研究了这一领域的一些开创性论文,这些论文加强了我的理解,更重要的是提炼了我对深度学习的直觉。

在未来,我相信通过应用批量标准化等更多正规化技术以及采用更现代的架构(如GoogLeNet的初始模块ResNetXception),可以实现更高的准确性。

我希望你喜欢阅读这篇文章。随意留下评论和鼓掌:)。您也可以在TwitterMedium上关注我,了解更多关于我的“AI之旅”的文章。继续学习和建设!

未经允许不得转载:零点智能 » 使用深度学习识别交通标志,准确率达98%
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

零点智能 人工智能社区,加Q群:469331966

投稿&建议&加Q群