排序算法

基本概念

排序算法是程序设计中最基本的算法,常用的排序算法有十种,可以分为内部排序和外部排序两种类型:

内部排序

  • 内部排序是指待排序列完全存放在内存中所进行的排序过程,适合不太大的元素序列。
  • 常见的内部排序算法:冒泡排序,快速排序,插入排序,选择排序,希尔排序,堆排序。

外部排序

  • 外部排序是针对于极大量数据的排序算法,由于数据无法一次放入内存,算法采用“排序-归并” 策略,首先将部分数据依次读入内存,排序后生成有序的临时文件,在归并阶段将这些临时文件组成较大的有序文件。
  • 常见的外部排序算法有归并排序,计数排序,桶排序,基数排序。


关于排序算法的简单介绍:

排序算法的稳定性

如果两个具有相同键的对象在输入输出中的顺序与在输入未排序数组中出现的顺序相同,则该排序算法被认为是稳定的。有些排序算法本质上是稳定的,例如插入排序,合并排序,冒泡排序等。而有些排序算法则不是,例如堆排序,快速排序等。

但是,任何给定的不稳定算法都可以修改为稳定。可以使用特定的排序算法来使其稳定,但是通常,可以通过更改键比较操作将本质上不稳定的任何基于比较的排序算法修改为稳定,以便将两个键的比较视为位置。具有相同键的对象的系数。参考文献

冒泡排序

冒泡排序(Bubble Sort)重复遍历要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。 重复地进行直到没有再需要交换,也就是说该数列已经排序完成。
算法步骤:

  1. 以升序为例,比较相邻两个元素大小,如果第一个元素大于第二个,则交换两个元素,否则不做改变。
  2. 从数组的第一个元素开始,遍历到数组的倒数第二个元素。

冒泡排序

代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
#include <vector>
using namespace std;

void bubble_sort(vector<int> &num, int len){
for(int i = 0; i < len-1; i++){
for(int j = 0; j<len-1-i; j++){
if(num[j] > num[j+1])
swap(num[j], num[j+1]);
}
}
}

int main(){
vector<int> num= { 61, 17, 29, 22, 34, 60, 72, 21, 50, 1, 62 };
int len = num.size();
for(int i=0; i < len; i++)
cout << num[i] << ' ';
cout << '\n';
Bubble_sort(num, len);
for(int i=0; i < len; i++)
cout << num[i] << ' ';
return 0;
}

选择排序

选择排序的方法为第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。 以此类推,直到全部待排序的数据元素的个数为零。 选择排序是不稳定的排序方法。
算法步骤:

  1. 记录数据中的第一个元素的index
  2. 遍历数据并与该元素进行比较,如果小于该元素则更新index,完成遍历后交换第一个元素和index所指向的最小值。
  3. 重复以上步骤直至完成排序。
    选择排序

代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
void selection_sort(vector<int> &num, int len){
int min;
for(int i=0; i<len; i++){
min = i;
for(int j=i; j<len; j++){
if(num[j] < num[min])
min = j;
}
swap(num[i], num[min]);
}
}

插入排序

将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)

代码实现:

1
2
3
4
5
6
7
8
9
10
void intersection_sort(vector<int> &num, int len){
for(int i=1; i<len; i++){
for(int j=i; j>0; j--){
if(num[j] < num[j-1])
swap(num[j-1], num[j]);
}
}
}


希尔排序

希尔排序(Shellsort),也称递减增量排序算法,是插入排序的一种更高效的改进版本。希尔排序是非稳定排序算法。希尔排序是基于插入排序的以下两点性质而提出改进方法的:

  1. 插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率
  2. 但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位
    希尔排序原理的视频介绍:希尔排序

算法实现:

1
2
3
4
5
6
7
8
9
10
void shell_sort(vector<int> &num, int len){
for(int gap = len / 2; gap > 0; gap /= 2)
for(int i = gap; i < len; i++){
int temp = num[i];
int j = i;
for( ; j >= gap && temp < num[j-gap]; j-=gap)
num[j] = num[j-gap];
num[j] = temp;
}
}

快速排序

快速排序是在实践中最快的已知排序算法,算法的平均运行时间是O(NlogN), 最坏的性能为O(N^2).
算法步骤:

  1. 从数列中随机挑出一个元素,称为 “基准”(pivot);

  2. 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;

  3. 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序;

算法实现:

1

归并排序

归并排序(Merge sort)是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。

作为一种典型的分而治之思想的算法应用,归并排序的实现由两种方法:

  • 自上而下的递归(所有递归的方法都可以用迭代重写,所以就有了第 2 种方法);
  • 自下而上的迭代;

和选择排序一样,归并排序的性能不受输入数据的影响,但表现比选择排序好的多,因为始终都是 O(nlogn) 的时间复杂度。代价是需要额外的内存空间。

算法步骤:

  1. 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列;

  2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置;

  3. 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置;

  4. 重复步骤 3 直到某一指针达到序列尾;

  5. 将另一序列剩下的所有元素直接复制到合并序列尾

参考资料:
[1] 关于排序算法的一点知识——实例和伪代码
[2]

pytorch学习

pytorch常用代码

pytorch踩坑记录

  1. ERROR: Unexpected bus error encountered in worker. This might be caused by insufficient shared memory (shm)
    • 出现这个错误的情况是,在服务器上的docker中运行训练代码时,batch size设置得过大,shared memory不够(因为docker限制了shm).解决方法是,将Dataloader的num_workers设置为0.
    • 参考:https://zhuanlan.zhihu.com/p/59271905
  2. AttributeError: module ‘scipy.misc’ has no attribute ‘toimage’
    • 解决方法:由于安装的scipy版本是1.4版本,该版本删除了toimage函数,最方便的解决办法降版本
      
      $ pip uninstall scipy
      $ pip install scipy==1.2.0
  3. ModuleNotFoundError: No module named ‘tkinter’
    • 在utils.py中添加以下代码:
      import matplotlib
      matplotlib.use(‘agg’)
      import matplotlib.pyplot as plt

基于鸟瞰图的点云目标检测:PIXOR

摘要

该文章解决了在自动驾驶环境下从点云实时检测三维物体的问题。因为检测是安全的必要组成部分,所以计算速度至关重要。然而,由于点云的高维性,现有方法的计算成本很高。本文通过从鸟瞰图( BEV )中表示场景来更有效地利用3D数据,并提出PIXOR,这是一种无需处理的、单级的检测器,输出从像素级别的神经网络定向解码估计3D对象。模型的输入形式、网络架构和优化器是为了平衡高精度和实时效率而特别设计的。作者在两个数据集上验证PIXOR的效果——KITTI BEV对象检测基准数据集和大规模3D车辆检测基准数据集。在这两个数据集上,我们表明检测器在平均精度(AP)方面明显优于其他最先进的方法,同时仍以大于28 FPS的速度运行。

简介

在过去的几年里,我们已经看到了大量利用卷积神经网络来产生精确的2D物体检测的方法,通常是从单个图像为主的模型(如Faster R-CNN和YOLO)。然而,在机器应用中,例如自主驾驶,我们对检测3D空间中的物体更为感兴趣。为了规划安全路线,这是运动规划的基础。

3D对象检测的最新方法利用不同的数据源。基于相机的方法利用单目或立体图像(双目摄像机RGB+HHA)。然而,从2D图像中精确的3D估计是困难的,特别是在长距离范围内。随着廉价RGB-D传感器如微软Kinect、英特尔RealSense和苹果PrimeSense的普及,出现了几种利用深度信息并将其与RGB图像融合的方法。与单目方法相比,它们已经显示出显著的性能提升。在自动驾驶的情况下,像LIDAR(光探测和测距)这样的高端传感器更常见,因为为了安全需要更高的精确度。处理LIDAR数据的主要困难是传感器以点云的形式产生非结构化数据,每360度扫描通常包含大约10^5个3D点。这给现代探测器带来了巨大的计算挑战。

在三维物体检测的背景下,已经探索了不同形式的点云表示。主要的想法是形成一个结构化的表示,其中可以应用标准卷积运算。现有表示主要分为两种类型: 3D体素网格和2D投影。3D体素网格将点云转换成规则间隔的3D网格,其中每个体素单元可以包含标量值(例如,占用率)或矢量数据(例如,根据该体素单元内的点计算的统计数据)。3D卷积通常用于从体素网格中提取高阶表示。然而,由于点云本质上是稀疏的,因此体素网格非常稀疏,很大一部分计算是冗余和不必要的,用这种表示的典型系统仅运行在1-2 FPS。

另一种方法是将点云投影到平面上,然后将平面离散为基于2D图像的表示,在此应用2D卷积。在离散化期间,手工制作的特征(或统计)被当做2D图像的像素值计算。常用的投影是范围视图(即360度全景视图)和鸟瞰图(即俯视图)。这些基于2D投影的表示更加紧凑,但是它们在投影和离散化过程中会带来信息损失。例如,范围视图投影将具有失真的对象大小和形状。为了减轻信息损失,MV3D建议将2D投影与摄像机图像融合,以带来额外信息。然而,融合模型相对于输入模态的数量具有近似线性的计算成本,使得实时应用不可行。

