多伦多大学自动驾驶视觉感知笔记-全-

多伦多大学自动驾驶视觉感知笔记(全)

001:欢迎学习自动驾驶汽车专项课程

在本节课中,我们将介绍多伦多大学自动驾驶汽车专项课程的整体框架、学习目标以及必要的预备知识。

大家好,欢迎来到多伦多大学的自动驾驶汽车专项课程。我是Steven。

我是Jonathan,我们将作为您在整个专项课程中的讲师。自动驾驶汽车是沉睡的巨人,它有能力改变一切:道路安全、每个人的出行便利性,同时大幅降低驾驶成本。本专项课程将教授您在自动驾驶行业开启职业生涯所需的知识。

无论您来自学术界还是工业界,这些课程都将为您提供基础知识和实践技能,帮助您与自动驾驶汽车共同构建新的未来。在整个课程中,我们将向您展示来自我们研究以及我们自己的自动驾驶汽车(我们称之为“Autonoousose”)的真实世界数据和场景。

经过数十年的应用研究和获奖研究,我们多伦多大学团队很高兴向您展示自动驾驶的核心技术细节。

本专项课程共包含四门课程。

在第一门课程中,我将向您介绍自动驾驶汽车的软件和硬件架构。在本课程结束时,您将能够为自动驾驶汽车设计一个基本的硬件系统,识别自动驾驶软件栈的主要组件,并为自动驾驶汽车项目制定安全评估策略。

您还将学习建立完整的车辆运动模型,为纵向控制定义PI控制器,为横向车辆控制定义路径跟踪控制器,并在CARLA模拟器中测试您的控制设计。

在专项课程的第二门课程中,我将教授您自动驾驶汽车的状态估计、传感与定位。在本课程结束时,您将理解参数和状态估计的关键方法,开发并使用典型车辆定位传感器的模型,将扩展卡尔曼滤波器和无迹卡尔曼滤波器应用于车辆状态估计问题,并将LiDAR扫描生成的点云注册到静态环境的3D地图上。

在第三门课程中,我将教授您自动驾驶汽车的视觉感知。完成本课程后,您将能够将3D点投影到相机图像平面上,校准针孔相机模型,应用特征检测、描述和匹配算法进行定位与建图,开发并训练用于目标检测和语义分割的神经网络。您将把这些方法应用于车辆跟踪和可行驶区域估计。

在第四门也是最后一门课程中,我将教授您自动驾驶汽车的运动规划。在本课程结束时,您将能够设计轨迹展开运动规划方法,计算与静态和匀速运动物体的碰撞时间,在复杂的道路网络上规划路线,为车辆定义高级行为及状态转换(例如通过路口、绕过停放的车辆和并道),在存在静态障碍物的环境中开发运动学上可行的路径,计算满足速度、曲率和移动物体运动规划约束的速度曲线,规划行为并执行操作以安全地在世界中导航,并在CARLA模拟器中获得调试和测试自动驾驶算法的宝贵经验。

因此,在专项课程结束时,您将对自动驾驶软件栈的架构和组件有详细的了解,并将能够编程您自己的自动驾驶汽车。

由于我们教授的是如何编程您自己的自动驾驶汽车,本专项课程有几项先决条件。

首先,您应精通线性代数,并熟悉矩阵、向量、矩阵乘法、秩、特征向量和逆矩阵。这些背景知识将帮助您理解整个课程中的控制、状态估计、感知和规划算法。

您还应熟悉统计学,特别是高斯概率分布。这些知识对于状态估计和感知非常重要,例如当我们从GPS和惯性测量中估计车辆速度和航向时。

您还应熟悉基础微积分和物理学,例如力、力矩、惯性和牛顿定律。

当然,了解如何驾驶汽车是有帮助的,但由于汽车是自动驾驶的,这并非本专项课程的硬性要求。

如果您不具备这些必要的先决条件,Coursera上有优秀的机器人学、人工智能、深度学习等课程可以帮助您为本专项课程做好准备。

自动驾驶是一个不断发展和变化的领域,因此不仅跟上自动驾驶知识,而且跟上机器人学、人工智能和深度学习的发展,将有助于您保持技术技能的敏锐。

我们还有很长的路要走,我们需要先驱者来帮助我们到达那里。那么,您准备好开始这段旅程了吗?

🎼是的。


在本节课中,我们一起学习了多伦多大学自动驾驶汽车专项课程的总体介绍,包括其四门核心课程(架构与控制、状态估计与定位、视觉感知、运动规划)的主要内容、学习目标以及学习本课程所需的数学和物理预备知识。

自动驾驶汽车视觉感知:P02:课程概述与大纲

在本节课中,我们将概述多伦多大学自动驾驶汽车专项课程中的视觉感知课程。我们将介绍课程目标、核心内容以及每周的学习计划。

欢迎来到多伦多大学自动驾驶汽车专项课程的视觉感知课程。我是Steve,将再次担任本课程的讲师。至此,您应该已经完成了自动驾驶汽车专项课程的前两门课程。在前两门课程中,您学习了如何使用包括GPS、IMU和激光雷达在内的多种传感器来构建模型、控制并定位自动驾驶汽车。

本课程将聚焦于自动驾驶汽车中最通用的传感器之一:摄像头。😊

摄像头充当自动驾驶汽车的“眼睛”。汽车使用摄像头来检测道路上的其他交通参与者,对其运动和行为进行建模。来自摄像头的图像也可用于检测和定位道路标记、信号和标志,以实现安全且合法的驾驶行为。我们还可以像使用激光雷达传感器一样,利用摄像头进行定位。

在本课程结束时,您将能够使用并校准这些摄像头,为自动驾驶汽车构建基础的感知系统。

现在,让我们浏览课程大纲,了解每周的学习内容。😊

以下是本课程各模块的详细内容:

第一周:3D计算机视觉基础
本模块将介绍3D计算机视觉的基础知识。您将学习图像是如何形成的、相机投影几何、如何校准相机,以及如何通过立体视觉从相机生成3D点云。最后,您将学习如何使用常见的图像滤波器处理图像。

第二周:手工特征提取与视觉里程计
在第二模块中,您将学习如何通过手工设计的特征从图像中提取有用信息。您将学习如何提取这些特征、为它们提供描述符,并学习如何匹配这些特征。最后,我们将利用这些匹配的特征来定位相机,这个过程被称为视觉里程计。

第三周:神经网络基础
第三模块将涵盖神经网络,这个概念已经改变了我们对自动驾驶汽车感知的思考方式。在本模块中,您将学习前馈神经网络的构建模块、如何训练这些网络以及如何评估其性能。同时,您还将学习一种特殊类型的前馈神经网络——专为处理相机图像而设计的卷积神经网络。

第四周:2D目标检测
在第四模块中,您将使用神经网络执行2D目标检测,这是一个联合分类与回归任务。您将学习如何构建2D目标检测问题、如何评估2D目标检测模型、如何构建执行2D目标检测任务的神经网络,以及如何在自动驾驶汽车的背景下使用2D目标检测器的输出。具体来说,您将使用2D目标检测结果来预测3D位置并跟踪道路场景中感兴趣的物体。

第五周:语义分割
课程的第五周将涵盖语义分割,这是自动驾驶汽车感知系统的另一个重要组成部分。与2D检测模块类似,您将学习如何构建语义分割问题、评估语义分割模型,并使用卷积神经网络执行语义分割任务。最后,您将学习如何使用语义分割输出来执行可行驶空间估计和车道边界检测。

第六周:课程项目
在课程的最后一周,您将应用所学的一切知识来完成最终的课程项目。该项目要求您仅使用相机数据作为输入,实现一个自动驾驶汽车避障系统。

完成本课程后,您将为开发自动驾驶汽车所需的复杂感知系统的主要组成部分打下良好的基础。

希望您会发现这门课程既有趣又有益于您的职业生涯。无论您想成为自动驾驶汽车研究员、感知系统开发者还是系统集成工程师,现在都是构建自动驾驶汽车的好时机。

在本节课中,我们一起学习了视觉感知课程的总体目标、摄像头在自动驾驶中的核心作用,以及涵盖从3D视觉基础到神经网络应用、再到最终实践项目的完整课程大纲。

003:认识讲师史蒂文·瓦斯兰德

在本节课中,我们将通过讲师史蒂文·瓦斯兰德的个人经历与职业发展,了解他如何从一名广泛的兴趣爱好者成长为自动驾驶与机器人领域的研究者,并探讨该领域的核心挑战与学习路径。


童年与广泛的兴趣

我童年时兴趣广泛,热爱各种事物。我曾认为自己可以成为一名科学家,或许是物理学家,也可能是化学家,但当时对未来并没有明确的想法。我的成长过程是懵懂的。

我是一名重度运动爱好者,热爱越野滑雪和山地自行车。这些活动占据了我的大部分时间,我并未过多担忧未来,度过了一个田园诗般的童年。

青年时期的挑战与方向

大约在16至18岁时,我开始思考必须做一些有趣的事情来度过人生。正是在这个阶段,我开始接受各种挑战,并逐渐认识到是什么在持续驱动着我前进。

我热爱解决数学问题,也喜欢在放学后与朋友们一起研究化学或微积分难题。这些都是我在没有明确目标的情况下,利用课余时间所做的事情。

直到我开始认真尝试选择一项事业时,我才发现,在这些特定的应用领域中,我所喜爱的事情会频繁出现。


职业生涯的起点:航空航天工业

大学毕业后,我在普拉特·惠特尼加拿大公司工作,这是一家小型喷气发动机制造商。在普拉特·惠特尼工作期间,经历非常吸引人。

我们有机会测试喷气发动机控制系统,这让我接触到了许多有趣的航空学知识。然而,航空学领域的挑战在于其极其严格的安全要求。

你所做的每一件事都必须严谨可靠,并且每次执行都必须精确无误。因此,该领域的发展实际上受制于经过反复验证的、已有30年历史的技术。

作为一名试图推动变革、改变世界的年轻工程师,这让我感到非常沮丧。于是,我带着从航空航天工业中获得的宝贵经验以及对安全的专注,进入了研究生阶段的学习。

转向机器人学与无人机研究

我进入了航空与航天领域,并开始建造无人机。这本质上是解决问题和开发算法。

例如,让无人机降落在行驶的汽车上,或者让自动驾驶汽车自行泊车、穿越障碍区域。所有这些问题都非常实际,具有高度应用性。

在构建过程中,你可以直观地看到结果。如果无人机错过了汽车,它就会坠毁失败。这其中有一种令人兴奋的趣味性。


构建自动驾驶汽车的基础步骤

要制造一辆自动驾驶汽车,首先必须将一辆普通汽车改造成线控驾驶车辆。这意味着你可以将计算机接入汽车,并向油门、转向和刹车系统发送指令,汽车将实际执行这些操作。

一旦具备了这种能力,你就可以开始编程让它执行任何你想要的指令。

接下来,你必须在后备箱中添加一整套传感器和计算设备。拥有了所有这些部件,你就可以开始构建自己的自动驾驶车辆。

研究重点与学术使命

我的研究实验室围绕一个理念建立:如果我们拥有一个真实的测试平台,并将其置于真实情境中,我们就能更快地了解当前方法的局限性。

我们真正关注的是寻找特别适合学术界的研究问题。例如,处理恶劣天气条件、应对某些边缘情况,或者试图从理论上证明我们所使用的方法具有可量化的安全性。

在学术界,我们确实可以研究许多具体问题。我们可以尝试各种大胆的想法,可以比工业界更自由、或许更快地探索可能的解决方案空间。


行业展望与当前挑战

我们肯定会看到这些车辆的性能不断提升。例如,特斯拉的自动驾驶系统每年都会变得越来越好。我们还将看到真正自主的车队在受限领域内运行,这些领域被认为是安全的。

但我仍然认为,要实现最终的、在任何你想驾驶的地方都能实现的完全自动驾驶,我们还有很多工作要做。

这仅仅是因为汽车可能失败的方式太多,它们必须理解的情境太复杂,有太多无法提前预测的奇怪状况。

给新学习者的建议

如果你是自动驾驶领域的新手,认为这是一个美妙的领域并想尝试,我认为你首先应该完成这个Coursera专项课程。

除此之外,还有数百种方法可以丰富你的知识,并更好地了解如何进入这个领域。整个领域依赖于机器人学、计算机视觉、神经网络与深度学习的基础。

这些领域你可以进行广泛学习,其有趣之处在于它们也适用于其他美妙的应用。例如机器人操纵器、手机摄像头系统与视频拍摄功能的改进、无人机飞行与物体跟踪等。你可以用这些知识做很多事情。

对于那些正在思考自动驾驶是否是一个值得探索的方向的人来说,这个市场显然将持续增长。对我们项目中毕业生的需求是巨大的,这非常惊人。


总结与个人感悟

距离我在放学后随意研究数学难题已经过去很久了。想到我现在从事自动驾驶汽车的研究,并且已经玩转无人机多年,我只是一个幸运的人。

在本节课中,我们一起了解了史蒂文·瓦斯兰德如何将广泛的兴趣与实际问题解决相结合,最终投身于自动驾驶与机器人技术的前沿研究。我们探讨了构建自动驾驶系统的关键步骤、学术界的研究重点、行业面临的挑战,并为初学者提供了清晰的学习路径指引。这个领域充满活力与机遇,正等待着新的探索者。

004:认识讲师乔纳森·凯利

概述

在本节课中,我们将认识本课程的讲师乔纳森·凯利教授,了解他的个人背景、职业选择动机,以及他对自动驾驶技术安全性与创新性的看法。


我的名字是乔纳森·凯利,我是加拿大多伦多大学航空航天研究所的一名助理教授。

童年时期,我每个月都喜欢去杂志店。我总是阅读《大众机械》或《大众科学》这类杂志。

这些杂志包含了从太空旅行到新型交通工具,再到新计算设计的各种有趣文章。内容确实非常吸引人。

因此,这最终促使我开始将科学技术视为一个可能的职业选择。

在我还很小的时候,发生了一件事,即第一次航天飞机灾难——挑战者号灾难。我至今仍清楚记得那一幕。

我当时想,天哪,一定是出了什么严重的问题,后果是多么可怕。那时发生了巨大的爆炸。

我记得当时震惊地坐着,认为技术似乎总是有效的。这是我的看法:技术总是有效的,它不会失败。汽车不会就这样爆炸。

在电视上看到这一幕,让我真切地认识到,工程确实至关重要。在这些威胁生命或可能改变人生的情境中,扎实的工程是极其重要的。

自动驾驶汽车在许多方面非常相似。作为工程师,我们有责任将这些系统设计得尽可能安全可靠。

这是最令人兴奋的技术革命之一,它真的即将到来。新的想法、新的技术、新的设计每天都在涌现。

我经常坐在办公桌前,看到人们提出的创新性新想法和技术时,都会感到眼前一亮。

这种兴奋感也来自于它实际上正在改变技术格局,并且正在做一些可能惠及许多人的事情。


总结

本节课我们一起认识了乔纳森·凯利教授。我们了解到他从小对科学技术的兴趣如何引导他走向工程师生涯,以及挑战者号灾难如何让他深刻认识到工程安全的重要性。最后,他表达了对自动驾驶技术革命性潜力的兴奋,并强调了工程师在确保系统安全可靠方面的重大责任。

005:摄像头传感器

在本节课中,我们将要学习摄像头作为自动驾驶汽车核心传感器的重要性,并介绍图像形成的基本原理——针孔相机模型。

为什么摄像头至关重要?📷

在自动驾驶领域,大多数主要厂商都将摄像头作为其车辆传感器套件中的主要传感器。摄像头是一种信息丰富的传感器,能够捕捉车辆周围环境的惊人细节,但需要大量的处理才能利用图像中的信息。

在所有常见的自动驾驶传感器中,摄像头提供了来自环境中物体的最详细的外观信息。外观信息对于场景理解任务(如物体检测、分割和识别)特别有用。正是外观信息使我们能够区分路标或交通灯状态、跟踪转向灯,并将重叠的车辆解析为独立的个体。

由于其高分辨率的输出,摄像头能够收集并提供比自动驾驶中使用的其他传感器多几个数量级的信息,同时成本相对较低。高价值的外观信息和低成本的结合,使摄像头成为我们传感器套件中必不可少的组成部分。

图像是如何形成的?🔍

让我们看看摄像头是如何收集如此大量信息的。摄像头是一种被动的外感受传感器。它使用成像传感器来捕捉由世界物体发出的光线所传递的信息。

光线从物体上的每一点向各个方向反射,其中一部分光线射向相机传感器。然而,如果使用一个简单的开放式传感器设计,我们最终会得到模糊的图像,因为成像传感器在同一位置收集了来自物体上多个点的光线。

针孔相机模型📐

解决这个问题的方案是在成像传感器前放置一个屏障,屏障中心有一个微小的孔或光圈。该屏障只允许少量光线通过光圈,从而减少图像的模糊度。这个模型被称为针孔相机模型,它描述了世界中的一个点与其在图像平面上相应投影之间的关系。

针孔相机模型中两个最重要的参数是:

  1. 焦距:针孔与图像平面之间的距离,用 f 表示。焦距定义了物体投影到图像上的大小,在使用镜头改善相机性能时,对相机对焦起着重要作用。
  2. 相机中心:针孔中心的坐标,用 (c_x, c_y) 表示。这些坐标定义了物体投影将占据的成像传感器上的位置。

尽管针孔相机模型非常简单,但它能很好地表示图像创建过程。通过确定特定相机配置的焦距和相机中心,我们可以用数学方法描述从世界物体发出的光线将击中图像平面的位置。这使我们能够为状态估计和物体检测建立一个图像形成的测量模型。

历史实例与现代发展🚀

针孔相机模型的一个历史实例是暗箱。历史证据表明,这种成像形式早在公元前470年就在古代中国和希腊被发现。其简单的结构——在成像表面前有一个针孔光圈——使其易于自行重现。

自暗箱发明以来,我们已经取得了长足的进步。当今的相机使我们能够收集极高分辨率的数据。由于先进的光学镜头可以收集大量光线并将其精确聚焦在图像平面上,它们可以在弱光条件下或远距离工作。相机传感器的分辨率和灵敏度不断提高,使相机成为地球上最普遍的传感器之一。

这些进步对于理解自动驾驶汽车周围的环境也极为有益。正如我们在第一门课程中讨论的,专为自动驾驶汽车设计的摄像头需要在各种光照条件和物体距离下都能良好工作。这些特性对于在所有操作条件下安全驾驶至关重要。

总结📝

在本节课中,我们一起学习了摄像头作为自动驾驶传感器的实用性。我们还看到了最基本形式的针孔相机模型,我们将在整个课程中使用它来构建视觉感知算法。

在下一个视频中,我们将描述图像是如何形成的,这个过程被称为投影几何,它将世界中的物体与其在成像传感器上的投影联系起来。

006:相机投影几何 📷

在本节课中,我们将学习如何通过坐标系变换来建模相机的投影几何。这些变换可用于将世界坐标系中的点投影到图像坐标系中。我们将基于上一节介绍的针孔相机模型,使用矩阵代数来建模这些变换,并将其应用于一个三维点,以获取其在二维图像平面上的投影。最后,我们将了解相机拍摄的二维图像在软件中是如何表示的。掌握了投影方程和图像定义后,您将能够在后续课程中创建用于检测三维物体和定位自动驾驶汽车的算法。

问题定义

首先,让我们定义需要解决的问题。假设在世界坐标系中有一个点 O_world。我们的目标是将这个点从世界坐标系投影到相机的图像平面上。

光线从物体上的点 O_world 出发,穿过相机光圈,到达传感器表面。您会发现,通过光圈在传感器表面上的投影会导致世界中的物体图像发生翻转。为了避免这种混淆,我们通常在相机中心前方定义一个虚拟图像平面。

让我们用这个虚拟传感器平面重新绘制相机模型,以取代相机镜头后方真实的图像平面。我们将此模型称为简化相机模型,并需要建立一个模型来描述如何将点从世界坐标 (X, Y, Z) 投影到图像坐标 (U, V)。

相机参数定义

以下是解决此问题所需定义的相机相关特性。

首先,我们选择一个世界坐标系,用于定义所有物体和相机的位置坐标。

其次,我们将相机坐标系定义为附着在镜头光圈中心(即光学中心)的坐标系。

正如我们在第二门课程中学到的,我们可以定义一个平移向量和一个旋转矩阵来建模任意两个坐标系之间的变换。在本例中,我们将处理世界坐标系和相机坐标系之间的变换。

我们将相机位姿的参数称为外参,因为它们相对于相机是外部的,并且特定于相机在世界坐标系中的位置。

接着,我们定义图像坐标系,它附着在从光学中心发出的虚拟图像平面上。

然而,图像像素坐标系附着在虚拟图像平面的左上角。因此,我们需要将像素位置调整到图像坐标系中。

最后,我们定义焦距,即相机坐标系与图像坐标系之间沿相机坐标系 Z 轴的距离。

投影步骤

我们的投影问题简化为两个步骤。

首先,我们需要将点从世界坐标投影到相机坐标。然后,我们再将其从相机坐标投影到图像坐标。之后,我们可以通过缩放和偏移将图像坐标转换为像素坐标。

现在,我们有了几何模型,可以随时将点从世界坐标系投影到图像坐标系。

数学建模

现在,让我们用线性代数来构建执行此投影所需的数学工具。

首先,从世界坐标到相机坐标的变换。这是通过刚体变换矩阵 T 完成的,其中包含旋转矩阵 R 和平移向量 t

下一步是将相机坐标变换为图像坐标。为了执行此变换,我们定义一个 3x3 矩阵 K。这个矩阵取决于相机的内参,即相机内部的组件,如相机几何结构和镜头特性。

