超万字实录详解如何打造“好用”自动驾驶智能芯片算法工具链

如题所述

导读:

11月8日,地平线「你好,开发者」工具链技术专场在智猩猩顺利完结直播。专场由地平线工具链产品规划负责人秦畅主讲,主题为《如何打造“好用”的自动驾驶智能芯片算法工具链》。

本文是此次专场主讲环节的实录整理。如果对直播回放以及Q&A有需求,可以点击阅读原文前去观看。

大家好,我是地平线工具链产品规划负责人秦畅。今天我将会给大家介绍《如何打造“好用”的自动驾驶智能芯片算法工具链》,并谈一下在实践过程中,地平线的经验和产生的思考。

分享将从四个方面展开。首先什么是算法工具链;我们需要去解决的关键问题;地平线在解决这些关键问题的过程中做的一些实践。最后,谈一下我个人对未来工具链需要进一步怎样做的一些思考。

01

什么是算法工具链

首先谈到智能芯片,它是如何体现智能的呢?芯片作为一个硬件电路实现的大集成,本身是不会有智能的概念出现。谈到智能芯片的智能性,其实是来自于智能神经网络算法的加持。

在slide的左上,是一个神经网络里面非常经典的感知机模型,由一系列的神经元通过确定的规则排布得到了一个网络结构。这个网络结构将会建立起输入层与输出层的映射关系,通过喂入批量标记好的输入和 Label之后,经过学习可以对未来的、未知的、同类型的输入,达到预测输出的效果。拆到单个神经元来看,内部所含的计算是很简单的。比如我们对每一个输入乘以权重系数,再对其进行累加,这个过程也就是矩阵乘法里面的细颗粒的过程。

同样的,如果完全是这样的计算堆叠,我们就会看到它在数学上会等效于大量的矩阵连乘(连乘在数学上等价于某一个矩阵乘法的效果)。如果仅仅是这样,就失去了对复杂函数的表达能力。所以一般会在神经元后面加一些非线性激活函数,像引入ReLU、Sigmoid这些函数。学习的过程就是通过喂批量输入的样本和标记好的Label,去学权重设置,使得这一套权重能够尽可能很好地拟合这些输入和标记好的Label的映射关系。未来同类型的未知输入,就可以可靠地对需要的预测结果进行预测。

到智驾领域的算法应用,当然不再是感知机这样简单的应用了,当前我们仍然看到的还是CNN。虽然我们看到目前技术趋势上会有一些明显的变迁,但大部分应用中的方案仍是以CNN为主。比如Tesla在2020年左右披露的数据,在Tesla的一个方案里面,98%的计算量来自于卷积。那么卷积又会是一个什么样的计算?

左下角这张图中间是一个卷积的参数,我们叫它卷积核。左侧的是输入,右边是输出。卷积计算的过程中,卷积核对应到输入部分可视区域内的数据。我们将输入部分的数据与卷积核进行点积乘法,再做累加,这样就得到了输出的数据。通过卷积核在输入上的可视窗的滑动,遍历完所有输入的区域之后,就得到了完整的输出,整个过程类似右边动态图的效果。这个过程同样具有大量的矩阵计算特点。所以我们现在通过这两点可以看到,在卷积神经网络的计算里面有一个非常鲜明的特点,涉及到大量的基础计算,以乘加为主,量会特别大。

现在我们看到了一个明显的趋势就是未来Transformer的方案升级。Transformer确实给计算架构带来了一些鲜明的挑战,但是从刚刚分析的计算特点的角度来说,至少在智驾领域上的应用,主要的计算量贡献还是来自于矩阵乘计算和全连接计算,尽管对整体的体系结构带来了很大的挑战,但是计算的特点没有明显的变化。比如一个98%的矩阵乘的贡献会落到90~95%之间,其他的引入了更多激活计算,比如LayerNorm、Softmax用的更重。当然,对数据的形变、 layout管理方面,也会带来更高的挑战。

那么,对于智能芯片来说,它解决的问题就会变成如何加速神经网络的计算。这一页左上角是一个非常简单的计算模型的示意。我们在计算过程中从memory里面拿数据,放到计算单元里执行,再把数据回写。在这样的模型里面,怎么样更高效地做神经网络计算呢?我们会自然地想到,在让整个模型保持现状的情况下,使它算得更快、频率更高,但提升频率是非常有限的。我们可以看到计算器件CPU在近几年频率上是没有非常大幅度提升的。但是对于我们所需要承载的神经网络计算量而言,如果要提速,那不是一两倍的概念,而是一个数量级的概念,所以单纯的提速是不可行的。

那么另外一条路是什么呢?我们看看左下的计算模型。

我会铺更多的计算单元,这里放到了6个。这个计算模型一次获取6个数据,在一个计算周期之内,6个计算单元同时工作,它再去回写的时候就回写6个数据。在一个计算模型里面,我们不考虑额外读写上的压力,简单去考虑,会带来6倍的性能提升。这就是我们的神经网络加速器,或者说我们的智能化芯片去做加速的核心Idea,就是去铺更多的计算单元。