在这篇论文中,作者提出了一种精确的实时三维物体检测器,称之为PIXOR (ORiented 3D object detection from PIXel-wise neural network predictions),它是在点云上运行的网络。PIXOR是一种单级、无二次处理(例如YOLO和SSD)的密集物体检测器,它以高效的方式利用2D鸟瞰图(BEV)表示。我们选择BEV表示,因为它在计算上比3D体素网格更友好,并且还保留了度量空间,这使得我们的模型能够探索关于对象类别大小和形状的先验。我们的探测器在鸟瞰图中输出真实世界尺寸的精确定向边界框。请注意,这些是三维估计,但必须假设物体在地面上,因为车辆不会飞,这也是自动驾驶场景中的合理假设。

作者在两个数据集上展示了其方法的有效性。具体来说,PIXOR在所有先前的方法中,实现了KITTI鸟瞰对象检测基准的最高平均精度(AP),同时也是其中运行最快的(超过28FPS)。作者还在KITTI上提供了深入的消融研究,以调查每个模块贡献了多少性能增益,并通过将其应用于大规模TOR4D数据集来证明PIXOR的可扩展性和泛化能力。

模型结构

在本文中,作者提出了一种高效的三维物体检测器,它能够在给定LIDAR点云的情况下产生非常精确的边界框。这里的边界框估计不仅包含3D空间中的位置,还包含航向角,因为准确预测这对于自主驾驶非常重要。我们利用LIDAR点云的2D表示,因为它比3D体素网格表示更紧凑,因此可以进行实时推断。图1显示了提议的3D物体检测器的概述。

模型检测管道

Input Representation

由于要用标准卷积神经网络执行卷积操作,因此我们必须假设输入位于网格上。然而,3D点云是非结构化的,因此标准卷积不能直接应用在其上。一种选择是使用体素化来形成3D体素网格,其中每个体素单元包含位于该体素内的点的某些统计数据。为了从三维体素网格中提取特征表示,经常使用三维卷积。但这3DCNN的计算代价非常昂贵,并且因为LIDAR点云非常稀疏,以至于大多数体素单元都是空的。

相反,我们可以单独从鸟瞰图(BEV)中描绘场景。通过将维度从三维降低到二维,而不会丢失点云中的信息。因为我们可以将高度信息保留到颜色通道中。不仅如此,我们还有效地得到了更紧凑的表示方式,因为可以对BEV表示应用2D卷积。在自动驾驶的情况下,因为感兴趣的对象是在同一场地上,这种尺寸减小是合理的。除了计算效率之外,BEV表示还有其他优势。与前视图表示相比,由于对象不相互重叠,因此它简化了对象检测的问题。它还保留了度量空间,因此网络可以利用物体物理尺寸的先验信息。

体素化LIDAR表示的常用特征是占有率occupancy、强度intensity(也称反射率)、密度和高度特征。在PIXOR中,作者只使用占用率和强度作为特征。作者定义感兴趣的场景的三维物理尺寸L × W × H。然后,计算分辨率为dL × dW × dH的占用率特征,再计算分辨率为dL×dW×H的强度特征。注意,作者在占用率特征中增加了两个额外的通道,以覆盖范围外的点。最终表示的形状为L/dL × W/dW × (H/dH + 3)

Network Architecture

PIXOR使用一个全卷积结构,设计用于密集定向的3D物体检测,并且没有使用类似于R-CNN的分支处理,相反网络在单个阶段输出像素预测,每个预测对应于3D对象估计。因此,根据定义,PIXOR的召回率是100%。多亏了完全非传统的架构,这样密集的预测可以非常有效地计算出来。就网络预测中3D对象的编码而言,作者构建了直接编码的模型,而不求助于预定义的object anchors。所有这些设计使得PIXOR变得非常简单,并且由于网络架构中的零超参数而得到很好的推广。具体来说,不需要设计object anchors,也不需要调整从第一阶段传递到第二阶段的关注区域以及相应的非最大抑制阈值。

整个体系结构可以分为两个子网络:主干网络Backbone network和头网络Header network。主干网络用于提取卷积特征并映射成输入的一般表示。它具有很高的表示能力来学习一个健壮的特征表示。头网络用于进行特定于任务的预测,在该例子中,它有一个具有多任务输出的单分支结构:对象分类和定位。
模型结构

Backbone Network

卷积神经网络通常由卷积层和池化层组成。卷积图层用于提取输入要素的过度完整表示,而池化图层用于对要素地图大小进行下采样,以节省计算并帮助创建更健壮的表示。许多基于图像的物体检测器中的主干网络通常具有16倍下采样因子,并且通常被设计成具有更少的高分辨率层和更多的低分辨率层。它对图像很有效,因为对象通常像素尺寸很大。然而,在本文的情况下,这将导致一个问题,因为对象可能非常小。当离散分辨率为0.1m时,典型车辆的尺寸为18×40像素。16倍下采样后,它仅覆盖约3像素。

一个直接的解决方案是使用更少的池化层。然而,这将减小最终特征图中每个像素的感受野的大小,这限制了表现能力。另一个解决方案是使用膨胀卷积,但这将导致在高级特征地图中出现棋盘状伪像(checkerboard artifacts)。作者的解决方案很简单,使用16倍下采样因子,但是做了两个修改。首先,在较低的级别添加更多的通道数量较少的层,以提取更多的细节信息。其次,我们采用了类似FPN的自上而下的分支,将高分辨率特征图和低分辨率特征图相结合,以便对最终的特征表示进行上采样。

从Figure2可以看出主干网络中总共有五个层块。第一块由两个卷积层组成,通道为32,步长为1。第二至第五块由残差层组成(层数分别等于3、6、6、3)。每个残差块的第一卷积具有步长2,以便对特征图进行下采样。总的来说,下采样系数是16。为了对要素图进行上采样,其添加了一条自上而下的路径,每次对要素图进行2倍的上采样。然后,通过像素求和,这与相应分辨率的自下而上的特征图相结合。使用两个上采样层,这导致最终的特征图具有四倍下采样的大小。

Header Network

头网络是一个多任务网络——对象识别和方向定位。它被设计成小巧高效。分类分支输出1个通道特征图,跟sigmoid函数激活;回归分支输出线性的6通道特征图。在这两个分支之间分享权重的层数上存在权衡。一方面,我们希望更有效地利用重量。另一方面,由于它们是不同的子任务,我们希望它们更加独立和专业化。在下一章中,我们对这种权衡进行了一项调查性实验,发现两项任务的权重分配会带来稍微更好的性能。

我们将每个对象参数化为定向的bounding box b {θ,xc,yc,w,l}=,每个元素对应于在[−π,π][−π,π]范围内的航向角、对象的中心位置(xc,yc)和对象的大小(w,l)。与基于长方体的三维物体检测相比,我们省略了沿Z轴的位置和尺寸,因为在像自主驾驶这样的应用中,感兴趣的物体被限制在同一个接地面上,因此我们只关心如何在那个平面上定位它。给定这样的参数化,回归分支的表示是{cos(θ),sin(θ),dx,dy,w,l},对于位置(px,py)(如图3中的红点所示)。注意,航向角被分解为两个相关值,以加强角度范围约束。

学习目标的定义

在推理过程中,我们将θ解码为atan2(sin(θ),cos(θ))。(dx,dy)对应于从像素位置到物体中心的位置偏移。(w,l)对应于对象大小。值得注意的是,对象位置和大小的值在现实世界的度量空间中。最后的学习目标是{cos(θ),sin(θ),dx,dy,log(w),log(l)},它在训练集之前被标准化为具有零均值和单位方差。

Learning and Inference

与传统的多任务网络类似,其Loss的计算方式如下所示,其中focal_loss为分类的损失,smooth为回归的损失。
损失函数

基于鸟瞰图的点云目标检测:Birdnet+

简介

自动驾驶汽车中的车载3D对象检测通常依赖于LiDAR设备捕获的几何信息。尽管通常优选使用图像特征进行检测,但是许多方法仅将空间数据作为输入。利用这些信息进行推理通常涉及使用紧凑的表示形式,例如鸟瞰图(BEV)投影,这会导致信息丢失,从而阻碍了对象3D框的所有参数的联合推理。在本文中,作者提出了一个完整的端到端3D对象检测框架,该框架可以通过使用两阶段对象检测器和临时回归分支仅从BEV图像中推断出定向3D框,从而无需进行后处理阶段。该方法在很大程度上优于其前身(BirdNet),并在KITTI 3D对象检测基准测试中获得了评估中所有类别的最新结果。

核心思想:

通过将点云数据投影为BEV表示将3D目标检测任务转化为2D图像检测问题,然后采用两阶段的Faster RCNN模型实现检测任务。