由于这两个变换都是矩阵乘法,我们可以定义一个矩阵 P = K * [R|t],将点从世界坐标系直接变换到图像坐标系。

世界坐标系中点 O_world 的坐标现在可以通过方程 O_image = P * O_world 投影到图像平面上,即 O_image = K * [R|t] * O_world

齐次坐标与最终变换

让我们看看计算这个方程还缺少什么。当我们检查矩阵维度时,会发现矩阵乘法无法执行。为了解决这个问题,我们将点 O_world 的坐标转换为齐次坐标,方法是在其三维坐标末尾添加一个 1,正如我们在状态估计课程中所见。现在维度匹配,我们可以开始计算投影了。

现在我们需要执行最后一步,将图像坐标转换为像素坐标。我们通过将 X 和 Y 除以 Z 来得到图像平面上的齐次坐标。

您已经完成了基本的相机投影模型。在实践中,我们通常会对更复杂的现象进行建模,例如非方形像素、相机轴倾斜、畸变和非单位纵横比。幸运的是,这只会改变相机矩阵 K,您学到的方程只需添加几个额外参数即可直接使用。

图像的数字表示

既然我们已经建立了三维点投影到二维图像平面的坐标公式,接下来我们想定义二维彩色图像坐标中的值是什么。

我们将从灰度图像开始。首先,我们将图像的宽度 N高度 M 定义为其具有的行数和列数。三维空间中的每个点都投影到图像上的一个像素,该像素由我们之前推导出的 UV 坐标定义。

放大后,我们可以看到这些像素组成一个网格。在灰度图像中,每个像素的亮度信息被写为一个无符号 8 位整数。一些相机可以生成无符号 16 位整数以获得更高质量的图像。

对于彩色图像,我们有一个值为 3 的第三维度,我们称之为深度。这个深度的每个通道代表图像中某种颜色的含量。虽然存在许多其他颜色表示方法,但在本课程中我们将使用 RGB 表示法,即红、绿、蓝。

总而言之,图像在数字上表示为一个 M x N x 3 的像素数组,每个像素代表一个三维点在二维图像平面上的投影。

总结

在本节课中,您学习了如何将世界坐标系中的三维点投影到图像坐标系中的二维点。您看到执行此投影的方程依赖于相机的内参以及相机在世界坐标系中的位置。正如我们将在后续课程中看到的,这个投影模型用于我们开发的每一个视觉感知算法,从物体检测到可行驶空间估计。最后,您还学习了图像在软件中如何表示为一个代表像素位置的数组。现在,您已经准备好开始直接在软件中处理图像,正如您将在本周的评估中所做的那样。

在下一个视频中,您将学习如何通过一个称为相机标定的过程,计算特定相机的内参和外参,从而定制相机模型。

007:相机校准

在本节课中,我们将学习如何通过数学工具进行相机校准,以获取投影几何所需的相机参数。

概述 📋

上一节我们介绍了投影几何所需的相机参数。本节中,我们将学习如何通过相机校准这一数学过程来获取这些未知参数。核心在于,通过已知几何形状的场景(如棋盘格),建立3D点与其在2D图像上投影点之间的对应关系,从而求解出相机的内参和外参矩阵。

投影方程回顾 🔄

首先,让我们回顾一下之前学过的投影方程。三维空间中的齐次坐标点 [X, Y, Z, 1]^T 可以通过相机投影矩阵 P 变换到相机平面。矩阵 P 包含了相机的内参和外参。

投影后的齐次坐标需要转换为非齐次形式,以获得像素坐标系下的 uv 坐标。这是通过将图像坐标除以 Z 分量实现的。

最后,uv 可以乘以一个任意尺度因子 s。引入尺度因子 s 有助于后续构建校准问题的公式。

需要重点注意的是,尺度在理解单目图像信息时扮演着挑战性的角色。因为一旦点从3D世界投影到2D图像平面,深度信息就丢失了。沿着从相机中心出发的同一条射线上的所有3D点,都会投影到图像平面的同一个位置。因此,仅凭图像信息无法直接确定点的深度。

相机校准问题定义 🎯

相机校准问题定义为:在给定未知的3D点坐标及其在图像平面上的对应投影点的情况下,求解图中标红的未知相机内参和外参。

我们的方法将包括先获取投影矩阵 P,然后将其分解为内参矩阵 K、外参旋转矩阵 R 和平移向量 T

校准方法:使用已知几何场景 🧩

为了进行校准,我们使用一个已知几何形状的场景,以便从2D图像中获取3D点的位置。通过测量图像中观察到的点之间的实际3D距离,可以解决尺度问题。

最常用的例子是一个已知方格尺寸的3D棋盘格,它提供了一个固定的、可观测的点位置图。

我们定义世界坐标系(图中黄色坐标系),计算3D点坐标及其在图像中的投影。将3D点与2D投影点关联起来,可以手动完成(例如点击紫色点),也可以使用棋盘格检测器自动完成。

然后,我们可以建立一个方程组来求解 P 矩阵的未知参数。

建立线性方程组 ➗

现在,让我们构建需要求解的线性方程组。

首先,通过矩阵乘法将投影方程展开为三个方程,使等式右边得到 [0, 0, 0]^T。我们将每个方程的右边移到左边。

然后,将第三个方程代入方程1和方程2,最终每个点得到两个方程。因此,如果我们有 n 个点,就有 2n 个关联方程。

将这些方程写成矩阵形式,就得到了所示的齐次线性系统。

由于这是一个齐次线性系统,我们可以使用伪逆,或者更好的方法是使用奇异值分解来获得最小二乘解。

线性校准方法的优缺点 ⚖️

我们这种简单的线性校准方法有几个优点:易于公式化、有闭式解,并且通常能为非线性校准方法提供非常好的初始点。

以下是该方法的一些缺点:

  • 求解 P 矩阵的缺点是,我们不能直接得到相机的内参和外参。
  • 我们的线性模型没有考虑复杂的现象,例如径向畸变和切向畸变。
  • 由于我们通过线性最小二乘法求解,因此无法对解施加约束,例如要求焦距为非负值。

从 P 矩阵分解出 K, R, T 🔧

相机投影矩阵 P 本身可用于将3D点投影到2D,但它有几个缺点:它不告诉你相机的姿态,也不揭示相机的内部几何结构。

幸运的是,我们可以使用一种称为 RQ 分解 的线性代数运算,将 P 分解为内参矩阵 K、旋转矩阵 R 和平移向量 T

让我们看看如何进行这种分解。

首先,我们将 P 的表示改为相机中心 C 的函数。C 是当乘以 P 时投影到 [0, 0, 0]^T 的点。我们将 K 乘入矩阵,形成两个子矩阵:K R-K R C。我们将 KR 的组合称为 M 矩阵。

现在,我们可以将投影矩阵 P 表示为 [M | -M C]

从这里,我们利用任何方阵都可以分解为一个上三角矩阵 R 和一个正交基 Q 这一事实,将 M 分解为上三角矩阵 R 和正交基 Q。在线性代数中,这个过程称为 RQ 分解,它是更常提到的 QR 分解 的一种变体(在QR分解中,正交矩阵 Q 在前,上三角矩阵 R 在后)。

请注意,RQ 分解输出的 R 与我们的旋转矩阵 R 是不同的变量,不要混淆。

现在,让我们看看如何通过比对这两个表达式,利用矩阵 M 的 RQ 分解输出来获取 KRT

  • 内参校准矩阵 KM 的 RQ 分解输出的上三角矩阵 R
  • 旋转矩阵 R 是正交基 Q
  • 最后,我们可以直接从 KP 矩阵的最后一列提取出平移向量 T

关于分解的唯一性 💡

RQ 分解是从相机 P 矩阵计算 KRT 的强大工具。然而,需要做一些数学假设来保证这些矩阵解的唯一性。我们将在本课的实践 Jupyter Notebook 中更详细地探讨这些假设。

工具与实现 🛠️

单目相机校准是一项成熟的技术,在 C++、Python 和 MATLAB 中都有优秀的实现。你可以通过补充材料中提供的链接,测试一些最常见的实现。

总结 📝

本节课中我们一起学习了:

  1. 可以通过称为相机校准的过程来找到相机投影矩阵 P
  2. 这个矩阵可以通过 RQ 分解 被分解为相机内参矩阵 K 和相机外参(旋转矩阵 R 和平移向量 T)。

接下来,我们将讨论如何通过立体视觉从两个相机传感器获取深度信息。下次见。

008:立体视觉

在本节课中,我们将学习立体视觉的基本原理。自动驾驶汽车需要精确的深度感知来确保安全运行。我们将探讨如何利用两个或多个摄像头,通过多视图几何学来获取深度信息,并重点介绍双目立体相机的几何模型及其三维坐标计算方法。

立体视觉的历史背景 👁️

上一节我们提到了深度感知的重要性,本节中我们来看看立体视觉这一概念是如何被发现的。

立体视觉的过程被称为立体视差。查尔斯·惠斯通在1838年首次描述了这一过程。他认识到,由于每只眼睛从略微不同的水平位置观察视觉世界,因此每只眼睛看到的图像存在差异。距离眼睛不同距离的物体,在两只眼睛中成像的水平位置也不同,从而产生了被称为双目视差的水平视差深度线索。

然而,历史证据表明立体视觉的发现远早于此。事实上,列奥纳多·达·芬奇的一些画作就描绘了通过立体视差感知深度的精确几何关系。

直到19世纪,立体视差现象主要被用于娱乐。人们使用红蓝立体图,通过佩戴不同颜色的眼镜(通常是红色和青色)来观看,以产生立体3D效果。

如今,我们使用立体相机和复杂的算法,从两幅图像中推导深度,其概念与达·芬奇的画作相似。

立体相机的几何模型 📐

现在,让我们深入探讨立体传感器的几何结构。

为了简化问题,立体传感器通常由两个具有平行光轴的相机构成。大多数制造商会进一步对齐两个相机在三维空间中的位置,使得两个成像平面仅在X轴方向上有偏移。

给定两个相机之间已知的旋转和平移关系,以及一个三维空间点 O 在两个相机坐标系下的投影像素位置 p_Lp_R,我们可以建立必要的方程来计算点 O 的三维坐标。

为了使计算更简单,我们做出一些假设:

  • 首先,我们假设用于构建立体传感器的两个相机是相同的。
  • 其次,我们假设在制造立体传感器时,我们尽可能保持两个相机的光轴对齐。

以下是立体传感器的一些重要参数定义:

  • 焦距:相机中心到成像平面的距离。
  • 基线:左相机中心和右相机中心沿共享X轴的距离。

通过定义一个基线 B 来表示两个相机坐标系之间的变换,我们假设旋转矩阵是单位矩阵,且平移向量中只有X分量非零。因此,R和T变换简化为单个基线参数 B

在继续之前,我们将上图投影到鸟瞰图以便于可视化。

三维坐标计算 🧮

上一节我们定义了立体相机的几何模型,本节中我们来推导计算三维坐标的公式。

我们想要计算点 O 相对于左相机坐标系的 XZ 坐标。在计算出 XZ 坐标后,Y 坐标可以很容易地估算出来。

我们已知基线 B、焦距 f,以及点 O 在左右成像平面上的投影坐标。

我们可以从左侧相机的测量值中看到两个相似三角形:由深度 Z 和位置 X 构成的三角形,与由焦距 f 和左侧测量值X分量 x_L 构成的三角形相似。

根据相似性,我们可以构建方程:
Z / f = X / x_L

对于右侧测量值,我们可以进行同样的推导,但需要包含基线的偏移量。在这种情况下,两个三角形由 Z 和距离 X - B,以及焦距 f 和右侧测量值X分量 x_R 定义。

类似地,我们可以得到第二个通过右相机参数和测量值关联 ZX 的方程:
Z / f = (X - B) / x_R

从视差到三维坐标 🎯

从这两个方程出发,我们现在可以推导出点 O 的三维坐标。

我们定义视差 D 为同一像素点在左右图像中图像坐标的差值:D = x_L - x_R

我们可以使用X和Y偏移量 u_0v_0 在图像坐标和像素坐标之间轻松转换。

然后,我们利用两个相似三角形关系方程来求解 Z 的值,过程如下:

  1. Z / f = X / x_LX = (Z * x_L) / f
  2. Z / f = (X - B) / x_RX = (Z * x_R) / f + B
  3. 令两式相等:(Z * x_L) / f = (Z * x_R) / f + B
  4. 整理得:Z * (x_L - x_R) / f = B
  5. 代入视差 D = x_L - x_R,最终得到深度公式:
    Z = (f * B) / D

计算出 Z 的值后,我们使用以下表达式计算 X
X = (Z * x_L) / f

最后,我们可以在Y方向上重复相同推导过程,得到 Y 的表达式:
Y = (Z * y_L) / f

现在,点位置的三维分量可以从我们已有的两组像素测量值中明确得出。

实践中的挑战与总结 📝

现在我们已经建立了从立体传感器计算三维坐标所需的方程,但要执行此计算,会出现两个问题。

首先,我们需要计算焦距、基线和X、Y偏移量,即我们需要校准立体相机系统。其次,我们需要找到每个左右图像像素对之间的对应关系,以便计算它们的视差。

幸运的是,校准问题可以通过立体相机标定来解决。这是我们上一视频讨论的单目相机标定过程的扩展,已有成熟的实现方案。

然而,对应关系问题需要专门的算法来高效执行匹配并计算左右图像像素之间的视差,我们将在下一个视频中进一步讨论。

立体视觉输出的深度存在一些局限性,尤其是当点远离立体相机时。然而,给定一个良好的视差估计算法,其输出对于自动驾驶汽车来说,在较近距离内仍然是一个有用的密集深度信息来源,其密度超过了普通激光雷达传感器所能达到的水平。

课程总结

本节课中,我们一起学习了以下内容:

  • 立体视觉的历史背景。
  • 立体相机的几何模型和关键参数(焦距、基线)。
  • 如何根据两个相机传感器之间的几何变换以及像素之间的视差,来估算像素三维坐标的公式,核心公式为:
    Z = (f * B) / D
    X = (Z * x_L) / f
    Y = (Z * y_L) / f

在下一课中,我们将学习更多关于视差生成算法的知识,并使用Python和OpenCV展示如何从立体图像对计算视差的完整示例。

我们下次见。

009:视觉深度感知与视差计算

在本节课中,我们将学习如何从立体图像对中提取三维信息。具体来说,我们将重点探讨如何通过立体匹配来估计视差,这是计算深度的关键步骤。我们还将了解极线约束如何将复杂的二维搜索问题简化为高效的一维搜索。

回顾与问题定义

上一节我们介绍了从立体图像对中提取三维信息的基本公式。然而,我们遇到了一些需要估计的未知参数。

本节中,我们来看看如何估计这些缺失的参数,特别是通过立体匹配来计算视差。我们还将了解到,由于极线约束的存在,高效的视差估计成为可能。

立体匹配问题

视差是指同一个三维点在两个不同相机图像中位置的差异。计算视差,就需要在左右立体相机图像中找到相同的点。

这个问题被称为立体对应问题。

以下是解决此问题最朴素的方法:

  • 穷举搜索:对于左图像的每一个像素,在整个右图像中进行搜索。这种解决方案效率极低,通常无法在自动驾驶汽车上实时运行。同时,由于许多像素具有相似的局部特征,正确匹配它们也很困难。

极线约束简化搜索

幸运的是,我们可以利用立体几何将搜索问题从整个图像空间的二维搜索,约束到一条一维的线上。

让我们重新审视立体相机设置,看看为什么这种简化是有效的。

我们已经确定了一个点如何投影到两个相机。现在,让三维点沿着连接它与左相机中心的直线移动。它在左相机成像平面上的投影不会改变。

然而,在右相机平面上的投影呢?投影会沿着一条水平线移动。这条线被称为极线,它直接源于立体相机对中两个相机固定的横向偏移和成像平面对齐。

我们可以将对应对的搜索约束在极线上,从而将搜索从二维简化到一维。

需要注意的一点是,只有当两个相机的光轴平行时,才会出现水平的极线。如果光轴不平行,极线是倾斜的。在这种情况下,我们将不得不求助于多视图几何,这超出了本课程的范围。

对于两个已校准的视图(如我们的立体相机),倾斜的极线并不是一个大问题。事实上,我们可以通过一个称为立体校正的过程,将光轴扭曲为平行。校正后,我们就回到了水平的极线。我们不会详细介绍如何进行校正,因为标准计算机视觉库(如OpenCV和MATLAB)中都有现成的实现。

基础立体匹配算法

综上所述,我们将介绍第一个基础的立体匹配算法。

对于每一条极线,执行以下步骤:

  1. 选取左图像点:在左图像的这条极线上选取一个像素。
  2. 比较与匹配:将这个左图像像素与右图像同一条极线上的每一个像素进行比较。通过最小化一个代价来选择与左像素最匹配的右图像像素。例如,一个非常简单的代价可以是像素强度的平方差。
  3. 计算视差:通过从左图像位置减去右图像位置来计算视差,公式为:d = u_left - u_right

立体匹配的深入研究

立体匹配是计算机视觉中一个被深入研究的问题。人们定义了更复杂的代价函数和搜索区域,试图提高计算效率或视差精度。

存在多种方法,包括局部方法和全局方法,它们在识别对应点和计算视差时所考虑的主要图像区域有所不同。

与计算机视觉中的大多数问题一样,立体视觉算法也在公共基准上进行评估,其中最著名的是Middlebury立体基准。如果你感兴趣,许多性能最佳的立体匹配算法都在那里发布了结果,并且也提供了代码。

总结与展望

通过本节课,你现在应该了解了立体传感器如何通过视差估计从图像生成深度。本周的作业将给你一个机会计算自己的视差图,并用它们来创建你自己的“碰撞时间”驾驶员辅助模块。

在下一课中,我们将描述图像滤波,这是我们在后续为视觉感知设计卷积神经网络时将使用的一个重要概念。

感谢观看,下次再见。

自动驾驶汽车的视觉感知:第3课第1部分:特征匹配 🚗👁️

在本节课中,我们将要学习特征匹配,这是将图像特征应用于自动驾驶视觉感知任务的最后一步。特征匹配的目标是,在不同图像中找到对应同一个真实世界点的特征。我们将介绍距离函数的概念,并详细讲解一种简单而强大的匹配算法——暴力匹配法。


概述:特征匹配流程回顾

在深入细节之前,让我们简要回顾一下使用特征进行视觉感知的三个核心步骤:

  1. 特征检测:在图像中识别出独特的点(特征点)。
  2. 特征描述:为每个特征点从其周围邻域提取一个描述符(一个向量),用于表征该点的外观。
  3. 特征匹配:比较不同图像中特征的描述符,找到彼此对应的特征对。

匹配得到的特征对可以用于多种应用,包括状态估计、视觉里程计和物体检测。然而,正确匹配至关重要,因为频繁的错误匹配会导致这些应用发生灾难性的失败。因此,特征匹配在构建自动驾驶汽车鲁棒的感知方法中扮演着关键角色。


特征匹配问题定义

特征匹配的核心问题可以描述为:给定图像1中的一个特征及其描述符,我们试图在图像2中找到与之最匹配的特征。

那么,如何解决这个问题呢?


暴力匹配算法

解决匹配问题最简单的方法被称为暴力特征匹配。其算法描述如下:

首先,定义一个距离函数 D,用于比较两个特征 FiFj 的描述符,并计算它们之间的距离。两个描述符越相似,它们之间的距离就越小。

其次,对于图像1中的每一个特征 F,我们使用距离函数 D 计算它与图像2中每一个特征 FJ 的距离。

最后,我们选择图像2中与图像1的特征 Fi 距离最小的那个特征 FC 作为匹配结果。这个特征被称为最近邻,即在描述符空间中最接近原始特征的点。

核心距离函数

最常用的描述符比较距离函数是平方差之和

SSD = Σ (descriptor1[i] - descriptor2[i])^2

SSD对描述符之间的差异进行二次惩罚,因此对大的变化非常敏感,而对小的变化不敏感。其他可用的距离函数还包括绝对差之和(对所有变化平等惩罚)和汉明距离(专用于描述符元素均为二进制值的特征)。

在接下来的示例中,我们将使用SSD距离函数。


暴力匹配实践与潜在问题

我们的匹配技术和距离选择看起来相当简单。但你认为我们提出的最近邻匹配技术可能会出什么问题呢?

让我们通过两个案例来看看。

案例一:成功匹配

假设图像1中黄色边界框内的特征有一个四维描述符 F1。我们计算 F1 与图像2中第一个特征 F2 的SSD,得到值9。再计算 F1 与图像2中第二个特征 F3 的SSD,得到值652。继续计算与其他所有特征的距离,发现其他距离值都相对很大。因此,我们选择具有最小距离(9)的特征 F2 作为匹配。从视觉上看,F1F2 确实是场景中同一个兴趣点,匹配正确。

案例二:无对应匹配时的错误

现在考虑另一种情况:图像1中的特征 F1 在图像2中没有对应的特征。按照相同的流程,我们计算 F1 与图像2中所有特征的SSD。假设 F2F3 是最近邻,其中 F2 的距离值441是最小的。尽管441这个值已经表明 F2F1 相当不相似,但暴力匹配算法仍会返回 F2 作为最佳匹配。这显然是错误的,因为 F1F2 并非场景中同一个点。


改进:引入距离阈值

如何解决上述问题呢?我们可以通过设置一个距离阈值 Δ 来改进匹配的接受标准。

这意味着,图像2中任何与 F1 的距离大于 Δ 的特征,即使它是所有特征中距离最小的,也不会被认定为有效匹配。

以下是更新后的暴力匹配算法步骤:

  1. 定义距离函数 D,用于量化两个特征描述符的相似性。
  2. 设定一个可接受匹配的最大距离阈值 Δ(通常根据具体应用和使用的描述符凭经验确定)。
  3. 对于图像1中的每个特征,计算其与图像2中每个特征的距离。
  4. 找出最近邻(最短距离),仅当该最短距离小于阈值 Δ 时,才将其视为有效匹配。

