写在前面的话
长期以来,自动驾驶一直是互联网豪门的专属游戏。这个领域内的中小型公司大多都被收购,因此相关技术一直被掌握在少数公司手中,无法被广泛普及,从而导致了这个领域的进入门槛相当之高。同时。网络上关于自动驾驶的文章少之又少。即便偶而发表,也大多停留在概念和抽象分析层面上,让人读后如隔靴搔痒。因此,我试图写一个以自动驾驶为主题的系列,深入浅出地分析相关技术,作为自己学习成果的记录,也与大家分享。
图像分割与车道识别
图像分割指的是将一副图片从语义上划分成多个部分。可以看到,下图(右侧)包含了许多语义信息:前景中的骑手和马,背景中的汽车,草地和树木等。我们希望通过某种方法,识别出图像中的每一个像素所属的语义元素,这个过程就是图像分割。下图(左侧)就是图像分割的结果,不同的颜色代表不同的语义元素。

通过图像分割,我们可以清晰地得到语义元素在图像中的大小和位置,这些信息将作为后续图像处理的基础。
而本文要介绍的车道识别实际上就是一类特定的图像分割问题。我们的输入图像是车辆前方道路图像(由固定在前挡风玻璃上的摄像头所拍摄),输出则是一个包含两个颜色的图像,不同的颜色分别代表了车道和背景。如下图所示:

车道识别的目标
车道识别归根结底是图像识别问题。但是,在传统的图像识别问题中,一张图像只有一个识别结果。比如,在我之前的博客利用卷积神经网络识别CIFAR-10中,每个图像都被归为某一个类别。因此这种识别是图像级别的。然而,在车道识别中,图像识别将是像素级别的。也就是说,我们需要针对图像中的每一个像素,给出它所属的类别。例如,如果输入图像是一个
的三通道的RGB图像,那么输出就是一个
的矩阵,矩阵的每个元素表示该像素所属的类别。如下图所示:

为了解决这个问题,我们很自然地想到了是否可以利用卷积神经网络。答案是肯定的。但是,在应用卷积神经网络的具体方式上,我们却有多个选择。
基于卷积网络的方法
卷积网络(convolutional neural network,CNN)通常在卷积层之后会接上若干个全连接层, 将卷积层产生的特征图(feature map)映射成一个固定长度的特征向量。CNN适合于图像级的分类任务,因为它们最后会得到整个输入图像的一个概率向量,比如识别CIFAR-10图像的CNN会输出一个10维的向量,用于表示输入图像属于每一类的概率(softmax归一化)。
如果我们利用CNN来判断图像中的某个像素是否属于车道时,我们通常需要分析它临近的若干像素,而窗口的大小就决定了哪些临近像素会被考虑。我们将窗口截取的图像输入卷积神经网络,网络的输出是位于窗口中心位置的像素的分类(属于或不属于车道)。当我们滑动这个窗口时,就能够给出图像每个像素的分类,如下图所示。

滑动窗口方法的缺点很明显:
- 计算量巨大。由于窗口之间存在重叠,因此存在重复计算的问题。导致算法的整体性能较低,无法做到实时分析。
- 存储开销巨大。例如对每个像素使用的图像块的大小为
,然后不断滑动窗口,每次滑动的窗口给CNN进行判别分类,因此则所需的存储空间根据滑动窗口的次数和大小急剧上升。
- 窗口大小不确定。窗口大小的选取取决于经验估计,而且窗口大小的选择对最终分类准确度有很大影响。
- 窗口大小限制了感知区域的大小。通常窗口的大小比整幅图像的大小小很多,因此只能提取一些局部的特征,从而导致分类的性能受到限制。
全卷积网络的组成
为了克服卷积网络在图像分割问题中的缺点,我们在卷积网络的基础上发展出了全卷积网络(fully convolutional network,FCN)。FCN可以对图像进行像素级的分类,从而实现了语义级别的图像分割(semantic segmentation)。全卷积网络一般由卷积层(convolutional layer)、池化层(pooling layer)和反卷积(deconvolutional layer)层组成。
卷积层
每个卷积层的输入都是一个三维数组
,其中
、
和
分别是三维数组的是高度、宽度和深度。 由于输入层的数据是图像,因此
和
分别代表图像的高度和宽度,
代表颜色通道。每个卷积层都包含一组可被学习的卷积核。每个卷积核的宽度和高度很小,但是会延伸到输入的整个深度。卷积核中的每一个参数都相当于传统神经网络中的权值参数,但是它只与对应的局部像素相连接,将卷积核的各个参数与对应的局部像素值相乘之和,就可以得到卷积层的输出。
池化层
通过卷积层获得了图像的特征之后,理论上我们可以直接使用这些特征训练分类器(如softmax),但是这样做将面临巨大的计算量的挑战,而且容易产生过拟合(overfitting)的现象。为了进一步降低网络训练参数及模型的过拟合程度,我们在每个卷积层之后连接一个下池化层。池化的方式通常有以下两种:
- 最大化池化(max pooling):选择池化窗口中的最大值作为采样值;
- 平均值池化(mean pooling):将池化窗口中的所有值相加取平均,以平均值作为采样值。

反卷积层
全卷积网络通常包含若干个反卷积层,它能够将卷积层产生的特征图映射回原始像素。在介绍反卷积之前,我们先来看看卷积运算和矩阵运算之间的关系。
考虑如下一个简单的卷积层运算,其中输入矩阵的大小为
,卷积核大小为
,步长
,填充
,输出矩阵的大小为
。这个卷积操作的示意图如下所示。