技术细节:

  • BEV 表示:

  • 将LIDAR点云数据编码成3个通道的2D结构,这三个通道为: 最大高度(< 3m),平均强度和单元格中的归一化点密度。

  • 编码不包括每个单元的最低点信息(groud truth)

  • ROI 区域:前左右35m范围

  • 单元网格的大小:每个单元网格的大小为5cm

  • 特征提取:

  • 采用ResNet-50网络,为了解决BEV视图下行人难以检测的问题,作者对ResNet-50做了一些修改:
    (i)采用conv3 layer, 下采样因子设为8
    (ii) 利用特征金字塔网络(FPN)一边从每个ResNet块的输出中提取每个对象对应的特征

  • Region Proposal:

  • anchor尺寸:1616, 4848, 80*80

  • anchor长宽比: 1:1, 1:2, 2:1

  • anchor是轴对齐的

  • Feature pooling分辨率: 7*7

  • Classfication & Bounding box regression

  • RPN的第一阶段的proposals采用BEV图上的2D bounding box来表示,第二阶段负责对这些proposals进行分类。

  • 预测步骤由两个全连接层完成, 每个层具有1024个元素,这些元素最终被馈送到一组individual heads中, 每个head由FC层组成并负责不同的任务,这些heads有三个分支,分别负责分类,轴对齐的框回归和离散的yaw的分类

点云数据生成鸟瞰图表示

点云数据

点云数据是点的集合,由激光雷达采集获得,点云数据应表示为具有N行,具有4列的numpy数组。每行对应一个点,该点在空间(x,y,z)使用3个值表示。 第四个值是附加值,通常为反射率(强度)。

鸟瞰图表示

鸟瞰图是俯视视角下点云的一种图形表示。 在自动驾驶中,该表示方法的合理性基于一个前提: 所有的车辆都在地面行驶,因此可以直接将数据展平在x, y平面中。 点云数据转换为鸟瞰图的步骤为:

  • 设置感兴趣的区域(Region of Interest), 区域大小为L * W * H.
  • 设置分辨率,将Region of Interest栅格化,并将点云的位置映射到鸟瞰图上的像素位置,计算occupancy map
  • 按照高度将鸟瞰图划分为不同的通道,以保存点云数据中的高度信息。

微信截图_20200730102703.png

鸟瞰图生成代码

import numpy as np


# ==============================================================================
#                                                                   SCALE_TO_255
# ==============================================================================
def scale_to_255(a, min, max, dtype=np.uint8):
    """ Scales an array of values from specified min, max range to 0-255
        Optionally specify the data type of the output (default is uint8)
    """
    return (((a - min) / float(max - min)) * 255).astype(dtype)


# ==============================================================================
#                                                         POINT_CLOUD_2_BIRDSEYE
# ==============================================================================
def point_cloud_2_birdseye(points,
                           res=0.1,
                           side_range=(-10., 10.),  # left-most to right-most
                           fwd_range = (-10., 10.), # back-most to forward-most
                           height_range=(-2., 2.),  # bottom-most to upper-most
                           ):
    """ Creates an 2D birds eye view representation of the point cloud data.

    Args:
        points:     (numpy array)
                    N rows of points data
                    Each point should be specified by at least 3 elements x,y,z
        res:        (float)
                    Desired resolution in metres to use. Each output pixel will
                    represent an square region res x res in size.
        side_range: (tuple of two floats)
                    (-left, right) in metres
                    left and right limits of rectangle to look at.
        fwd_range:  (tuple of two floats)
                    (-behind, front) in metres
                    back and front limits of rectangle to look at.
        height_range: (tuple of two floats)
                    (min, max) heights (in metres) relative to the origin.
                    All height values will be clipped to this min and max value,
                    such that anything below min will be truncated to min, and
                    the same for values above max.
    Returns:
        2D numpy array representing an image of the birds eye view.
    """
    # EXTRACT THE POINTS FOR EACH AXIS
    x_points = points[:, 0]
    y_points = points[:, 1]
    z_points = points[:, 2]

    # FILTER - To return only indices of points within desired cube
    # Three filters for: Front-to-back, side-to-side, and height ranges
    # Note left side is positive y axis in LIDAR coordinates
    f_filt = np.logical_and((x_points > fwd_range[0]), (x_points < fwd_range[1]))
    s_filt = np.logical_and((y_points > -side_range[1]), (y_points < -side_range[0]))
    filter = np.logical_and(f_filt, s_filt)
    indices = np.argwhere(filter).flatten()

    # KEEPERS
    x_points = x_points[indices]
    y_points = y_points[indices]
    z_points = z_points[indices]

    # CONVERT TO PIXEL POSITION VALUES - Based on resolution
    x_img = (-y_points / res).astype(np.int32)  # x axis is -y in LIDAR
    y_img = (-x_points / res).astype(np.int32)  # y axis is -x in LIDAR

    # SHIFT PIXELS TO HAVE MINIMUM BE (0,0)
    # floor & ceil used to prevent anything being rounded to below 0 after shift
    x_img -= int(np.floor(side_range[0] / res))
    y_img += int(np.ceil(fwd_range[1] / res))

    # CLIP HEIGHT VALUES - to between min and max heights
    pixel_values = np.clip(a=z_points,
                           a_min=height_range[0],
                           a_max=height_range[1])

    # RESCALE THE HEIGHT VALUES - to be between the range 0-255
    pixel_values = scale_to_255(pixel_values,
                                min=height_range[0],
                                max=height_range[1])

    # INITIALIZE EMPTY ARRAY - of the dimensions we want
    x_max = 1 + int((side_range[1] - side_range[0]) / res)
    y_max = 1 + int((fwd_range[1] - fwd_range[0]) / res)
    im = np.zeros([y_max, x_max], dtype=np.uint8)

    # FILL PIXEL VALUES IN IMAGE ARRAY
    im[y_img, x_img] = pixel_values

    return im

pointcloud = np.fromfile(str("000000.bin"), dtype=np.float32, count=-1).reshape([-1, 4])
bev = point_cloud_2_birdseye(pointcloud)

生成的鸟瞰图:
313.png

KITTI数据集标签

KITTI detection数据集标签

数据集标签实例:
微信截图_20200730120209.png

标签含义:

Values Name Description
1 type Describes the type of object: ‘Car’, ‘Van’, ‘Truck’,’Pedestrian’, ‘Person_sitting’, ‘Cyclist’, ‘Tram’,’Misc’ or ‘DontCare’
1 truncated Float from 0 (non-truncated) to 1 (truncated), where truncated refers to the object leaving image boundaries
1 occluded Integer (0,1,2,3) indicating occlusion state: 0 = fully visible, 1 = partly occluded, 2 = largely occluded, 3 = unknown
1 alpha Observation angle of object, ranging [-pi..pi]
4 bbox 2D bounding box of object in the image (0-based index): contains left, top, right, bottom pixel coordinates
3 dimensions 3D object dimensions: height, width, length (in meters)
3 location 3D object location x,y,z in camera coordinates (in meters)
1 rotation_y Rotation ry around Y-axis in camera coordinates [-pi..pi]
1 score Only for results: Float, indicating confidence in detection, needed for p/r curves, higher is better.

第1个字符串:代表物体类别

‘Car’, ‘Van’, ‘Truck’,’Pedestrian’, ‘Person_sitting’, ‘Cyclist’,’Tram’, ‘Misc’ or ‘DontCare’

注意,’DontCare’ 标签表示该区域没有被标注,比如由于目标物体距离激光雷达太远。为了防止在评估过程中(主要是计算precision),将本来是目标物体但是因为某些原因而没有标注的区域统计为假阳性(false positives),评估脚本会自动忽略’DontCare’ 区域的预测结果。

第2个数:代表物体是否被截断

从0(非截断)到1(截断)浮动,其中truncated指离开图像边界的对象

第3个数:代表物体是否被遮挡

整数0,1,2,3表示被遮挡的程度

0:完全可见 1:小部分遮挡 2:大部分遮挡 3:完全遮挡(unknown)

第4个数:alpha,物体的观察角度,范围:-pi~pi

是在相机坐标系下,以相机原点为中心,相机原点到物体中心的连线为半径,将物体绕相机y轴旋转至相机z轴,此时物体方向与相机x轴的夹角

第5~8这4个数:物体的2维边界框

xmin,ymin,xmax,ymax

第9~11这3个数:3维物体的尺寸

高、宽、长(单位:米)

第12~14这3个数:3维物体的位置

x,y,z(在照相机坐标系下,单位:米)

第15个数:3维物体的空间方向:rotation_y

在照相机坐标系下,物体的全局方向角(物体前进方向与相机坐标系x轴的夹角),范围:-pi~pi

第16个数:检测的置信度

Apollo学习

Cha1: 自动驾驶概述

自动驾驶的等级共有六个等级:

  • L0: 基本等级,驾驶者是唯一决策者
  • L1:Driver Assistance, 车辆为驾驶员提供转向或加速支持,如cruise control
  • L2: Partical Automation, 车辆负责部分驾驶功能,如Automatic Cruise control, Automatic Lane Keeping, 但驾驶员始终保持对系统的控制
  • L3: Conditional Automation, 车辆自主驾驶,但驾驶员必须准备在必要时间随时接管。
  • L4: No Human Interference,(Without Steering Wheel, Throttle or Brake but restricted in Geofence) 在该等级中驾驶的所有功能由车辆接管,并且并不期望驾驶员的介入。该等级的车辆可能根本没有方向盘和任何驾驶员控制装置, 但车辆的行驶会被限制在某些区域(地理围栏),在地理围栏外车辆不能自主操作,或者根本无法操作
  • L5: Full Automation, 车辆可在任何可驾驶区域自主运行,并且该等级下车辆的自主驾驶水平大于或等于人类驾驶员的水平

