一文读懂迁移学习和ASPP如何使自动驾驶汽车的目标检测更加智能

译文 精选
人工智能 无人驾驶
自动驾驶汽车是不能犯错误的,忽视一个红绿灯或一个行人都可能意味着灾难。但城市环境是动态的,在这样的环境中目标检测是一个大难题。

译者 | 张哲刚

审校 | 重楼

自动驾驶汽车是不能犯错误的,忽视一个红绿灯或一个行人都可能意味着灾难。但城市环境是动态的,在这样的环境中目标检测是一个大难题。

我使用空洞空间卷积池化金字塔(ASPP)和迁移学习来优化自动驾驶汽车的目标检测,结果如何呢?结果是这个模型能够在多个尺度下很好地检测到目标,即使在光线不太好的情形下,实时运行的效果也非常好。

下面叙述一下我的实施过程。

面临问题:户外目标检测

自动驾驶汽车依靠卷积神经网络(CNNs)来检测目标物体,但现实世界中有很多干扰因素,例如:

  • 交通灯大小比例是变化的——距离远时较小,距离近时较大。
  • 车道标记会随着角度而变形。
  • 会有遮挡的情形——可能会看不到停放的汽车后面的行人
  • 照明条件的差异——可能会有阴影、眩光或夜间驾驶情形。

传统的卷积神经网络(CNNs)难以进行多尺度目标检测如果开始训练需要很长时间。这时候空洞空间卷积池化金字塔(ASPP)和迁移学习就有了用武之地。

ASPP:以不同比例来检测捕获目标

CNNs适用于大小固定目标,但现实世界中目标物体的大小和距离大都是各不相同 空洞空间卷积池化金字塔(ASPP)通过使用膨胀卷来检测和捕获目标多个尺度的特征从而解决这个问题。

ASPP 的工作原理

ASPP使用多个具有不同膨胀率的卷积滤波器来提取不同分辨率的特征涵盖了型目标大型目标以及介于两者之间的所有目标物体

下面讲讲我是如何在PyTorch中实现ASPP的,归一化和注意力机制相结合,在复杂的应用环境中也能够表现出强大的性能:

import torch 
import torch.nn as nn 
import torch.nn.functional as F 

class ASPP(nn.Module): 
""" 
A more advanced ASPP with optional attention and group normalization. 
""" 
def__init__(self,in_channels,out_channels,dilation_rates=(6,12,18), groups=8): 
super(ASPP,self).__init__() self.aspp_branches = nn.ModuleList() 

#1x1 Conv branch 
self.aspp_branches.append( 
nn.Sequential( 
nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=1, padding=0, bias=False), 
nn.GroupNorm(groups, out_channels), nn.ReLU(inplace=True) ) 
)
) 
For rate in dilation_rates: 
self.aspp_branches.append(
 nn.Sequential( 
nn.Conv2d(in_channels,out_channels,kernel_size=3,stride=1, padding=rate,dilatinotallow=rate,bias=False), nn.GroupNorm(groups,out_channels), nn.ReLU(inplace=True) 
) 
) 

#Global average pooling branch 
self.global_pool = nn.AdaptiveAvgPool2d((1, 1)) 
self.global_conv = nn.Sequential(
  nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=1, bias=False),
  nn.GroupNorm(groups, out_channels), 
nn.ReLU(inplace=True) 
) 

#Attention mechanism to refine the concatenated features self.attention = nn.Sequential( 
nn.Conv2d(out_channels*(len(dilation_rates)+2),
out_channels, kernel_size =1, bias=False), 
nn.Sigmoid() 
)

self.project=nn.Sequential( 
nn.Conv2d(out_channels*(len(dilation_rates)+2), out_channels, kernel_size=1, bias=False), 
nn.GroupNorm(groups, out_channels), 
nn.ReLU(inplace=True) 
) 
def  forward(self, x):
cat_feats = [ ] 
for branch in self.aspp_branches: 
cat_feats.append(branch(x)) 
g_feat = self.global_pool(x) 
g_feat = self.global_conv(g_feat) 
g_feat = F.interpolate(g_feat,size=x.shape[2:],
mode='bilinear', align_corners=False) 
cat_feats.append(g_feat) 

#Concatenate along channels
 x_cat = torch.cat(cat_feats, dim=1)
#channel-wise attention 
att_map = self.attention(x_cat) 
x_cat = x_cat * att_map 
out = self.project(x_cat)
 return out
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.

实现原理:

  • 不同的感受野可以使模型一次性检测到小型目标(例如远处的红绿灯)和大型目标(例如公共汽车)。
  • 全局平均池化分支衍生的全局上下文有助于消除对目标的误判断。
  • 轻量级注意力着重于信息量最大的通道,从而提高复杂纷乱场景下的检测准确性。