算法复杂度与实用工具

暴力匹配在待匹配特征数量合理时是合适的,但其计算复杂度是二次方的(O(N²)),随着特征数量增加,会变得效率低下。

对于大规模特征集,通常会使用特殊的数据结构(如 Kd树)来加速最近邻搜索,提升计算效率。

无论是暴力匹配还是基于Kd树的匹配,都在OpenCV库中有所实现,方便开发者直接使用。


总结

本节课我们一起学习了特征匹配。我们回顾了特征检测、描述和匹配的完整流程,并深入探讨了暴力匹配算法。该算法通过计算描述符之间的距离(如SSD)来寻找最近邻。我们还发现了简单最近邻匹配的缺陷——当查询特征在目标图像中没有真实对应时,仍会返回一个“最佳”匹配。为此,我们引入了距离阈值 Δ 来过滤掉不可靠的匹配,从而提高匹配的准确性。

暴力匹配法简洁有效,但为了构建安全可靠的自动驾驶汽车感知系统,我们还需要更精确的结果。在下一课中,我们将学习如何改进暴力匹配器,以处理那些经常导致错误结果的、棘手的模糊匹配。

我们下节课再见! 👋

014:特征匹配 - 处理匹配中的歧义

在本节课中,我们将要学习特征匹配中的一个常见问题——匹配歧义,并介绍一种通过距离比率公式来处理此问题的方法,从而使我们的匹配结果更加鲁棒。

概述

到目前为止,我们已经学习了如何检测特征、计算其特征描述符,以及如何使用距离函数进行暴力匹配。我们即将开始将特征检测、描述和匹配应用于自动驾驶汽车的感知任务。在本节中,我们将探讨匹配歧义问题,并学习如何通过距离比率公式,使匹配对由相似特征描述符引起的匹配错误更具鲁棒性。

回顾:特征匹配的两种情况

首先,让我们回顾上一课讨论的两种特征匹配情况。

在第一种情况下,我们有一个有效的特征描述符,它与图像2中的特征F2的距离很小,而与其他特征的距离很大。在这种情况下,我们可以成功地在图像2中找到与图像1中特征F1的正确匹配。我们的暴力匹配算法在这种情况下效果很好,能够无缝地找到正确的匹配。

在第二种情况下,图像1中的特征F1在图像2中根本没有匹配项。我们通过设置距离阈值Delta来修改暴力匹配算法,以消除此类情况下的错误匹配。由于图像2中的两个特征与F1的距离都大于阈值Delta,匹配器会拒绝将F2和F3作为潜在匹配项,因此不返回任何匹配。

引入:第三种情况——匹配歧义

现在,让我们考虑第三种情况。我们再次尝试将图像1中的特征F1与图像2中的对应特征进行匹配。

这里展示的特征向量中,特征F1与图像2中特征F2的SSD值为9。当我们测试另一个特征F3时,得到的SSD值也是9。这两个特征的SSD值都小于本例中设定的阈值20,因此它们都是有效的匹配。那么,我们应该怎么做呢?

我们将情况三中的特征F1称为具有歧义匹配的特征。

解决方案:距离比率公式

David Lowe在1999年提出了一个优雅的解决方案。该方案步骤如下:

  1. 首先,计算图像1中特征FI与图像2中所有特征Fj之间的距离。
  2. 与我们之前的算法类似,我们选择图像2中与特征FI距离最小的特征FC作为最近邻匹配
  3. 接着,我们找到图像2中与特征FI距离第二小的特征FS,即次近邻匹配
  4. 最后,我们计算最近邻匹配FC比次近邻匹配FS近多少。这可以通过距离比率来完成。

距离比率可以定义为:特征FI与图像2中最近邻匹配FC之间的距离,除以特征FI与图像2中次近邻匹配FS之间的距离。

公式距离比率 = 距离(FI, FC) / 距离(FI, FS)

如果距离比率接近1,则意味着根据我们的描述符和距离函数,特征FI同时匹配FS和FC。在这种情况下,我们不想在后续处理中使用这个匹配,因为我们的匹配器显然无法确定图像2中的哪个位置对应图像1中的特征。

更新匹配算法

让我们用距离比率公式来更新我们的暴力匹配器算法。更新内容是用距离比率作为保留匹配的度量标准,取代了单一的距离值。

我们通常将距离比率阈值(记为ρ)设置在0.5左右。这意味着我们要求最佳匹配至少比次佳匹配距离初始特征描述符近两倍。

重新审视情况三,我们可以看到,使用距离比率并将相应阈值ρ设为0.5,我们可以丢弃歧义匹配,只保留正确的匹配。

以下是算法更新后的核心步骤:

  1. 对于图像1中的每个特征FI,在图像2中找到两个最近邻特征(FC和FS)。
  2. 计算距离比率:距离(FI, FC) / 距离(FI, FS)
  3. 如果该比率小于阈值ρ(例如0.5),则接受FC作为有效匹配;否则,拒绝该匹配。

总结与展望

本节课中,我们一起学习了什么是歧义匹配,以及如何通过距离比率公式来处理这些歧义匹配。

然而,即使采用了距离比率公式,在使用现代描述符时,仍然可能有高达50%的典型匹配是错误的。这是因为图像中的重复模式以及像素值的微小变化,常常足以干扰匹配过程。我们称这些错误匹配为异常值

在下一课中,我将介绍一种方法,以便在使用匹配特征进行进一步的感知任务之前,能够考虑并消除这些异常值。

015:异常值剔除

概述

在本节课中,我们将学习如何将图像特征应用于自动驾驶车辆定位,并重点解决特征匹配中出现的“异常值”问题。我们将介绍一种强大的异常值处理方法——随机抽样一致性算法,并展示它如何提升定位结果的鲁棒性。

车辆定位问题定义

上一节我们介绍了特征提取、描述和匹配的三步框架。本节中,我们来看看如何利用这个框架解决一个实际问题:车辆定位。

我们的定位问题定义如下:给定同一场景、不同视角的两张图像,找到第一张图像(红色坐标系)与第二张图像(绿色坐标系)之间的平移量 T。为简化问题,我们暂不考虑因视角不同带来的尺度和倾斜变化,专注于平移。

要解决此问题,我们需要执行以下步骤:

  1. 找到图像1在图像2的U轴上的位移,记为 T_u
  2. 找到图像1在图像2的V轴上的位移,记为 T_v

我们将通过匹配图像间的特征,然后求解能最佳对齐这些匹配特征的位移量来找到 T_uT_v

特征匹配与数学模型

我们首先在图像1和图像2中计算特征及其描述符,然后使用上一课开发的暴力匹配器进行匹配。

你是否注意到匹配结果有些不对劲?我们稍后会回到这个问题。首先,让我们用匹配到的特征来数学化地定义我们的解决方案。

我们将来自图像1和2的一个特征对表示为 F1_iF2_i,其中 i 的范围在0到 n 之间(n 是匹配算法返回的特征对总数)。每个特征由其像素坐标 (u_i, v_i) 表示。

假设应用平移 T_uT_v 后,图像1中的每个像素都应与图像2中的对应像素重合。我们可以用特征对来建模这个平移关系:

F2_i = F1_i + [T_u, T_v]^T

这里,T_uT_v 对所有特征对都是相同的,因为我们假设是刚体运动。现在,我们可以使用最小二乘估计来求解 T_uT_v。最小二乘问题的解是使所有特征对像素之间误差平方和最小的 T_uT_v 值。

异常值的出现与影响

现在我们已经定义了定位问题,让我们回到特征匹配的结果。

通过视觉观察特征位置,可以发现紫色圆圈中的特征对实际上是一个错误的匹配。即使我们使用了距离比率法,这种情况也经常发生。我们称这样的特征对为异常值

异常值可能在我们特征集中占很大一部分,并且通常对我们的模型解(尤其是使用最小二乘估计时)产生不成比例的负面影响。让我们看看是否能识别这些异常值,并避免在最小二乘解中使用它们。

随机抽样一致性算法

异常值可以使用一种基于模型的异常值剔除方法处理,即随机抽样一致性算法。该算法由 Martin Fishler 和 Robert Bolles 于 1981 年提出,是机器人学中最常用的基于模型的异常值剔除方法之一。

以下是 RANSAC 算法的步骤:

首先,给定一个用于从一组数据点中识别问题解的模型,确定计算该模型参数所需的最少数据点数量 M。在我们的案例中,问题是定位,模型参数是最小二乘解的 T_uT_v 偏移量。

以下是算法的具体迭代过程:

  1. 从数据中随机选择 M 个样本。
  2. 仅使用选中的 M 个样本来计算模型参数。
  3. 使用计算出的参数,统计剩余数据点中有多少个符合这个计算出的解。被接受的点被保留,称为内点
  4. 如果内点数量 C 令人满意,或者算法已达到预设的最大迭代次数,则终止并返回计算出的解和内点集;否则,返回步骤 1 重复。
  5. 最后,根据最佳内点集(即内点数量最多的那个集合)重新计算并返回模型参数。

应用 RANSAC 解决定位问题

现在我们可以重新审视我们的定位问题,并尝试处理特征匹配中的异常值。

提醒一下,我们的模型参数 T_uT_v 将每个特征对从第一张图像同等位移到第二张图像。要估计 T_uT_v,我们只需要一个特征对。

现在,让我们针对此问题走一遍 RANSAC 算法。

首先,我们从匹配样本中随机选择一个特征对。然后,我们使用这个特征对来估计模型,计算沿 U 轴的位移 T_u 和沿 V 轴的位移 T_v

接着,我们需要通过计算内点数量来检查模型是否有效。这里我们使用一个容差来确定内点,因为模型很难达到 100% 的精确匹配。

不幸的是,我们的第一次迭代选择了一个糟糕的特征匹配来计算模型参数。当使用这个模型来计算图像1中有多少特征能平移到图像2中的匹配位置时,我们发现没有一个能做到。由于内点数为零,我们返回并随机选择另一个特征对,重新启动 RANSAC 过程。

我们再次使用新的随机采样特征对计算 T_uT_v,得到新的模型参数。使用这个模型,我们计算图像1中有多少特征能平移到图像2中的匹配位置。这次我们可以看到,大多数特征实际上都符合这个模型。事实上,12个特征中有11个被认为是内点。由于我们的大多数特征都是内点,我们对这个模型感到满意,可以停止 RANSAC 算法。

总结

本节课中,我们一起学习了如何将图像特征正确应用于自动驾驶车辆应用。你了解了在特征匹配范围内什么是异常值,以及如何通过 RANSAC 算法处理异常值。异常值剔除是提高特征匹配鲁棒性的关键过程,能极大地提升定位结果的质量。

在下一个视频中,我们将利用目前所学,使用相机图像特征来估计自动驾驶车辆的位置。这将帮助我们跟踪车辆在环境中的运动,这个过程被称为视觉里程计,对于平稳安全的导航至关重要。

016:视觉里程计

在本节课中,我们将学习视觉里程计,这是自动驾驶汽车基于视觉进行状态估计的一项基础任务。我们将了解其定义、优势与挑战,并深入探讨其数学定义和核心处理流程,特别是3D到2D运动估计的方法。

概述

在本模块中,我们已经学习了如何检测、描述和匹配特征,以及如何处理特征匹配结果中的异常值。我们还探讨了如何使用特征在图像中定位物体,这可用于从一对图像中估计物体的深度。本节课,我们将学习如何执行视觉里程计。

什么是视觉里程计?

视觉里程计,简称VO,可以定义为通过检查运动对其车载摄像头图像所引起的变化,来增量式估计车辆姿态的过程。它与您在第二门课程中学到的轮式里程计概念相似,但使用的是摄像头而非编码器。

那么,对于自动驾驶汽车而言,使用视觉里程计可能比常规轮式里程计提供哪些优势呢?

  • 不受车轮打滑影响:在转弯或不平坦地形上,视觉里程计不受车轮打滑的影响。
  • 轨迹估计更准确:与轮式里程计相比,由于图像能提供更大量的信息,视觉里程计往往能产生更准确的轨迹估计。

然而,视觉里程计也存在一些挑战:

  • 无法估计绝对尺度:这意味着从单个摄像头估计的运动可以在3D世界中拉伸或压缩,而不会影响像素特征位置,只需调整两幅图像之间的相对运动估计即可。因此,我们通常至少需要一个额外的传感器(通常是第二个摄像头或惯性测量单元)来在使用VO时提供准确缩放的轨迹。
  • 对光照敏感:摄像头对极端光照变化敏感,使得在夜间以及存在车灯和街灯的情况下难以执行VO。
  • 存在漂移:与其他里程计估计机制一样,VO的位姿估计会随着估计误差的累积而随时间漂移。因此,我们通常以单位行驶距离的百分比误差来引用VO的性能。

视觉里程计的数学定义

让我们从数学上定义视觉里程计问题。给定两个连续的图像帧 Ik-1Ik,我们希望估计一个由平移 t 和旋转 R 定义的变换矩阵 Tk

Tk = [R | t]

将从时间步 k=1K 估计的每个变换序列连接起来,将为我们提供摄像头在图像序列上的完整轨迹。由于摄像头刚性连接在自动驾驶车辆上,这也代表了车辆轨迹的估计。

视觉里程计的一般流程

现在我们描述视觉里程计的一般流程。我们被给予两个连续的图像帧 IkIk-1,并且想要估计这两个帧之间的变换 Tk

以下是核心步骤:

  1. 特征检测与描述:我们最终得到一组特征:图像 k-1 中的 Fk-1 和图像 k 中的 Fk
  2. 特征匹配:我们随后匹配两个帧之间的特征,以找出在两个目标帧中都出现的特征。
  3. 运动估计:之后,我们使用匹配的特征来估计两个摄像头帧之间的运动,该运动由变换 Tk 表示。

运动估计是VO中最重要的一步,也将是本节课的主题。

我们执行运动估计步骤的方式取决于我们拥有的特征表示类型。

  • 2D到2D运动估计:两个帧中的特征匹配纯粹用图像坐标描述。这种形式的视觉里程计非常适合在图像帧中跟踪物体,例如在摄像中的视觉跟踪和图像稳定方面极其有用。
  • 3D到3D运动估计:特征匹配在世界3D坐标系中描述。这种方法需要能够在3D空间中定位新的图像特征,因此与深度摄像头、立体摄像头和其他可以提供深度信息的多摄像头配置一起使用。

这两种情况都很重要,并遵循我们将在本课剩余部分使用的相同的一般视觉里程计框架。

深入探讨:3D到2D运动估计

让我们更仔细地看看3D到2D运动估计,其中来自帧 k-1 的特征在3D世界坐标中指定,而它们在帧 k 中的匹配则在图像坐标中指定。

以下是执行3D到2D运动估计的方法:

我们被给予帧 k-1 中的特征集及其3D世界坐标的估计。此外,通过特征匹配,我们还拥有这些相同特征在新帧 k 中的2D图像坐标。请注意,由于我们无法直接从单目视觉里程计恢复尺度,因此在从图像坐标形成齐次特征向量时,我们包含了一个缩放参数 s

我们想利用这些信息来估计两个摄像头帧之间的旋转矩阵 R 和平移向量 t

这个图示是否让您想起了我们之前学过的东西?如果您想到了相机标定,那么您是正确的。事实上,我们在视觉里程计中也使用了与标定相同的投影几何方程。

需要注意的标定和VO之间的一个简化区别是,相机内参标定矩阵 K 是已知的,因此我们不需要再次求解它。我们的问题现在简化为从使用所有匹配特征构建的方程组中估计变换分量 Rt

PnP算法:求解旋转和平移

求解旋转 R 和平移 t 的一种方法是使用透视n点算法。给定3D中的特征位置、它们在2D中的对应投影以及相机内参标定矩阵 K,PnP求解外参变换如下:

  1. 直接线性变换:PnP首先使用直接线性变换来求解 Rt 的初始猜测。用于估计 Rt 的DLT方法需要一个线性模型,并构建一组线性方程来使用SVD等标准方法求解。
  2. 非线性优化:然而,我们拥有的方程在 Rt 的参数上是非线性的。在下一步中,我们使用迭代非线性优化技术(例如列文伯格-马夸尔特方法)来细化初始DLT解。
  3. 所需点数:PnP算法至少需要三个特征来求解 Rt。当只使用三个特征时,会产生四种可能的解,因此需要第四个特征点来决定哪个解是有效的。
  4. 集成RANSAC:最后,可以通过假设PnP在四个点上生成的变换是我们的模型,将RANSAC集成到PnP中。然后,我们选择所有特征匹配的一个子集来评估该模型,并检查所产生的内点百分比,以确认所选点匹配的有效性。

PnP方法是解决视觉里程计问题的一种高效方法,但仅使用可用匹配的一个子集来计算解。

改进与扩展

我们可以通过应用您在课程2中学到的批量估计技术来改进PnP。通过这样做,我们还可以整合来自其他车载传感器的额外测量值,并将视觉纳入状态估计流程中。有了视觉的加入,我们可以更好地处理GPS拒止环境,并提高我们位姿估计的准确性和可靠性。

在实现VO算法时,还有许多更有趣的细节需要考虑。幸运的是,PnP方法在OpenCV中有一个稳健的实现。事实上,OpenCV甚至包含了一个集成了RANSAC用于异常值剔除的PnP版本。您可以在补充阅读的链接中找到关于如何在OpenCV中使用PnP的描述。

总结

在本节课中,您学习了为什么视觉里程计是估计自动驾驶汽车轨迹的一个有吸引力的解决方案,以及如何为3D到2D对应关系执行视觉里程计。下周,我们将深入探讨一种从图像中提取信息用于自动驾驶汽车感知的基本方法:深度神经网络。

至此,您已经完成了自动驾驶汽车视觉感知的第二周学习。如果您没有完全掌握本周解释的所有材料,请不要担心。在本周的作业中,您将获得所有这些主题的实践经验。您将使用特征检测、匹配和PnP算法,在Python中构建您自己的自动驾驶汽车视觉里程计系统。下个模块再见。

017:前馈神经网络 🧠

在本节课中,我们将学习一种改变了我们思考自动驾驶感知方式的技术:人工神经网络。我们将了解如何利用这些算法构建自动驾驶汽车的感知系统,并学习设计和训练深度神经网络的不同组成部分。虽然本模块无法涵盖人工神经网络的所有知识,但它将为你提供一个良好的入门介绍。

概述:神经网络作为函数近似器

前馈神经网络通过一个函数 F(X, θ) 定义了从输入 X 到输出 Y 的映射。例如,我们可以使用神经网络来输出摄像头图像中所有汽车的位置。这里的 θ 是一组需要学习的参数,我们并非直接使用一个完美的函数 F,而是通过一个通用的神经网络结构来构建对真实函数的近似。因此,神经网络可以被视为函数近似器

通常,我们将前馈神经网络描述为一系列函数的组合。每一个函数 f_i 都建立在前一个函数 f_{i-1} 之上。这种层层堆叠的结构,正是“深度学习”这一名称的由来。

网络结构可视化

现在,让我们直观地理解这种函数组合。下图展示了一个四层前馈神经网络:

  • 输入层:代表输入数据 XX 可以是标量、向量、矩阵,甚至是像图像这样的N维张量。
  • 隐藏层:第一隐藏层通过函数 F1(x) 处理输入。第二隐藏层处理第一隐藏层的输出,依此类推。我们可以添加任意数量的隐藏层,但每增加一层都会引入更多需要学习的参数和运行时计算量。
  • 输出层:这是神经网络的最后一层,它将最后一个隐藏层的输出转换为我们期望的输出 Y

这种网络被称为“前馈”,是因为信息从输入 X 开始,经过一系列中间步骤,单向地流向输出 Y,没有任何反馈连接。

在自动驾驶视觉感知中的应用

既然我们已经了解了基本结构,现在来看看如何将其应用于自动驾驶的视觉感知任务。本课程专注于视觉感知,因此我们的输入 X 始终是图像。

以下是几个关键应用示例:

  • 图像分类:这是最基本的感知任务,要求神经网络通过一个标签告诉我们图像中包含什么。
  • 目标检测:这是一个更复杂的任务,要求神经网络不仅识别图像中的物体(标签),还要估计它们的位置。
  • 像素级任务:我们可能对每个像素进行分析。例如,深度估计(估计每个像素的深度值以确定物体位置)和语义分割(确定每个像素属于哪个类别,如汽车、行人、道路等)。

在所有这些情况下,我们都可以使用神经网络来学习从图像原始像素值到我们试图生成的感知输出之间的复杂映射,而无需显式地为这种映射关系建模。这种表示难以建模的过程的灵活性,正是神经网络如此流行的原因。

核心:隐藏层的构成与学习

那么,如何学习参数以创建鲁棒的感知模型呢?这个过程称为神经网络训练。我们通过提供输入 X 及其对应的真实输出 F*(x) 的数据对,来驱动神经网络函数 F(X, θ) 逼近真实函数 F*(x)。通过比较网络输出与真实输出,并优化网络参数以减少误差。

训练数据只指定每个样本的期望输出,并未规定网络应该用它的隐藏层做什么。网络必须自行决定如何修改这些层,以最好地实现 F*(x) 的近似。事实上,隐藏单元正是神经网络区别于其他机器学习模型的独特之处

让我们更清晰地定义隐藏层的结构。一个隐藏层由一个仿射变换和一个逐元素的非线性函数 G 组成。这个非线性函数被称为激活函数

n 个隐藏层的输入是 H_{n-1}(上一个隐藏层的输出)。对于第一个隐藏层,其输入就是图像 X