自动驾驶等级

Cha2: 高精度地图(HD map)

简介:
高精度地图包含大量的辅助信息,如道路网的精确三维表征(交叉路口布局,路标位置),语义信息(信号灯,速度限制, 车道)等,高精度地图的精度可达到厘米级。高精度地图可用于定位,感知和规划等过程中。
定位:
高精度地图可以同于定位,车辆通过传感器感知的周围环境进行自定位,即根据所感知的环境信息找到自身在地图上所处位置。其流程为首先通过传感器采集数据(图像,三维点云等),然后对数据进行预处理(消除不准确和低质量数据),坐标转换(统一坐标系),数据融合, 最后根据数据融合的数据和地狱进行匹配。
感知:
高精度地图和帮助无人驾驶车进行感知,用于克服传感器硬件,天气,亮度等条件对传感器感知能力的限制,除此之外高精地图可以用来缩小传感器检测范围,即为传感器提供ROI,其优点是可以提高检测精度和速度,同时也节约了计算能力。
规划:
高精度地图可以帮助车辆寻找合适的行车空间,用于路线规划;同时也可以告知车辆其他车辆可能出现的位置。在具有速度限制,障碍物的区域,高精度地图也可以为车辆的减速,变道等操作提供依据。

Apollo高精度地图

Apollo的高精度地图采用OpenDRIVE标准,该标准格式可以像API一样方便调用,Apollo对该制图标准进行了改进,即Apollo OpenDRIVE.两种标准比较见下图。
Apollo高精度地图道路元素
两种标准对比
Apollo高精度地图的准确性要求比较高,因此需要调查车队对地图进行不断的更新,其构建过程如下图。除了高精度地图以外,Apollo还提供了俯瞰图和3D点云地图。
地图构建过程

Cha3: 定位

定位是让无人车确定自身位置的方法,常用的定位的方法有:GNSS RTK, 惯性导航,Lidar定位和视觉定位。定位所需要的输入有:GPS, IMU, LiDAR等。
GNSS RTK:
GNSS RTK定位的基础是三角测量,如下图所示,在平面上确定一个点的位置需要三个地标,在空间中则需要一个额外的地标来确定海拔。因此GPS需要至少4颗卫星才能进行定位。
三角测量原理
GPS是全球定位系统,是美国开发的定位系统名称。但该类系统的通用名称叫做全球导航卫星系统(GNSS)。GPS系统有三部分组成:

  • 卫星:大约30颗,距地约两万公里
  • 控制站:监控和控制卫星,验证GPS精度
  • GPS接收器: 不直接检测距离,而是检测信号飞行时间,计算方法如下下图。为保证精度,每颗卫星都有原子钟以保证时间的准确性
    GPS定位进一步减小误差的方法是实时运动定位(RTK), 通过卫星和基站的误差进行修正,通过误差可以将误差限制在10cm内。因此GPS定位系统的精度可以信赖,但收遮挡和区域的影响,并且更新频率很低(10Hz)。
    GPS结构
    距离计算方法
    RTK原理

惯性导航:
基本原理是初中物理(v, a, t),加速度采用三轴加速度计进行测量,并且借助陀螺仪的(3D Gyro)将加速度从Ego坐标系转换到全局坐标系。加速度计+陀螺仪=IMU,IMU工作频率很高,实时性强,但误差会随着时间而积累。
LiDAR定位:
LiDAR定位的方法是通过点云匹配,滤波等算法实现的。点云匹配算法(迭代最近点/ICP)的思想是:假设需要对两次点云扫描的数据进行匹配,首先建立两次扫描的匹配点对,然后将每对点之间的距离误差相加来计算平均距离误差,接下来的目标是通过点云的平移和旋转来最小化平均距离误差,从而可以找到传感器扫描和地图之间的匹配,最终将传感器扫描到的车辆位置转换为全球地图上的精确位置。滤波算法通过过滤冗余信息来在地图上找到最可能的车辆位置。Apollo采用直方图滤波算法(误差平方和算法/SSD),该方法首先将传感器扫描的点云滑过地图上的每个位置,在每个位置计算扫描的点与高精度地图对应点之间的误差或距离,然后对误差的平方求和,得到的结果越小,则扫描结果与地图的匹配越好。此外还有卡尔曼滤波算法,该算法根据过去的状态和新的传感器测量结果预测当前的状态,即使用了预测更新周期。
LiDAR的优点在于稳健性(robust),但其难点是构建高精度地图并使其保持最新(瞬态元素太多?)。

视觉定位:
视觉定位的优点是:图像数据类型获取成本较低,并且摄像头传感器种类繁多,价格便宜; 但通过图像很难实现精准定位,因此经常与其他传感器数据相结合来准确定位车辆(如:3D map + Image)。比如采用粒子滤波来确定汽车检测到的树对应地图上的哪棵树(如下图)。同样可以使用粒子滤波的原理确定车辆在车道线上的位置。
粒子滤波视觉定位
粒子滤波过程

Apollo定位
Apollo使用基于GPS, IMU和Lidar的多传感器融合定位系统,利用不同传感器的特性和优势,提高了定位的稳定性和准确性。数据融合的框架是卡尔曼滤波。
数据融合框架

Cha4: 感知

在自动驾驶控制系统的架构当中,自动驾驶车辆的感知系统是车辆路径规划的主要依据之一。感知系统最主要采用的传感器是Camera和Lidar。Camera图像的形式为RGB或者灰度,可以提供颜色,形状和纹理等信息; Lidar图像提供环境的点云特征,其原理是Lidar中激光束的反射,所收集的信息形成点云,每个点代表反射回传感器的激光束。点云数据中包含距离,高度以及表面和纹理信息。最终,感知模块的输出有四周车辆的三维识别框; 所检测的车辆的速度,角速度,方向; 地面标识,车道线等。 这里要注意的是感知模块的输出不是传感器的原始信息。在感知层面上,自动驾驶车辆主要有四个核心任务:

  • Detection: 检测,确定物体的具体位置
  • Classification: 分类,明确对象类别
  • Tracking: 跟踪,随着时间的推移观察移动物体
  • Segmentation: 分割,即语义分割,将图像的每个像素与语义类别相匹配

Pipeline of Classification

机器学习
机器学习是一种方法论,采用模型来对传感器的数据进行处理,模型需要学习过程,根据其学习的方法可分为:

  • supervised Learning: 需要有标注的数据进行训练
  • Unsupervised Learning: 采用无标注的数据进行训练
  • Semi-supervised Learning: 半监督,两者结合
  • Reinforcement Learning: 强化学习,允许模型采用不同的方法解决问题,并衡量哪一种方法更加成功。

反向传播算法
反向传播算法的过程由前馈误差测定,和反向传播三个部分组成。

卷积神经网络
标准的神经网络在处理图像分类问题的时候,需要首先采用平滑化操作(Flatten)将二维的图像变成一维的向量,即将所有的图像展开为一维像素序列,但这种方法的问题是会丢失图像的空间信息。CNN通过卷积操作来维持输入像素之间的空间关系来解决这一个问题。 如下图的CNN结构,在每一层上CNN采用不同的卷积算子来提取不同的图像特征如边缘,部件,色彩等,最终通过学习过程来找出哪些特征是重要的。
CNN检测原理

目标检测与分类
检测算法有两种流派:

  • Two stage model: RCNN, fast FCNN, Faster RCNN
  • One stage model: YOLO, SSD

跟踪
在检测出目标后,需要对目标进行跟踪,Tracking的意义在于可以解决在视频流中物体的遮挡问题,并且可保留识别码。知乎:工程实践中,目标检测为何要加目标追踪?
分割
语义分割设计对图像的每个像素进行分类,在自动驾驶中用于寻找通行空间。算法上语义分割依赖于全卷积网络(FCN)。不同于CNN,FCN采用卷积层来替代传统CNN结果的平滑层(flat layers)。在FCN中,输入图像经过多次卷积后得到的输出图像将比输入图像小,为了进行像素分割操作,可网络的中间输出采用上采样处理,使得最终输出的大小与原始图像大小相匹配。如下图,网络的前半部分被称为编码器,后半部分为解码器
FCN网络(Encoder + Decoder)

Apollo感知
对于三维对象的检测,Apollo的感知系统采用ROI来重点关注相关对象,Apollo将ROI fliter应用于点云和图像数据,用来缩小搜索范围并加快感知。其检测网络的结构如下图,检测网络的输出用于构建对象的Bounding box。紧接着采用检测和跟踪结合的算法来在时间步中检测单个对象。Apollo采用高精度地图来确定前方是否有信号灯,采用YOLO来检测车道线和动态目标。在通过YOLO网络检测后,在线检测模块会并入来自其他传感器的数据对车道线预测进行调整,车道线最终被放入Virtual Lane的数据结构中。同时来自其他传感器的数据也会对YOLO网络所检测到的动态对象进行调整,以获得每个对象的类别,位置,速度和前进方向。车道预测结果和动态对象检测结果最终均被传递到规划和控制模块。
Detection Network

