前不久,机器之心AI技术年会在线召开。杜克大学电子与计算机工程系教授陈怡然发表主旨演讲《高效人工智能系统的软硬件协同设计》。演讲视频回顾:https://www.bilibili.com/video/BV1yP4y1T7hq?spm_id_from=333.999.0.0以下是陈奕然在机器之心AI技术年会上的演讲内容。机器之心在不改变原意的情况下进行了编辑。我经常用到这张图,它出自Kurzweil博士大约32年前写的一本书《The Age OF Intelligent Machines》,他预测了未来人工智能所需的计算能力的发展。我们对这张图做了一些扩展,大概一台设备上可以集成多少算力。在过去的100年里,计算能力几乎呈现出指数增长的趋势。到现在为止,比如英伟达今天发布的最新GPU的计算,包括上周苹果发布的GPU芯片,基本上可以带来超过一个人脑级别的计算能力,为我们带来了极大的可能。人工智能计算平台种类繁多,从熟悉的GPU、FPGA、ASIC到新的架构,都遵循一个原则:要么更高效,要么更专业耗时更长,要么更灵活。当尺寸统一时,总会有设计上的矛盾。例如,GPU的计算能力最高,功耗也最高。一些非常容易重构的计算会在FPGA上实现,但能效比可能不会那么好。ASIC的能效比很好,但需要较长的开发周期。基本上,它是为特定应用而设计的。当出货量比较小的时候,效果不是特别好。大家都熟悉冯诺依曼瓶颈。其实就是可以通过不断增加计算单元来实现计算能力,但归根结底瓶颈在于数据能否及时传递到计算单元。在过去的40到50年里,片上计算能力与通过存储带宽提供给片上的片外数据之间的差距越来越大,这也带来了“内存墙”的概念。当然,在实际的具体设计中,比如不能及时排除产生的热量,不能无限制地增加使用频率,(这些)迫使我们去寻找新的计算设计。现在比较流行的设计是near-storage或者in-memorycomputing设计,思路很简单。既然瓶颈来自于数据之间的流动,尤其是存储空间到计算空间的流动,那你能不能想办法让计算和存储发生在同一个地方??这其实和冯诺依曼体系恰恰相反,把两者分开,这个还要结合起来。为什么可以这样做?因为新的计算类型,比如神经网络或者图计算,经常会出现一边不变,另一边不断变化的情况。例如,当A乘以B时,A不断变化,而B保持不变。在这种情况下,可以在存储B的地方进行计算,而无需移动数据。我们做了很多尝试,比如用DRAM做存储。最近阿里好像写了一篇内存计算的文章,把内存设计成有计算单元,直接在里面进行相应的计算。这是一些有益的尝试。与从外部获取数据进行计算相比,这种计算能效提高数千倍,是一个非常有前途的未来发展方向。我们今天讲的一些内容都与这些有关,包括内存计算深度学习加速器、模型优化、分布式训练系统,以及涉及神经网络架构设计自动化的一些操作。看到内存计算,第一个想法就是把神经网络参数存储在一个地方,数据进入这个地方后直接计算,避免了数据处理。更常见的场景是将这些参数存储在一个特殊的纳米芯片上,一些器件(在纳米芯片中)的电阻值可以通过改变电流或电压来编程(然后就可以使用),表示一个参数。当涉及到一个比较大的矩阵形式时,所有的输入和所有的输出都可以在某些交叉节点处连接起来,非常类似于神经网络中矩阵的状态。所有的输入都相当于一个向量,向量乘以一个矩阵可以看作是所有的电压输入,电压通过电流产生一个合成电流,就是它们所有统计量的总和。这以非常有效的方式实现了向量和矩阵的乘法。过去十年,我们在杜克做了很多相关的尝试,设计了一些芯片。2019年我们发表了一篇关于VLSI的文章,可以连接新型内存和传统CMOS。整合后,整个卷积神经网络可以映射成这样的矩阵状态。同时可以选择精度,可以选择精度和能效。权衡(tradoff)。与传统的CMOS设计相比,性能最终可提升数十倍。我们今年在ISSCC还有一个工作,主要是因为我们的研究生闫伯南在北大的课题组工作。这种设计思想也可以应用于传统的SRAM。这种设计的一个特点是传统设计通过电阻来表示参数,实际上是一种模拟计算方法。换句话说,组织连续体表达了一个参数,它需要一个模数转换,这是非常昂贵的。而我们在ISSCC:ADC-LessSRAM上的工作其实就是一个二进制整数表达式,比如0和1,这使得去掉数模转换,直接实现数字状态下的计算成为可能。这在当时是一项技术突破。可以看下图DigitalCIM(最右边ThisWork),能效可以达到27.4左右(TOPS/W,8位状态),已经超越了所有其他的原创设计,整体密度非常高.在28nm工艺下,每平方毫米几乎有数万亿个晶体管。除了电路之外,架构和编译器之间还必须有支持才能实现整个计算系统的设计。2014-2015年之前开始做相关设计,比如设计一个编译器,找到程序中可以用来加速的部分,同时把芯片上的各种数组以某种方式连接起来,把大网络分成小型网络,或在不同网络层之间转换值。值得一提的是,我们经常会在大型网络中遇到很多数组,而网络并行计算至少有两种方式——数字并行和模型并行。所谓数字并行,就是输入(input)很多,可以把一些数据并行的分成不同的计算单元(PE)。模型并行性也是如此,大型模型可以分块计算。然而,这两种并行方法并不排斥。即使是一个Layer,当你将这一层映射到不同的PE时,每个PE中仍然可能使用不同的并行方式。比如下图就是用黑点和白点表示的。大部分的并行方式都是通过模型并行来操作的,还有一小部分是数字并行。为什么?因为只有整合不同的并行分配方式,才能在整个计算能力得到充分发挥的情况下,所有的数和数刚好达到稳态平衡,整体能效最高。如果是单个表达式,有些地方会出现空白或者计算会比较慢,拖累整个计算。这是HyPar发表于2019年的一篇文章,2020年,我们发现了一个新问题。其实我们并没有过多考虑卷积网络本身的表达方式,而你往往在卷积层中一层层计算,会有一些中间结果直到最后才用到。到达。这样就会产生第三种并行模式,即Tensor并行模式。比如下图中,输入输出有三种并行的可能。3乘3,共9个表情。如果考虑到每个PE有9种表达方式,人工优化是没有办法的,必须通过自动化的方法(比如线性规划)来完成整个系统的表达。同时,你也不可能把这些表达式一一找出来,需要一些分层的方法。比如先有一些大的区别,再从小的区别到最后的单一表达。如果用三种颜色来表达,你会发现即使在一层映射下,不同的PE也有不同的操作表达式来满足整体最优的数据流。这样,能源效率可以提高一倍。同样的想法不仅可以用在深度学习中。深度学习只是图计算的一个特例。任何能够以图的形式表达数据流的计算都是图计算。深度学习虽然内容很丰富,但仍然只是图计算的一种特殊表现形式。因此,您可以使用内存计算来进行图计算。这是我们在另一个HPCA2018上的工作。我们发现图计算,尤其是深度优先或网络优先搜索等算法,可以通过图计算的方式表达在矩阵上。与在传统CPU平台上计算相比,能效将提升数百倍。说完整个架构设计,如何在算法上继续优化计算能效?下一个例子是结构稀疏化。稀疏早已为人所知。当神经网络的某些权重较小或接近于零时,无论输入有多大,对输出都没有影响。这时候根本不需要计算结果,直接把结果(实际上是零)扔掉就行了。在2016年之前,神经网络的所有稀疏化操作基本上都是非结构化稀疏化,如果看到零,请将其删除。这就带来了一个问题——所有的数据在计算机中存储的时候都有一个局部性(locality),因为时域和空域的局部性,当一个数字被使用的时候,基本上会有一个期望,这个数字会不断地used,或者说它周围存储的数字以后也会被不断的使用。当你删除很多零时,它会产生很多洞。当你找到一个号码时,你期待下一个号码,但你根本不保存它。整个缓存会陷入一种状态:不断从很远的地方取号,发现不需要这个,就继续找。如何解决这个问题呢?在做细化的时候,我们还是希望能够表达去除零点或者某个局部的计算,比如去除整行或者整列。这样,仍然可以在满足存储局部性的前提下实现计算优化。说起来容易,关键是怎么做呢?我们2016年的NeurIPS文章讲到structuralsparsity,这篇文章后来非常出名。(文章讲的是什么)基本上这些参数都可以查到,对应一定的存储结构,可以让这些数字以block-by-block的方式存储。这样,在清除的时候,整行或者整列都被清除,在满足优化条件的前提下,仍然可以满足局部性。这可以用在CNN、LSTM、RNN甚至一些更复杂的计算中。这种技术现在基本上是神经网络优化的标准。另一种常见的神经网络优化是量化。网络训练需要高精度,但推理不需要。这就引出了一个很有意思的事情:什么样的精度最好优化,这个精度怎么表达。传统上,您可以一个一个地找到最好的结果。比如一位,两位,四位……随便找找看。但是你会发现你还需要考虑这个位在存储中是如何表示的。例如,对于这一层,当某个位对于所有数字都为零时,则不需要存储整个位。比如只保证你只需要这四位中的第二位和第四位,而不是每一个都需要,这样就丰富了整个精度的优化。这也是我们第一次将结构稀疏化应用于比特级稀疏化研究。我们使用GroupLASSO的方法,将整个列或者数据表达式的整个结构中的所有位都去掉,大大降低了存储成本。这是我们2021年的一篇文章。接下来就是训练,这是一个很复杂的事情。我们经常教学生损失函数应该接近饱和。但是在公司,永远不可能有足够的计算能力让你饱和。基本上,您将获得100台机器进行24小时的训练。不管你训练什么,你都要结束它,这使得训练本身非常有效率。传统上,我们使用分布式服务器多次复制模型,但每次复制的模型只使用一部分数据进行训练。那么如何保证最终的结果考虑到所有的数据呢?你需要在训练时将这些神经网络的梯度发送到parameterserver,平均后发送回来更新局部神经网络。这就产生了一个问题:当节点服务器过多时,整个系统会被梯度传输产生的数据流完全占满。该怎么办?后来我们发现,当参数足够多的时候,生成的梯度会满足一定的分布,根本不需要传输原始数据。我们只需要计算分布的一些参数,比如数据是多还是少,传过去,另一端就可以完全复制分布,得到相应的结果。我们在手机端完成了这样一个操作,结合很多手机进行训练,同时做推理。在推理的时候,我们采用了聚类的方法,将那些非零数尽可能的通过行和列的变换调整到一起,然后发送到手机端集中计算,减少了手机之间的通信,提高了计算效率.我们曾经和一家公司做过测试,在全球找了上千个CDNweb服务器,搭建了一个StyleTransfer(风格迁移)应用,通过分布式计算和表达来完成整个计算,效果非常好。基本上整个训练和推理都可以通过手机实时连接服务器完成。刚才说了这么多,其实有一个问题:所有这些东西都需要一些非常有经验的、花费不菲的工程师来设计相应的神经网络。这也是目前神经网络落地成本中非常大的一部分。我们可以通过自动化的方法,比如强化学习和优化,来优化整个神经网络,因为它可以被模拟成某种优化过程,但是这些传统的优化过程是非常昂贵的。我们曾经想以图形方式做到这一点。深度神经网络架构通过有向图和无环有向图来表示。它有很多个cell,不同的cell叠加起来就完成了整个神经网络架构。我们要找的是cell内部的拓扑结构,看看最终的神经网络设计是否符合要求。这是一项对拓扑敏感的研究。另外,你会发现在做这些事情的时候,拓扑结构相似的神经网络的准确率是相似的,存在相关性。虽然相关系数不是1,但基本上是一个比较高的数字。因此,可以通过架构来预测这样的架构是否能够满足我们的性能要求,而这种预测可以指导整个神经网络架构的搜索。下面是一些具体的结果。我们将一些离散状态的架构或拓扑结构映射到连续状态空间,并生成向量之间的角度(相似度)作为性能的关键表达。通过这种方式,我们可以预测优化会是什么样子,并不断接近优化结果。显然,这种做法与人类设计相反,这不是人类设计神经网络的方式。人们看看有没有小模型可以满足要求,如果不能满足,可以添加更多。我们也做了这样的尝试,叫做Automatednetworkdepthdiscovery,设计一些规则,让你可以从最小的网络开始不断添加,每一层添加不同的层,或者添加很多层,看最后满足什么样的架构这个要求。当然,你要设计一些具体的规则或者做一些尝试。这些尝试很有趣。最后,你总是可以优化到设计平衡边界上的某个点,但没有办法固定优化到某个点。你只能走到这个边界上的某个点,让它慢慢自由流动。我们仍然没有足够的了解来让我们完全控制优化的方向和范围。因此,这项工作还需要进一步研究。我们只是证明了可行性,并没有对规则的完备性做更多的研究。最后,软硬件协同设计需要考虑的参数很多,包括软硬件协同、具体电路和架构设计、算法本身对硬件的优化等。我们的团队积累了多年的积累。从2012年开始,我们开始研究神经网络在不同硬件上的表达方式,之后在架构设计、分布式设计、自动化设计等方面做了很多尝试。我大概看到了如何从最简单的表达方式入手,最后只需要按下一个按钮,就完成了AI软硬件结合的构建。谢谢你们!