仿射变换由可乘的权重矩阵 W 和可加的偏置矩阵 B 构成。这些权重和偏置就是神经网络定义中需要学习的参数 θ

最后,变换后的输入会通过激活函数 G。大多数情况下,G 本身不包含需要网络学习的参数。

激活函数示例

目前大多数神经网络中,默认的激活函数选择是线性整流单元

ReLU 使用仿射变换输出与0之间的最大值作为其逐元素非线性函数:G(z) = max(0, z)。由于它们非常接近线性单元,因此很容易优化。

让我们看一个ReLU隐藏层计算的例子。假设我们已知:

  • 上一隐藏层输出 H_{n-1}(2x3矩阵)
  • 权重矩阵 W(2x5矩阵)
  • 偏置矩阵 B(5x3矩阵)

首先计算仿射变换(注意权重矩阵被转置):
Z = W^T * H_{n-1} + B
结果 Z 是一个5x3矩阵。

然后将矩阵 Z 通过ReLU非线性函数。ReLU会阻止仿射变换产生的任何负值传递到下一层。

神经网络隐藏层中可以使用的逐元素非线性激活函数还有很多。事实上,隐藏单元的设计是该领域另一个非常活跃的研究方向,目前还没有很多确定性的理论指导原则。

其他常见的激活函数例子包括:

  • Sigmoid 非线性函数
  • 双曲正切 非线性函数
  • Maxout 非线性函数(ReLU的一种泛化)

总结

本节课中,我们一起学习了前馈神经网络的主要构建模块,包括构成我们所使用的机器学习模型核心的隐藏层。我们还了解了不同类型的激活函数,其中ReLU是该领域许多实践者的默认选择。

在下一课中,我们将探索输出层,然后研究如何从训练数据中学习权重和偏置矩阵,为在本模块后续训练我们的第一个神经网络做好准备。

018:输出层与损失函数

在本节课中,我们将学习如何为特定的感知任务定义合适的神经网络。我们将回顾机器学习算法的通用设计流程,并重点介绍其中缺失的关键组件:输出层与损失函数。我们将了解如何根据任务类型(如分类或回归)来选择合适的输出层和损失函数。

机器学习算法设计流程回顾

上一节我们介绍了前馈神经网络这一强大的机器学习模型。本节中,我们来看看如何为特定任务设计完整的神经网络。

通常,包括神经网络在内的监督式机器学习模型有两种运行模式:推理训练

给定一组参数 θ,输入 X 通过模型 F(x; θ) 得到输出 y。这种模式称为推理,通常是我们将算法部署到现实世界时使用的模式。此时网络及其参数是固定的,我们用它从新的输入数据中提取感知信息。

然而,我们还需要定义如何获得参数集 θ。这就需要第二种模式:训练。训练的唯一目的是为当前任务生成一组令人满意的参数。

以下是训练通常如何进行的步骤:

  1. 我们从一个与推理相同的工作流程开始。但在训练期间,我们拥有训练数据,因此我们知道期望的模型输出 F*(x) 是什么。
  2. 我们通过一个损失函数成本函数,将网络的预测输出 y 与真实输出 F*(x) 进行比较。
  3. 损失函数以网络的预测输出 y 和真实输出 F*(x) 作为输入,并提供两者差异的度量。
  4. 我们通常通过修改参数 θ 来最小化这个度量,使得网络的输出 y 尽可能接近 F*(x)
  5. 我们通过一个优化过程来修改 θ。这个过程接收损失函数的输出,并提供一组新的参数 θ,使得损失函数的值更低。

我们将在下一课中详细学习这个优化过程。现在,让我们将设计流程扩展到神经网络。

神经网络的输出层与损失函数

我们在上一课讨论的前馈神经网络,接收输入 X,将其通过一系列隐藏层,然后将隐藏层的输出传递给一个输出层。这就是神经网络推理阶段的终点。

对于训练,我们将预测输出传递给损失函数,然后使用优化过程来产生一组新的参数 θ,以降低损失函数的值。

传统机器学习算法设计与人工神经网络设计之间的主要区别在于,神经网络仅通过输出层与损失函数交互。因此,输出层和损失函数根据手头的任务一起设计是非常合理的。

让我们深入了解自动驾驶中通常遇到的主要感知任务。

自动驾驶中的主要感知任务

第一个重要的任务是分类。分类可以描述为接收一个输入 X,并将其映射到 K 个类别中的一个。例如:

  • 图像分类:将图像映射到一个特定类别,例如判断它是否包含猫或狗。
  • 语义分割:将图像中的每个像素映射到一个类别。

第二个常用任务是回归。在回归中,我们希望将输入映射到一组实数。例如:

  • 深度估计:估计图像中每个像素的真实深度值。

我们也可以将这两个任务混合在一起。例如,目标检测通常包含一个回归任务(估计包含目标的边界框)和一个分类任务(识别边界框内是哪种类型的对象)。

现在,我们将描述与这些基本感知任务相关的输出层和损失函数对。

分类任务:Softmax 输出层与交叉熵损失

通常,对于 K 类分类任务,我们使用 Softmax 输出层。Softmax 输出层能够表示 K 个类别上的概率分布。

Softmax 输出层接收神经网络最后一个隐藏层的输出 H 作为输入。然后,它通过一个仿射变换,得到一个变换后的输出向量 Z。接下来,使用 softmax 逐元素函数将向量 Z 转换为离散概率分布。

对于每个元素 z_i,该函数计算 exp(z_i) 与 Z 所有元素 exp 之和的比值。结果是一个介于 0 和 1 之间的值,并且所有这些元素的总和为 1,使其成为一个合适的概率分布。

公式softmax(z_i) = exp(z_i) / Σ_j exp(z_j)

让我们看一个数值例子来更好地解释 Softmax 输出层。在这个例子中,我们希望分类包含猫、狗或狐狸的图像。

首先,我们定义输出向量的第一个元素对应于网络认为图像是猫的概率。类别的顺序是任意的,对网络性能没有影响。

取仿射变换的输出,我们通过将输出每个元素的指数除以所有元素指数的总和来计算概率。

给定线性变换层的输出值为 13、-7 和 11,我们得到该图像是猫的概率为 88.0%,是狐狸的概率为 11.9%,是狗的概率非常低。

现在,让我们看看如何设计一个使用 Softmax 输出层输出的损失函数,以显示我们的估计有多准确。

与 Softmax 输出层一起使用的标准损失函数是交叉熵损失,它由取 softmax 函数的负对数形成。

交叉熵损失有两个项来控制网络的输出与真实概率的接近程度。

公式L = -z_i + log(Σ_j exp(z_j))

其中,z_i 是在通过 softmax 函数之前,对应于真实类别的隐藏层输出。这通常被称为类别 logit

当最小化这个损失函数时,类别 logit z_i 的负数鼓励网络为正确类别的概率输出一个大的值。另一方面,第二项鼓励仿射变换的输出变小。这两项共同鼓励网络最小化预测类别概率与真实类别概率之间的差异。

为了更好地理解这个损失,让我们看一个数值例子,了解如何从分类神经网络的输出计算交叉熵损失。

回顾我们之前的例子,我们首先需要选择我们的 z_i 是什么。z_i 是线性变换输出中对应于输入真实类别的元素。在这种情况下,z_i 是线性变换输出中对应于猫类别的元素。

一旦我们确定了 z_i,就使用交叉熵来计算最终的损失值。在这种情况下,网络正确预测输入是一只猫,得到的损失函数值为 0.12。

现在让我们再次计算,但使用一个错误的网络输出。网络的输入仍然是一张猫的图像。网络仍然为线性变换输出的猫条目分配值 13,但这次,狐狸条目将获得值 14。计算交叉熵损失,我们发现它的值为 1.31,是上一张幻灯片值的 10 倍多。

请注意,即使输出只差 1,损失函数也会严重惩罚错误的预测。这种差异加速了学习过程,并在训练期间迅速将网络输出引导至真实值。

回归任务:线性输出层与均方误差损失

到目前为止,我们已经介绍了针对分类任务的输出层和损失函数。现在让我们来看看回归任务最常用的输出层。

线性输出层主要用于回归任务,以对常见概率分布的统计量进行建模。线性输出层仅由一个仿射变换组成,没有任何非线性。

用线性输出层建模的统计量取决于我们选择与之搭配的损失函数。例如,为了对概率分布的均值进行建模,我们使用均方误差作为损失函数。

公式MSE = (1/n) * Σ (y_pred - y_true)^2

总结

本节课中,我们一起学习了构建机器学习模型所需的关键组件。你了解到,要构建一个机器学习模型,你需要定义一个网络模型、一个损失函数以及一个学习网络参数的优化过程。

你还学会了根据神经网络模型需要完成的任务(分类或回归)来选择相应的损失函数。上面描述的线性和 Softmax 输出单元是当今神经网络中最常用的输出层,可以与各种特定任务的损失函数结合,为自动驾驶执行各种有用的感知任务。

当然,还存在许多其他输出层和损失函数,这仍然是深度学习中的一个活跃研究领域。

在下一个视频中,我们将讨论神经网络设计过程的最后一个组成部分:优化。这涉及到如何为特定任务获得最佳参数集 θ。下次见。

019:基于梯度下降的神经网络训练

概述

在本节课中,我们将学习如何通过训练过程为前馈神经网络找到最佳参数集。我们将重点介绍梯度下降优化算法,包括其工作原理、参数初始化方法、停止条件,以及如何通过使用小批量数据来提高训练效率。


神经网络训练流程回顾

上一节我们介绍了前馈神经网络的构成以及如何使用损失函数评估其性能。本节中,我们来看看如何通过训练过程来优化网络参数。

给定训练输入数据 X 和对应的正确输出 F*(x),训练流程如下:

  1. 将输入 X 通过网络隐藏层和输出层,得到最终输出 y
  2. 通过损失函数 L 比较网络预测输出 F(x, θ) 与正确输出 F*(x) 之间的误差。

网络输出 y 是参数 θ 的函数,而 θ 包含了网络中所有线性变换的权重和偏置。我们的目标是通过调整 θ,使整个数据集上的损失函数值最小化。

梯度下降优化算法

为了最小化损失函数,我们使用其梯度作为指导来更新参数 θ。这种优化过程称为梯度下降。

首先,我们来看神经网络的损失函数。在自动驾驶任务中,通常有数千个训练样本对 (X, F*(x))。整个训练集上的损失可以计算为所有单个样本损失的平均值:

L(θ) = (1/N) * Σ L_i(θ)

其中,N 是训练样本总数,L_i(θ) 是第 i 个样本的损失。

损失函数 L(θ) 关于参数 θ 的梯度,等于每个样本损失梯度的平均值:

∇_θ L(θ) = (1/N) * Σ ∇_θ L_i(θ)

这里利用了梯度和求和都是线性算子这一性质。

基于上述梯度公式,我们现在可以描述批量梯度下降优化算法。批量梯度下降是一种一阶迭代优化方法。“迭代”意味着它从一个初始参数猜测 θ 开始,并逐步改进这些参数。“一阶”意味着该算法仅使用一阶导数(梯度)来更新参数。

以下是批量梯度下降的步骤:

  1. 初始化:初始化神经网络的参数 θ
  2. 设定停止条件:确定一个终止算法的条件,并返回最终的参数集。
  3. 迭代循环
    • 计算梯度:计算损失函数关于参数 θ 的梯度 ∇_θ L(θ)
    • 更新参数:按照以下公式更新参数:
      θ ← θ - ε * ∇_θ L(θ)
      其中,ε 称为学习率,它控制每次迭代中参数沿负梯度方向调整的幅度。

让我们看一个二维情况下的批量梯度下降可视化示例。假设我们试图找到最小化函数 J(θ) 的参数 θ1θ2,其形状像一个椭圆碗。

梯度下降迭代地寻找新的参数 θ,使我们在每次迭代中沿着碗向下移动一步。算法首先初始化参数 θ,得到一个初始损失值(图中红点)。然后计算该初始点处的梯度,并使用更新步骤获得新参数,从而到达损失函数上更低的位置。重复此过程,直到满足停止条件,最后得到的参数集 θ1θ2 就是最小化损失函数的最优解。

参数初始化与停止条件

上述算法中还有两个关键问题:如何初始化参数 θ,以及如何决定何时停止算法。这两个问题的答案在很大程度上依赖于实践中效果良好的启发式方法。

对于参数初始化,我们通常使用标准正态分布来初始化权重,并将偏置设置为0。值得注意的是,文献中还有一些针对特定激活函数的其他启发式方法。

确定梯度下降的停止条件则更为复杂。主要有三种方式:

  1. 最大迭代次数:当达到预定义的最大梯度下降迭代次数时停止。
  2. 参数变化量:当参数 θ 在迭代之间的变化非常小时停止。小的变化意味着算法不再有效地更新参数,可能意味着已经达到了一个最小值。
  3. 损失值变化量:当损失函数值在迭代之间的变化变得很小时停止。同样,这通常意味着优化可能已经收敛到一个最小值。

选择哪种停止条件很大程度上取决于具体问题的实际情况。我们将在下一课中重新讨论停止条件,研究训练过程中的一些陷阱以及如何避免它们。

随机/小批量梯度下降

不幸的是,批量梯度下降算法存在严重缺陷。为了计算梯度,我们需要使用反向传播,这涉及为整个训练集计算网络输出。批量梯度下降在整个训练集上评估梯度,使得执行单次更新步骤非常缓慢。

幸运的是,损失函数及其梯度都是对整个训练数据集求平均。我们知道,从 N 个样本中估计出的均值的标准误差为 σ / √N,其中 σ 是分布的标准差。这意味着梯度估计误差的下降速度小于样本数量的线性增长。这一观察非常重要,因为我们现在可以使用训练数据的一个小子集(即小批量)来计算梯度估计。

那么,使用小批量如何修改我们的批量梯度下降算法呢?修改实际上非常简单。基础算法中唯一的改变在于采样步骤。我们选择训练数据的一个子集 N‘ 作为小批量,然后以与批量梯度下降相同的方式评估梯度并执行更新步骤。

这个算法被称为随机或小批量梯度下降,因为我们在每次迭代中随机选择样本组成小批量。然而,这个算法引入了一个需要确定的额外参数,即小批量的大小。

以下是选择小批量大小时需要考虑的一些因素:

  • 硬件效率:像GPU这样的多核架构,如果使用极小的批量大小,通常无法充分利用其计算能力。因此,存在一个绝对的最小批量大小,低于此值处理一个小批量的时间不会减少。此外,使用2的幂次方作为批量大小(如32、64、128、256)通常能更好地匹配GPU的计算和内存架构,从而高效利用资源。
  • 梯度估计精度:较大的批量大小通常能提供更准确的梯度估计,确保参数更新方向能更可靠地改善网络性能。然而,这种估计精度的提升是次线性的。
  • 正则化与收敛速度:另一方面,较小的批量大小已被观察到具有正则化效果,通常在批量大小为1时能获得最佳的泛化能力。同时,优化算法如果能够快速计算梯度的近似估计并更频繁地迭代,通常会比计算精确梯度但迭代次数更少时收敛得更快。

权衡这些因素后,典型的2的幂次方小批量大小范围在32到256之间。对于大型模型或为了改善泛化能力,有时也会尝试更小的批量大小。

最后需要注意的一点是,在采样小批量之前,必须对数据集进行洗牌。完全不洗牌数据集可能会降低网络的有效性。

文献中存在许多随机梯度下降的变体,每种都有其优缺点。选择使用哪种变体可能很困难,有时某种变体对特定问题效果更好。作为自动驾驶应用的一个简单经验法则,Adam 优化方法是一个安全的选择。它对初始参数 θ 相当鲁棒,并且被广泛使用。

总结

本节课中,我们一起学习了如何使用批量梯度下降来优化神经网络的参数。我们了解到,这个优化算法有许多提出的变体,其中 Adam 是一个安全的默认选择。恭喜你,你已经完成了构建和训练神经网络所需的基本步骤。

在下一课中,我们将讨论如何选择一些优化参数(如学习率)来改善网络训练,以及如何使用验证集来评估神经网络的性能。下次见。

020:数据划分与神经网络性能评估

在本节课中,我们将学习如何划分数据以对神经网络模型进行无偏的性能评估,以及如何通过观察神经网络在不同数据划分上的表现来获取洞见。

概述

到目前为止,我们已经讨论了什么是神经网络,以及如何根据训练数据找到最佳权重。然而,我们仍需更深入地探讨如何高效地训练神经网络。本节将介绍如何划分数据,以及如何解读神经网络在不同数据集上的性能表现。

数据划分的必要性

假设我们面临一个实际问题:我们有一个包含10000张交通标志图像及其对应分类标签的数据集。我们的目标是训练一个神经网络来进行交通标志分类。

我们是否应该在所有数据上训练,然后直接部署分类器?这种方法很可能会失败。原因在于,对于一个足够大的神经网络,经过足够多的训练迭代后,它几乎总能获得非常低的训练损失。这是因为典型的深度神经网络拥有大量参数,使其在很大程度上能够“记住”训练数据。

因此,更好的方法是将数据划分为三个部分:训练集验证集测试集

数据划分详解

顾名思义,训练集在神经网络训练期间被模型直接观察。损失函数正是在这个训练集上被直接最小化的。但如前所述,我们预期该函数在此集合上的值会非常低。

验证集由开发者用来测试当超参数改变时神经网络的性能。超参数是指那些修改网络结构或影响训练过程,但不属于训练期间学习的网络参数的参数。例如:学习率、层数、每层单元数以及激活函数类型。

最后的划分称为测试集,用于获得性能的无偏估计。在开发神经网络架构时,测试集应被严格隔离,确保神经网络在训练或超参数优化过程中从未见过这些数据。测试集的唯一用途应是在将最终架构和超参数集部署到自动驾驶汽车之前,评估其性能。

划分比例

在大数据时代之前,数据集通常在数千个样本的规模。在这种情况下,默认的划分比例大约是:60%用于训练,20%用于验证,20%保留用于测试。

然而,如今拥有数百万样本的数据集并不少见。在这种情况下,将20%的数据放在验证集和测试集中是不必要的,因为验证和测试所需的样本量远小于此。在这种情况下,我们可能会发现,98%的数据用于训练,1%用于验证,1%用于测试的划分方式也很常见。

回到我们的交通标志检测问题。我们假设交通标志数据集包含10000个标记样本。我们可以根据“60-20-20”的小数据集经验法则,将数据分为训练集、验证集和测试集。

性能评估与解读

我们现在使用损失函数来评估神经网络在每个划分上的性能。对于分类问题,损失函数定义为预测值与真实标签之间的交叉熵。交叉熵严格大于零,因此其值越高,分类器的性能越差。

请记住,神经网络只直接观察训练集,而开发者使用验证集来确定最佳超参数。训练的最终目标仍然是最小化测试集上的误差,因为这是对我们系统性能的无偏估计,并且网络从未见过这些数据。

让我们考虑以下几种性能表现场景。

场景一:良好拟合

假设我们的评估器在训练集上的交叉熵损失为0.21,在验证集上为0.25,在测试集上为0.3。此外,由于数据集中标签存在误差,我们预期可实现的最小交叉熵损失为0.18。

在这种情况下,我们拥有一个相当不错的分类器,因为三个集合上的损失相当一致,并且损失接近整个任务可实现的最小损失。

场景二:欠拟合

现在考虑第二种场景,训练损失现在是1.9,大约是最小损失的10倍。正如上一课所讨论的,我们预期任何合理规模的神经网络,在给定足够训练时间的情况下,几乎都能完美拟合数据。但在这种情况下,网络未能做到。我们将神经网络无法降低训练损失的这种情况称为欠拟合

场景三:过拟合

我们可能面临的另一种情况是训练集损失低,但验证集和测试集损失高。例如,我们可能遇到验证损失大约是训练损失10倍的情况。这种情况被称为过拟合,是由于神经网络优化其参数以精确复现训练数据输出而导致的。当我们在验证集上部署时,网络无法很好地泛化到新数据。训练损失和验证损失之间的差距称为泛化差距。我们希望这个差距尽可能低,同时保持足够低的训练损失。

性能优化策略

接下来,我们看看如何尝试从欠拟合或过拟合状态转变为良好的评估器。

如何应对欠拟合

我们首先从如何补救欠拟合开始。

以下是应对欠拟合的主要方法:

  • 延长训练时间:如果架构适合当前任务,延长训练时间通常会导致更低的训练损失。
  • 增加网络容量:如果架构太小,延长训练时间可能没有帮助。在这种情况下,您需要向神经网络添加更多层,或为每层添加更多参数。
  • 更换架构:如果以上选项都没有帮助,您的架构可能不适合当前任务,您需要尝试不同的架构来减少欠拟合。

如何应对过拟合

现在,让我们继续探讨减少过拟合的最常用方法。

以下是应对过拟合的主要方法:

  • 收集更多数据:在过拟合的情况下,最简单的方法是收集更多数据。不幸的是,对于自动驾驶汽车,收集训练数据非常昂贵,因为它需要工程时间进行数据收集,以及大量的标注员时间来正确定义真实输出。
  • 使用正则化:另一种解决过拟合的方法是正则化。正则化是指对学习算法进行的任何修改,旨在降低泛化差距,但不降低训练损失。
  • 重新审视架构:如果其他方法都失败了,最终的解决方案是重新审视架构,检查它是否适合当前任务。

总结

在本节课中,我们学习了如何解读神经网络在训练集、验证集和测试集上的不同性能表现场景。如果确定我们的网络欠拟合,最简单的解决方案是训练更长时间或使用更大的神经网络。然而,在自动驾驶汽车感知中,更常遇到的情况是过拟合,即在训练数据上的良好表现并不总能转化为在实际道路上的良好表现。

在下一课中,我们将重点介绍如何通过各种正则化策略来减轻过拟合的影响。这些策略将使在标记数据集上训练出色的感知算法,能够在不断变化的世界中继续良好地工作。