pipeline of Apollo

传感器数据比较
传感器性能比较

Radar和Lidar补充知识
在汽车系统中雷达主要应用于自适应巡航控制、盲点警告、碰撞浸膏和碰撞预防等系统中。尽管雷达技术已经成熟,它仍在不断进步,作用不断提升。其他传感器测量速度的方法是计算两次读数之间的差距,而雷达则通过多普勒效应来直接测量速度。多普勒效应对传感器融合至关重要。因为它可以把速度作为独立的测量参数,从而提升了融合算法的收敛速度。雷达还可以生成环境的雷达地图,进而实现定位。因为雷达波在坚硬表面会回弹。因此,它可以直接测量对象距离,无需在视线范围内也可以。雷达可以看到其他车辆底部。并发现可能会被阻挡的建筑物和对象。在车上的所有传感器中,雷达是至不容易受雨雾影响的。而且视野宽阔,可达150度,距离可达200多米。与激光雷达和摄像头相比,雷达分辨率较低,尤其是在垂直方向,分辨率非常有限。分辨率低意味着来自静态物体的反射可能产生问题。例如,街道上检修孔盖或汽水罐,可能产生很高的雷达反射率,但他们并不大。我们将其称为雷达杂波。因此,当前的车载雷达通常会忽视静态物体。

激光雷达是激光探测与测量的简称,而雷达则谁无线电探测与测量的简称。雷达使用无线电波,而激光雷达则使用红激光束来确定传感器和附近对象的距离。目前的激光雷达大多使用 900 纳米光波长度的光源。但部分激光雷达使用的光波长度更长,在雨雾中性能更好。当前的激光雷达使用旋转座架发射激光,扫描周边环境。激光室脉冲式的,脉冲被对象反射,然后返回一个点云,来代表这些物体。激光雷达的空间分辨率远远高于雷达。因为激光束越聚焦,垂直方向的扫描层数量就越多,因此每层的激光雷达的密度也越高。目前,激光雷达还不能直接测量对象的速度,必须使用两次或多次扫描之间的位置差来确定。激光雷达受天气和传感器清洁程度影响也很大,因此需要保持清洁。它们块头也比其他传感器更大,因此也很难安装,除非你只想在车顶安装一个大的激光扫描器。

感知融合策略
Apollo采用Lidar和Radar来检测障碍物,用于融合输出的主要算法为卡尔曼滤波。

Cha6: 规划

轨迹规划的目标是生成免碰撞和舒适的可执行的轨迹,该轨迹有一系列的点组成,每个点都有一个关联速度,以及一个时间戳(tamestap)用来表示何时到达该点。路线规划使用了三个输入:地图,当前在地图上的位置,目的地。在搜索可行路径之前,采用了基于图形搜索方法。首先将地图重新格式化为图形(graph)。graph由节点(node)和边缘(edge)组成。节点代表路段,边缘代表路段之间的连接。轨迹规划不仅需要再地图上规划路线,还需要根据环境以及周围的车辆行人等进行低等级高精度的轨迹生成。所生成的轨迹具有三个维度,空间上的两个维度和一个时间维度。现实世界的规划问题具有多种约束,如考虑车辆道路条件的限制,法律法规,乘客的舒适感,速度限制,在轨迹的终点车辆是否和道路中心线平行对齐等。在具体实现方法采用优化理论,通过最小化损失函数来找到最适合的路径。在路径规划中,笛卡尔坐标系并不适合解决这种问题,Frenet 坐标系描述了汽车相对于道路的位置,在 Frenet 框架中,s 代表沿道路的距离称为纵坐标,d表示与纵向线的位移称为横坐标。
Frenet坐标
路径-速度解耦规划(EM规划)
路径-速度解耦规划包括两步: 路径规划和速度规划。在路径规划中,首先生成候选曲线,使用成本函数对候选路径进行评估,需考虑的因素包括: 平滑度、安全性、与车道中心的偏离程度等。速度规划需要的是一系列与路径相关联的速度序列。
路径规划: 具体实施步骤上,首先将路段分割成单元格,对这些单元格中的点进行随机采样。通过从每个单元格中取一个点并将点连接,创建了候选路径,通过重复此过程可以构建多个候选路径。使用成本函数对这些路径进行评估并选择成本最低的路径,成本函数可能考虑以下因素:与车道中心的偏离、与障碍物的距离、速度和曲率的变化、对车辆的压力等因素。
路径规划

速度规划: 速度规划即需要选择与路径相关联的速度曲线。通过ST图可以帮助设计和选择速度曲线。在这里需要将ST图离散为多个单元格,每个单元格内速度保持不变,通过该方法可简化速度曲线的构建并维持曲线的近似度。在 ST 图中可以将障碍物可表达为在特定时间段内阻挡道路的矩形。为避免碰撞,速度曲线不得与此矩形相交,使用优化引擎为该图选择最佳的速度曲线。优化算法通过复杂的数学运算来搜索,受到各种限制的低成本解决方案,这些限制可能包括:法律,速度、与障碍物的距离、汽车的性能等。

ST图
优化: 为了将离散解决方案转换为平滑轨迹,可使用“二次规划”技术。二次规划将平滑的非线性曲线与这些分段式线性段拟合。

Lattice规划
Lattice规划,通过使用Frenet坐标可以将环境投射到纵轴和横轴上,目标是生成三维轨迹: 纵向维度、横向维度、时间维度。在具体处理上,可以将三维问题分解成两个单独的二维问题,即轨迹的纵向分量(ST轨迹)和横向分量(SL轨迹)。

ST轨迹的终止状态有三种:

  • ** 巡航:** 巡航意味着车辆将在完成规划步骤后定速行驶,实际上在对图上的点进行采样,在图中横轴代表时间,纵轴代表速度。对于该图上的点,这意味着汽车将进入巡航状态,在时间 t 以 s 点的速度巡航,对于这种模式,所有最终状态的加速度均为零。

  • 巡航

  • ** 跟随: ** 在这种情况下要对位置和时间状态进行采样,并尝试在时间t出现在某辆车后面,在跟随车辆时,需要与前方的车保持安全距离,这时速度和加速度将取决于要跟随的车辆,这意味着在这种模式下,速度和加速度都会进行修正。

  • 跟随

  • ** 停止: ** 对于这种模式只需对汽车何时何地停止进行抽样,这里速度和加速度会被修正为0。

  • 停止

SL的终止状态:
无论车辆进入怎样的终止状态,车辆都应该稳定地与车道中心线对齐。因此只需要在小区域内,对横向终止位置进行采样。具体来说采样的是道路上相邻车道中心线周围的位置,为了确保稳定性。汽车驶向的终止状态应该与车道中心一致,在路径规划中,车辆的最终状态应与车道对齐并直线行驶而结束,即车的朝向和位置的一阶和二阶导数都应该为零。这意味着车辆既不是横向移动的那是一阶导数,也不是横向加速那是二阶导数,这意味着车辆正沿着车道直行。

拟合多项式与评估
如何连接初始状态和结束状态?

拟合多项式与评估

Cha7 控制

控制是驱使车辆前行的策略,对于汽车而言,最基本的控制输入为转向、加速、制动。控制器使用一系列路径点来接收轨迹,其目标是使用可行的控制输入,最大限度地降低与目标轨迹的偏差,最大限度地提高乘客的舒适度。可用于实现这些目标的三种控制策略比例积分微分控制(PID)、线性二次调节器(LQR)、模型预测控(MPC)。

PID
PID是一种线性控制算法,不适用于复杂系统,对无人驾驶车,需要应用不同的 PID 控制器来控制转向和加速,这意味着很难将横向和纵向控制结合起来。并且PID控制器依赖于实时误差测量,意味着受到测量延迟的限制。

LQR
LQR是基于模型的控制器,使用车辆状态来最小化误差。Apollo 使用 LQR 进行横向控制,横向控制包含四个参数:横向误差、横向误差的变化率、朝向误差、朝向的变化率。用集合x表示 ,x可捕获车辆的状态。车辆有三个控制输入:转向、加速、制动,用集合u表示。
x,u
在线性控制时,其控制器模型如下图。x_dot是导数或x向量的变化率。
linear Quadrator regulator
该等式是线性的,因为用△x来改变x时,并用△u来改变u,x点的变化也会让这个等式成立。
linear Quadrator regulator
控制的目标是为了让误差最小化同时尽可能少地使用控制输入,由于使用这些会有成本。为了尽量减少这些因素,可以保持误差的运行总和和控制输入的运行总和。当汽车往右偏转得特别厉害之际,添加到误差总和中,当控制输入将汽车往左侧转时,从控制输入总和中减去一点。然而这种方法会导致问题,因为右侧的正误差只需将左侧的负误差消除即可,对控制输入​​来说也是如此。相反可以让x乘以u,这样负值也会产生正平方,称这些为二次项。为这些项分配权重并将它们加在一起,最优的u应该加倍减总和,在数学中将这个值称为成本函数。经常写出加权二次项的总和,这里Q和R代表x和u的权重集合, X和Ut是转置矩阵,意味着它们几乎与x和u相同,只是重新排列 以便矩阵乘法。x乘以Xt,u乘以Ut,实质上是将每个矩阵乘以它自己。最小化成本函数是一个复杂的过程,但通常可以依靠数值计算器找到解决方案。在Apollo中,LQR控制器被表达为:u=-Kx。求解的目标就是找到一个最优的K。

