ModuleNotFoundError: No module named ’ ’
export PYTHONPATH="$PWD" && python3 models/export_onnx.py --weights ./weights/ --img 640 --batch 1
·AlexNet应该算第一个深度CNN;
·ZFNet采用DeconvNet和visualization技术可以监控学习过程;
·VGGNet采用小滤波器3X3去取代大滤波器5X5和7X7而降低计算复杂度;
·GoogleNet推广NIN的思路定义Inception基本模块(采用多尺度变换和不同大小滤波器组合,即1X1,3X3,5X5)构建模型;
·Highway Networks借鉴了RNN里面LSTM的gaiting单元;
·ResNet是革命性的工作,借鉴了Highway Networks的skip connection想法,可以训练大深度的模型提升性能,计算复杂度变小;
·Inception-V3/4用1X7和1X5取代大滤波器5X5和7X7,1X1滤波器做之前的特征瓶颈,这样卷积操作变成像跨通道(cross channel)的相关操作;
·DenseNet主要通过跨层链接解决vanishing gradient问题;
·SE-Net是针对特征选择的设计,gating机制还是被采用;
·前段时间流行的Attention机制也是借鉴于LSTM,实现object-aware的context模型。
·detection问题的R-CNN,fast RCNN,faster RCNN,SSD,YOLO,RetinaNet,CornerNet等,
·解决segmentation问题的FCN,DeepLab,Parsenet,Segnet,Mask R-CNN,RefineNet,PSPNet,U-Net等,
·处理激光雷达点云数据的VoxelNet,PointNet,BirdNet,LMNet,RT3D,PIXOR,YOLO3D等,
·实现激光雷达和图像融合的PointFusion,RoarNet,PointRCNN,AVOD等,
·做图像处理的DeHazeNet,SRCNN (super-resolution),DeepContour,DeepEdge等,
·2.5 D视觉的MatchNet,DeepFlow,FlowNet等,
·3-D重建的PoseNet,VINet,Perspective Transformer Net,SfMNet,CNN-SLAM,SurfaceNet,3D-R2N2,MVSNet等,
·以及解决模型压缩精简的MobileNet,ShuffleNet,EffNet,SqueezeNet
作者提出了一个带分支结构的多任务网络结构,包含一个二值分割分支(lane segmentation)和一个实例分割分支(lane embedding),从而实现端到端、任意数量的车道线检测。具体来说,二值分割分支输出所有的车道线像素,而实例分割分支将输出的车道线像素分配到不同的车道线实例中。
论文中将实例分割任务拆解成语义分割(LanNet 一个分支)和聚类(LanNet一个分支提取 embedding express,
Mean-Shift 聚类)两部分。如上图所示,LanNet 有两个分支任务,分别为 a lane segmentation branch
and a lane embedding branch。Segmentation branch
负责对输入图像进行语义分割(对像素进行二分类,判断像素属于车道线还是背景);Embedding branch
对像素进行嵌入式表示,训练得到的 embedding 向量用于聚类。最后将两个分支的结果进行结合利用 Mean-Shift
算法进行聚类,得到实例分割的结果。
二值分割网络
Lanenet的一个分支网络为二值分割网络(下面那个),该网络将车道线像素与背景区分开。损失函数使用的是标准的交叉熵损失函数,由于目标类别是2类(车道/背景),并且高度不平衡,因此参考了《ENet: A Deep Neural Network Architecture for Real-Time Semantic Segmentation》,使用了bounded inverse class weighting。
实例分割网络
Lanenet的另一个分支网络为实例分割网络(上面那个)。该分支网络参考了《Semantic Instance Segmentation with a Discriminative Loss Function》,使用基于one-shot的方法做距离度量学习,将该方法集成在标准的前馈神经网络中,可用于实时处理。该分支网络训练后输出一个车道线像素点距离,基于归属同一车道的像素点距离近,不同车道线像素点距离远的基本思想,利用聚类损失函数聚类得到各条车道线。具体实现的原理如下。
1、语义分割中主要考虑的两个问题:
语义分割:负责对图像进行二分类,主要判断像素属于车道线还是背景
1、在构建label时,为了处理遮挡问题,论文中对被车辆遮挡的车道线和虚线进行了还原
2、Loss使用交叉熵,解决图像中车道线像素和背景像素不均衡问题,参考论文ENet
2、聚类(embedding branch):
为方便对图像像素进行聚类,实例分割loss中设置δ_d > 6*δ_v。
对像素点进行嵌入式表达,训练得到的embedding向量用来聚类
在进行聚类时,首先使用mean shift聚类,使得簇中心沿着密度上升的方向移动,防止将离群点选入相同的簇中;之后对像素向量进行划分:以簇中心为圆心,以2δ_v为半径,选取圆中所有的像素归为同一车道线。重复该步骤,直到将所有的车道线像素分配给对应的车道。
像素embedding时,embedding_branch为每个像素初始化一个embedding向量,并在设计loss时,使属于同一车道线的像素向量距离尽可能的小,属于不同车道线的像素向量距离尽可能大,从而区分车道线上的像素属于哪条车道。
这部分,根据车道线向量距离不同从而区别车道的算法,主要是基于Loss函数的,计算方差和距离
如公式所示,L_var的主要作用就是把属于同一条车道线的像素点往一起推,如果像素点和中心点距离超过一定阈值,就会产生loss;
L_dist作用是把不同类中心点往距离加大的方向拉,如果中心点之间的距离小于一定阈值,就会产生loss。使用L_var+L_dist作为模型的损失函数,进行迭代,迭代终止条件为δd > 6δv
网络结构
Lanenet中的网络结构使用encoder-decoder的ENet,该网络共有三个阶段,这两个分支共享前两个阶段;但是第三个阶段的encoder和和最后的decoder部分则相互独立,这样可以节省一定的计算开销同时取得较好的效果。二指分割网络分支将会输出单通道图像,实例分割网络分支输出N通道图像,每个分支的权重相等。ENet的网络结构图如下:
论文提出了Spatial CNN,这里的Spatial不是指Spatial Convolution,而是CNN通过特征的设计架构传递空间信息。SCNN更有效的学习空间关系,能平滑的找出连续的有强先验的结构目标。
本篇论文提出了一种新的神经网络结构用于提取车道线,相比于传统网络在各个层之间直接进行卷积的方法不同的是,该网络按照一定方向(上、下、左、右)按照顺序进行卷积,作者认为这样更符合真实世界中物体结构的延伸,尤其是车道线这种持续延伸的目标。
以 SCNN_D 为例,假定经过一系列卷积操作后, feature map size 为 C x H x W, 从 H维度出发,将feature map 切成 H slices,然后第一个 slice 经过一个 1 x w x C的卷积、非线性操作后加到下一个 slice 上(一般这个 w 是个较大尺度的卷积核,作者实验证明 w=9
结果最理想)。这个操作继续往下进行,直到最后一个 slice 也被更新了。
图中的标记符 D, U, R, L 分别表示 SCNN that
is downward, upward, rightward, and leftward 四个方向进行上述操作。
而是把这四个顺序卷积的部分嵌入到一个已有的网络模型中去,作者选用的网络是LargeFOV。新的网络如下图所示,
这里(a)是训练的网络,
(b)是用网络进行预测的流程,需要注意的是,(b)中右侧输出了四个数值,这四个数分别对应四条车道线的概率,0.99就是有车道线,0.02就是没有车道线,所以图中就显示了三条车道线,至于为啥是四条,这就属于作者设置的一个先验信息了,如果超过四条,最终也只会输出四条。当然,他既然这么做,就说明至少这个数据集里没有超过四条的
对于存在值大于0.5的车道标记,在对应的概率图每20行搜索以获得最高的响应位置,然后通过三次样条函数连接这些点(cubic
splines)。这就是最终的预测。
上图(a)显示了baseline和LargeFOV之间的差异:
fc7输出通道为128 fc6扩张卷积的扩张率为4 每个ReLU层前加了BN层 添加了一个小型网络用于预测是否存在车道线
训练时,输入和输出的图片分辨率设置为800×288
SCNN与传统的Dense MRF/CRF相比,在信息传递方向不同,示意图如下所示:
图(a):MRF/CRF中每个像素点会直接接收其他所有像素点的信息(大卷积核实现),这其中有许多冗余计算。
图(b):在SCNN中,信息是顺序传递的。
为了判断车道标记是否正确的检测到,论文将车道标记视为宽度为30像素的线,计算ground truth和预测值之间的IoU.如果预测的IoU大于某个阈值,则认为是true positives (TP). 如下图6所示,这里设置了0.3和0.5作为阈值,分别对应松散和严格的评估。
提出基于知识蒸馏(Knowledge Distillation)的车道线检测模型SAD,能增强CNN的特征表达能力。由于SAD只参与模型训练,因此不会增加推断时的计算复杂度。
深入调查了SAD的内在机制,尝试把SAD加入多种轻量级车道线检测模型,ENet-SAD可以在多个数据集上取得当时的SOTA效果
知识蒸馏:transfer knowledge from large networks(teacher network) to small
networks(student network)。简单来说,就是用teacher的中间输出去指导student训练
发展到当前,很多研究不再使用teacher
network去干预网络的学习过程,而是使用网络自身的输出(activation-based/gradient-based,类似显著性检测中的热图)去做“自蒸馏”
在训练一半的模型中加入SAD,the preceding block to mimic the attention maps of a deeper block.
加入SVD后,一方面浅层block的attention map被修正,提取到更丰富的上下文信息;另一方面浅层学习到更好的特征反作用于深层。
道路检测问题定义
本文对路线检测的定义是有所不同的,定义为,“基于全局图片特征的row-based selection”.或者说在每一个预设定的行上选择行单元。
方法:
将pixel分割转换为grid分类的问题。对HW的图像划分成h(w+1)的网格,分割时需要处理HW个点的进行分类,类别数目(C+1);现在只需要处理hC个点的分类,类别数目(w+1)。
之前方法:
1: 速度慢
因为分割是逐像素分类的,要对图像中每一个像素点进行分类。为了分割车道线要进行非常密集的计算,导致的结果就是速度比较慢。其实车道线像素其实只占图像很少一部分,想想也不需要进行这么舍本逐末的操作。
:2: 局部感受野
分割的另一个问题是感受野问题。因为分割一般是全卷积得到分割结果,而卷积基本上是比较局部的,所以每个像素的感受野有限。在其他分割问题中可能问题不大,但在车道线检测中,问题就很大了。由于我们关注的问题大多是上图这种语义线的检测,需要对全局有很好的感知才能实现良好的定位。比如在图1中,对于车道线的定位只有靠周围车流走向这种全局信息才能很好地定位。
虽然有些很好的工作,比如SCNN使用不同方向上的特征传播实现信息传递,间接完成了增大感受野,增加全局信息的目标,但是速度更慢了。
因此:
其实直观感受来说,做车道线检测没必要这么大动干戈把每个像素都进行分类。基于此,我们的方法遵循着一个非常简单的动机:有没有更简单的方式建模车道线检测?
为了更简单地建模车道线,也为了解决上述两个分割方法存在的问题,我们提出了一个全新的车道线检测定义:将车道线检测定义为寻找车道线在图像中某些行的位置的集合,即基于行方向上的位置选择、分类(row-based classification):
如果np.argmax求取的可能车道线为W+1位置,则过滤掉,判断有无车道线的条件
假设我们要检测一条车道线的图像大小为HxW,对于分割问题,我们需要处理HxW个分类问题。
由于我们的方案是行向选择,假设我们在h个行上做选择,我们只需要处理h个行上的分类问题,只不过每行上的分类问题是W维的。因此这样就把原来HxW个分类问题简化为了只需要h个分类问题,而且由于在哪些行上进行定位是可以人为设定的,因此h的大小可以按需设置,但一般h都是远小于图像高度H的。
这样,我们就把分类数目从HxW直接缩减到了h,并且h远小于H,更不用说h远小于HxW了。因此我们的方法将计算复杂度缩减到了一个极小的范围内,解决了分割速度慢的问题,极大地提速的了车道线检测算法的速度,这也是我们方法能够达到300+FPS的原因。下图展示了我们方法和基于分割的车道线检测方法的比较。
解决感受野小的问题:
局部感受野小导致的复杂车道线检测困难问题。由于我们的方法不是分割的全卷积形式,是一般的基于全连接层的分类,它所使用的特征是全局特征。这样就直接解决了感受野的问题,对于我们的方法,在检测某一行的车道线位置时,感受野就是全图大小。因此也不需要复杂的信息传递机制就可以实现很好的效果
解决没有可视化车道线:
no-visual-clue
在很多道路上, 由于车辆拥堵, 车道线被车挡住了, 需要通过车的位置和环境的语义信息去猜测。 在这种情况下, 没有视觉信息(车道的颜色, 形状)去引导车道线的识别, 造成了很大的困难。 下面这张图就是很好的例子。
为了解决以上两个问题, 作者提出了基于row achor的网络, 让网络在不同的行中选择属于车道线的列, 减少了传统语义分割pixel level prediction的复杂度, 同时使用global feature来增加网络的感受野, 提升在有车辆遮挡关系下的网络推理能力。
论文的贡献有以下几点:
1:提出了很有效的办法来解决fast speed和no visual cue的问题 Global Feature, Row Anchor
2:提出了结构化损失函数structural loss来对道路的形状进行约束 Structural Loss
3:在CULane数据集上取得了SOTA, 可达到300fps的识别速度。
蓝线上的网络做语义分割, 得到segmentation loss,
(only vaild during training)
下面的网络得到structural loss和classication loss
全连接层作用:
从上图我们可以看出,我们用一个3x3x5的filter 去卷积激活函数的输出,得到的结果就是一个fully connected layer 的一个神经元的输出,这个输出就是一个值。因为我们有4096个神经元。我们实际就是用一个3x3x5x4096的卷积层去卷积激活函数的输出。
以VGG-16再举个例子吧,
对224x224x3的输入,最后一层卷积可得输出为7x7x512,如后层是一层含4096个神经元的FC,则可用卷积核为7x7x512x4096的全局卷积来实现这一全连接运算过程。
它把特征representation整合到一起,输出为一个值。
如下 测试网络
// 测试阶段,网络输入输出如下:
static const int INPUT_C = 3;
static const int INPUT_H = 288;
static const int INPUT_W = 800;
static const int OUTPUT_C = 101;
static const int OUTPUT_H = 56;
static const int OUTPUT_W = 4;
row anchor * cell size = 56(tusimple) * 101(tusimple) * 4 = 22624 (全连接层维度)
self.cls = torch.nn.Sequential(
torch.nn.Linear(1800, 2048),
torch.nn.ReLU(),
torch.nn.Linear(2048, self.total_dim),
)
def forward(self, x):
# n c h w - > n 2048 sh sw
# -> n 2048
//res block x2/x3/ fea为最深层的输出特征层
x2,x3,fea = self.model(x)
// only vaild during training
if self.use_aux:
x2 = self.aux_header2(x2)
x3 = self.aux_header3(x3)
x3 = torch.nn.functional.interpolate(x3,scale_factor = 2,mode='bilinear')
x4 = self.aux_header4(fea)
x4 = torch.nn.functional.interpolate(x4,scale_factor = 4,mode='bilinear')
aux_seg = torch.cat([x2,x3,x4],dim=1)
aux_seg = self.aux_combine(aux_seg)
else:
aux_seg = None
fea = self.pool(fea).view(-1, 1800)
group_cls = self.cls(fea).view(-1, *self.cls_dim)
if self.use_aux:
return group_cls, aux_seg
return group_cls
预测输出,行为anchor_row tusimple = 56
列则为cell划分的多少 size = 100 + 1
实验中 :取100,最恰当
输入size(800,288)
ppp = (int(out_j[k, i] * col_sample_w * img_w / 800) - 1, int(img_h * (row_anchor[cls_num_per_lane-1-k]/288)) - 1 )
cv2.circle(vis,ppp,5,(0,255,0),-1)
准确度定义:
车道线检测是寻找语义上的线,而不是局限于表观存在的线
行上的水平选择:
分割方法是逐像素上channel维度的选择:
↓
Xavier上配置情况:
https://blog.csdn.net/cxhmike/article/details/108589401
tensorrt7.0.0,11
fp16
1050ti : 21ms
940mx: 46ms
比论文基于GPU没有加速的快很多;
…
…
…
网络架构如图所示:
其中使用了知识蒸馏技术,
只是没有用额外的网络,辅助的loss训练 instance效果。
其中辅助的loss,蒸馏loss来自
feature = self.same2(outputs, True) # 42 -> 42
下采样的环节
class hourglass_same(nn.Module):
def __init__(self, in_channels, out_channels):
super(hourglass_same, self).__init__()
self.down1 = bottleneck_down(in_channels, out_channels)
self.down2 = bottleneck_down(out_channels, out_channels)
self.down3 = bottleneck_down(out_channels, out_channels)
self.down4 = bottleneck_down(out_channels, out_channels)
self.same1 = bottleneck_dilation(out_channels, out_channels)
self.same2 = bottleneck_dilation(out_channels, out_channels)
self.same3 = bottleneck_dilation(out_channels, out_channels)
self.same4 = bottleneck_dilation(out_channels, out_channels)
self.up1 = bottleneck_up(out_channels, out_channels)
self.up2 = bottleneck_up(out_channels, out_channels)
self.up3 = bottleneck_up(out_channels, out_channels)
self.up4 = bottleneck_up(out_channels, out_channels)
self.residual1 = bottleneck_down(out_channels, out_channels)
self.residual2 = bottleneck_down(out_channels, out_channels)
self.residual3 = bottleneck_down(out_channels, out_channels)
self.residual4 = bottleneck_down(in_channels, out_channels)
#self.residual = nn.MaxPool2d(2, 2)
self.bn = nn.BatchNorm2d(out_channels)
self.bn1 = nn.BatchNorm2d(out_channels)
self.bn2 = nn.BatchNorm2d(out_channels)
self.bn3 = nn.BatchNorm2d(out_channels)
self.bn4 = nn.BatchNorm2d(out_channels)
self.prelu = nn.PReLU()
def forward(self, inputs):
outputs1 = self.down1(inputs) # 64*32 -> 32*16
outputs2 = self.down2(outputs1) # 32*16 -> 16*8
outputs3 = self.down3(outputs2) # 16*8 -> 8*4
outputs4 = self.down4(outputs3) # 8*4 -> 4*2
outputs = self.same1(outputs4) # 4*2 -> 4*2
feature = self.same2(outputs, True) # 4*2 -> 4*2
outputs = self.same3(self.prelu(self.bn(feature))) # 4*2 -> 4*2
outputs = self.same4(outputs, True) # 4*2 -> 4*2
outputs = self.up1( self.prelu(self.bn1(outputs + self.residual1(outputs3, True))) )
outputs = self.up2( self.prelu(self.bn2(outputs + self.residual2(outputs2, True))) )
outputs = self.up3( self.prelu(self.bn3(outputs + self.residual3(outputs1, True))) )
outputs = self.up4( self.prelu(self.bn4(outputs + self.residual4(inputs, True))) )
#outputs = self.up3( self.prelu(self.bn3(outputs)) )
#outputs = self.up4( self.prelu(self.bn4(outputs)) )
#outputs = self.prelu(outputs)
return outputs, feature
关键点检测的网络Lane Instance Point Network:
Confidence Loss:
判定是否存在车道线关键点 1 × 1 × 32 × 64
Offset Loss:
判断预测的X和Y的坐标偏移 1 × 2 × 32 × 64
Embedding Feature Loss:
车道线实例分割预测 1 × 4 × 32 × 64
Tusimple :
CuLane:
PINet Test
Test
Object
Pytorch implement of PINet Tensorrt C++ implement of PINet Purpose
Tensorrt performance under X86 architecture Tensorrt performance under
Xavier Dataset data source:tusimple dataset 0531 directoryimage format:jpg
image size:1280 x 720
image channels:RGB
count of images:14300
disk space size: 3GB
X86 Computer
OS:ubuntu 18.04
CPU:AMD Ryzen 7 3700X 8-Core Processor
CPU Frequency: 3600 mhz
ram:32GB 3200mhz
video card:Nvidia Titan
vram: 6G
disk:Seagate cool fish 7200rpm
Xavier OS:ubuntu 18.04
CPU:ARMv8 Processor rev 0 (v8l)
CPU Frequency: 2036 mhz
ram:16GB
Explain
end to end:
elapsed time of read image, inference,post processing,draw lane line result to image
execute:
elapsed time of copy host ram to device vram,inference exectute, copy device vram to host ram
totally end to end :
elapsed time of dataset test, sum of end to end
totally execute:
elapsed time of dataset test, sum of execute
end to end = totally end to end / count of image in dataset
execute = totally execute / count of image in dataset
PINet Runtime Result
1:elapsed time of inference execute under x86 architecture, Tensorrt C++ implement faster 1.5 times than Pytorch implement
2: elapsed time of end to end under x86 architecture, Tensorrt C++ implement faster 10 times than Pytorch implement
3:elapsed time of inference execute under Xavier, x86 architecture faster 2 times,takes 20 ms on average
还存在后处理的时间,除去离群的点
https://mp.weixin.qq.com/s/BJSzqaEOSA5hkFUPrQdvag
在行车记录仪上测试结果如下:
有不错的鲁棒性:
网络结构形似沙漏状,重复使用top-down到bottom-up来推断关键点位置。每一个top-down到bottom-up的结构都是一个stacked hourglass模块。
https://blog.csdn.net/u013841196/article/details/81048237
每次降采样之前,分出上半路保留原尺度信息;
每次升采样之后,和上一个尺度的数据相加;
两次降采样之间,使用三个Residual模块提取特征;
两次相加之间,使用一个Residual模块提取特征。
由于考虑了各个尺度的特征,本文不需要像CPM3方法一样独立地在图像金字塔上多次运行,速度更快。
作用:n阶Hourglass子网络提取了从原始尺度到尺度1/2^n的特征。不改变数据尺寸,只改变数据深度。
如四阶Hourglass:
文章的创新点在于decoder的方式,不同于U-Net在上采样后直接和encoder的feature map进行级联,本文通过RefineNet进行上采样,把encoder产生的feature和上一阶段decoder的输出同时作为输入,在RefineNet中进行一系列卷积,融合,池化,使得多尺度特征的融合更加深入。
另一创新点就是RefineNet模块中的链式残余池化,为了验证这个模块的效果,作者做了对比实验,证明在加入该模块后,分割效果确实变好了。
Abstract
为了解决下采样过程中导致的信息损失,论文提出了RefineNet,通过利用下采样过程中能够获取到的所有信息。
网络组建使用了恒等映射的残余连接。
另外,论文还介绍了链式残余池化(chained residual pooling ),可以高效的获取背景信息。
在公共数据集(VOC 2012)中做了实验,实现了最佳效果
Introduction
反卷积不能恢复low-level的feature,deeplab使用空洞卷积来解决这一问题。
卷积有两个问题:
对高像素feature map的卷积会消耗算力,同时也需要大的GPU内存,通常对于高像素的图都会resize
空洞卷积的特性决定了它会损失一些细节信息。
FCN等方法提出的特征融合虽然能够保留低维和高维信息,但缺少了空间信息?
本文的贡献:提出了RefineNet,它是一种多路径的提炼网络,利用多级抽象特征进行高分辨率的语义分割,通过递归方式提炼低分辨率的特征,生成高分辨率的特征
级联的refineNet可以end-to-end训练,使用了恒等映射的残余连接
提出了链式残余池化。使用不同尺寸的窗口池化,并且使用残余连接和可学习的权重把他们融合起来
论文:Single-Shot Refinement Neural Network for Object Detection
论文链接:https://arxiv.org/abs/1711.06897
代码链接:https://github.com/sfzhang15/RefineDet
第一个是anchor refinement module
1)过滤掉部分负样本候选框,减小分类器的搜索范围。
2)粗调整候选框的位置和大小
第二个是object detection module
1)对候选框进行分类和精调
两个模块中间有一个连接模块transfer connection block
RefineDet 是一种新颖的基于单镜头的对象检测算法。它与 R-CNN 和 R-FCN 等两阶段方法相比精度更高,但保留了 SSD 和 YOLOv3 等一阶段方法的效率优势。
检测小型对象时有更高精度,18年底,检测红绿灯小目标更好。
NanoDet 是一种 FCOS 式的单阶段 anchor-free 目标检测模型,它使用 ATSS 进行目标采样,使用 Generalized Focal Loss 损失函数执行分类和边框回归(box regression)
NanoDet 是一个速度超快和轻量级的移动端 Anchor-free 目标检测模型。该模型具备以下优势:
超轻量级:模型文件大小仅几兆(小于4M——nanodet_m.pth);
速度超快:在移动 ARM CPU 上的速度达到 97fps(10.23ms);
训练友好:GPU 内存成本比其他模型低得多。GTX1060 6G 上的 Batch-size 为 80 即可运行;
方便部署:提供了基于 ncnn 推理框架的 C++ 实现和 Android demo。