021:神经网络正则化

在本节课中,我们将学习如何通过正则化技术来减少神经网络的过拟合问题,从而提高模型在新数据上的泛化能力。

概述

上一节我们介绍了如何划分数据集并评估损失函数,并强调了训练后网络通常面临的是过拟合而非欠拟合问题。本节中,我们将探讨几种在训练过程中应用的正则化策略,以减轻过拟合。

从示例问题开始

让我们通过一个简单的示例来演示神经网络开发的迭代过程。我们的目标是将一个二维笛卡尔空间划分为橙色和蓝色两个部分。属于蓝色空间的点应标记为类别1,属于橙色空间的点应标记为类别2。然而,我们无法直接获取这些类别或其边界,只能通过带有噪声的传感器测量来获取带标签的样本点。

我们首先收集传感器数据,并将其按60/40的比例划分为训练集和验证集。训练集数据点用白色轮廓表示,验证集数据点用黑色轮廓表示。

欠拟合情况

我们使用一个简单的神经网络,它只有一层,每层有两个隐藏单元。以下是该设计选择得到的空间分类结果:

  • 训练集损失为 0.264
  • 验证集损失为 0.268
  • 两者都远高于该任务可达到的最小损失 0.1

这显然是一个欠拟合的情况。将网络的分类结果与真实的空间分类进行比较,我们发现神经网络未能捕捉到问题的复杂性,没有按要求将空间正确分割为四个部分。

过拟合情况

为了解决欠拟合问题,我们增加了网络规模,添加了五个额外的层,并将每层的隐藏单元数增加到6个。我们的模型现在表达能力更强,应该能更好地表示真实的分类。

我们重新训练模型并评估性能。结果如下:

  • 验证集损失为 0.45
  • 训练集损失为 0.1,等于该任务的最小可达到损失。

我们处于对训练数据过拟合的状态。过拟合是由于网络学习了训练数据中的噪声造成的。因为神经网络参数众多,它能够在空间中划分出与噪声训练样本对应的小区域(如红色圆圈内所示)。这通常发生在我们为当前问题过度增加网络规模时。

正则化方法

我们了解到,补救过拟合的一种方法是通过正则化。让我们看看神经网络中常用的第一种正则化方法。

参数范数惩罚

适用于神经网络的最传统的正则化形式是参数范数惩罚的概念。这种方法通过向目标函数添加惩罚项 Ω(θ) 来限制模型容量。

我们使用一个加权参数 α 将范数惩罚添加到现有的损失函数中:
J(θ) = L(θ) + α * Ω(θ)

其中,α 是一个新的超参数,用于加权范数惩罚对损失函数总值的相对贡献。通常,Ω(θ) 是衡量 θ 值大小的度量。最常见的是 L^p 范数:当 p=1 时,是绝对值和;当 p=2 时,是平方和等。此外,我们通常只约束神经网络的权重,这是因为神经网络中权重的数量远多于偏置的数量,因此权重惩罚对最终网络性能的影响更大。

神经网络中最常用的范数惩罚是 L2 范数惩罚。L2范数惩罚试图最小化神经网络每一层所有权重的L2范数。

让我们看看L2范数惩罚应用于我们问题上的效果。记住,我们最新的设计导致了在训练数据集上的过拟合。在损失函数中添加L2范数惩罚后,由于验证集损失低于未正则化的网络,我们得到了更好的空间分类估计。然而,验证集损失的降低伴随着训练集损失从 0.1 增加到 0.176。在这种情况下,泛化差距的减小幅度大于训练集损失的增加幅度。但需要注意,不要正则化过度,以免再次陷入欠拟合状态。

在大多数神经网络包中添加范数惩罚非常容易。如果你怀疑存在过拟合,L2范数惩罚可能是一个非常简单的补救措施,可以避免在设计过程中浪费大量时间。

Dropout 正则化

正如本视频前面提到的,研究人员开发了专门针对神经网络的正则化机制。一个常用且强大的机制称为 Dropout

让我们看看Dropout在网络训练中是如何应用的。

  1. 选择概率:第一步是选择一个概率,我们称之为 p_keep。在每个训练迭代中,这个概率用于选择要保留在网络中的节点子集。这些节点可以是隐藏单元、输出单元或输入单元。
  2. 前向传播:然后,我们切断从这些单元出发的所有连接,并评估输出 y
  3. 权重缩放:由于我们按照保留概率 p_keep 的比例移除单元,因此在训练结束时,我们将最终的权重乘以 p_keep。这对于在切换到完整网络进行推理时避免错误缩放输出至关重要。

Dropout可以直观地解释为强制模型在缺失输入和隐藏单元的情况下学习,换句话说,是用其自身的不同版本进行学习。它在训练过程中为广泛的神经网络模型家族提供了一种计算成本低廉但功能强大的正则化方法,在实践中能显著减少过拟合。此外,Dropout不会显著限制可使用的训练程序类型或模型。它几乎适用于任何使用分布式过参数化表示的模型,并且可以使用随机梯度下降进行训练。最后,所有神经网络库都实现了Dropout层并可供使用。我们建议在拥有密集前馈神经网络层时使用Dropout。

早停法

你应该了解的最终正则化形式是 早停法。为了直观地解释早停法,我们观察神经网络在训练集上评估的损失函数演变过程。

给定足够的容量,当神经网络记住训练数据时,训练损失应该能够降低到接近0的值。然而,如果我们有独立的训练集和验证集,验证损失会达到一个点并开始增加。这种行为在过拟合状态下是典型的,可以通过称为早停法的方法解决。

我们之前讨论过,可以根据各种停止标准来停止优化。早停法 在验证损失持续增加达到预设的迭代次数或周期数时结束训练。这通常被解释为神经网络即将进入过拟合状态之前的那个点。停止训练算法后,返回具有最低验证损失的那组参数。

最后需要注意的是,早停法不应作为正则化的首选,因为它也限制了训练时间,这可能会影响网络的整体性能。

总结

恭喜,你现在已经准备好开始构建自己的神经网络了。在本节课中,你学习了如何在神经网络陷入过拟合状态时提高其性能。神经网络的设计和训练还有许多有趣的方面,我们鼓励你通过本模块附带的额外资源继续探索这个迷人的领域。

在下一个也是本模块的最后一课中,我们将讨论一个对基于视觉的感知具有巨大实践和历史重要性的神经网络架构:卷积神经网络。到时见。

022:卷积神经网络

在本节课中,我们将深入学习卷积神经网络(Convolutional Neural Networks, ConvNets),理解它们为何对视觉感知任务至关重要。我们将探讨卷积层如何利用互相关运算替代全连接层的矩阵乘法,使其特别适合处理图像数据。同时,我们也会分析卷积神经网络相较于标准前馈神经网络的优势。

🧠 卷积神经网络简介

如果你关注过自动驾驶汽车的最新进展,那么“卷积神经网络”或简称“ConvNets”这个词你一定听过不止几次。事实上,我们目前就在自己的自动驾驶汽车Autonomous上使用卷积神经网络执行多种感知任务。

卷积神经网络是一种专门用于处理具有已知网格状拓扑结构数据的神经网络。这类数据的例子包括:在一维时间序列数据、二维图像,甚至是三维视频。卷积网络主要由两种类型的层构成:卷积层池化层

一个简单的卷积网络架构例子是VGG16。该网络接收图像,并将其通过一系列卷积层、池化层以及更多卷积层和池化层进行处理。目前我们无需过多担心VGG16架构的具体设计细节,在后续学习目标检测的视频中,我们会详细讨论这个架构。

接下来,让我们看看这两种层在实践中是如何工作的。

🔗 从全连接层到卷积层

我们之前描述的神经网络隐藏层通常被称为全连接层。顾名思义,全连接层将每个节点的输出连接到下一层的每一个节点输入。这意味着输入中的每个元素都会影响输出中的每个元素,这在软件中通过密集矩阵乘法实现。

虽然听起来有些反直觉,但卷积层使用互相关(cross-correlation)而非卷积(convolution)作为其线性算子,以此替代通用的矩阵乘法。使用互相关的逻辑在于:如果参数是通过学习得到的,那么是否翻转输出并不重要。由于我们学习的是卷积层的权重,这种翻转完全不影响最终结果。这导致了所谓的稀疏连接:卷积层中的每个输入元素,由于卷积操作使用了有限大小的核,只影响少数几个输出元素。

🖼️ 卷积层的工作原理

让我们从描述卷积层的实际工作方式开始。假设我们想对一个输入图像应用卷积层。我们将此图像称为输入体积,因为卷积层也可以将其他层的输出作为其输入。

输入体积的宽度是其水平维度,高度是其垂直维度,深度是通道数。在我们的例子中,这三个特征的值都是3。

为什么我们在计算高度或宽度时不考虑灰色像素?灰色像素是通过一个称为填充的过程添加到图像中的。每边添加的像素数称为填充大小,在这个例子中是1。填充对于保持执行卷积所需的形状至关重要。

我们通过一组滤波器来执行卷积操作。每个滤波器由一组权重和一个偏置组成。核的通道数需要与输入体积的通道数相对应。在这个例子中,每个滤波器有三个权重通道,分别对应输入图像的红、绿、蓝通道。通常,每个卷积层有多个滤波器。

让我们看看如何应用两个滤波器从输入体积得到输出体积。我们首先取滤波器的每个通道,在该通道与输入体积的对应通道之间执行互相关运算。然后,我们将所有通道互相关运算的输出与滤波器的偏置相加,得到最终的输出值。

注意,每个滤波器产生一个输出通道。我们稍后会回到这一点。

现在,让我们看看如何得到输出体积的其余部分。完成第一次计算后,我们将滤波器位置在水平方向上移动预设数量的像素。当到达输入体积宽度的末端时,我们将滤波器位置在垂直方向上移动预设数量的像素。垂直和水平移动的步长通常是相同的值,我们称这个值为卷积层的步长

最终,我们得到一个具有自身宽度、深度和高度值的输出体积。

📐 输出体积尺寸计算

假设滤波器尺寸为 M x M,我们有 K 个滤波器,步长为 S,填充为 P,我们可以推导出输出体积宽度、高度和深度的表达式。

你可能会觉得这很难跟踪。但在设计卷积网络时,了解最终会得到什么尺寸的输出层非常重要。例如,如果你试图在道路场景中检测小的交通标志和交通灯,你就不希望输出体积的尺寸缩减太多,因为它们只在图像中占据少量像素,如果输出体积过于紧凑,它们的可见性可能会丢失。

以下是计算输出尺寸的公式:

  • 输出宽度 = (输入宽度 - M + 2P) / S + 1
  • 输出高度 = (输入高度 - M + 2P) / S + 1
  • 输出深度 = K (滤波器数量)

🧊 池化层的工作原理

上一节我们介绍了卷积层如何提取特征,本节中我们来看看卷积网络的另一个核心组件:池化层。

池化层使用池化函数,用附近输出的汇总统计量来替换前一层的输出。池化有助于使特征表示对输入的小幅平移具有不变性。如果我们对输入进行小幅平移,池化层的输出不会改变。这对于物体识别非常重要,例如,如果我们将图像中的汽车平移一小段距离,它仍然应该被识别为汽车。

让我们以最常用的池化层——最大池化为例。最大池化使用最大值函数来汇总输出体积的局部区域。给定灰色的输入体积,最大池化将一个 M x M 区域应用于最大值函数,然后以类似于卷积层的步长移动该区域。

同样,我们可以根据以下公式推导出池化层输出的宽度、高度和深度。在我们之前的例子中,滤波器大小 M 为2,步长为2,因此我们最终得到一个2x2的输出。

让我们看看这个池化层如何帮助我们实现平移不变性。例如,让我们将之前的输入体积平移一个像素。由于平移而添加的像素显示为蓝色,而被移除的像素显示为红色。我们可以像上一张幻灯片那样,对这个输入体积应用最大池化。当我们将新输出与原始体积输出进行比较时,我们发现只有一个元素发生了变化。

🚗 卷积网络在自动驾驶中的优势

到目前为止,我们已经讨论了卷积网络如何运作,但还没有说明它们在自动驾驶汽车背景下的实用性。卷积网络的有效性主要有两个重要原因。

首先,与具有类似结构的全连接网络相比,卷积网络的卷积层通常具有少得多的参数。这通过参数共享减少了过拟合的机会,并允许卷积网络处理更大的图像。

其次,也是更重要的,是平移不变性。通过使用相同的参数处理图像的每个区块,卷积网络能够检测物体或对像素进行分类,即使它在图像平面上发生了平移。这意味着无论汽车出现在哪里,我们都能检测到它。

📜 卷积神经网络简史

在结束本节课之前,我想简要介绍一下卷积神经网络的历史。卷积神经网络在深度学习历史上扮演了重要角色。事实上,卷积网络是最早表现良好的神经网络模型之一,在当时其他前馈架构(尤其是在与ImageNet数据集相关的图像分类任务上)失败的时候取得了成功。在许多方面,卷积网络为深度学习的其余部分铺平了道路,并为神经网络在总体上获得相对较新的认可做出了贡献。

最后,卷积网络是最早解决重要商业应用的神经网络之一。最著名的是Yann LeCun在90年代初的手写数字识别器,并且至今仍处于深度学习商业应用的前沿。

事实上,在本课程的下一周,我们将向你展示如何使用卷积网络检测道路场景中的一系列不同物体。二维目标检测,我们下周见。

📝 总结

本节课中,我们一起学习了卷积神经网络的核心概念。我们了解了卷积层如何利用互相关和稀疏连接高效处理图像数据,以及池化层如何通过下采样和提供平移不变性来增强网络的鲁棒性。我们还探讨了卷积网络参数更少、具备平移不变性这两大优势,使其特别适合自动驾驶中的视觉感知任务。最后,我们回顾了卷积网络在深度学习发展史上的重要地位。这些知识为我们后续学习目标检测等具体应用打下了坚实的基础。

023:物体检测问题 🚗

概述

在本节课中,我们将学习自动驾驶汽车视觉感知中的一个核心模块:2D物体检测。我们将定义物体检测问题,了解其重要性,并学习如何评估物体检测器的性能。


2D物体检测简介

物体检测是自动驾驶汽车感知栈中的一项顶层要求,用于识别车辆、行人、标志和交通灯的位置,以便汽车知道可以在哪里以及如何行驶。

本周,我们将深入探讨如何定义2D物体检测问题,如何应用卷积神经网络来解决它,以及为何这个问题具有挑战性。我们还将解释物体检测如何作为2D物体跟踪问题的输入,并在自动驾驶汽车的背景下执行跟踪任务。


2D物体检测的历史背景

2D物体检测的历史始于2001年,当时Paul Viola和Michael Jones发明了一种非常高效的人脸检测算法。这个被称为Viola-Jones物体检测框架的算法,是第一个能够从简单网络摄像头提供可靠实时2D物体检测的框架。

下一个重大突破发生在四年后,Nevit Deal和Bill Triggs提出了方向梯度直方图特征描述符。他们的算法应用于行人检测问题,在当时超越了所有其他方法。Deal-Triggs算法一直占据榜首,直到2012年。

2012年,多伦多大学计算机科学系的Alex Krizhevsky、Ilya Sutskever和Geoffrey Hinton用他们的卷积神经网络AlexNet震撼了计算机视觉界。AlexNet以巨大优势赢得了2012年ImageNet大规模视觉识别挑战赛。当时,它是该比赛中唯一基于深度学习的参赛作品。但自那以后,所有获胜作品都基于卷积神经网络,并且最近超越了95%的人类识别率。

这种卓越性能从2D物体识别延伸到了2D物体检测,当今的检测器几乎完全基于卷积神经网络。


2D物体检测问题定义

在探讨如何使用卷积神经网络进行自动驾驶汽车的物体检测之前,让我们先正式定义一般的2D物体检测问题。

给定一个2D图像输入,我们需要估计场景中所有物体的位置(由边界框定义)和类别。通常,我们选择与自动驾驶应用相关的类别。我们通常最感兴趣的是动态物体类别,即在场景中移动的物体。这些包括车辆及其子类、行人和骑行者。

2D物体检测问题并非易事。我们需要估计的物体范围在图像空间中并不总是完全可见。例如,背景物体通常被前景物体遮挡。这要求我们使用的任何算法都必须能够“想象”物体的完整范围以正确检测它们。

此外,靠近图像边缘的物体通常会被截断。这种现象造成了边界框大小的巨大变化,估计的边界框大小取决于物体被截断的程度。

2D物体检测算法面临的另一个问题是尺度问题。物体离传感器越远,看起来就越小。我们的算法需要在不同的尺度下确定这些物体的类别。

最后,我们的算法还应该能够处理光照变化。这在自动驾驶汽车的背景下尤其重要,因为图像可能受到整体光照变化(从明亮的阳光到夜间驾驶)以及部分变化(由于反射、阴影、降水和其他干扰效应)的影响。


问题的数学形式化

现在我们已经直观地理解了物体检测是什么,让我们从数学上形式化这个问题。

物体检测可以定义为一个函数估计问题。给定一个输入图像 X,我们希望找到一个函数 F(x, θ),它产生一个输出向量。这个向量包括:

  • 边界框左上角的坐标:X_minY_min
  • 边界框右下角的坐标:X_maxY_max
  • 类别分数:S_class1S_classK

S_classI 指定了我们的算法认为该物体属于第 I 类的置信度,I 的范围是从 1K,其中 K 是感兴趣的类别数量。

你能想到任何估计这个函数的方法吗?我们上周介绍的卷积神经网络是估计这类函数的绝佳工具。对于物体检测,输入数据定义在2D网格上,因此我们使用卷积神经网络作为我们选择的函数估计器来执行此任务。

我们将在下一课讨论如何使用卷积神经网络进行2D物体检测,但首先,我们需要弄清楚如何衡量我们算法的性能。


评估2D物体检测器性能

给定一个2D物体检测器的输出(通常用红色表示),我们希望能够比较它与通常由人类标注的真实输出(称为真实边界框)的拟合程度。

评估过程的第一步是通过交并比指标(简称IOU)将检测器的定位输出与真实框进行比较。

IOU 定义为两个多边形交集的面积除以它们并集的面积。

然而,计算交并比并没有考虑类别分数。为了考虑类别分数,我们定义了真正例

真正例 是指与任何真实边界框的IOU大于预定义阈值,并且这些输出框的类别也应与其对应的真实框类别匹配的输出边界框。这意味着2D检测器应为正确的类别给出最高的类别分数,并且该分数应大于一个分数阈值。

另一方面,假正例 是指分数大于分数阈值,但与所有真实边界框的IOU都小于IOU阈值的输出框。这可以简单地计算为应用分数阈值后的检测物体总数减去真正例的数量。

我们想要估计的最后一个基本量是假反例的数量。假反例 是指通过IOU没有与之关联的任何检测的真实边界框。

一旦我们确定了真正例、假正例和假反例,我们就可以根据以下公式确定2D物体检测器的精确率召回率

  • 精确率 = 真正例数量 / (真正例数量 + 假正例数量)
  • 召回率 = 真正例数量 / 真实物体总数 = 真正例数量 / (真正例数量 + 假反例数量)

一旦我们确定了精确率和召回率,我们就可以改变物体类别分数阈值来获得一条精确率-召回率曲线

最后,我们确定平均精确率作为精确率-召回率曲线下的面积。曲线下面积可以使用数值积分计算,但通常使用在0到1范围内11个召回点(以0.1为增量)的精确率值的平均值来近似。

我知道这些概念第一次理解起来可能有点多,但别担心,在评估练习中,你将有机会通过一个循序渐进的实践笔记本来学习如何在Python中编写所有这些方法。


性能评估示例

让我们通过一个例子来学习如何使用学到的指标评估2D物体检测网络的性能。我们只对检测道路场景中的汽车感兴趣。这意味着我们只有一个感兴趣的类别,因此只需要考虑一组分数。

我们得到了由人类标注的汽车真实边界框,用绿色显示。我们使用卷积神经网络处理图像,得到用红色显示的检测输出边界框。你可以注意到,网络错误地将一辆大卡车的前部检测为汽车。查看分数,我们看到卷积神经网络给这个错误检测赋予了相当高的汽车分数。

现在,让我们使用平均精确率来评估卷积神经网络的性能。

第一步是获取所有估计的边界框,并根据物体类别分数对它们进行排序。然后,我们继续计算每个预测框与相应真实框之间的IOU。如果一个框不与任何真实框相交,则其IOU设为零。

首先,我们设置一个类别分数阈值,比如0.9。这个阈值意味着我们只信任网络预测分数大于0.9的结果,并消除任何分数小于0.9的边界框。

接下来,我们设置一个IOU阈值,本例中使用0.7,然后继续消除任何IOU小于0.7的剩余预测。在这种情况下,两个剩余的预测的IOU都大于0.7,所以我们没有消除任何框。

我们现在可以计算真正例的数量,即应用分数和IOU阈值后剩余的边界框数量,在本例中是2。假正例的数量在这种情况下是0,因为在应用分数阈值后剩余的所有框在应用IOU阈值后也仍然保留。

最后,假反例的数量是在应用分数和IOU阈值后,没有与之关联的检测的真实边界框的数量。在本例中,假反例的数量是2。

我们神经网络的精确率计算为真正例数量除以其与假正例数量之和。在本例中,我们没有假正例,所以精确率是 2 / 2 = 1。

为了计算召回率,我们将真正例数量除以真实边界框的数量,即真正例数量与假反例数量之和。本例中的召回率是 2 / 4 = 0.5。

在这种情况下,检测器是一个高精确率、低召回率的检测器。这意味着检测器会漏掉场景中的一些物体,但当它检测到一个物体时,在类别分类和边界框定位上犯的错误很少。