成本函数

MPC
模型预测是一种复杂的控制器,最初用于工业领域。也是一种基于模型的控制器。MPC是一种多变量的控制算法,采用程序的内在动态模型,过去的控制信号,针对滚动预测域的最佳化成本函数J来计算最优化的控制信号。它由预测模型,反馈校正,滚动优化和参考轨迹四个部分组成。对于其预测能力,预测越深入,控制器就越精确,不过需要的时间也越长。所以需要在准确度与快速获取结果之间做出取舍,获取结果的速度越快,越能快速地将控制输入应用到实际车辆中。
微信截图_20200306105245.png
MPC优劣对比:MPC考虑了车辆模型,因此比PID控制更精确,也适用于不同的成本函数。另一方面与PID控制相比,模型预测控制相对更复杂、更缓慢、更难以实现。在实践中无人驾驶车的控制可扩展性的重要程度,通常意味着值得为MPC投入实现成本,所以MPC成为了一个非常重要的无人驾驶车控制器。

Tensorflow入门_Udacity

Cha1: Hello World

基本术语

  • 人工智能:一种计算机科学分支,旨在让计算机达到人类的智慧。实现这一目标有很多方式,包括机器学习和深度学习。
  • 机器学习:一系列相关技术,用于训练计算机执行特定的任务。
  • 神经网络:一种机器学习结构,灵感来自人类大脑的神经元网络。神经网络是深度学习的基本概念。
  • 深度学习:机器学习的一个分支,利用多层神经网络实现目标。通常“机器学习”和“深度学习”可以相互指代。

基本术语的逻辑关系

机器学习算法应用

  • 游戏: AlphaGo- Google DeepMind团队
  • 医疗: 采用机器学习算法检测皮肤癌等,精度与人类专家相当
  • 自动驾驶:

Tensorflow

  • Tensorflow Lite: 可以在Android和iOS设备上创建移动应用
  • Tensorflow.js: 可以在网络浏览器中运行相应的应用
  • Tensorflow实现语言: Python, JavaScript, Swift, R, Julia等。但目前,Python和JavaScript是最完整的实现语言。
  • Tensorflow开发环境: 本课程采用python和Google的开发环境Colab(类似于Jupyter Notebook,Colab采用交互式Notebook进行Python开发,程序在Google Cloud上托管,因此不需要配置软件环境,仅需在网络浏览器上使用)

练习:Python和Colab初级知识

Cha2: 机器学习简介

Sebastian访谈

大佬告诉我们机器学习比计算机编程要简单,所以我们要相信大佬,大胆的去做吧,奥利给!

传统算法和机器学习算法区别

传统算法和机器学习算法区别

机器学习术语

  • 特征:模型的输入
  • 样本:用于训练流程的输入/输出对
  • 标签:模型的输出
  • 层级:神经网络中相互连接的节点集合。
  • 模型:神经网络的表示法
  • **密集全连接层 (FC)**:一个层级中的每个节点都与上个层级中的每个节点相连。
  • 权重和偏差:模型的内部变量
  • 损失:期望输出和真实输出之间的差值
  • MSE:均方误差,一种损失函数,它会将一小部分很大的差值视作比大量很小的差值更糟糕。
  • 梯度下降法:每次小幅调整内部变量,从而逐渐降低损失函数的算法。
  • 优化器:梯度下降法的一种具体实现方法。(有很多算法。在这门课程中,我们将仅使用“Adam”优化器,它是 ADAptive with Momentum 的简称,并且被视为最佳优化器。)
  • 学习速率:梯度下降过程中的损失改进“步长”。
  • 批次:在训练神经网络的过程中使用的一组样本。
  • 周期:完全经过整个训练数据集一轮
  • 前向传播:根据输入计算输出值
  • 反向传播:根据优化器算法计算内部变量的调整幅度,从输出层级开始,并往回计算每个层级,直到抵达输入层。

练习:摄氏度转化为华氏温度

密集层 (Dense Layer)

密集层也叫全连接层,即一个层级的每一个节点都与上一个层级的所有节点相连。

全连接层

上图的全连接层在Tensorflow中可以用以下代码实现:

1
2
3
hidden = keras.layers.Dense(units=2, input_shape=[3])
output = keras.layers.Dense(units=1)
model = tf.keras.Sequential([hidden, output])

训练的流程是指讲权重和偏差调整为最佳值,使模型能够将输入和输出相匹配。但训练过程只改变w和b,并不改变模型,即下图中的数学公式。(w: 权重 b: 偏差)
密集层中的数学运算

Cha3: 首个模型 - Fashion MNIST

Sebastian访谈

问: 未来十年内,哪些前沿性科技和挑战对深度学习的影响最大?未来最重要的挑战是什么?
答: 其一通用人工智能,其二贴合市场

Fashion MNIST

Fashion MNIST是一个数据集,该数据集由28*28像素的灰度服饰图像组成,共70000张图像,下图是其所包含的所有服饰标签的完整列表:
Fashion MNIST dataset

神经网络

相关术语:

  • 扁平化:将二维图像转换为一维向量的过程
  • ReLU:一种激活函数,使模型能够解决非线性问题
  • Softmax:一种函数,能够为每个潜在输出类别生成概率
  • 分类:一种机器学习模型,用于区分两个或多个输出类别

** ReLU函数(修正线性单元)**
ReLU 是一种激活函数。激活函数有好几种,例如 ReLU、Sigmoid、双曲正切、ELU,但是 ReLU 是最常用的激活函数,通常默认都设为 ReLU。该函数如下图所示,其特性为:如果输入是附属或0,则函数输出为0, 如果输入是正数, 那么输出将等于输入。ReLU使网络能够解决非线性问题,在实际问题中,其大多都为非线性问题。在这种情况下,向密集层添加ReLU有助于解决问题。扩展阅读

ReLU函数

本章节的神经网络模型如下图所示:
该章节神经网络模型

**Input image:**上面提到,Fashion MNIST数据集中的图像大小均为28 * 28 = 784像素,并且神经网络的输入必须是向量,因此需要将图像转化为以为数组,该过程即为扁平化,其实现代码为: tf.keras.layers.Flatten(input_shape=(28,28,1))
密集层:输入将和密集层完全连接,该密集层共128个单元,在这里需要激活函数,其实现代码为:
输出层:输出层共有10个单元,因为该数据集总共有10个类别,在这里每张图片的预测结果应为图像在每个类别中的概率大小,其和为1。(对于任何一个分类神经网络,最后都会添加一个密集层,其包含的单元数量和类别数量一样,并且始终会添加softmax语句)

训练和测试

数据集通常会划分为不同的子集,如训练集测试集,用于训练神经网络和测试神经网络的最终效果。通过测试数据集,我们可以使用网络从未见过的数据测试网络。这样我们便能检测模型的泛化程度,即泛化到训练期间未见过的数据的效果,而不是仅仅记住训练样本。同样,我们通常会使用验证数据集。此类数据集不用于训练,而是在训练期间用于测试模型。我们在一定的训练步数之后使用验证集,判断训练进展如何。例如,如果在训练过程中损失降低了,但是验证集的准确率下降了,则表明模型在记住测试集。训练完毕时,也会使用验证集衡量模型的最终准确率。 扩展阅读:Google 机器学习速成课程的训练和测试集部分
当有一个很大的数据集合,一部分用于训练一部分用于测试,测试集的选择需要注意随机化处理,一般训练集越大,训练出的模型越精确,测试集越大,对评估指标的信心越充足,置信区间就越窄。 如果数据集规模比较大,则取其10% ~ 15%就可以得到良好的验证效果,如果数据集比较小,则验证阶段就需要交叉验证等复杂操作。典型陷阱:不要将用于测试的集合同时用于训练,这样会得出非常优秀的结果(如测试准确率100%)。

Code总结

  • epochs: epochs的参数对模型的精度有直接的影响,周期数过小,模型欠拟合,精度不够。周期数过大,模型过拟合,在训练集精度很高,在测试集上训练精度很低.
  • 密集层神经元数量: 神经元过少会明显减小检测的精度,总体上来说,神经元增加会增加会提升精度(根据源码中的练习得出的不完全实验结论)
  • 增加网络层数
  • 玄学: 官方教程的源码和我的源码的结果差别很大,其一在于同样的参数我的模型最终精度会比课程给的源码的精度低一些。关键我还是按照官方给的代码手打的,而且课程给的参考源码有明显错误,比如模型的输出层没有使用softmax。

个人练习: https://colab.research.google.com/drive/1ItgEKIIG55Mc11dpgTQFLMJ-cvMmSnNX#scrollTo=z4Ji8sEmR4YH
课程源码: https://colab.research.google.com/drive/1RV1-alG87M8JWnMU0NaytOGHsieRq9-I#scrollTo=9ODch-OFCaW4

本章总结