成果:

  • 不同规格尺度的目标均可以检测得到(不再漏掉较小的红绿灯)。
  • 平均精确度(mAP)提高14%
  • 更好地处理了遮挡问题,部分隐藏的目标也能够检测到

迁移学习:站在巨人的肩膀

当预先训练的模型已经存在时,从零开始训练一个目标检测模型并不是一个理想选择这时候,我们可以利用迁移学习来微调一个已经理解目标的模型。

我使用了 DETR(Detection Transformer),这是Facebook AI基于Transformer的对象检测模型能够学习上下文,比如它不仅可以识别到一个停车标志,还能理解这是道路场景组成的一部分。

下面是我在自动驾驶数据集上微调DETR的操作

import torch 
import torch.nn as nn 
from transformers import DetrConfig, DetrForObjectDetection 

class CustomBackbone(nn.Module): 
def __init__(self,in_channels=3,hidden_dim=256): super(CustomBackbone, self).__init__() 
# Example: basic conv layers + ASPP 
self.initial_conv= nn.Sequential( 
nn.Conv2d(in_channels, 64, kernel_sizestride=2, padding=3,bias=False), 
nn.BatchNorm2d(64), 
nn.ReLU(inplace=True), 
nn.MaxPool2d(kernel_size=3,stride=2, padding=1) 
) 
self.aspp=ASPP(in_channels=64,out_channels=hidden_dim) 

def forward(self, x): 
x = self.initial_conv(x) 
x = self.aspp(x) 
return x 

Class DETRWithASPP(nn.Module): 
def __init__(self, num_classes=91): 
super(DETRWithASPP, self).__init__() 
self.backbone = CustomBackbone()

config=DetrConfig.from_pretrained("facebook/detr-resnet-50") 
config.num_labels = num_classes 
self.detr=DetrForObjectDetection.from_pretrained("facebook/detr-resnet-50",config=config) 

self.detr.model.backbone.body = nn.Identity() 
def forward(self, images, pixel_masks=None): 
features = self.backbone(images) 

feature_dict = { 
"0": features 
} 
outputs=self.detr.model(inputs_embeds=None, pixel_values=None,pixel_mask=pixel_masks,  
features=feature_dict,output_attentions=False) return outputs

model = DETRWithASPP(num_classes=10) 
images = torch.randn(2, 3, 512, 512) 
outputs = model(images)
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.

成果

  • 训练时间缩短了80%。
  • 改善了夜间和大雾天气时的实际性能。
  • 训练需要相对较少的标记数据。

使用合成图像来增强数据

自动驾驶汽车需要海量数据集,但现实世界的标记数据却很有限那怎么办呢?解决方法使用生成对抗网络GAN生成合成数据

我使用GAN创建了虽是虚拟非常逼真的车道标记和交通场景,以扩展数据集。

下面是一个简单的GAN用于生成车道标记:

import torch 
import torch.nn as nn 
Import torch.nn.functional as F 

class LaneMarkingGenerator(nn.Module): 
""" 
A DCGAN-style generator designed for producing synthetic lane or road-like images. 
Input is a latent vector (noise), and the output is a (1 x 64 x 64) grayscale image. You can adjust channels, resolution, and layers to match your target data. 
""" 
def __init__(self, z_dim=100, feature_maps=64): 
super(LaneMarkingGenerator, self).__init__()
self.net = nn.Sequential( 
#Z latent vector of shape (z_dim, 1, 1)
nn.utils.spectral_norm(nn.ConvTranspose2d(z_dim, feature_maps * 8, 4, 1, 0, bias=False)),
 nn.BatchNorm2d(feature_maps * 8), 
nn.ReLU(True), 

#(feature_maps * 8) x 4 x 4
nn.utils.spectral_norm(nn.ConvTranspose2d(feature_maps * 8, feature_maps * 4, 4, 2, 1, bias=False)), nn.BatchNorm2d(feature_maps * 4), 
nn.ReLU(True),

#(feature_maps * 4) x 8 x 8
nn.utils.spectral_norm(nn.ConvTranspose2d(feature_maps * 4, feature_maps * 2, 4, 2, 1, bias=False)), 
nn.BatchNorm2d(feature_maps * 2),
nn.ReLU(True), 
#(feature_maps * 2) x 16 x 16
nn.utils.spectral_norm(nn.ConvTranspose2d(feature_maps * 2, feature_maps, 4, 2, 1, bias=False)),
nn.BatchNorm2d(feature_maps),
 nn.ReLU(True), 

#(feature_maps) x 32 x 32
nn.utils.spectral_norm(nn.ConvTranspose2d(feature_maps, 1, 4, 2, 1, bias=False)), nn.Tanh()
) 

def forward(self, z): 
return self.net(z)