让我们看看当我们将分数阈值从0.9降低到0.7时,检测器的性能如何变化。所有边界框的分数都大于0.7,因此我们不会通过分数阈值消除任何框。然而,当我们检查剩余框的IOU时,我们可以看到其中两个的IOU小于0.7。通过消除这两个框,我们得到3个真正例。

为了计算假正例的数量,我们需要查看在应用分数阈值之后、但在应用IOU阈值之前,有多少个检测保留下来。在本例中,假正例的数量是2。

最后,我们查看在应用IOU和分数阈值后,仍然没有关联检测的真实边界框的数量,得到1作为假反例的数量。

请注意,在将分数阈值从0.9降低到0.7后,精确率从1下降到了0.6,而召回率从0.5增加到了0.75。我们可以得出结论,降低分数阈值的效果是以牺牲检测结果的准确性为代价,检测到场景中更多的物体。

如果我们继续这个过程,并以0.1的递减步长估计分数阈值,我们会得到下表。然后,我们继续绘制精确率-召回率曲线,使用y轴上的精确率值和x轴上的召回率值。

请注意,我们还将精确率-召回率点 (1, 0) 作为图中的第一个点,以及 (0, 1) 作为图中的最后一个点。这允许我们通过计算在0到1之间、以0.1为增量的11个召回点下的PR曲线面积来近似平均精确率。计算这个平均值,得到汽车检测器的AP为0.75。

检测器的平均精确率值可以被视为在所有分数阈值上的平均性能,允许客观比较不同检测器的性能,而无需考虑生成这些检测的确切分数阈值。


总结

在本节课中,我们一起学习了如何形式化2D物体检测问题,以及如何使用平均精确率性能指标来评估2D物体检测器的性能。

下一课,你将学习如何将卷积神经网络用作自动驾驶汽车的2D物体检测器。我们下节课见。

024:基于卷积神经网络的2D物体检测

概述

在本节课中,我们将学习如何使用卷积神经网络构建一个标准的单阶段2D物体检测架构。我们将回顾2D物体检测问题,并详细探讨用于此任务的卷积神经网络的基本配置,包括特征提取、先验框(锚框)的使用以及网络的输出层设计。


回顾2D物体检测问题

上一节我们介绍了通用的物体检测问题以及如何使用平均精度来评估2D物体检测器的性能。本节中,我们来看看如何使用卷积神经网络来执行2D物体检测。

给定一张输入图像,我们的目标是同时定位场景中的所有物体并确定它们所属的类别。


用于2D物体检测的卷积神经网络架构

下图展示了用于2D物体检测的基本卷积神经网络配置,我们称之为神经网络架构。

该架构以一张图像和一组手动设计的先验框作为输入。

首先,图像通过一个特征提取器进行处理。
其次,一组全连接层以特征提取器的输出作为输入,为每个2D先验框提供位置精修和分类。
最后,对全连接层的输出执行非极大值抑制,以生成最终的检测结果。

接下来,我们将深入探讨其中的每一个步骤。


特征提取阶段

特征提取器是2D物体检测器中计算成本最高的组件。通常,整个架构所需计算量的高达90%都用于此阶段。

特征提取器的输出宽度和高度通常远低于输入图像,但其深度通常比输入图像大两到三个数量级。

特征提取器的设计是一个非常活跃的研究领域,新的提取器不断涌现。最常用的特征提取器包括VGG、ResNet和Inception。在本节课的剩余部分,我们将以VGG特征提取器作为主要示例进行讲解。

VGG特征提取器基于牛津大学VGG小组提出的VGG16分类网络的卷积层。它是最早被提出用于检测的特征提取器之一,构建起来相当简单。

与大多数卷积网络一样,VGG特征提取器通过交替堆叠卷积层和池化层来构建。

所有卷积层的尺寸为 3x3xK,步长为 1,零填充为 1
所有最大池化层的尺寸为 2x2,步长为 2,无填充。

这些特定的超参数是通过大量实验得出的,在广泛的问题中表现极佳,使得VGG成为一个非常流行的提取器。

让我们看看这些层如何影响输出体积的形状。

上周,我们推导了卷积层处理后,输出宽度、高度和深度的三个公式。

对于VGG特征提取器,所有卷积都是尺寸为3x3xK,步长为1,零填充为1。这意味着将输入体积通过任何卷积层只会将其深度改变为K。

另一方面,VGG提取器的池化层是2x2,步长为2,无填充。使用池化层的公式进行相同分析,我们注意到VGG提取器的最大池化层将输入体积的宽度和高度减少一半,同时保持深度不变。

现在让我们看看VGG提取器如何处理输入图像。

给定一个 M x N x 3 的输入图像,我们将其通过前两个深度为64的卷积层,然后是第一个池化层。此时,输出体积的宽度和高度减少了一半,而深度扩展到了64。

我们可以将VGG特征提取器分为若干个子部分,每个子部分以一个池化层结束。第一个子部分称为VGG提取器的conv1子部分。

然后,我们将输出体积通过其余五个子部分,最终得到形状为 M/32 x N/32 x 512 的输出体积。

例如,如果我们有一个 1240 x 960 x 3 的图像作为输入,我们最终的输出体积形状将是 40 x 30 x 512。我们将使用这个输出体积来生成最终的检测结果。


先验框(锚框)的概念

我们神经网络架构中要描述的下一个步骤是先验框(也称为锚框)的概念。

为了生成2D边界框,我们通常不会从零开始,在没有任何先验信息的情况下估计边界框的角点。我们假设我们对框在图像空间中的位置以及这些框应该有多大确实有一个先验知识。这些先验被称为锚框,是在整个图像上手动定义的,通常在一个等间距的网格上。

假设我们有一组接近我们真实标注框的锚框。在训练期间,网络学习获取每个锚框,并尝试在质心位置和框尺寸上将其移动到尽可能接近真实边界框的位置。这被称为残差学习,它利用了这样一个概念:将现有框微调一小段距离来改进它,比在整个图像中搜索可能的物体位置要容易得多。

在实践中,残差学习已被证明比尝试直接估计没有任何先验的边界框能提供更好的结果。

文献中提出了许多关于如何使用锚定边界框生成最终预测的不同方法。通常,锚框与特征图交互,为每个锚框生成固定大小的特征向量。

我们将解释微软研究院在其Faster R-CNN架构中提出的用于这种交互的首批方法之一。但请注意,这并不是执行这种交互的唯一方式。

Faster R-CNN的交互方法相当简单:对于特征图中的每个像素,我们关联K个锚框。然后,我们在该像素的邻域上执行一个 3x3xD* 的卷积操作。这为该像素产生了一个 1x1xD* 的特征向量。我们使用这个 1x1xD* 的特征向量作为与该像素关联的K个锚框中每一个的特征向量。

然后,我们将提取的特征向量馈送到神经网络的输出层。


输出层:回归头与分类头

2D物体检测器的输出层通常包括一个回归头和一个分类头。

回归头通常包含多个全连接隐藏层和一个线性输出层。回归的输出通常是一个残差向量,需要将其添加到当前锚框上以获得真实边界框。

更新锚框尺寸的另一种方法是回归一个从锚框中心到真实边界框中心的残差,以及两个比例因子,当与锚框的宽度和高度相乘时,可以校正真实边界框的宽度和高度。

分类头也由多个全连接隐藏层组成,但最终是一个Softmax输出层。Softmax输出是一个向量,每个类别有一个分数。最高分数通常定义了当前锚框的类别。


冗余检测与非极大值抑制

根据我们目前的描述,你可能会注意到,对于输出特征图的每个像素,我们最终会得到K个边界框。即使我们只考虑所有感兴趣类别中分类分数高的框,图像中仍然会有许多冗余检测。

在计算平均精度时,我们只考虑每个真实边界框的一个检测。任何冗余检测都被视为误报,会导致精度降低,从而降低整体平均精度。

我们的目标是实现完美检测,这意味着我们希望图像中每个物体只输出一个框。因此,我们需要采取措施来消除网络产生的许多冗余检测。

实际上,这只是在推理时的一个问题。事实证明,在训练期间驱动网络为每个锚框输出尽可能好的残差是有益的。


总结

本节课中我们一起学习了以下内容:

首先,你学习了卷积神经网络可用于解决2D物体检测问题,并研究了VGG特征提取器架构的细节。
其次,你学习了如何使用锚框作为物体位置的先验,并通过估计边界框残差来输出最终的物体位置。

在下一课中,我们将描述如何使用分类和回归损失函数来训练2D物体检测器。为此,我们将利用大部分K个输出框。然后,我们将回到为每个物体输出单个边界框的挑战,并引入非极大值抑制的概念来解决它。

025:训练与推理

📚 概述

在本节课中,我们将学习如何训练和部署用于2D目标检测的卷积神经网络。我们将重点解决一个关键问题:如何处理每个目标对应的多个预测边界框。具体来说,我们将学习在训练阶段通过小批量选择,以及在推理阶段通过非极大值抑制来处理这些冗余的预测框。

上一节我们介绍了使用卷积网络进行目标检测的基线方法。然而,处理锚点网格中的所有锚点会导致每个物体被检测出多个边界框,而不是预期的单个框。本节中,我们来看看构建和训练用于2D目标检测的卷积神经网络所需的最后几个组件。

🧠 神经网络训练回顾

我们有一个卷积模型和训练数据对:输入图像 X,以及边界框位置和类别 F*(X)。我们的目标是使用输出边界框 Y = F(X, θ) 来近似 F*(X)

回想一下,训练过程首先评估一个损失函数,该函数衡量我们预测的边界框与真实边界框的相似程度。然后将计算出的损失函数输入优化器,优化器会输出一组新的参数 θ 用于下一次迭代。请注意,特征提取器和输出层在训练过程中都会被修改。

如果 F*(X)F(X, θ) 是一一对应的,问题会很简单。然而,我们网络的输出是多个边界框,这些框可能与单个真实框相关联。让我们看看如何解决这个问题。

🎯 锚点的分类与回归目标

请记住,对于特征图中的每个像素,我们关联了 K 个锚点。这些锚点是如何映射回原始图像的呢?我们的特征提取器将初始输入的分辨率降低了32倍。这意味着,如果我们将特征图中的每个像素与一组锚点关联,这些锚点将通过放置在步长为32的网格上被转移到原始图像中。

我们可以将真实边界框与这些锚点一起可视化。你会注意到,有些锚点与真实框重叠,有些则不重叠。我们使用交并比来量化这种重叠,并将锚点分为两类。

我们首先指定两个IOU阈值:一个正锚点阈值和一个负锚点阈值

  • 任何IOU大于正锚点阈值的锚点被称为正锚点
  • 任何IOU小于负锚点阈值的锚点被称为负锚点
  • 任何IOU介于两个阈值之间的锚点则被完全丢弃。

那么,如何在训练中使用这些正锚点和负锚点呢?以下是分类和回归目标的分配方法:

对于分类

  • 我们希望神经网络预测负锚点属于背景类。背景类通常是我们添加到感兴趣类别中的一个额外类别,用于描述不属于这些类别的任何物体。
  • 另一方面,我们希望神经网络将与真实框相交的任何正锚点分配为真实框的类别。

对于回归

  • 我们希望调整正锚点的参数,使其与真实边界框的参数对齐。
  • 负锚点不用于边界框回归,因为它们被假定为背景。

⚖️ 小批量选择与难负样本挖掘

这种在训练中处理多个回归锚点的方法并非没有问题。所提出的IOU阈值机制导致大多数回归锚点都是负锚点。如果使用所有这些锚点进行训练,网络将看到远多于正样本的负样本,从而导致对负类的偏见。

解决这个问题的方法其实很简单:我们不是使用所有锚点来计算损失函数,而是按照负锚点与正锚点3:1的比例来采样选定的小批量样本。负样本通过一个称为在线难负样本挖掘的过程来选择,其中负小批量成员被选为具有最高分类损失的负锚点。这意味着我们将训练网络来修正负分类中的最大错误。

例如,如果我们的小批量大小为64个样本,负小批量将是具有最高分类损失的48个负样本,剩余的16个锚点将是正锚点。如果正样本数量少于16个,我们可以复制一些正样本来填充小批量,或者用负锚点填充剩余的位置。

📉 损失函数定义

正如我们上周所描述的,我们使用交叉熵损失函数作为卷积网络分类头的损失。总分类损失是小批量中所有锚点的交叉熵损失的平均值。

分类损失公式
L_cls = (1/N) * Σ_i [ -log(s_i) * s_i_star ]
其中,归一化常数 N 是选定的小批量大小。s_i 是分类头的输出,s_i_star 是真实分类(对于负锚点设为背景,对于正锚点设为真实边界框的类别)。

对于回归,我们以类似的方式使用 L2 范数损失。然而,我们只尝试修改那些是正锚点的锚点。这在数学上通过 L2 范数上的一个乘数 p_i 来表示,如果锚点是负的,则 p_i 为0;如果是正的,则为1。

回归损失公式
L_reg = (1/N_pos) * Σ_i [ p_i * || b_i - b_i_star ||^2 ]
为了归一化,我们除以正锚点的数量 N_pos。提醒一下,b_i_star 是真实边界框表示,而 b_i 是估计的边界框。请注意,我们不直接估计框参数,而是通过加性残差或乘性尺度来修改锚点参数,因此 b_i 必须从估计的残差中构造出来。

🎨 损失函数可视化

让我们可视化一下我们试图通过这些损失函数教会神经网络学习什么。

给定一个输入图像、一个真实边界框和来自锚点先验的一组输入锚点,我们正在教神经网络将锚点分类为包含背景(紫色)或汽车(蓝色)。这是通过最小化上面定义的交叉熵损失来实现的。

然后,我们希望神经网络仅移动那些包含感兴趣类别的锚点,使其与最接近的真实边界框匹配。这是通过最小化上面定义的L2范数损失来实现的。

到目前为止,你应该已经很好地掌握了如何在训练期间处理每个物体的多个输出框。但是,当我们在推理过程中实时运行神经网络时,我们该怎么做呢?

🚀 推理与非极大值抑制

请记住,在推理过程中,我们没有真实值来确定正锚点和负锚点,也不评估损失函数。我们只希望场景中每个物体有一个输出框。

这时就需要非极大值抑制登场了,这是一种极其强大的方法,用于改进基于锚点的神经网络的推理输出。

非极大值抑制的输入是一个预测边界框列表 B,每个边界框由回归坐标和类别输出分数组成。它还需要一个预定义的IOU阈值作为输入,我们称之为 η

该算法如下:

  1. 我们首先根据输出分数对列表 B 中的边界框进行排序。
  2. 我们还初始化一个空集合 D 来保存输出边界框。
  3. 然后,我们遍历排序后的框列表 B_bar 中的所有元素。
  4. 在循环内部,我们首先确定列表 B_bar 中分数最高的框 B_max(这应该是 B_bar 中的第一个元素)。
  5. 然后,我们将这个边界框从边界框集合 B_bar 中移除,并将其添加到输出集合 D 中。
  6. 接下来,我们找到集合 B_bar 中剩余的所有与当前最大框 B_max 的IOU大于 η 的框。这些框与当前的 B_max 框显著重叠。
  7. 任何满足此条件的框都会从列表 B_bar 中移除。
  8. 我们不断迭代列表 B_bar,直到它为空,然后返回列表 D

现在,D 包含了每个物体的单个边界框。

让我们通过一个视觉示例来理解非极大值抑制算法在实践中是如何工作的。

假设我们已经按降序对边界框列表进行了排序。为了更清楚地展示非极大值抑制的工作原理,我们在这里明确显示了分数列表。

B_max 将是排序列表 B_bar 的第一个边界框。然后,我们将每个边界框与 B_max 进行比较。在这种情况下,只有一个框 B3B_max 有非零IOU。我们计算该IOU并将其与我们的IOU阈值 η 进行比较。在这种情况下,IOU大于阈值 η,因此我们将 B3 从列表 B_bar 中移除。

我们对列表中剩余的下一个最高分数重复此过程。同样,只有一个框(本例中为 B4)与 B_max 有非零IOU。计算IOU并与阈值比较后,我们从列表 B_bar 中消除 B4,并将 B2 添加到输出列表 D 中。

我们注意到初始列表 B_bar 现在为空,因此我们的非极大值抑制算法退出,并返回输出框列表 D,该列表按预期包含了每个物体的一个边界框。

✅ 总结

恭喜!你现在已经完成了训练和部署基于卷积神经网络的2D目标检测器以用于自动驾驶汽车所需的内容。

在本视频中,我们探讨了如何在训练期间调整网络输出以保持类别平衡,以及在推理期间限制网络输出以选择每个物体的一个输出边界框。

在下一课中,我们将讨论如何将这些2D目标检测器的输出用于对自动驾驶汽车至关重要的各种任务,包括将2D目标检测转换为3D、跟踪物体运动,以及将2D目标检测应用于交通标志和信号检测。下节课见!

026:2D物体检测器在自动驾驶汽车中的应用

在本节课中,我们将探讨2D物体检测在自动驾驶汽车中的三个关键应用。我们将学习如何将2D检测扩展到3D空间,如何进行物体跟踪以建模其他道路参与者的行为,以及如何检测交通标志和信号灯。这些应用共同构成了自动驾驶汽车理解周围环境、做出安全决策的基础。

🚗 从2D检测到3D定位

上一节我们介绍了如何进行2D物体检测。然而,自动驾驶汽车需要在三维空间中理解场景才能安全行驶。本节中,我们来看看如何将2D检测结果扩展到3D。

自动驾驶汽车需要三维场景理解来安全地穿越环境。知道行人、汽车、车道和标志在汽车周围的位置,完全定义了自动驾驶时可以安全采取哪些行动。这意味着仅在图像平面检测物体是不够的,我们需要将问题从2D扩展到3D,并在世界坐标系中定位检测到的物体。

使用卷积神经网络进行3D物体检测是一个相对较新的课题,该领域的结果在不断变化。与2D检测类似,3D物体检测也需要类别分类。这对于跟踪物体至关重要,因为例如,汽车的运动方式与行人不同,如果知道物体类别,就可以做出更好的预测。此外,我们还需要估计物体质心在3D中的位置、3D中的范围以及3D中的方向。这些详细的状态信息都能改善运动预测和碰撞避免,并提高汽车在交通中行驶的能力。

这个物体状态可以表示为:

  • 质心位置的3D向量:表示为物体的X、Y、Z位置。
  • 范围的3D向量:表示为物体的长、宽、高。
  • 方向角的3D向量:表示为物体的滚转角、俯仰角和偏航角。

对于道路场景,我们通常只关心偏航角,因为大多数道路参与者无法在很大程度上改变其滚转和俯仰角(地面条件决定了这些角度)。因此,只跟踪偏航角就足够了。

那么,如何从2D边界框得到物体位置和范围的准确3D估计呢?将2D物体检测结果扩展到3D最常见且最成功的方法是使用激光雷达点云。

给定图像空间中的一个2D边界框和一个3D激光雷达点云,我们可以使用相机投影矩阵的逆矩阵,将边界框的角点作为射线投影到3D空间中。这些线的多边形相交区域称为视锥体,通常包含与我们图像中物体相对应的3D点。然后,我们从激光雷达中获取此视锥体内的数据,将其转换为我们选择的表示形式,并训练一个小型神经网络来预测定义3D边界框所需的七个参数。

现在,我们应该将视锥体中激光雷达点的哪种表示输入神经网络呢?以下是一些选择:

  • 直接处理原始点云数据。
  • 相对于某个固定点(例如视锥体中心)对点云数据进行归一化。
  • 对点进行预处理以构建固定长度的表示,例如X、Y、Z点的直方图,这使其作为卷积神经网络的输入更加方便。

无论使用哪种表示,我们都期望得到定向3D边界框形式的结果。

需要记住,上述讨论的过程只是执行3D物体检测的一种方式。那么,为什么我们选择将2D检测扩展到3D,而不是直接在3D中检测物体呢?原因如下:

  • 成熟度:2D物体检测器比3D检测器成熟得多。我们通常能从成熟的2D物体检测器获得非常高的精确率和召回率,这在3D物体检测文献中尚不可及。
  • 分类便利:我们可以从2D物体检测器结果中免费获得分类。无需使用激光雷达数据或将3D信息输入网络来确定我们看到的是汽车还是柱子,这在2D图像数据中非常明显。
  • 计算效率:如果无法对物体可能出现的位置做出假设,在3D空间中搜索可能的物体计算成本相当高。将2D物体检测器扩展到3D通常允许我们限制物体实例的搜索区域,从而保持实时性能可控。

然而,将2D物体检测器扩展到3D也会带来一系列独特的问题:

  • 性能上限:使用这种方法进行3D物体检测的一个突出问题是,我们将3D姿态估计器的性能限制在一个上限,即2D检测器的性能。
  • 遮挡与截断:当面对严重的遮挡和从相机视角的截断时,2D到3D的方法通常会失败,而这些可能不影响激光雷达数据。
  • 延迟:由于这种方法串行性质引起的延迟通常不可忽略。延迟表现为感知延迟,这意味着我们的汽车将在一定延迟后看到道路上的物体。如果这种延迟显著,系统可能不够安全,因为车辆反应时间受感知延迟的限制。

🔄 物体跟踪:连接时间序列的检测

了解了3D定位后,我们来看看2D到3D物体检测的另一个重要应用:物体跟踪。跟踪涉及将同一物体的一系列检测结果拼接成一条轨迹,该轨迹定义了物体随时间推移的运动。

我们将从描述一个既可用于2D也可用于3D的简单跟踪方法开始,希望您能从第二门课程中感到熟悉。当我们执行物体检测时,我们通常在每一帧中独立检测物体。然而,在跟踪中,我们通过已知的物体动态模型来整合预测的位置。