当然铺的话也是有讲究的。右边是2017年谷歌发布的TPU-V1的架构。这是一个非常经典的实验。很多的行业同仁会拿它来举例子。我们可以看到在黄色部分是它所提供的计算单元的情况,这个地方的面积基本上也体现出了配重的占比,以大型的矩阵乘单元为最主要的核心,同时会辅以一些激活单元,刚刚也提到在神经网络里面会加一些非线性激活函数去处理这些任务。同时,一个网络不会这么的纯粹,只有激活加一些矩阵乘,同时像Pooling、Normalize这些操作会加上一个特定的单元,一般会称呼为DSU。它的作用非常明确是用来做什么的,而且可能是一系列不好在其他部件去实现的能力集合。

同时,为了支撑这套系统去运转,可以看到蓝色部分相关的内容,它放置了一个支撑存储数据管理的相关系统,维护适合于这一套架构的SRAM缓存,再去管理和DDR数据交互。在这个过程中,红色部分会涉及到一些驱动整个异构系统运作的必要控制单元。这仍然是一个非常经典的设计,现在各家的架构多多少少在这里面都能看到一些影子。当然现在会有所变化,比如会考虑在BPU加速核里引入一些标量单元,去处理未来神经网络里可能面临的一些分支计算,或者做核内调度。

在向量计算方面,如果需要考虑处理的特别计算越来越多,如果还是一个DSU的概念,那么DSU会越来越复杂。它承载的具体执行的计算类别会越来越多,所以也会出现一些替换。用一个更加强悍、灵活的可编程单元去承载一个任务,不仅仅可以去承载一些已经预见的、其他部件不好去做的事情,同时提供给未来开发者一个完全开放、具有可编程性,能够去做未来可能产生的新的计算类型。

那么对于我们工具链来说,恰好夹在中间了。我们会看到这样的情况,往上看,面向各种算法Deep Learning开发的框架,算法开发者基于这些框架得到各式的模型,这是输入;往下看,在地平线的工具链打造里面,面向地平线征程系列的平台,包括我们已经量产的征程2、征程3和征程5,以及未来将会采用在今年车展上发布的BPU纳什架构的下一代,应该是征程6系列。

我们要在他们之间去建立一个桥梁,使得算法能够在这些芯片上跑起来。

02

自动驾驶智能芯片算法工具链解决的关键问题

跑起来是前提,跑起来之后桥梁是否稳定有两个核心要素:

第一个是我的算法部署在你的芯片上是否足够准确。没有准确为前提,所有的高效也都是免谈。所以“准确”我放在前面了。

第二个就是它的效率是否足够高。这将会成为算法工具链所需要解决的两个关键的问题。

在现在算法工具链语境下,我讲的准确性问题不是像算法训练一样,模型是否能够有很好的预测能力,是否训得足够好,它并不是这样的准确性问题。准确性的问题来自于硬件的选择。刚刚提到智能化的芯片加速神经网络模型,会堆叠更多的计算单元,堆叠也就是意味着更高的成本。成本会体现在两个方面:

第一个是需要更多的面积去承载这些计算单元;另一个则是更多的功耗。一次推一批计算的时候,需要的功耗也是一个很重要的因素。

我们做出来的芯片毕竟是面向商业化的场景,需要综合考虑成本及使用功耗的问题,所以在堆叠这些计算单元的同时,我们也想到它必然会增加成本,同时去考虑如何减少这样的成本。

这里面有一个重要的方向是数值精度上的选择。这一页贴了两张图,这个图是一个加减运算器的实现,整数和浮点数的对比。我们要看到的一点是,浮点数的电路实现的复杂度明显高于整型的硬件实现的。核心原因来自于二进制的浮点数表示和整数的形式很不一样,原因在这里不深究。

下面这张图是我比较喜欢用的一列数,现在可能看起来比较老了。虽然是2014年的数据,但里面还可以看到一个明显的特点:中间偏左的部分是它能效的代价,右边是面积的代价。我们以加法为例,做加法的时候用8b的加法实现,和16b的半精度和 float32去比较可以看到,随着计算精度的提升,能耗面积的代价都在增长,所以这就成为了一个很好的选择。我们在铺大量的算力单元时会充分考虑使用 Int8的计算,当然也会做出一些不同的选择。我认为完全的Int8可能用起来会比较困难,会适当的去考虑比如在一些向量激活的计算上引入浮点,或在矩阵的计算上适当的保留一些浮点。比如Int16是不是能够保留?所以各家去看芯片体现它有多少个cost时,一般都会指明它支持什么样的计算精度。当然,如果加速的平台没有与训练的平台做到完全一致的浮点计算,它就会带来一个准确性的挑战。训模型的时候,在这个算法的开发环境下做算法验证,它就是一个完全的float32甚至更高的验证方式,你在芯片上改变了计算精度,你肯定对我的计算部署准确性是有一定的影响的。

