譯者 | 張哲剛
審校 | 重樓
自動(dòng)駕駛汽車是不能犯錯(cuò)誤的,忽視一個(gè)紅綠燈或一個(gè)行人都可能意味著災(zāi)難。但城市環(huán)境是動(dòng)態(tài)的,在這樣的環(huán)境中目標(biāo)檢測(cè)是一個(gè)大難題。
我使用空洞空間卷積池化金字塔(ASPP)和遷移學(xué)習(xí)來優(yōu)化自動(dòng)駕駛汽車的目標(biāo)檢測(cè),結(jié)果如何呢?結(jié)果是這個(gè)模型能夠在多個(gè)尺度下很好地檢測(cè)到目標(biāo),即使在光線不太好的情形下,實(shí)時(shí)運(yùn)行的效果也非常好。
下面敘述一下我的實(shí)施過程。
面臨問題:戶外目標(biāo)檢測(cè)
自動(dòng)駕駛汽車依靠卷積神經(jīng)網(wǎng)絡(luò)(CNNs)來檢測(cè)目標(biāo)物體,但現(xiàn)實(shí)世界中有很多干擾因素,例如:
- 交通燈大小比例是變化的——距離遠(yuǎn)時(shí)較小,距離近時(shí)較大。
- 車道標(biāo)記會(huì)隨著角度而變形。
- 會(huì)有遮擋的情形——可能會(huì)看不到停放的汽車后面的行人。
- 照明條件的差異——可能會(huì)有陰影、眩光或夜間駕駛情形。
傳統(tǒng)的卷積神經(jīng)網(wǎng)絡(luò)(CNNs)難以進(jìn)行多尺度目標(biāo)檢測(cè),如果從零開始訓(xùn)練則需要很長(zhǎng)時(shí)間。這時(shí)候空洞空間卷積池化金字塔(ASPP)和遷移學(xué)習(xí)就有了用武之地。
ASPP:以不同的比例來檢測(cè)捕獲目標(biāo)
CNNs適用于大小固定的目標(biāo),但現(xiàn)實(shí)世界中目標(biāo)物體的大小和距離大都是各不相同的。 空洞空間卷積池化金字塔(ASPP)通過使用膨脹卷積,來檢測(cè)和捕獲目標(biāo)多個(gè)尺度的特征,從而解決了這個(gè)問題。
ASPP 的工作原理
ASPP使用多個(gè)具有不同膨脹率的卷積濾波器來提取不同分辨率的特征,涵蓋了小型目標(biāo)、大型目標(biāo)以及介于兩者之間的所有目標(biāo)物體。
下面講講我是如何在PyTorch中實(shí)現(xiàn)ASPP的,將組歸一化和注意力機(jī)制相結(jié)合,在復(fù)雜的應(yīng)用環(huán)境中也能夠表現(xiàn)出強(qiáng)大的性能:
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
實(shí)現(xiàn)原理:
- 不同的感受野可以使模型一次性檢測(cè)到小型目標(biāo)(例如遠(yuǎn)處的紅綠燈)和大型目標(biāo)(例如公共汽車)。
- 全局平均池化分支衍生的全局上下文有助于消除對(duì)目標(biāo)的誤判斷。
- 輕量級(jí)注意力著重于信息量最大的通道,從而提高復(fù)雜紛亂場(chǎng)景下的檢測(cè)準(zhǔn)確性。
成果:
- 不同規(guī)格尺度的目標(biāo)均可以檢測(cè)得到(不再漏掉較小的紅綠燈)。
- 平均精確度(mAP)提高14%。
- 更好地處理了遮擋問題,部分隱藏的目標(biāo)也能夠檢測(cè)到。
遷移學(xué)習(xí):站在巨人的肩膀之上
當(dāng)預(yù)先訓(xùn)練的模型已經(jīng)存在時(shí),從零開始訓(xùn)練一個(gè)目標(biāo)檢測(cè)模型并不是一個(gè)理想選擇。這時(shí)候,我們可以利用遷移學(xué)習(xí)來微調(diào)一個(gè)已經(jīng)理解目標(biāo)的模型。
我使用了 DETR(Detection Transformer),這是Facebook AI基于Transformer的對(duì)象檢測(cè)模型。它能夠學(xué)習(xí)上下文,比如,它不僅可以識(shí)別到一個(gè)停車標(biāo)志,還能理解這是道路場(chǎng)景組成的一部分。
下面是我在自動(dòng)駕駛數(shù)據(jù)集上微調(diào)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)
成果:
- 訓(xùn)練時(shí)間縮短了80%。
- 改善了夜間和大霧天氣時(shí)的實(shí)際性能。
- 訓(xùn)練時(shí)需要相對(duì)較少的標(biāo)記數(shù)據(jù)。
使用合成圖像來增強(qiáng)數(shù)據(jù)
自動(dòng)駕駛汽車需要海量的數(shù)據(jù)集,但現(xiàn)實(shí)世界中的標(biāo)記數(shù)據(jù)卻很有限。那怎么辦呢?解決方法是使用生成對(duì)抗網(wǎng)絡(luò)(GAN)生成合成數(shù)據(jù)。
我使用GAN創(chuàng)建了雖是虛擬但非常逼真的車道標(biāo)記和交通場(chǎng)景,以擴(kuò)展數(shù)據(jù)集。
下面是一個(gè)簡(jiǎn)單的GAN,用于生成車道標(biāo)記:
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)
成果:
- 不需要手動(dòng)標(biāo)記,數(shù)據(jù)集增加了5倍 。
- 經(jīng)過訓(xùn)練的模型對(duì)于邊緣場(chǎng)景的處理更加穩(wěn)健。
- 數(shù)據(jù)集偏差得以減少(訓(xùn)練樣本更加多樣化)。
最終成果:目標(biāo)檢測(cè)得以更加智能、更加快速
通過結(jié)合 ASPP、遷移學(xué)習(xí)和合成數(shù)據(jù),我為自動(dòng)駕駛汽車構(gòu)建了一個(gè)更精確而又可擴(kuò)展的目標(biāo)檢測(cè)系統(tǒng)。最終主要成果如下:
- 目標(biāo)檢測(cè)速度:110 毫秒/幀
- 較小目標(biāo)檢測(cè)(紅綠燈):+14%mAP
- 遮擋處理:更強(qiáng)大的遮擋物檢測(cè)功能
- 訓(xùn)練時(shí)間:縮短至6小時(shí)
- 所需訓(xùn)練數(shù)據(jù):50%可以由GANs合成
下一步:如何讓它變得更出色
- 添加實(shí)時(shí)跟蹤功能,隨時(shí)跟蹤檢測(cè)到的目標(biāo)。
- 使用更先進(jìn)的Transformers(如OWL-ViT)進(jìn)行零樣本目標(biāo)檢測(cè)。
- 進(jìn)一步優(yōu)化推理速度,以便更好地在嵌入式硬件上部署。
結(jié)論
ASPP、Transformers和數(shù)據(jù)合并這三項(xiàng)組合算得上是自主目標(biāo)檢測(cè)的三面手,它們能夠把以往那些反應(yīng)遲鈍、容易出現(xiàn)盲點(diǎn)的模型進(jìn)化為快速而敏銳的系統(tǒng),從而可以在一個(gè)街區(qū)之外就能觀測(cè)到紅綠燈。通過采用膨脹卷積來實(shí)現(xiàn)多尺度目標(biāo)檢測(cè),利用遷移學(xué)習(xí)來進(jìn)行快速微調(diào),還能夠使用GAN生成的數(shù)據(jù)來填補(bǔ)每一個(gè)空白。這樣,我們能夠?qū)⑼评頃r(shí)間縮短接近一半,并節(jié)省大量的訓(xùn)練時(shí)間。這是一個(gè)巨大的飛躍,使得自動(dòng)駕駛汽車可以像我們?nèi)祟愐粯佑^察這個(gè)世界,并且更快、更精確。哪怕是在最混亂無序的街道上,有朝一日也定能夠信心十足地飛馳。
譯者介紹
張哲剛,51CTO社區(qū)編輯,系統(tǒng)運(yùn)維工程師,國(guó)內(nèi)較早一批硬件評(píng)測(cè)及互聯(lián)網(wǎng)從業(yè)者,曾入職阿里巴巴。
原文標(biāo)題:How I Made Object Detection Smarter for Self-Driving Cars With Transfer Learning & ASPP,作者:Vineeth Reddy Vatti