跟踪需要一组约束场景变化速度的假设。例如,我们假设我们的相机和被跟踪的物体不能在极短时间内传送到不同位置。同时,我们假设场景变化平滑且渐进。所有这些假设在道路场景中都是逻辑上有效的。

让我们直观地看看物体跟踪是什么样子。给定第一帧中检测到的物体及其速度向量,我们首先通过使用速度向量建模其运动来预测物体在第二帧中的位置。然后我们在第二帧中获得新的检测结果,我们称这些检测为测量值。我们将每个检测与相应的测量值关联起来,然后使用关联的测量值更新我们的物体预测。

以下是每个必要步骤的描述:

  1. 定义状态:我们将边界框的状态定义为其在图像空间中的位置和速度。
  2. 运动模型预测:每个物体都有一个更新其状态的运动模型。例如,这里显示的恒定速度运动模型用于将每个边界框移动到第二帧中的新位置。请注意,我们在运动模型中添加了零均值高斯噪声,因为模型并不完美。
  3. 关联测量:在预测步骤之后,我们从2D物体检测器获得第二帧的测量值。然后,我们通过计算所有预测和所有测量值之间的交并比(IOU)来将每个测量值与一个预测相关联。如果一个测量值与某个预测具有最高的IOU,则它与该预测相关联。
  4. 状态更新:最后一步是使用卡尔曼滤波器来融合测量值和预测更新。我们建议回顾第二门课程中介绍的卡尔曼滤波器方程,以了解此步骤是如何执行的。卡尔曼滤波器更新整个物体状态,包括位置和速度,我们可以将其用于后续的预测步骤。

需要注意的是,我们不需要跟踪物体的大小,而是可以依赖检测器。我们通常为物体分配其最后已知测量值的大小。还有一些细节需要处理,特别是如何初始化轨迹和如何终止它们。如果我们获得与任何先前预测都不相关的2D检测器结果,则初始化新轨迹。类似地,如果一个预测的物体在预设的帧数内没有与测量值相关联,则终止不一致的轨迹。最后,我们需要注意,通过在3D中定义IOU,我们可以使用相同的方法进行3D物体跟踪。

让我们看一段来自自动驾驶汽车的3D物体跟踪视频。请注意,轨迹略有抖动,因为检测结果在帧间位置估计中存在一些噪声,并且此代码中使用的运动模型不包括准确的车辆运动学或动力学模型。这是由于缺乏对其他车辆转向和加速输入信息的了解。尽管如此,我们仍然可以准确地检测到在环境中移动的多个车辆,并在此过程中就优先权和安全交互做出谨慎的决策。

🚦 交通标志与信号灯检测

在2D物体检测的框架内,我们将描述的最后一个概念是交通标志和交通信号灯的检测。正确检测这些道路规则指示器,可以引导自动驾驶汽车在繁忙的街道上合法行驶。然而,这项检测任务也面临其自身的一系列挑战。让我们看看最突出的几个。

以下是安装在自动驾驶汽车上的摄像头拍摄的典型行车记录仪风格图像。通常,必须在远距离检测交通标志和信号灯,汽车才能知道如何及时做出适当反应。在远距离,交通标志和信号灯在图像中占据的像素非常少,这使得检测问题特别具有挑战性。此外,交通标志变化很大,通常包括多达50个需要可靠分类的类别。另一方面,交通信号灯在世界不同地区可能呈现不同的外观,并且具有多种状态,需要自动驾驶汽车检测这些状态才能安全地通过有信号灯的交叉路口。此外,交通信号灯的状态会随着汽车的移动而改变,这可能会在尝试在图像空间中跟踪交通信号时导致一些问题,因为外观会随着交通信号灯的状态而变化。

幸运的是,我们迄今为止描述的标准物体检测器无需重大修改即可用于检测交通标志和信号。然而,当前的方法依赖于多阶段分层模型来更稳健地执行此检测任务。

让我们考虑这里显示的两阶段模型。这两个阶段共享特征提取器的输出来执行各自的任务。在此示例中,第一阶段输出与类别无关的边界框,指向图像中的所有交通标志和信号,而不指定每个框属于哪个类别。然后,第二阶段获取第一阶段的所有边界框,并将其分类为红色、黄色或绿色信号、停车标志等类别。此外,一些方法还使用第二阶段来进一步细化第一阶段提供的边界框。

这种多阶段方法并非交通标志和信号检测所独有,许多通用物体检测框架都采用多阶段方法来生成准确的物体类别和位置。如果您感兴趣,我们已提供其中一些方法作为补充阅读材料。

📝 总结

在本节课中,我们一起学习了2D物体检测在自动驾驶中的核心应用。

我们看到,2D物体检测器的输出可用于生成3D物体位置。我们研究了如何在连续的图像帧中对2D物体检测器输出执行跟踪,以创建贯穿环境的物体轨迹。我们还探讨了2D物体检测器如何无需重大修改即可用于检测交通标志和交通信号,不过,利用分层模型的专用方法通常比标准方法表现更好。

现在,我们已经掌握了自动驾驶汽车中使用的主要物体检测类型所需的工具。

恭喜您成功完成了关于2D物体检测的这个模块。这个模块内容相当深入,我们建议您查看我们提供的资源,以获取更多关于如何使用神经网络进行2D和3D物体检测和跟踪的信息。下周,我们将讨论视觉感知堆栈的另一个重要组成部分,它在像素级别运行:语义分割。到时见。

027:语义分割问题

概述

在本节课中,我们将学习自动驾驶汽车视觉感知中的一项重要任务:语义分割。我们将定义语义分割问题,并学习如何使用常见的评估指标(如类别交并比)来评估语义分割模型的性能。


语义分割的定义

上一节我们介绍了本模块的学习目标,本节中我们来看看语义分割的具体定义。

给定一张输入图像,我们希望将图像中的每个像素分类到一组预设的类别中。

这些类别可以是静态的道路元素,例如道路、人行道、电线杆、交通信号灯和交通标志。也可以是动态的障碍物,例如汽车、行人和骑自行车的人。此外,我们通常还会设置一个背景类别,用于包含所有未包含在预设类别中的内容。

与目标检测类似,我们通过一个函数估计器来实现语义分割。

那么,我们如何调整通用的神经网络以用于分割任务呢?给定一张图像,我们将每个像素作为输入,并为每个像素输出一个类别得分向量。一个像素属于得分最高的那个类别。因此,我们希望我们的估计器能为图像中的每个像素,为其正确的类别给出最高的得分。

例如,一个道路像素应该具有非常高的道路类别得分,而其他类别的得分则要低得多。当看到一个属于人行道的像素时,函数估计器应该提供比所有其他类别得分更高的人行道得分。


语义分割的挑战

在尝试进行语义分割时,我们通常会面临许多与目标检测相同的问题。因此,语义分割是一个不简单的问题。

以下是语义分割面临的主要挑战:

  • 遮挡和截断:使得准确预测物体边界变得困难。
  • 尺度问题:我们需要同时处理具有精细细节的近距离物体和仅由几个像素捕获的远距离物体。
  • 光照变化:需要能够处理场景中的光照变化。

然而,语义分割还有一个自身特有的主要困难。这个困难是由图像空间中边界模糊性引起的,特别是对于细长的物体(如电线杆)、外观相似的物体(如道路和人行道)以及远处的物体。在后续课程中,我们将看到在设计语义分割算法时如何处理这个问题。


语义分割的算法基础

你认为我们可以使用什么算法来解决语义分割问题?如果你的答案是卷积神经网络,那么你是正确的。与目标检测类似,卷积神经网络已被证明是解决语义分割问题非常有效的选择。

我们将在下一课中详细讨论这种解决方案,但首先,让我们来确定如何衡量语义分割网络的性能。


语义分割的评估指标

上一节我们提到了卷积神经网络是解决方案,本节中我们来看看如何评估这些模型的性能。让我们从回顾一些基本的分类指标开始。

以下是评估所需的基础指标:

  • 真正例:正确分类为某个特定类别X的像素数量。
  • 假正例:不属于类别X,但被分类为该类别的像素数量。
  • 假反例:属于类别X,但未被分类为该类别的像素数量。

这些术语与目标检测中使用的术语相同。

使用这三个指标,我们可以计算最常用的语义分割评估指标:类别交并比。类别交并比的计算公式为:真正例 / (真正例 + 假正例 + 假反例)

让我们来看一个计算的可视化例子。地面真值分割数据表示为每个像素一个类别。同样,通过将输出得分最高的类别作为我们的预测类别,预测结果也可以放在类似的格式中。

我们首先计算用R表示的道路类别的交并比。要测量的第一个指标是真正例的数量。我们可以通过计算预测中正确分类的道路像素数来确定真正例,在本例中是3。第二个指标,假正例,在我们的例子中是0,因为我们的算法没有将任何人行道像素分类为道路。最后,我们的算法将两个道路像素分类为人行道,因此,假反例计数为2。我们最终的道路交并比是 3/5。

让我们对人行道类别遵循这个计算过程。我们可以看到我们有四个正确分类的像素,因此我们的真正例计数是四。此外,我们的算法错误地将两个像素分配给人行道类别,而实际上它们属于道路类别。因此,我们的假正例计数是二。最后,算法没有遗漏任何人行道像素,所以它的假反例计数是0。然后我们可以计算出人行道类别的交并比为 4/6。


评估注意事项

我们是在单张图像上执行了类别交并比计算。当在多张图像上执行此计算时,必须记住要在所有图像上计算真正例、假正例和假反例的总和,然后再计算交并比。先计算每张图像的交并比再求平均,实际上会得到一个不正确的类别交并比分数。

此外,通常更好的做法是独立地查看每个类别的交并比分数,而不是将它们平均。这是因为全局的交并比度量偏向于覆盖较大图像区域的物体实例。在尺度变化强烈的街景中,这可能会有问题。其他指标,如每个实例的交并比,通常用于弥补这个问题。

Cityscapes基准测试是评估自动驾驶汽车语义分割算法最常用的基准之一。提交到Cityscapes基准测试的语义分割模型,主要使用每个类别的交并比作为排名指标。虽然也会为每个模型计算实例级别的交并比。


总结

在本节课中,我们一起学习了语义分割的定义,即为一幅2D图像中的每个像素提供一个类别标签。我们还学习了如何使用每个类别的交并比来评估语义分割模型。

在下一课中,我们将学习如何使用卷积神经网络来解决语义分割问题。

028:语义分割卷积网络

在本节课中,我们将学习如何使用卷积神经网络来执行语义分割任务。与用于目标检测的卷积网络相比,用于语义分割的卷积网络在训练和推理阶段几乎相同,这使得其应用相对更简单。然而,语义分割本身存在一些需要特别注意的复杂性。

语义分割问题回顾

上一节我们定义了语义分割问题,并学习了如何通过计算类别交并比来评估算法性能。本节中,我们来看看如何构建模型来解决这个问题。

语义分割任务接收一张相机图像作为输入,并为该图像中的每一个像素提供一个类别分类作为输出。这个问题可以被建模为一个函数逼近问题,而卷积网络再次成为逼近该函数的工具。

基础架构分析

我们首先分析一个直观的想法:使用与目标检测相同的卷积模型,即一个特征提取器后接一个输出层。由于我们不需要定位物体,因此这里不需要锚框。

以下是该架构的核心流程分析:

  1. 特征提取器:我们可以使用VGG架构。输入图像尺寸为 M x N x 3
  2. 分辨率变化:每经过一个池化层,图像分辨率会减半。而卷积层会增加特征图的深度。
  3. 输出特征图:经过特征提取器后,输出的特征图尺寸会缩小为原图的 1/16

然而,我们的最终输出需要为原始图像中的每个像素提供分类。面对一个缩小了16倍的特征图,一个简单的解决方案是:

  1. 将下采样后的特征图通过一个Softmax层,计算每个像素在子采样空间中的类别得分。
  2. 通过取Softmax层输出的最高得分来确定每个像素的类别。
  3. 最后,将下采样的输出上采样回原始图像分辨率。

上采样的挑战

你认为上述方法效果会如何?为了理解朴素的上采样为何可能产生不理想的结果,我们需要了解上采样如何增加图像分辨率。

以最近邻上采样一个 2x2 的图像块为例。上采样因子 S=2。该过程会生成一个初始为空的 4x4 网格,然后每个新像素用原始图像块中最近邻像素的值填充。

这种方法导致的问题是,上采样后的输出图像边界非常粗糙。正如上一课所提到的,语义分割中最具挑战性的问题之一就是获得物体周围平滑且准确的边界。对于细长物体(如电线杆)、外观相似的物体(如道路和人行道)以及远处的物体,边界尤其难以准确界定。

朴素上采样还会引入两个额外问题:

  • 任何宽度或高度小于16像素的物体通常会在上采样后的图像中完全消失。
  • 这既影响细长物体,也影响远处物体,因为它们的尺寸低于像素能从原始图像中显现所需的最小维度。

特征解码器

为了解决这些问题,研究人员提出了通常被称为特征解码器的组件。特征解码器可以看作是特征提取器的镜像:它不使用卷积-池化范式来下采样分辨率,而是使用上采样层后接卷积层来上采样特征图的分辨率。

以下是特征解码器的维度变化分析:

  1. 输入:一个下采样了16倍、深度为512的特征图。
  2. 上采样-卷积块:每个这样的块被称为一个“反卷积”块。经过第一个块后,输入特征图的分辨率被上采样至原来的2倍。
  3. 深度控制:每个后续卷积层的滤波器数量控制着特征图的深度。为了降低计算复杂度,我们通常在解码过程中缩减深度,但这取决于具体应用的设计选择。
  4. 输出:最终,我们得到一个与输入图像分辨率相似的特征图。

需要指出,这种上采样机制是众多语义分割方法中最简单的一种。请查阅补充材料,了解为语义分割提出的更高效、更强大和更复杂的上采样模型。

输出层与损失函数

现在我们有了与输入图像尺寸相似的特征图,接下来需要生成语义分割输出。

我们可以通过一个线性输出层,后接一个Softmax函数来执行语义分割。该层与我们之前在目标检测中描述的分类输出层非常相似。实际上,该层为每个像素提供一个K维向量,其中第k个元素表示神经网络认为该像素属于第k类的置信度。

从视觉上理解,给定一个图像块及其对应的真实标签,我们可以将每个像素表示为一个K维的独热向量。独热向量为正确类别赋值为1,其余类别赋值为0。神经网络的目标是驱动Softmax的输出,使得每个像素正确类别的得分尽可能接近1,其余类别的得分尽可能接近0。在推理时,我们取最高得分的索引来恢复所需的输出表示;而在训练时,我们直接使用Softmax输出得分。

根据定义,语义分割问题要求神经网络为每个像素分类提供一个单一类别。为了学习执行此任务,我们再次使用交叉熵分类损失来训练神经网络。该损失函数与我们用于目标检测分类头的损失函数相似。

交叉熵损失驱动神经网络学习每个像素的概率分布,使其最接近真实类别的概率分布。其公式可表示为:

L = - (1/N_total) * Σ Σ s*_ij * log(s_ij)

其中:

  • N_total 是一个小批次中所有图像被分类像素的总数。
  • s_ij 是神经网络对像素 (i, j) 的输出(Softmax后)。
  • s*_ij 是该像素的真实分类(独热编码)。

通常,一个小批次包含8到16张图像,具体数量取决于GPU的内存。

架构总结与展望

总结一下,我们得到了以下用于语义分割的卷积网络模型:

  1. 特征提取器:接收图像输入,输出一个富有表现力但低分辨率的特征图。
  2. 解码器:接收该特征图,并通过上采样得到一个与原始输入图像分辨率相同的最终特征图。
  3. 输出层:一个线性层后接Softmax函数,为输入图像中的每个像素生成类别置信度向量。

本节课描述的架构是可用于语义分割的较简单模型之一。关于语义分割最优神经网络模型的研究已经有很多。例如,将提取器中池化层的索引传播到解码器的上采样层,已被证明可以提高语义分割模型的性能和计算速度。我们在补充材料中列出了一些描述语义分割架构的最新文献。

恭喜!你现在应该已经掌握了使用卷积网络解决语义分割问题的基础知识。大多数最先进的分割网络都共享我们在本视频中描述的结构,即以特征提取器、解码器和输出层作为主要构建块。如果你有兴趣深入了解当前性能顶尖的算法,我们提供了一些最新方法的链接作为参考。

在下节课中,我们将描述如何使用语义分割输出来帮助自动驾驶汽车感知道路场景。下次见!

029:道路场景理解中的语义分割 🚗

在本节课中,我们将学习如何利用语义分割模型的输出来理解道路场景,特别是估计可行驶空间和车道边界。

概述

上一节我们介绍了如何使用卷积神经网络进行语义分割。本节中,我们将看看如何利用这些网络的输出进行道路场景理解。具体来说,您将学习如何利用语义分割模型的输出来估计可行驶空间和车道边界。

语义分割输出回顾

首先,快速回顾一下语义分割模型的预期输出。

一个语义分割模型接收一张图像作为输入,并为每个像素输出一个类别分类。

可行驶表面估计

现在,让我们利用这个输出来执行一些对自动驾驶汽车有用的任务。我们将讨论的第一个任务是根据语义分割输出在三维空间中估计可行驶表面。

可行驶空间定义为车辆前方可以安全行驶的区域。在语义分割的上下文中,可行驶表面包括来自道路、人行横道、车道标记、停车位,有时甚至包括铁轨的所有像素。

估计可行驶表面非常重要,因为它是从3D深度传感器构建占据栅格的主要步骤之一。您将在第4课中了解更多关于占据栅格的知识,它们将用于在避碰过程中表示环境中障碍物的位置。

以下是执行可行驶表面估计的步骤:

  1. 生成语义分割输出:首先,我们使用卷积神经网络生成图像帧的语义分割输出。
  2. 获取3D坐标:然后,我们通过立体数据或将激光雷达点云投影到图像平面,生成该帧中部分像素的3D坐标。通过已知的传感器间外部标定,我们可以将激光雷达点投影到图像平面,并与相应的像素匹配。
  3. 选择可行驶表面点:接着,我们选择属于可行驶表面类别的3D点。如前所述,此类别至少应包括道路、车道标记和人行横道。在某些场景下,根据驾驶环境,也可能包括其他类别,例如铁轨。所有其他类别都被排除,即使它们的高度与可行驶表面相似。
  4. 拟合表面模型:最后,我们使用这个3D点子集来估计一个可行驶表面模型。该模型的复杂度可以从简单的平面到更复杂的曲面模型。

平面模型拟合

让我们描述如何根据分割图像数据和激光雷达点稳健地拟合一个平面可行驶表面模型。

我们首先将平面模型定义为:
A*X + B*Y + Z = D
其中 (X, Y, Z) 是平面上的任意点。系数 AB1 定义了平面法向量,D 是常数偏移量。

我们可以形成一组线性方程来估计该平面模型的参数,使用每个被识别为属于可行驶表面的点作为测量值。

模型的参数现在由向量 P 总结,测量值被分离到矩阵 AB 中。如第2课所述,我们可以使用最小二乘法找到该线性方程组的解。

因此,我们的平面参数向量 P 等于 A 的伪逆乘以 B

该模型有三个待估计的自由参数。因此,我们至少需要三个非共线的3D点来拟合平面。在实践中,我们将拥有比所需多得多的点,因此我们可以再次应用最小二乘法来识别使所有点到平面的均方距离最小的参数。

当然,有些点可能被错误标记,可能并不真正属于可行驶表面。那么,我们如何避免错误的语义标签影响可行驶表面平面估计的质量呢?

幸运的是,我们在本课程的第二周学习了一种非常强大且稳健的估计方法。具体来说,我们可以使用 RANSAC 算法 来稳健地拟合我们的可行驶表面平面模型,即使我们的语义分割输出存在一些错误。

RANSAC 算法步骤

以下是用于可行驶表面估计的 RANSAC 算法步骤回顾:

  1. 随机采样:首先,我们随机选择拟合模型所需的最小数据点数。在本例中,我们随机选择 3 个 3D 点。
  2. 计算模型参数:其次,我们使用这三个点计算模型参数 ABD,并使用最小二乘法求解平面参数。
  3. 确定内点:然后,我们确定满足这些模型参数的 3D 点的数量。通常对于可行驶表面估计,大多数异常值是由于位于边界处的错误分割输出造成的。
  4. 判断与迭代:如果异常值数量少,我们终止并返回计算出的平面参数。否则,我们随机采样三个新点并重复该过程。
  5. 最终拟合:一旦算法满足其终止条件,我们从最大内点集中的所有内点计算道路的最终平面表面模型。

如果道路实际上是平坦的,使用平面表面模型效果很好,这在许多驾驶场景中是一个有效的假设。对于更复杂的场景,例如从平坦道路进入陡峭爬升的高速公路入口,则需要更复杂的模型。

语义车道估计

虽然我们估计了可行驶表面,但我们仍未确定车辆可以在该表面的哪个位置行驶。通常,车辆应保持在由其车道标记或道路边界确定的车道内。

车道估计是在给定可行驶表面的情况下,估计自动驾驶汽车可以行驶位置的任务。文献中存在许多方法来完成此任务。例如,一些方法直接从卷积神经网络估计车道标记来确定汽车可以行驶的位置。

然而,为了进行更高级的决策,我们的自动驾驶汽车还应该知道车道边界处有什么。车道边界处的类别范围可以从路缘石、对向交通所在的道路,到动态和停放的车辆。自动驾驶汽车必须根据车道边界处出现的物体来制定其操作策略,尤其是在紧急靠边停车期间。

我们将把估计车道及其边界处情况的任务称为 语义车道估计。请注意,如果我们使用语义分割的输出估计车道,我们会免费获得边界分类。

让我们通过一个简单的方法来阐明车道估计任务。