到工具链的层面,我们要去做好量化的过程,使得我们虽然要去配合硬件做具体计算单元的计算精度降级,但整体模型的预测效果不能有太明显的损失。比如在智驾行业,基本上也会有一些共识:我们在驾驶相关的模型部署上,接受一些压缩的方法去换取更高的性能,但精度的偏差不能太大。比如较于我的浮点的验证环境下作为base,它的相对的精度损失一般会控制在1%以内。如何去做好这个事情呢?这就牵扯到神经网络,神经网络是支持我们去做这个事情的,涉及到模型的量化压缩的一个技术方向。这个事情怎么解释呢?一个神经网络里面每一层都在做计算,每一层都会输出一些结果,我们把这些中间结果叫做特征。

怎么去理解特征呢?就是一个常识性理解,一些研究对中间产出的特征做可视化。比如在一个人脸的任务里面,一些浅层的特征里面可视化之后可以看到一些前景、背景的区分,一些边缘的特征出现;在中层的特征上,我们看到了人眼、人脸、眉毛这样一些中层特征;到偏高层的时候开始见到整个人脸,再在这个基础上做预测。这是可视化的效果,但计算机二进制数值的表示是数据的分布效果。比如更常用、更容易理解的是在计算机屏幕上大家今天看的直播活动,看到了很多画面,画面每一页代表了各式各样不同的信息。整体来说,它在计算层面上就是各种 RGB数值的组合,这就是数值的分布带来的效果。

数值分布会留下了一些可压缩的空间,这一页左下角的部分是谈到量化压缩时候经常会引用的一张非常经典的图。上面是压缩之前在浮点数据分布的空间之内,我们去建立的一个到Int的等比映射,相应的数据都等比例落下来。这样我们数据的表达范围变少了,但可以看到里面由于 Int的表达能力,比如不映射过来的时候能到111.5,取一个round函数的方法,四舍五入映射到整型数据上会有一些偏差。但是整体来说,它的分布不会有太明显的变化,当然前提是量化做得好。

还有一种数值的分布改变,压缩有一种更通俗但不是很严谨的理解。拿一个像素的方式去举例:我找了一张猫的图片,原图在左边,它的像素会偏高一点;右边对它进行了一些处理,把它的像素值减少了。虽然我们可以明显看到里面是有一些损失存在的,没那么清晰了,但其实不影响我们去理解它。两个对比之下,不影响我去理解它是同一只猫。这就是我所理解量化的、比较好传递的核心认知。

在工具链具体的实施上,我们真正把这个想法落到工具的实现里面去。刚刚讲到的量化会很简单,在这种情况下可以直接做一个映射,但适用性就会非常有限的。如果把量化直接暴力解释为映射,各种数据分布就会给我们带来挑战。

在原始的数字分布中间,很明显会是一些有效的数据在两端噪声比较多。如果完全根据它的最大值、最小值的区间,映射到 Int8的表示范围内真正有效的数据去表示的时候,用到 Int8表达能力就会很少。这就会严重导致数据相对分布的表达失真。对应到刚刚那张猫的图片,像素损失太严重了,我不能够再认识出来它是一只猫了,更别说是同一只猫。

为了做好这件事,工具链常见两条路径,一个是后量化的路径,另一个是量化训练的路径。后量化的“后”侧重讲的是训练后的概念,我使用一定数量的样本,用训练好的模型去推理,这样就会拿到一批用真实数据去推并产出各层的数据分布。大概像左边这张图,能够看到普遍都具有这个特点:中间的分布非常密集,两端散得非常开且数据非常稀疏。我们可以先做一个假设性的认定,数值分布应该在中间取,规则可以在简单示意图上做非常简单的划分,把有效的空间截取出来之后,其他的直接丢弃,只在有效的数据范围内做映射。这样就能尽量有效地还原数值的分布,把量化做好。

为什么说是尽量呢?有一些后量化理论上也不能确保我们解决所有问题。我们在用后量化去解决问题的时候,会提到一个概念叫“量化不友好”,但是这个概念我尝试去做后量化的时候,可能会发现这样一种情况:呈现数值分布是集中的,但是集中不在一个点上,分布在左右两头,中间比较稀疏的分布特点。这样去找范围再去映射,就很难真正有效地应用到 Int8的表达能力,因为中间原始数据会有一段空档。为了解决这个问题,确保量化方式的稳定性,对各种情况都能处理,就会提到“量化训练”的概念。我们把量化的行为追加到训练的过程中的好处是:发现了量化不友好的问题之后,可以调整权重分布,使它达到所谓的“量化友好”的状态。因为在训练过程中,权重本来就是在调整的过程中的,训到一定程度之后,又可以开始做前面的统计、映射这些过程了。核心就是权重可变,解决问题的潜力也就越来越高了。

当然还有一些其他的解决办法,比如混合精度。例如在做后量化的时候只是部分分类的数据呈现这样的特点时,量化不好做,那干脆就不要做了,我在这一层直接用浮点的计算。还有一些是只要分布是典型的,就可以在量化的方式上选择更多样的方式。我之前认为量化数据在一个集中点,如果

【本文来自易车号作者车东西,版权归作者所有,任何形式转载请联系作者。内容仅代表作者观点,与易车无关】

温馨提示:答案为网友推荐,仅供参考
相似回答