对于上述卷积运算,我们可以把上图所示的
的卷积核展开成一个如下所示的
的稀疏矩阵
,其中非0元素
表示卷积核的第i行、第j列的元素。

我们再把
的输入特征展成
的矩阵
:

那么输出矩阵
则是一个
的输出特征矩阵,把它重新排列成
的输出特征就得到最终的结果,通过上述的分析,我们可以看到卷积操作可以表示为和矩阵
相乘,那么反卷积操作就是和矩阵
的转置
相乘。因此,反卷积操作也被称为转置卷积操作(transposed convolutional layer)。
下图所示的是参数为
的反卷积操作,其对应的卷积操作参数为
。

我们可以发现对应的卷积和反卷积操作,
。但是反卷积操作却多了
。通过对比我们可以发现卷积层中左上角的输入只对左上角的输出有贡献,所以反卷积层会出现
。可以发现,反卷积层的输入和输出在
的情况下的关系为:

基于全卷积网络的方法
与经典的卷积网络在卷积层之后使用全连接层得到固定长度的特征向量进行分类不同,全卷积网络可以接受任意尺寸的输入图像,它利用反卷积层对最后一个卷积层的feature map进行上采样,使它恢复到输入图像相同的尺寸,从而可以对每个像素都产生了一个分类预测,同时保留了原始输入图像中的空间信息,从而实现了逐像素的分类。一个典型的全卷积网络的架构如下图所示。

特种属性的融合
在传统的全卷积网络的基础上,我们还采用了多层级融合的技术,将不同层级上的特征属性进行融合,从而提高了语义分割输出的精度,具体实现方法如下图所示。

可以看到,通过四个卷积层之后,我们分别得到了输入图像的四个不同解析度级别上的特征图(feature map)。接下来,我们把不同层级上的特征图通过反卷积操作放大到相同的大小,然后把它们相加,从而实现了不同层级上的特征属性的融合。
基于VGG16的微调
通常来说,用于图像语义分割的全卷积网络层数较多,包含的参数数量十分庞大。全新训练一个全卷积网络用于道路分割非常耗时。而我们的全卷积网络的前半部分(卷积层和池化层)和用于图像分类的VGG16网络完全一致。因此,我们可以在预先训练好的VGG16网络的基础上进行微调。
实验
KITTI数据集
我们在KITTI的Road/Lane Detection Evaluation数据集上测试这个网络。KITTI数据集包括:立体声、光流、视觉测距、3D物体检测和3D跟踪等多个基准数据集,包括了卡尔斯鲁厄市中心、周边农村地区和高速公路上收集的真实数据。每张图片最多可显示15辆汽车和30名行人。除了以原始格式提供所有数据,KITTI还提供了每个任务的基准。对于我们的每个基准,KITTI还提供了评估指标和评估网站。
网络架构和实现代码
网络的实现代码可以在这里找到。我们的网络结构如下表所示。
|
Name
|
Kernel Size |
Stride |
Padding |
Num Output |
|
data
|
– |
– |
– |
3
|
|
conv1_1
|
3 |
1 |
1 |
64
|
|
conv1_2
|
3 |
1 |
1 |
64
|
|
pool1
|
2 |
2 |
0 |
64
|
|
conv2_1
|
3 |
1 |
1 |
128
|
|
conv2_2
|
3 |
1 |
1 |
128
|
|
pool2
|
2 |
2 |
0 |
128 |
| conv3_1 |
3
|
1
|
1 |
256 |
|
conv3_2
|
3 |
1 |
1 |
256
|
| conv3_3 |
3 |
1 |
1 |
256
|
|
pool3
|
2 |
2 |
0 |
256
|
|
conv4_1
|
3 |
1 |
1 |
512 |
| conv4_2 |
3 |
1 |
1 |
512
|
|
conv4_3
|
3 |
1 |
1 |
512 |
|
pool4
|
2 |
2 |
0 |
512 |
| conv5_1 |
3 |
1 |
1 |
512
|
|
conv5_2
|
3 |
1 |
1 |
512 |
| conv5_3 |
3 |
1 |
1 |
512
|
|
pool5
|
2 |
2 |
0 |
512
|
| fc6 |
– |
– |
– |
4096
|
|
fc7
|
– |
– |
– |
4096
|
|
fc8
|
– |
– |
– |
2
|
|
upscore2
|
– |
– |
– |
2
|
|
upscore4
|
–
|
– |
– |
2
|
| upscore32 |
– |
– |
– |
2
|
其中,conv1-5表示卷积层,卷积层不会改变输入的大小,只会增加特征输出通道。pool1-5表示池化层,池化层的输出大小是输入的一半,而特征输出通道保持不变。fc6-7表示全连接层,用于综合卷积层和池化层学习到的特征。upscore2、upscore4和upscore32分别表示2倍、4倍和32倍上采样,用于将学习到的特征映射回原始像素。
实验结果
下图显示了该网络的分类精度约为99.85%,召回率约为85%,F1约为0.9。
我们从输出结果中选取了9幅图像。绿色的像素表示被正确分类的道路。红色的像素表示false positive的分类(也就是将不属于车道的像素误分类为车道)。蓝色的像素表示false negative的分类(也就是将属于车道的像素误分类为非车道)。可以看到,对于绝大多数像素,该网络都可以正确分类。