以下是执行语义车道估计的步骤:

  1. 提取车道分隔符掩码:给定语义分割模型的输出,我们首先提取属于作为车道分隔符出现的类别(例如路缘石、车道标记或铁轨)的像素的二进制掩码。
  2. 应用边缘检测器:然后,我们对二进制掩码应用边缘检测器。正如您在本课程第一周所记得的,边缘检测器可以像通过卷积估计梯度一样简单。这里我们使用著名的边缘检测器——Canny 边缘检测器。输出是被分类为边缘的像素,这些像素将用于估计车道边界。
  3. 选择车道模型:最后一步是确定用于估计车道的模型。模型的选择决定了下一步将是什么。这里我们选择一个线性车道模型,其中假设每个车道由单条线组成。
  4. 检测线:为了在输出边缘图中检测线,我们需要一个线检测器。霍夫变换线检测算法 被广泛使用,并且能够在边缘图中检测多条线。给定一个边缘图,霍夫变换可以生成一组连接边缘图中属于边缘的像素的线。所需线的最小长度可以设置为超参数,以强制算法仅检测足够长以成为车道标记一部分的线。
  5. 细化与分类:可以根据当前场景应用进一步的细化。例如,如果我们的相机朝向前方运动方向,我们知道我们的车道不应该是水平线。此外,我们可以移除任何不属于可行驶表面的线,以获得最终的车道边界基元集。最后一步是确定车道两侧出现的类别,这可以通过考虑估计车道线两侧的类别轻松完成。

我们不会详细讨论边缘检测器和霍夫变换,因为这些主题值得在本课程范围之外单独讨论。然而,强大的计算机视觉库(如 OpenCV)提供了关于如何使用霍夫变换检测线以及如何使用 Canny 边缘检测器检测边缘的开源实现和教程。如果您感兴趣,请查看补充材料中提供的链接,了解这些算法在实践中是如何使用的。

总结

在本节课中,我们一起学习了如何利用语义分割来估计可行驶空间、确定可行驶空间边界处的情况以及估计可行驶表面上的车道。

到目前为止讨论的应用是非常活跃的研究领域,您在本课中学到的知识为语义车道检测和可行驶表面估计任务提供了坚实的起点和有竞争力的基线。此外,语义车道检测和可行驶表面估计是语义分割模型在自动驾驶汽车背景下最突出的用途。

然而,语义分割还有许多其他有用的应用,最重要的是帮助自动驾驶汽车执行 2D 物体检测和定位。通过了解哪些像素需要评估物体,或者特征是在静态还是动态物体上,感知系统的鲁棒性可以显著提高。它可以作为合理性检查或过滤器,以避免在其他感知任务中包含错误信息。

语义分割是自动驾驶汽车的强大工具,也是高级感知栈的核心组成部分。下周当我们讨论最终作业时,您将有机会验证其实用性。到时见!

030:项目概述-使用CARLA进行物体检测与分割

在本节课中,我们将学习课程最终项目的具体要求。我们将概述项目目标、评分标准以及如何通过提供的YAML文件提交最终评估。

恭喜你坚持学习到课程的这个阶段。你已经接近完成。

现在是将所学知识整合起来,完成最终项目的时候了。

这个视频将为你提供完成最终课程评估所需的要求。

我们还将讨论评分方案,以及如何通过提供的YAML文件提交最终评估。

让我们从列出本次评估的目标开始。

项目目标

最终项目要求运用你在整个课程中学到的知识,为自动驾驶汽车实现一个环境感知栈。

具体来说,你将使用语义分割神经网络的输出来实现以下功能:

  1. 在三维空间中估算可行驶区域。
  2. 估算车道线。
  3. 过滤掉二维物体检测器输出中不可靠的估计结果。

最后,你将使用过滤后的二维物体检测输出来确定障碍物与自动驾驶汽车的距离。

通过完成这个项目,你将能够开发基础的感知栈,使自动驾驶汽车能够了解在道路场景中可以行驶的区域、视野内可能影响其决策的障碍物,以及这些障碍物的距离。

参考课程

你已经在之前的课程中学习了完成本次评估所需的大部分任务。以下是处理此作业时可能有用的参考课程列表:

  • 可行驶区域估算:可以参考模块1第3课来根据深度信息估算像素的XYZ坐标,以及模块5第3课来根据语义分割输出估算地平面。
  • 车道线估算:可以参考模块5第3课来估算车道线。
  • 估算最小碰撞距离模块4第4课对于估算距离将非常有用。

任务详解

现在,让我们详细描述为什么这些必需任务对自动驾驶汽车很重要。

三维可行驶区域估算

三维可行驶区域估算对于自动驾驶汽车安全穿越环境至关重要。

利用给定的传感器输入以及来自神经网络的语义分割数据,你需要估算三维空间中的地平面方程。

然后,根据距离阈值确定属于地平面的像素。图中黄色区域标出了你的算法应标记为可行驶空间的像素。

我们将为你提供代码,将你估算的三维地平面可视化为占据栅格图,这是本专业系列下一门课程将涵盖的主题。

请注意,可行驶空间并不等同于道路。如图所示,人行道后方的一部分区域也被标记为可行驶空间。

基于地平面估算的可行驶空间,提供了汽车物理上能够行驶的三维空间。

车道边界估算

为了明确汽车在法律上被允许行驶的区域,你需要执行车道边界估算。

为了完成这个任务,我们为你提供了语义分割的输出。

你需要使用这个输出来估算当前车道的左右车道边界。

障碍物距离估算

本次评估的最后一个任务要求你估算场景中障碍物与汽车的碰撞距离,这些障碍物由物体检测神经网络提供。

问题是该神经网络模型具有较高的召回率,但精确度较低,并在其输出中提供了一些错误结果。

你需要首先使用语义分割网络的输出来过滤掉物体检测网络中的错误结果。

然后,你需要计算与场景中每个障碍物的最小碰撞距离。

系统最终输出

让我们看看整个系统的最终输出会是什么样子。

  • 使用估算出的地平面,自动驾驶汽车将能够确定其在环境中物理上可以行驶的区域。
  • 使用估算出的车道边界,自动驾驶汽车将能够遵守道路规则。
  • 最后,使用过滤后的物体检测输出以及碰撞距离估算,自动驾驶汽车将能够定位道路上的障碍物,以判断是否有障碍物阻挡其路径。

项目提示

对于最终项目,请记住,提供的算法指南并非一成不变,在评估的每个步骤中都存在许多你可以使用的替代方法。

我们鼓励你在认为可以在结果或效率方面做得更好的地方,偏离提供的算法大纲。

如果本视频未能解答你的所有疑问,Jupyter笔记本本身还有进一步的说明。

也请不要害怕在讨论区提问。

希望你在这个最终项目中获得乐趣。待项目完成后,我们将再次见面以结束本课程。

祝你好运,并享受其中。

总结

本节课中,我们一起学习了最终项目的完整概述。我们明确了项目的三大核心目标:基于语义分割实现三维可行驶区域估算、车道线估算,以及过滤并计算障碍物距离。我们还回顾了完成这些任务可参考的先前课程,并理解了每个任务对于自动驾驶汽车感知系统的重要性。最后,我们了解了项目的灵活性,并获得了完成项目所需的支持渠道信息。

031:最终项目核心算法概念

在本节课中,我们将学习完成最终课程评估所需的核心算法概念。这些概念将帮助你构建自动驾驶汽车感知系统的三个主要方面。

上一节我们介绍了最终评估的整体框架,本节中我们来看看完成这些任务需要掌握的具体算法细节。

坐标系注意事项

首先,在开始作业时,必须明确所使用的坐标系。

所有需要的三维估计都将在相机坐标系中进行。这包括地面平面、碰撞距离以及其他三维量值。

然而,相机传感器通常的安装方向是使其Y轴指向下方。

这对你的作业意味着什么?

你唯一需要注意的是像素点高度的符号:高于相机的点将具有负的高度值,而低于相机的点将具有正的高度值

平面估计:线性最小二乘法

接下来,你需要了解如何在Python中解决用于平面估计的线性最小二乘问题。

对于评估的这一部分,你有几个选择。

我们提供了一个使用奇异值分解的函数来为你进行平面估计。

但是,如果你想挑战自己,可以尝试通过使用NumPy原生函数 numpy.linalg.lstsq 求解最小二乘问题,自己解决平面估计问题。

如果你选择此路径,请注意在RANSAC的每次迭代中选择多于三个点,因为在某些系统条件不佳的边缘情况下,求解器可能无法提供有用的结果。

一个更大的挑战是从头开始实现SVD平面拟合。SVD几乎总能提供数值稳定的解。此外,它可能比NumPy的 lstsq 函数更快。

选择哪种路径来估计平面由你决定,只要平面估计正确,生成它的方法都将获得满分。

车道线检测与合并

需要执行的第二个感知任务是车道边界检测。

霍夫变换线检测的输出是任何属于车道边界的线,包括水平线和人行道远侧的线。此外,每个车道边界将有多条来自霍夫变换线估计器的关联输出线。

你需要过滤掉所有不相关的线,并合并相关的线,以为每个车道边界生成一条线

以下是过滤和合并的步骤:

1. 过滤水平线

为了过滤掉水平线,我们可以依赖估计线的斜率。图像中的水平线往往具有非常接近0的斜率。然而,我们也希望移除倾斜严重的线,因此引入一个阈值作为此过滤步骤输出允许斜率的下限。

这个阈值的具体值需要通过实验确定,尝试在 0.1 到 0.3 之间的值以获得最佳效果。

2. 聚类与合并

从霍夫变换的输出中移除水平线后,你需要对相似的线进行聚类,然后合并它们以生成每个车道边界的一条线。

一个简单的聚类算法是:

  • 首先,从剩余的过滤线中随机选择一个聚类中心。
  • 然后,将任何具有与聚类中心线相似斜率或截距的线添加到该聚类中。

判断一条线是否接近聚类中心的标准是:其斜率与聚类中心斜率的差值小于特定的斜率阈值,并且其截距与聚类中心截距的差值也小于特定的截距阈值

斜率差值阈值通常选择最大为 0.3,而截距差值阈值以像素为单位定义,通常在 20 到 50 像素 之间。你需要测试不同的值,才能为评估找到一个满意的阈值。

生成每个车道边界一条线的最后一步是合并每个聚类内的线。最简单的方法是通过平均所有聚类成员的斜率和截距来实现。

利用语义分割过滤目标检测结果

完成评估所需的最后一个概念是如何使用语义分割的输出过滤目标检测器的不确定结果。

目标检测的输出通常是可靠的,但在本次评估中,你得到的是一个高召回率、低精确率的检测器,它能检测场景中的所有物体,但也会产生一些误报。

你需要在估计障碍物距离之前,使用语义分割的输出消除这些误报。最终结果应该是可靠包含障碍物的边界框。

以下是执行此过滤的方法:

你需要使用语义分割输出来统计边界框内与2D目标检测器分类输出类别相同的像素数量。

这里的技巧在于,这个数量取决于边界框的大小。在尝试用阈值过滤检测结果之前,你需要用边界框的面积对像素计数进行归一化

最终的归一化计数相当于计算边界框内由属于正确类别的像素所占据的面积比例。

至此,你应该已经准备好自信地应对最终评估了。

最后,我鼓励你思考除了建议的纲要之外,实现所需任务最终输出的不同方法。

我希望你喜欢这次评估,并发现它对学习有益。

本节课总结

本节课中,我们一起学习了完成自动驾驶视觉感知最终项目的核心算法概念。我们明确了在相机坐标系中处理三维估计时高度值的符号规则,探讨了使用SVD或最小二乘法进行平面估计的多种途径,详细介绍了通过斜率过滤、聚类和平均来合并霍夫变换车道线的方法,并讲解了如何利用语义分割的像素类别比例来过滤目标检测的误报,以提高障碍物距离估计的可靠性。掌握这些概念将帮助你成功构建评估所需的感知模块。

032:最终项目解决方案

在本节课中,我们将学习如何完成最终评估项目,实现一个基础的环境感知栈。我们将逐步讲解完成所有评分函数所需的解决方案。

概述

最终项目要求我们实现一个完整的环境感知栈,核心任务包括:地面平面估计、车道边界估计以及障碍物距离计算。我们将逐一解析每个任务的实现步骤。


地面平面估计 🛣️

上一节我们介绍了项目的整体目标,本节中我们来看看如何估计地面平面。要估计地面平面,首先需要计算场景中每个像素的X、Y和Z坐标。

从深度图计算XY坐标

我们首先实现 XY_from_depth 函数。该函数以深度图和数据集处理器提供的标定矩阵 K 作为输入,输出场景中每个像素的X、Y坐标。每个像素的Z坐标直接从深度图本身获取,因此函数无需计算此值。

以下是实现步骤:

  1. 从标定矩阵 K 中提取焦距和相机中心参数。
  2. 为图像中的每个像素创建一个U和V坐标网格。也可以直接遍历深度图中的每个像素,但使用数组结构可以简化存储和处理指令。
  3. 利用NumPy的广播功能,根据Jupyter笔记本和视频中提供的公式,在UV网格上计算并返回场景中每个像素的X和Y坐标。

核心公式
X = (U - cx) * Z / fx
Y = (V - cy) * Z / fy
其中,(fx, fy) 是焦距,(cx, cy) 是相机中心。

我们使用frame0中的几个测试点来验证函数,确认得到了每个点的正确结果。

使用RANSAC拟合平面

现在我们已经获得了场景中每个像素的XYZ坐标,接下来可以利用语义分割输出来获取属于道路的像素的XYZ坐标。这部分代码已提供。

之后,我们在评分函数 ransac_plane_fit 中实现RANSAC来估计平面参数。该函数以属于道路的像素的X、Y、Z点坐标作为输入。

以下是RANSAC算法的实现步骤:

  1. 设置终止条件:我们将最大迭代次数设为100,最小内点数为45000,将一个点被视为内点的距离阈值设为0.1米。
  2. 开始主RANSAC循环
    • 首先,从输入的函数中随机选择15个点。
    • 使用提供的 compute_plane 函数,利用这15个点计算一个平面模型。
    • 然后,根据到平面的距离阈值获取内点数量,并检查该数量是否大于之前所有迭代中的内点数量,以更新最佳内点集。
    • 最后,检查最小内点数终止条件,如果内点数大于所需阈值,则终止循环。
  3. 函数返回使用最佳内点集中所有点重新计算的平面模型。

测试我们的函数后,可以得出结论:我们得到的值非常接近预期结果。


车道边界估计 🚧

现在地面平面已经估计完成,我们继续使用语义分割输出来进行车道边界估计。

估计车道线提案

如Jupyter笔记本所述,第一步是通过检测属于疑似车道边界的所有线条来确定车道边界提案。我们在 estimate_lane_lines 函数中实现此功能。

该函数以数据处理器提供的语义分割输出作为输入,并创建一个新的分割图像,其中包含属于车道边界的类别输出,例如车道标记、路缘石和人行道。

为了估计车道线提案,我们可以应用Canny边缘检测,然后进行霍夫变换直线估计。输出被重塑以保证一个 n x 4 的输出张量维度。

可视化输出的车道提案,我们可以看到边界提案中包含水平线。此外,有16个输出线提案。然而,查看图像,我们只能看到8条。这意味着存在需要合并的冗余线条。

合并与过滤车道线

合并和过滤需要在 merge_lane_lines 函数中实现。该函数以上一个函数估计的车道提案作为输入,根据其斜率过滤掉水平线,然后根据斜率和截距合并剩余的线条。

我们为斜率和截距定义了相似性阈值,以决定哪些线应该合并。此外,图像空间中的水平线往往具有零斜率,因此我们确定一个最小斜率阈值,低于此阈值的线被认为过于倾斜,不属于我们的车道边界。

以下是实现此函数的步骤:

  1. 计算输入中所有线条的斜率和截距。
  2. 根据最小斜率阈值过滤线条,以消除水平线。
  3. 使用斜率和截距阈值对线条进行聚类。执行此聚类有多种方法,都会得到相同的解决方案。
    • 对于线条集合中的每条线,我们识别集合中所有落在斜率阈值内的其他线,然后检查截距是否也落在截距阈值内。
    • 任何满足这两个条件的线条都会被聚类并从集合中移除。
  4. 最后,通过计算聚类的平均斜率和截距,合并聚类后的提案,得到每个聚类的一条线。

可视化我们函数的输出,可以看到每个车道边界都有一条与之关联的线估计。

扩展车道边界

最后,通过实现提供的函数 extrapolate_lines 并找到当前车道中心最近的两条线,将车道边界扩展到可行驶空间的范围内。结果就是当前车道的左右车道边界。


障碍物距离计算 🚗

完成此评估所需的最后一个组件是,使用目标检测算法的输出,计算与场景中每个对象的碰撞距离。

通过分割过滤检测结果

然而,提供此输出的特定算法并不十分精确。因此,在估计场景中障碍物的深度之前,需要使用语义分割的输出对目标检测的输出进行过滤。这是通过 filter_detections_by_segmentation 函数执行的。

该函数以初始检测结果和语义分割输出作为输入。然后,它遍历每个检测,并将分割输出裁剪到检测边界框内的像素。计算与检测输出类别相同的像素数量,然后通过边界框面积进行归一化。如果计算出的归一化计数小于阈值0.3,则最终过滤掉边界框。这意味着,如果边界框面积中预测类别像素所占比例小于三分之一,我们就消除该边界框。

可视化剩余的边界框,我们可以看到,经过过滤过程后,只剩下一个完全包含汽车的边界框。

计算最小碰撞距离

最后,我们需要编写一个名为 find_min_distance_to_detection 的函数,以获取与场景中每个对象的最小碰撞距离。该函数以过滤后的检测结果以及图像中所有像素的X、Y和Z坐标作为输入。

在其第一步中,计算到过滤后检测中每个像素的距离。然后,计算每个检测中的最小距离,由函数返回。

测试我们的函数表明,我们得到了正确的最小碰撞距离。


总结

本节课中,我们一起学习了完成最终评估所需的完整步骤。我们实现了地面平面估计、车道边界估计以及障碍物距离计算,共同构建了一个能够引导自动驾驶汽车在其环境中安全行驶的基础感知栈。这是立志成为自动驾驶汽车工程师的关键技能。

在结束之前,建议你花时间比较我们提出的解决这些任务的方法与你自己的设计。请告诉我们你是如何完成的。恭喜你,现在已经完成了我们自动驾驶汽车专项课程第三门课的最终评估。

请记住,在这个不断变化的领域中,总有更多知识需要学习。继续努力吧!😊

033:课程总结与展望

在本节课中,我们将回顾并总结整个《自动驾驶汽车的视觉感知》课程的核心内容,并对后续学习方向进行展望。


恭喜你完成了自动驾驶汽车视觉感知课程。你在本课程中学到的概念是任何自动驾驶汽车软件栈的基础部分。

让我们总结一下到目前为止所学的内容。

第一周:3D计算机视觉基础

在第一周,我们学习了3D计算机视觉的基础知识,以及如何使用互相关卷积算子执行滤波操作。

  • 互相关/卷积操作:这是图像处理的基础,公式可表示为 (I * K)[x, y] = Σ_i Σ_j I[x+i, y+j] · K[i, j],其中 I 是输入图像,K 是卷积核。
  • 应用:你运用这些基础操作创建了一个基础的障碍物避让软件。

第二周:特征检测与视觉里程计

在第二周,我们转向了如何从图像中提取有用信息。

上一节我们介绍了基础的图像处理,本节中我们来看看如何提取和利用图像特征。

你学习了如何通过特征检测与描述来提炼图像信息。随后,你学习了如何利用这些特征,通过视觉里程计来获取里程估计。

第三周:深度神经网络基础

由于经典视觉感知方法的局限性,我们在第三周重点学习了深度神经网络。

以下是深度神经网络的核心组成部分:

  • 基本构件:你学习了深度神经网络的基本构建模块及其训练方法。
  • 卷积神经网络:你了解了一种特殊类型的前馈神经网络——卷积神经网络。

第四周:基于CNN的2D目标检测

在课程的第四周,你探索了用于2D目标检测的卷积神经网络。

你学习了2D目标检测的问题定义、如何训练一个2D目标检测器,以及如何使用2D目标检测的输出结果来增强自动驾驶汽车的感知栈。

第五周:语义分割

你在本课程中学习的最后一个主题是语义分割。在第五周,你学习了如何定义语义分割问题。

以下是语义分割的关键内容:

  • 问题定义:如何将图像中的每个像素分类到特定的语义类别。
  • 模型方法:如何使用卷积神经网络,通过编码器-解码器模型来执行语义分割。
  • 实际应用:如何使用语义分割的输出结果来执行可行驶区域估计和车道线分割。

最后,你将所有这些知识结合起来,在Carla模拟器中创建了一个自动驾驶汽车障碍物避让系统。


本课程介绍的概念是一个非常活跃的研究领域。我鼓励你通过阅读最新的论文和关注该领域的动态,更深入地探索这些引人入胜的主题。

为了帮助你开始,我提供了一份补充材料清单,这些材料将扩展你对自动驾驶汽车视觉感知这一丰富领域的理解。

在下一门课程中,我将介绍自动驾驶汽车软件栈的最后一组组件:运动规划。正如你将学到的,运动规划是一个复杂的、多尺度的问题,其中包含许多有趣的挑战等待你去探索。

所以,请加入我,继续你在掌握自动驾驶汽车技术道路上的下一段旅程。


本节课中我们一起学习了自动驾驶汽车视觉感知的完整知识体系,从基础的3D视觉和图像处理,到特征提取与里程计,再到深度神经网络、目标检测与语义分割,最终集成应用于模拟驾驶系统。这些内容是构建自动驾驶感知能力的核心。

posted @ 2026-03-26 12:27  布客飞龙III  阅读(15)  评论(0)    收藏  举报