机器学习的需要解决的问题主要由两个类别:递归问题和分类问题

  • Regression: 输出一个值的模型。例如,估算房屋价值。
  • Classfication: 一种模型,能够输出多个类别的概率分布。例如在 Fashion MNIST 模型中,输出是 10 个概率,每种服饰对应一个概率。我们在最后的密集层中使用 Softmax 激活函数创建了这个概率分布。

回归和分类的比较#

Cha4: CNN

Sebastian访谈

问:CNN特点
答:具有不变性,结构重复性,CNN的流行不是由于算法的变革,而是大数据
问:未来应用领域
答:图像,语言领域

CNN简介

卷积神经网络两个最重要的概念就是卷积(Convolutions)和最大池化(MaxPooling)
卷积: 卷积的运算如下图所示,简单来说就是窗口中的每个pixel的值与卷积核中相应位置的核素相乘,最后将所有的值相加放在居中位置。在这里涉及到边缘像素处理问题,有两种处理方式,其一是将边缘的像素舍弃,但这会造成信息丢失问题;其二是将原图像边缘补0,然后再进行卷积运算。
卷积运算示例
边缘像素处理
最大池化: 最大池化是指通过总结区域减小输入图像大小的流程,如下图所示,最大池化设计两个主要参数:网格(pool size)步长(stride). 最大池化的过程是找出网格中的pixel最大值并放在新的图像中的相应位置,步长代表窗口的滑动距离,下图中的步长为2。 MaxPooling的过程也叫做下采样。
最大池化示例

小结: 卷积是指向图像应用滤波器(核)的过程。最大池化是指通过下采样降低图像大小的过程。在 Keras 中使用 Conv2D 层级类型向神经网络模型中添加卷积层。此层级与密集层类似,并且需要将权重和偏差调整为合适的值。Conv2D 层级也有核(滤波器),我们也需要调整这些滤波器的值。在 Conv2D 层级中,我们将调整滤波器矩阵中的值,从而生成正确的输出。

术语:

  • CNN: 卷积神经网络。即至少有一个卷积层的网络。典型的 CNN 还包括其他类型的层级,例如池化层和密集层。
  • 卷积: 向图像应用核(滤波器)的过程
  • 核/滤波器: 小于输入的矩阵,用于将输入变成多个小区域
  • 填充: 在输入图像周围添加像素,像素值通常为 0
  • 池化: 通过下采样降低图像大小的过程。池化层有多种类型。例如,平均池化通过求平均值将多个值变成一个值。但是最大池化是最常见的池化类型。
  • 最大池化: 一种池化过程,通过获取多个值中的最大值,将多个值变成一个值。
  • 步长: 在图像上滑动核(滤波器)的间隔像素数量。
  • 下采样: 降低图像大小的操作

扩展阅读:卷积神经网络综合指南

cha5 CNN深度学习

Sebastian访谈

过拟合是由偏差方差权值(bias-variance trade-off)导致的,相对于数据来说,参数越多,越有可能选择完全随机的解,而不是一个好的解。有两种方法可以解决过拟合问题,第一种方法是拆分数据集,如90%的数据作为训练集,10%的数据作为测试集,然后执行交叉验证(cross validation),即使训练时没用到测试集检查模型性能,如果损失上升,则停止训练。第二种方法是约束网络。例如可以使权重加上偏差,假设使所有权重尽可能接近零,对这些权重的约束越强,越不可能过拟合。此外还可以设置测试集,训练集和验证集,这里还有一个问题,如果在测试集上测试了很多次,最终结果将过拟合测试集。

猫狗分类

CNN网络不仅可以处理灰度图像,还可以处理彩色图像。 在这里将采用CNN结构训练一个可以识别猫狗的模型,所采用的数据集是微软Asirra猫狗数据集。数据集中的图片有两种标签:0代表cat, 1代表dog。该数据集中的图片尺寸各不相同,因但神经网路要求的输入尺寸是固定的,此需要进行预处理。tf.keras.layers.Flatten(input_shape=(28, 28, 1))函数在图像尺寸大小不同时,扁平化生成的以为数组的大小也不同,因此需要先将尺寸统一,再进行扁平化操作。
彩色图像处理
计算机会将彩色图像解析为三维数组,如下图所示:
彩色图像表达
对RGB图像执行卷积运算即分别对R, G, B, 三个通道进行卷积操作,在计算出每个通道的结果后将其相加,再加上一个偏差值1,放入卷积输出中去。
彩色图像卷积输出
在实际使用CNN的时候,通常会采用多个三维过滤器,最后得出多个卷积输出结果。卷积输出的深度由卷积核的数量来决定。注意,在训练CNN时,我们将更新三维核中的值从而最小化损失函数。
多个卷积核

最大池化
在三维卷积输出的结果上执行最大池化运算的方法和灰度图像的运算方式相同。
彩色图像最大池化

colab键盘快捷键

  • ⌘/Ctrl+m,b:在当前选择的单元格下方创建一个空白代码单元格

  • ⌘/Ctrl+m,i:中断单元格的运行

  • ⌘/Ctrl+m,h:显示所有键盘快捷键列表

  • ⌘/Shift+Enter:执行当前单元格并自动新建下一个的单元格

  • 要查看关于任何 TensorFlow API 方法的文档,请将光标放置在其左括号的正后方,然后按 Tab 键:

    TensorFlow tf.constant 方法的弹出式文档

Softmax 与 S 型函数
在上个 Colab 中,我们使用了以下 CNN 结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
model = tf.keras.models.Sequential([
tf.keras.layers.Conv2D(32, (3,3), activation='relu', input_shape=(150, 150, 3)),
tf.keras.layers.MaxPooling2D(2, 2),

tf.keras.layers.Conv2D(64, (3,3), activation='relu'),
tf.keras.layers.MaxPooling2D(2,2),

tf.keras.layers.Conv2D(128, (3,3), activation='relu'),
tf.keras.layers.MaxPooling2D(2,2),

tf.keras.layers.Conv2D(128, (3,3), activation='relu'),
tf.keras.layers.MaxPooling2D(2,2),

tf.keras.layers.Flatten(),
tf.keras.layers.Dense(512, activation='relu'),
tf.keras.layers.Dense(2, activation='softmax')
])

注意,最后一个层级(分类器)由一个 Dense 层(具有 2 个输出单元)和一个 softmax 激活函数组成,如下所示:
tf.keras.layers.Dense(2, activation='softmax')
在处理二元分类问题时,另一个常见方法是:分类器由一个 Dense 层(具有 1 个输出单元)和一个 sigmoid 激活函数组成,如下所示:
tf.keras.layers.Dense(1, activation='sigmoid')
这两种方法都适合二元分类问题,但是请注意,如果决定在分类器中使用 sigmoid 激活函数,需要将 model.compile() 方法中的 loss 参数从 ‘sparse_categorical_crossentropy’ 更改为’binary_crossentropy’,如下所示:

1
2
3
model.compile(optimizer='adam', 
loss='binary_crossentropy',
metrics=['accuracy'])

General machine learning workflow

  1. Examine and understand data
  2. Build an input pipeline
  3. Build our model
  4. Train our model
  5. Test our model
  6. Improve our model/Repeat the process

Dynamic Occupancy Grid Prediction for Urban Autonomous Driving

Abstract

长期状况预测对智能车辆起着至关重要的作用。仍然需要克服的主要挑战是如何预测具有多个道路用户(例如行人,自行车和机动车辆)相互交互的复杂市区场景。该贡献在于通过结合用于环境表示的贝叶斯过滤技术和作为长期预测器的机器学习来解决这一挑战。更具体地说,动态占用网格图被用作深度卷积神经网络的输入。这产生了使用单个时间步长的空间分布速度估计值进行预测而不是原始数据序列的优势,从而减轻了处理多个传感器的输入时间序列的常见问题。此外,卷积神经网络具有使用上下文信息的固有特性,从而可以对道路用户交互进行隐式建模。损失函数中采用了像素级平衡,以抵消静态和动态单元之间的极端不平衡。主要优点之一是由于全自动标签生成而导致的无监督学习特征。在多个小时记录的传感器数据上对提出的算法进行训练和评估,并将其与蒙特卡洛模拟进行比较。实验表明,可以对复杂的交互过程进行建模

Introduction

过去几十年的研究在自动驾驶领域取得了巨大的成功。通过将自动驾驶汽车与人类驾驶员进行比较,人类可以通过估计未来场景演变的能力来补偿相对较长的反应时间。这还可以实现战略性和前瞻性的行为,难以用机器的快速反应和精确感知来弥补。因此,自动驾驶的长期状况预测仍然是要克服的主要挑战。我们应对汽车,卡车,自行车和行人共享道路的高度复杂的城市环境。道路使用者会各自做出反应,但具有特定于类型的运动约束。最重要的是,步行或驾车的人在这种情况下并不会独立行动,而是互动并考虑他人的可能行为。因此,个人的决定和道路可能会对某人或其他人的未来行为产生影响。因此,这种交通场景可能演变成许多可能的未来场景星座,从而激发了对预测结果进行概率表示的需求。简而言之,城市自动驾驶的长期状况预测应该能够纳入所有已知交通参与者的互动,并且由于问题的不确定性,可以应对可能结果的差异