class LaneMarkingDiscriminator(nn.Module):
 """ 
A DCGAN-style discriminator. It takes a (1 x 64 x 64) image and attempts to classify whether it's real or generated (fake). 
""" 
def __init__(self, feature_maps=64):
super(LaneMarkingDiscriminator, self).__init__()
self.net = nn.Sequential(
 #1x 64 x 64 
nn.utils.spectral_norm(nn.Conv2d(1, feature_maps, 4, 2, 1, bias=False)), nn.LeakyReLU(0.2, inplace=True),

 #(feature_maps) x 32 x 32
 nn.utils.spectral_norm(nn.Conv2d(feature_maps,
feature_maps * 2, 4, 2, 1, bias=False)),
 nn.BatchNorm2d(feature_maps * 2),
 nn.LeakyReLU(0.2, inplace=True), 

#(feature_maps * 2) x 16 x 16
 nn.utils.spectral_norm(nn.Conv2d(feature_maps * 2, feature_maps * 4, 4, 2, 1, bias=False)),
 nn.BatchNorm2d(feature_maps * 4), 
nn.LeakyReLU(0.2, inplace=True),


#(feature_maps * 4) x 8 x 8
  nn.utils.spectral_norm(nn.Conv2d(feature_maps * 4, feature_maps * 8, 4, 2, 1, bias=False)),
nn.BatchNorm2d(feature_maps * 8),
nn.LeakyReLU(0.2, inplace=True), 

#(feature_maps * 8) x 4 x 4
nn.utils.spectral_norm(nn.Conv2d(feature_maps * 8, 1, 4, 1, 0, bias=False)), 

) 

def forward(self, x): 
return self.net(x).view(-1)
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.

成果:

  • 不需要手动标记数据集增加了5倍
  • 经过训练的模型对边缘场景的处理更加稳健。
  • 数据集偏差得以减少(训练样本更加多样化)。

最终成果目标检测得以智能、更快速

通过结合 ASPP、迁移学习和合成数据,我为自动驾驶汽车构建了一个更精确而又可扩展的目标检测系统。最终主要成果如下

  • 目标检测速度:110 毫秒/帧
  • 小目标检测(红绿灯):+14%mAP
  • 遮挡处理:更强大的遮挡物检测功能
  • 训练时间缩短至6小时
  • 所需训练数据:50%可以由GANs合成

下一步如何让它变得更出色

  • 添加实时跟踪功能,随时跟踪检测到的目标
  • 使用更先进的Transformers(如OWL-ViT)进行零样本目标检测。
  • 进一步优化推理速度以便更好地在嵌入式硬件上部署。

结论

ASPP、Transformers和数据合并这三项组合算得上是自主目标检测的三面手它们能够把以往那些反应迟钝、容易出现盲点的模型进化为快速敏锐的系统,从而可以一个街区就能观测到红绿灯。通过膨胀卷来实现多尺度目标检测,利用迁移学习行快速微调,还能够使用GAN生成的数据来填补每一个空白。这样,我们能够将推理时间缩短接近一半,并节省大量的训练时间。这是一个巨大的飞跃,使得自动驾驶汽车可以像我们人类一样观察这个世界,并且更快、更精确。哪怕是在最混乱无序的街道上,有朝一日也定能够信心十足地飞驰。

译者介绍

张哲刚,51CTO社区编辑,系统运维工程师,国内较早一批硬件评测及互联网从业者,曾入职阿里巴巴。

原文标题:How I Made Object Detection Smarter for Self-Driving Cars With Transfer Learning & ASPP,作者:Vineeth Reddy Vatti

责任编辑:华轩 来源: 51CTO
相关推荐

2023-07-19 08:46:00

导航地图

2022-07-12 09:42:10

自动驾驶技术

2023-08-10 09:49:57

自动驾驶视觉

2022-12-30 09:57:54

自动驾驶应用

2023-02-28 09:21:58

智能骑车

2023-05-24 10:07:15

智能汽车

2022-08-29 10:16:00

自动驾驶边缘计算

2017-10-02 16:13:47

深度学习目标检测计算机视觉

2022-01-18 10:51:09

自动驾驶数据人工智能

2022-08-08 13:12:04

自动驾驶决策

2021-11-05 12:15:18

自动驾驶数据测试

2023-06-16 09:55:29

2021-11-18 22:43:56

自动驾驶技术安全

2022-03-23 10:34:37

自动驾驶技术控制

2017-07-21 10:42:27

自动驾驶应用机器学习

2023-04-07 13:05:39

自动驾驶雷达

2022-11-21 11:50:59

2020-11-06 10:36:39

自动驾驶

2019-09-19 14:10:12

人工智能物联网自动驾驶

2023-07-18 15:57:23

自动驾驶
点赞
收藏

51CTO技术栈公众号