一、背景 - 边缘智能
人工智能(Artificial intelligence)快速发展正在改变世界。以深度学习(Deep learning)第三波代表驱动力和驱动力AI浪潮正在改变和赋能金融、制造、农业、交通、医疗、零售、教育等行业,也极大地影响了我们每个人的生活。目前,移动设备上有各种AI应用场景不断涌现。对端智能能力提出了新的挑战,也带来了新的机遇。得益于今天AI技术的巨大突破和进步,我们可以通过语音、手势等方式与设备沟通,扫描面部可以确定身份支付,可穿戴设备可以分析健康状况并提出建议,系统可以根据用户的使用习惯自动优化,汽车将检测和识别周围环境给出指导或自动驾驶。这样的应用还有很多。所有这些都离不开背后。AI的助力。
在过去的几年里,智能计算被广泛应用于云中,即智能强烈依赖云。近年来,随着移动设备硬件能力的快速提高和智能需求的井喷,边缘计算开始兴起。在服务器上训练后,机器学习模型可以部署到端设备上,执行过程不依赖云,从而大大提高了用户体验。在不侵犯隐私的前提下,使用过程中产生的数据有助于提高模型的效果,最终形成良性的智能闭环。一般来说,端上智能具有以下优点:
- :避免与服务器沟通造成的延迟,保证实时性。ADAS在对实时性要求极高的情况下尤为重要,因为如果慢一点,人可能已经凉了。
- :计算在本地完成,不受网络质量的影响。由于环境的变化,手机、汽车等移动设备往往无法保证网络的高质量和可用性。
- :数据不需要离开设备。可以充分保护聊天记录、照片等隐私信息。
- :可根据个体用户的特点和习惯进行适应,做到千人千面,提高用户粘度。
- :如果计算放在服务器端,服务器的成本可能会随着用户的增长而同比增加。如果放在移动端上,就不会有这个问题。
正是由于边缘智能计算具有这些优点,它已成为该行业的热点。然而,它也带来了巨大的挑战。这些挑战主要来自边缘设备的资源限制,如计算速度、存储和功耗。
二、基本问题 - 算力之殇
在过去几年里,深度学习在视觉、自然语言处理、语音识别、推荐、智能控制等领域获得了许多重大的突破,并在很多的专项上超越了人类水平。众所周知,深度学习有三个基石:数据、算法和算力。大数据的兴起AI深度学习的主要优势之一是基于大量数据学习。算法为数据处理和学习提供了有效的方法。计算大量数据和复杂网络需要强大的计算能力支持。从某种意义上说,它们之间有一些互补的关系。比如近几年AutoML人们发现,机器搜索网络架构可以找到比人工设计更好的解决方案。Google的大神Jeff Dean(对,就是编译器从不给他警告,只有他给编译器警告的那位)就曾经表示:未来谷歌可能会用100倍的计算能力,取代机器学习专业知识。换句话说,如果你有足够的计算能力,你可以取代大多数算法设计。
这就导致了一个问题。有多少计算能力足够?估计没有人能对这个问题给出准确的答案,但我们可以通过一些数据来感受。OpenAI发布的分析表明:自2012年,也就是深度学习爆发以来,人工智能训练任务中使用的算力呈指数级增长,其目前速度为每3.五个月翻倍。相比之下,摩尔定律中芯片性能翻倍的速度是18个月。自2012年以来,人们对计算能力的需求增长了30多万倍。此外,随之而来的能耗和成本问题也不容小觑。马萨诸塞大学的研究人员表示,通过神经网络自动架构搜索训练Transformer网络,碳排放180吨,相当于从全新使用到报废三辆车。当然,这是训练所需的计算能力。因为大多数移动终端只需要推理,所以对计算能力的需求要小得多。以流行的自动驾驶为例。为了完成对周围环境的感知,自动驾驶汽车需要从camera、radar、LIDAR等待一系列传感器数据。Intel CEO Brian Krzanich曾在大会上keynote据估计,到2020年,一辆自动驾驶车每天大约会产生4辆TB的数据。许多数据需要实时处理。要处理如此多的数据,其计算能力需求可想而知。Imagination汽车市场总监Bryce Johnstone自动驾驶水平每升高一级,对计算能力的需求至少增加十倍。可能需要500级全自动驾驶TOPS上述计算能力。自动驾驶作为低配版ADAS,根据不同的功能,还需要10到100 GFLOPS的计算力。
这几年,机器学习学术研究空前繁荣。Jeff Dean曾发推送的数据:根据对arXiv据上述论文统计,到2018年底,平均每天产生100篇论文。难怪有一种感觉看不懂的感觉。。。然而,与学术界遍地开花的繁荣景象相比,工业界落地难的尴尬局面困扰着许多业内人士。不少AI产品停留在PPT和概念演示阶段,然后,就没有了。。。最大的障碍之一是端上的计算资源有限,难以支持各种实时AI相关计算任务。
深度神经网络(DNN)为代表的AI本质上,计算任务促进了边缘设备计算模式的转变。传统的移动终端基本上是I/O密集任务主要是,偶尔计算密集任务可能会让用户感到热,是否有问题。而DNN推理一次运行数十亿次,仍然是周期性的高频运行,系统中可能有多个DNN同时,这种传统的玩法根本扛不住。因此,我们的核心问题是算法需求与边缘硬件计算能力盾。而且这种矛盾很可能长期存在。就像之前的Andy and Bill’s Law,即使计算能力大大提高,市场也会有更多的场景需求,更高的准确性需要吃掉这些增长的计算能力。注意计算能力也与存储、能耗等资源密切相关,因此移动性能优化的目标不仅耗时,还包括内存占用和功耗。
三、解决方向 - 希望之光
该行业有几个基本思路:
- 给定算法模型如何让它跑得快?
- 专用硬件设计牛X
- 深度计算优化与硬件相结合
- 通过调度提高硬件利用率
- 如何尽可能简化模型?
- 如何设计更轻量化的网络结构?
- 在给定模型的情况下,计算量是否必要(如果没有,如何压缩)
这些想法分别稍作展开。
1 神经网络芯片 - 定义芯片的软件
以DNN计算模式的变化是现代流行机器学习模型在端上的广泛应用。DNN典型的计算(如矩阵乘加)一般具有逻辑简单、计算量大、重复计算的特点。这种计算模式的变化促进了芯片的发展和变化。主要的挑战来自两个方面:
- :1965年英特尔联合创始人戈登·摩尔提出了著名的摩尔定律:当价格保持不变时,集成电路上可容纳的部件数量每18-24个月翻一番,性能翻一番。在这一定律的推动或引导下,半导体产业发展了50多年。然而,物理元件不可能无限缩小。当晶体管体积达到物理极限时,这一趋势将是不可持续的。虽然摩尔定律被称为定律,但它不是来的科学定律,而是经验法则。今天,即使不能说它完全失,也可以说明显放缓了。
- :基于冯的现代主流处理器·诺依曼架构。在该架构中,计算模块与存储单元相互分离,数据需要从存储器中提取,然后在处理单元后写回存储器。这些频繁的访存操作造成了延迟,增加了功耗。这种结构的优点是通用性,适用于逻辑复杂的计算,但对于矩阵乘加等特殊计算,效率不高。更糟糕的是,摩尔定律支持的芯片计算性能在过去几十年中迅速增长,而存储器的性能增长远低于其,从而形成了处理器和存储器的速度gap。另外,DNN中间计算涉及大量参数和中间结果,因此需要大量参数memory bandwidth。这是由多种因素造成的DNN很多时候,计算的瓶颈存而不是计算。
针对上述挑战,业界进行了各种尝试克服。众所周知,神经网络的执行往往是矩阵计算。由于这是一个非常耗时的特殊计算,最好的方法是使用特殊的硬件(如编解码芯片)来实现高性能和低功耗。这种为特殊目的设计的集成电路被称为ASIC。FPGA与ASIC软件算法也是用硬件实现的。它只是一种可以自定义的半成品。近年来,各种神经网络芯片层出不穷。一时,各种"xPU"横空出世。看来26个字母都快用完了。。。
本质上,它们大多是为了解决上述基本问题。摩尔定律放缓或失效的自然途径是并行加速。CPU它经历了从单核到双核再到多核的发展过程。然而,由于硬件设计的器,由于硬件设计的一些限制,不能堆积太多的核。将并行计算推向大规模的先驱是Nvidia,早在2006年,Nvidia推出了CUDA,非常明智地将以前专门用于图形的领域GPU推向通用计算领域,摇身变成GPGPU。2012年,以绝对优势获得ImageNet冠军的AlexNet正是用了CUDA加快训练。Nvidia随着深度学习浪潮的兴起,它的硬件和软件生态已经成为服务器端的绝对主流和标准。与CPU不同,GPU拥有成千上万的核心,可以同时进行并行任务。这样,其整体性能就不受单个核心频率的限制。其局限性仅适用于规则的并行计算任务DNN中矩阵乘加非常适合,因此,在深度学习的任务中,GPU吞吐量可比CPU数量级以上。
然而,GPU毕竟设计之初并不是专门为神经网络设计的,它还是一个通用处理器。虽比CPU但硬件设计还是需要考虑一定的通用性,所以不能太激进。 这就给了ASIC进一步发展空间。ASIC由于是专用的,所以可以针对硬件设计,从而达到极致的能效。与GPU相比之下,用于神经网络的ASIC芯片的显著特点是通常有更多的计算单元(如TPU包含65536个8位MAC矩阵乘法单元)。然而,正如前面提到的,仅仅考虑计算速度是不够的。很多时候,瓶颈在于访问。很多时候我们谈算力指标言必FLOPS,其实这个指标很容易让人误解,似乎只需FLOPS到了一切都妥妥的了。其实由于访存速率的限制很多时候硬件算力只能发挥出一部分甚至是10%不到。在Roofline模型中,由计算平台的算力和带宽上限决定的"roofline"将整个区域分为两个部分-memory bound和compute bound。根据模型在图中的位置可以判断它的瓶颈在哪里。我们发现不少模型,尤其是像MobileNet等轻量级的模型由于计算密度较低大多会落入memory bound区域。也就是说,这种情况下硬件计算速度再快也白瞎。针对访存这块,神经网络芯片一般会在设计上做针对性的优化,比如更大的片上内存(如TPU的有28M)。另外,像有David Patterson(图灵奖获得者)背书的Google出品的TPU为了更大幅度缓解冯·诺依曼瓶颈,让脉动阵列架构重回到舞台。在这种架构下,每次执行乘法运算时,结果会被直接传递给后面的乘法器,并进行求和。在整个过程中,无需访问内存。去除这些访存开销,还会有额外好处,即功耗的降低。我们知道与计算相比,访存才是功耗大户。通过这种方式可以有效提高能效比。
以上只是举些小例子,其它各种神经网络的ASIC也是各显神通,用足了各种优化来克服传统计算方式的缺点。虽然目前这条路子仍然是绝对的主流,但人们同时也开始探索新型的计算形式和计算硬件用于神经网络计算,如:
- :又称脉冲神经网络(SNN)。它来自于大脑的启发。在神经系统中,神经元间通过突触发送脉冲信号,其间的连接可能被抑制或是被强化。神经信息被认为是通过神经元的触发频率来编码的。相比之下,传统方式则需要较大的memory来存储模型权重。相关的研究包括IBM的True North、Intel的Loihi、BrainChip的Akida NSoC和清华大学等机构提出的天机芯片架构。
- :虽然光纤可以以光的形式传输数据,但最终要分析这些数据时,需要进行光-电转换后再进行处理。MIT的研究者提出了一种使用光子技术实现神经网络的方法。这种可编程纳米光子处理器用光来执行人工智能算法的计算。德国明斯特大学物理研究所的研究者提出了一种在光子芯片上实现的全光学神经网络。使用基于光学计算的方法在加速和能耗方面都会有很大的优势。
- :量子计算基于量子力学的模型来实现计算。量子计算机的理论基础也是图灵机,它可以实现经典计算机能实现的计算。量子计算的核心优势是拥有指数级的存储和计算能力。因此,它可以使现在很多“不可解”的问题变得可解。这将是颠覆性的,因此也引得大家的极大关注。神经网络作为当前热点之一,自然也是量子计算的应用方向之一。如Google构建了量子神经网络QNN,用于在量子处理器上执行神经网络。
尽管经过科学家们的不懈努力,这些新型的计算方式和硬件的价值已初现端倪,但目前仍主要处在研究阶段。要将它们落地并大规模应用,还需要克服很多困难。而一旦它们技术成熟,那将会是革命性的。
2. 计算实现优化 - 结合硬件特性与能力
很多时候,程序执行符合80/20法则。即80%的时间在跑20%的代码(当然,这里的数是虚指)。对于神经网络来说也不例外。如对于视觉任务中典型的CNN,通常卷积与全连接层会占到总计算量的80%~90%甚至更高。另外像在语音、自然语言理解领域常用的RNN中大量存在LSTM和GRU层,它们本质上也是做矩阵计算。因此很多时候大家做性能优化会focus在矩阵计算上。
抛开实现谈优化意义不大,或者说油水不多。只有考虑与平台硬件结合的深度优化,才能充分挖掘潜在的性能。因此,在计算实现中,需要考虑硬件特性和能力。例如处理器对于不同类型指令性能差异(如大多计算硬件上乘法比加法更耗时和耗能)和存储器层次结构(每一级的延时和能耗都可能相差数量级)。考虑到平台相关的硬件加速能力还有CPU的并行指令、GPU的并行编程等等。
最简单的方法当然是按公式来计算,称为直接法。全连接层其实就是矩阵和向量乘,不用多说。卷积层的定义稍稍复杂一些。为简单先不考虑stride,dilation等特殊卷积,且卷积核size宽高相等。记输入为 X X X,其维度为 N × C i × H i × W i N \times C_i \times H_i \times W_i N×Ci×Hi×Wi,卷积核为 W W W,其维度 C o × C i × K × K C_o \times C_i \times K \times K Co×Ci×K×K,输出为 Y Y Y,维度为 N × C o × H o × W o N \times C_o \times H_o \times W_o N×Co×Ho×Wo,则对输出张量中的每个元素,计算公式为:
Y n , c , x , y = ∑ m = 0 C i − 1 ∑ u = 0 K − 1 ∑ v = 0 K − 1 X n , m , x + u , y + v W c , m , u , v Y_{n,c,x,y} = \sum_{m=0}^{C_i - 1} \sum_{u=0}^{K - 1} \sum_{v=0}^{K - 1} X_{n, m, x+u, y+v} W_{c, m, u, v} Yn,c,x,y=m=0∑Ci−1u=0∑K−1v=0∑K−1Xn,m,x+u,y+vWc,m,u,v
算法复杂度为 O ( N C i C o H o W o K 2 ) O(N C_i C_o H_o W_o K^2) O(NCiCoHoWoK2)。其优点简单粗暴,且几乎无需额外的内存开销。其缺点也是显而易见的,就是效率会比较低。现代计算库一般都会使用一系列的计算实现优化,几个比较典型的如:
- :一边是大量卷积计算需求,另一边是几十年来深度优化的成熟的BLAS库。如何将两者连接起来呢?经典的im2col方法将输入矩阵转化成Teoplitz矩阵,这样卷积操作就转化成了矩阵间乘法操作,从而可以作为GEMM利用成熟计算库来加速。但它有个缺点是需要比较大的内存开销来存储临时矩阵。
- :im2col方法虽然可以利用BLAS的GEMM函数加速,但它需要 O ( C i K 2 H i W i ) O(C_iK^2H_iW_i) O(CiK2HiWi)的额外存储空间。在移动端设备内存也是很稀缺的资源,有没有可能在享受到BLAS加速的同时又可以占用少一些内存呢。kn2系方法避免了Toepliz矩阵的构建,其基本思想是将卷积分解成为多个矩阵乘用GEMM计算,然后移位后累加。额外内存减少到 O ( C o H i W i ) O(C_o H_i W_i) O(CoHiWi)。其代价是更多次的GEMM调用。如果说im2col是拿空间换时间,那这个就是拿时间再换回一些空间。
- :这个矩阵乘法的优化大家一定不陌生,它也可以被扩展到卷积计算。我们知道,不少看上去还比较intuitive的算法会让人有种“我要是他我也能想出来”的错觉,然而有一些看完则会让人觉得“这怎么想出来的!”。该算法应该就属于后一种。通过一系列tricky的构造,它将分块递归过程中的8个子矩阵乘法干到7个,硬生生将复杂度从 Θ ( n 3 ) \Theta(n^3) Θ(n3)减到 O ( n 2.81 ) O(n^{2.81}) O(n2.81)。但其代价是会引入一些额外的矩阵加操作,因此在实际使用中需要有条件地使用,如矩阵比较大时。
- :根据经典的Convolution theorem,空间域上的卷积操作等同于频率域上的乘积。因此,可以将原卷积经过傅立叶变换转到频率域,在频率域经过乘积后再通过逆傅立叶变换转到原空间域。这个过程的计算复杂度为 O ( H i W i log ( H i W i ) ( C o C i + N C i + N C o ) + H i W i N C i C o ) O(H_iW_i \log (H_iW_i) (C_o C_i + NC_i + NC_o) + H_iW_iNC_iC_o) O(HiWilog(HiWi)(CoCi+NCi+NCo)+HiWiNCiCo)。先不要管这式子为毛这么长,总之与前面直接法时间复杂度相比,这里没有卷积核的size了。这非常好,同时这也意味着只有当卷积核越大,它的优势越明显。另外,虽然这里有一来一回域间变换的额外运算,但一方面有快速傅立叶变换助力,另一方面由于每一个输入feature map要被重复使用多次,因此对应的转换可以重用。当然,这也意味着,feature map的通道数越多越适用。
- :该方法基于Winograd algorithm。它以更多的加法数量和中间结果乘法数量为代价达到理论上最少的乘法次数。该方法的计算复杂度随着卷积核size平方的速度增长,因此和上面FFT-based方法相反,它主要适用于小的kernel(如3x3)。不过,好在现代主流的神经网络也是以这种小kernel为主,因此被广泛使用。
我们可以看到,就像其它很多计算任务一样,没有一种实现方法是万能的。因此,用时需要根据实际情况来。实际使用中还会有不少坑,如有些方法可能对精度有较大影响,而有些是基于一些硬件特性假设的。因此,如果你尝试了一种看起来非常牛X的计算优化,结果却不尽如人意,不要伤心,不要难过,很可能只是不满足该算法的某些隐式假设。为了避免人肉挑选之苦,业界也有尝试通过自动化的方法来为神经网络中每个卷积选取最合适的实现算法。
还有一类优化是并行优化。对于矩阵乘加这样天然可并行的计算,不并行白不并行。常用的手段有几类:
- :SIMD指令,即一条指令可以操作多个数据。比如对于4个乘法操作,原本要分4个指令,现在一条指令搞定。x86平台上有SSE和AVX指令集,ARM平台上有NEON指令集。当只能用CPU时,这几乎总能给性能带来明显的提升。
- :虽没有GPU的大规模并行能力,但CPU也有多核。其并行能力可以通过多线程来利用。我们大多时候不必从头写线程调度,可以依赖OpenMP这样的库,或者更高层的库。
- :这个topic就非常非常大了。比较主流的有CUDA和OpenCL。严格意义上讲两个不是完全平级的东西,虽然都可以指一种并行编程接口。前者是Nvidia家的,是一整个生态,只要用的N卡基本没谁就它了。后者是一个标准,具体由各厂家去实现,最大优点就是跨平台通用(不过也得看各家支持的情况。。。)。
再次回到访存优化的话题。无论是CPU还是GPU,都存在存储器层次结构(memory hierarchy)的概念。对于这个层次结构中的每两个层级之间,其速率都可能有数量级的差异。为了让计算尽可能少地访问内存,就需要让程序尽可能cache friendly。做得好和不好有时性能会是成倍的差异。在NN计算优化中,针对访存的优化很多。举两个常见且效果明显的典型例子:
- :对于两个矩阵相乘,我们知道用原始定义计算的话对cache不友好。如果矩阵比较大的话,尽管一个元素会多次参与计算,但当它下次参与计算时,早被踢出cache了。一个好的办法就是分块。那么问题来了,分块分多大,按哪个轴分,都对性能有很大影响。另问题更加复杂的是这个最优值还是和具体计算任务和硬件平台相关的。一种方法是针对平台做尽可能通用的优化。这就像做爆款,对大部分人来说都不错;另一种方式就是干脆量体裁衣,对给定模型在特定平台通过tuning的方式得到合适的调度策略。如TVM中的auto-tuning技术基于Halide中算法与调度分离的思想,然后通过自动搜索找出使性能最优的schedule。经过tuning后其性能一些时候甚至可以超过芯片厂商的推理引擎(毕竟厂商的推理引擎没法给每个模型量身定制)。
- :这是一种最为常用的graph-level的优化。它将多个算子融合成一个,从而减少运行时间。无论是像Conv-BN-ReLU这样的纵向融合,还是Inception module这种多分支结构的横向融合,其实本质上都没有减少计算量,但现实中往往能给性能带来比较明显的提升。主要原因之一是它避免了中间数据的传输开销。
3. 异构调度 - 充分榨取多硬件能力
在过去的几年里,服务器市场基本是Nvidia GPU的天下。尽管近几年它也受到了来自TPU等各方面的压力,但总体来说,对于服务器端异构多元化程度没那么大。而与之不同的是,在移动端,不仅硬件种类多,而且同一类硬件差异也大。CPU有ARM和x86的,GPU有Adreno、Mali、Vivante等(虽说有像OpenCL这样的接口标准,但和CUDA不同,毕竟是多厂商各有各的实现,良莠不齐。而且很多厂商还会有私有扩展)。各类芯片如CPU、GPU、FPGA、DSP、ASIC不仅能力各异,算力也有数量级的差异,能效甚至有几个数量级之差。当然,理想情况下,如果ASIC一统江湖,在性能、成本、算子支持等方面都完爆、吊打和摩擦其它芯片,那事情倒简单一些。但这毕竟还需要些时间。因此,当前如何在AI时代充分挖掘和利用这些异构硬件的计算能力就成了一个很有趣的问题。
要解决问题,首先是问题的定义与描述。绝大多数计算任务可以被抽象成计算图,或者更具体地,有向无环图(DAG)。如一个DNN就是一个典型的DAG,一个场景中pipeline的各个节点也可以抽象成一个DAG。如在监控场景,一般摄像头数据进来先做预处理,再进行物体检测,同时可能还会做光流。物体检测结果会送去做细分类(如车牌识别,车型识别,行人ID识别等),另外还会拿来做跟踪。形式化地,计算图记为 G = ( V , E , w , c ) G=(V, E, w, c) G=(V,E,w,c)。节点 V = v 1 , . . . , v n V=v_1, ..., v_n V=v1,...,vn为操作,边 E = e 1 , . . . , e m E=e_1, ..., e_m 标签: n13cp光纤传感器