交互和运动动力学模型是改善预测的常用工具。文献涵盖使用工程和学习模型进行的长期预测。手动设计道路用户预测中的依赖性(例如,使用数字地图或智能驾驶员模型[1])通常是相当局限性的假设,例如,车道跟踪[2]或汽车跟踪[3]场景。另外,考虑到上下文和对象关系的手工模型往往是高维的[1],[4],这激发了机器学习的替代性。与机器学习在分类方面的巨大成功相一致,提出了一些方法来预测关于离散操纵类的未来操纵,例如,停止,直行或转弯[5],[6]。然而,就时间序列而言预测轨迹与就泛化而言预测机动类别不同。为了在算法输出处获得时空分布,这是数据表示的问题。 Wiest [7],例如,切比雪夫多项式的预测参数在高斯混合模型中被视为随机变量

与算法输出相反,考虑输入处原始传感器数据的时间序列,尽管有数据表示,但仍会出现一些技术问题。在2018年5月21日至25日于澳大利亚布里斯班举行的IEEE国际机器人与自动化大会(ICRA)上,采样频率的变化,异步传感器的缩放和转换,或时域输入不规则,都是机器学习面临的挑战[8 ],但已由传感器融合社区进行了深入研究[9],[10]。从机器学习的角度来看,不是从时间序列中学习时间特征并面对上述问题,而是通过表示短期动态特征的一阶微分方程来关联单个时间戳的状态变量。此外,我们假设空间上下文可以弥补长期输入序列的不足。这个假设是受股票市场预测研究的启发[11],众所周知,使用过去的时间序列不足以预测未来的股票趋势[12]。而且,还不清楚它们是否有帮助[13]。然而,可以通过上下文信息(例如Twitter情绪[14]或在线聊天[15])来提高预测性能[11]。
图一
如图1所示,可以在动态占用栅格图(DOGMa)中看到空间上下文。DOGMa使用贝叶斯滤波[16]融合各种传感器,并采用了鸟瞰图(静态和动态)。 每个多通道像素或单元包含概率占用率和速度估计。 此外,DOGMas的类似图像的数据结构自然建议使用卷积神经网络(CNN)来利用上下文信息以及不同对象与给定基础结构之间的依赖关系。

因此,在这项工作中,结合了基于工程和基于学习的方法的优势。 卷积神经网络(CNN)用于对长期运动进行建模,输入端使用当前动态环境的贝叶斯估计。 对于训练,无需执行手工标记,但是,使用未来和过去的估计方法应用了将稀有动态与静态网格单元分离的算法。 首先,这可以在训练过程中在静态区域和动态区域之间实现平衡,而且可以对当前环境进行学习,得出的结果优于工程方法。

论文的剩余部分的结构如下。 在第二部分中,我们概述了动态占用网格。 第三部分介绍了用于无监督学习的全自动标签生成。 IV简要介绍了所采用的CNN架构。 在第V节中,引入了像素平衡损失函数,以抵消动态单元的高代表性。 结果显示在第七节,随后是结论在第八节

Filtered Dynamic Input

如图1所示,由多个传感器提供的动态占用栅格图(DOGMa)[16]可以提供鸟瞰图,例如360°的环境表示。DOGMa数据以R^|Ω|×W×H提供,空间宽度W和高度H分别指向东和北。 通道Ω={MO,MF,vE,vN,σ2vE,σ2vN,σ2vE,vN} 包含自由空间MF∈[0,1]和占用MO∈[0,1]的Dempster-Shafer质量,指向东vE和向北vN的速度以及速度方差和协方差。占用概率的计算公式为:

高PO表示图1中的暗像素。尽管图1仅包含3个RGB通道,但所有通道Ω和扩展都可用于馈送神经网络。

由于两个主要优点,有意避免将原始传感器数据输入到CNN中:1)DOGMa单元为每个单元提供带有协方差矩阵的速度估计,从而导致空间占用和速度分布。 为此,该算法使用了与时域中的等待时间和方差有关的高级传感器融合技术,这些技术很难通过基于学习的方法来补偿[8]。 2)DOGMa输出格式与传感器设置无关,例如将雷达和激光或仅具有不同范围的激光融合在一起,这在训练具有变化传感器的神经网络时可能是有利的。 通常,该算法使用工程模型来利用依赖传感器的优势。 这些模型可以,例如,考虑从激光测量获得的自由空间,或来自雷达的自我运动补偿的多普勒速度[16]。 如果传感器规格发生变化,则可以轻松调整参数

DOGMa基于顺序蒙特卡洛滤波。 网格单元具有固定的位置和宽度,并且地图边框会移动单元宽度,以使移动中的自我车辆保持在中心单元内。 过滤器算法在两个模块中运行:在第一个模块中,使用传感器特定的测量模型将传感器数据转换为经典的占用网格图[17],[18]。 这些网格用作第二个模块的通用数据接口,一个粒子过滤器估计空间占用率和速度分布[16]。 每个网格单元均独立更新,并且不对单元之间的交互进行建模。 尽管提出了从网格单元中提取建模对象的手工方法[19],但在这项工作中,不需要在CNN输入或标记时生成对象。 由于在Dempster-Shafer域[20]中的实现,DOGMa提供了有关占用率,自由空间和不可观察区域的概率信息

Automatic Output Label Generation

标注通常是一个广泛的过程,也是监督学习中的主要缺点之一。幸运的是,根据场景预测的性质,可以在以后的时间观察到所需的算法输出。一旦可以应用自动标签提取,生成培训数据就相对容易了。但是,大约99.75%的环境数据是静态的,这可能会使学习算法偏向于预测未来等于当前输入样本的琐碎结果。因此,需要对代表性不足的动态环境进行细分以应用平衡方法。文献[16]提出了一种在感知时在线对动态细胞进行分类的方法。但是,当静态物体进入视场或在嘈杂区域(例如,静态物体的边界)时,使用当前的像元速度估计会导致分类错误。由于标签提取可以离线进行,因此可以使用将来和过去的使用过程来实现更好的分类,从而例如区分正在生长的静态部分和实际运动的对象.

如图2所示,对于每个单元,提取准时间连续PO(t)。该图说明了在恒定空间坐标(E,N)下针对网格单元的PO(t)的提取,而自我车辆是 自身在动态环境中移动。 期望的预测结果是一个序列:

其中时间步长序列k =(1、2,…)可以在CNN的相应输出通道中表示。 另外,需要分割成静态和动态部分。 因此,一个可以写做:

在静态环境中具有恒定的占用概率PO,s和描述占用概率的序列(PO,d(k))完全源自动态元素。可以通过在静态和动态时间ts和td中按单元对数据进行划分来在时域中自动进行分段:当对象遍历某个单元时,DOGMa中的贝叶斯滤波器会平滑收敛到最大PO,而当目标遍历单元时,它会回到静态水平。物体离开细胞(见图2)。 PO(t)的上升接着信号的下降被用来检测动态对象的存在,因此之间的时间定义了间隔td。该计算基于二阶导数,然后进行非最大抑制。在离线提取序列中,通过将td内的时间步长(PO(k))设置为max(PO(td))来校正DOGMa滤波器的收敛延迟。曲线PO(t)可能非常嘈杂,尤其是在未知区域(PO = 0.5)或静态,自由和未知之间的空间过渡时。因此,使用时域中的高斯滤波器对Ω中的数据进行平滑处理。相应地平滑了空间域中的噪声,例如单个动态单元。

最后,(3)中的恒定静态PO,s由ts∈(t0,t0 + T)\ td的PO,s(ts)的中位数计算,其中t0表示DOGMa输入时间,T表示预测范围。 R | T |×W×H的网络输出提供了通道T = {PO,s,PO,d(1),PO,d(2),…,PO,d(| T | − 10)

CNN Architecture

深度神经网络架构可以利用DOGMa输入中的遥远关系。 但是,输出结构也应提供空间分布。 [21](DeconvNet)和[22](Fully Convolutional Networks,FCN)的网络体系结构在按比例缩减级联之后提供按比例缩放层。 这些方法旨在从单个输入图像进行像素精确的图像分割。 Shelhamer和Long [22] [23]周围的小组使用Zeiler和Fergus [24]提出的去卷积运算来缩小采样的特征图的大小,以实现可视化目的。 上采样是在单层中执行的,而较早的层结果则具有较高的分辨率,用于获得精细的输出图。 与可学习的内核相反,[21]和[25]的作者使用[26]的解池方法。[22]使用单个上采样步骤,[21]分阶段执行下规模镜像下采样阶段的升级,并重用最大池下规模的索引以通过下池进行上规模。 通过在每个解池层中进行后续卷积,可以使所得的稀疏特征图变得密集。 在我们的方法中,我们遵循由[21]提出的,由按比例缩小的级联和按比例放大的阶段进行镜像处理的单个输入DOGMa的策略。 但是,我们选择了可学习的反卷积内核而不是解池,并结合了按比例缩小阶段的绕过结果。

除了像素分类,我们的目标是在未来的时间步长预测占用的单元格。 此外,网络将环境的静态和动态部分分段,并在一个通道中传递静态网格单元的占用率,而在其余通道中传递动态单元的占用率。