课程介绍
本课程是由加州大学伯克利分校的 Sophia Shao 讲授,课程编号是 EE290 。本课程主要在硬件和机器学习之间搭建一个交互的桥梁,目的是构建一个加速机器学习应用的高效硬件。学生通过了解机器学习的关键特性,利用核心的硬件优化原则,以及学习前沿的工业界应用,以一种非常实际的方法来理解行业所面临的挑战。
课程内容
2012年,吴恩达(Andrew Ng.)教授领衔团队进行了一项“认猫活动”,即从数百万的 Youtube 视频中利用机器学习技术找到“猫”。如今,我们关注的任务类型实际上远不止通过数百万 Youtube 视频识别猫这么简单,它们需要涵盖各种各样不同的事物,这些都属于计算密集型且吞吐量要求高的任务,需要我们实时同时分析大量的数据。这为硬件领域带来了非常独特的挑战,同时也激发了我们如何才能提供高效的硬件,从而不必为获取效率低下的硬件付出高昂成本的思考。这节课 Shao 与我们深入探讨硬件上的计算执行方式,以及如何更有效地支持硬件,同时探讨该领域中一些常用的硬件优化技术。
Dataflow
Core Principles
在上一篇文章当中,我们集中讨论了卷积的多种执行方式,主要侧重于算法层面。接下来,Shao 将针对直接卷积(Direct Conv) 这一最常用的卷积执行方式介绍一些硬件方面的技巧和更高层次的优化手段。这节课将围绕两个核心原则,局部性(Locality) 和并行性(Parallelism) 展开论述。而且这两者并不局限于硬件加速器领域,无论在架构(Architecture) 或者数字系统(Digital System) 中,基本上都是围绕局部性和并行性这两个核心概念展开。
Locality
当我们提到局部性的时候,我们一般会提到数据迁移(Data Movement),实际上我们希望尽可能地降低数据迁移。如下图所示,在 65nm 工艺节点下,数字系统中的数据从最远处搬运,DRAM 到 ALU,比从最近处搬运,寄存器组到 ALU,功耗相差大约 200x。这就是为什么局部性在各类硬件设计优化中都是一个极其重要的考量因素。
在 DNNs 中,核心的运算执行的是乘累加运算(Multiply and Accumulate)操作。如下图所述,每一次 MAC 操作都需要读取滤波器的权重(Filter Weight) 和输入激活(Fmap Activation),并且需要实际读取先前累积的部分和(Partial Sum),我们将权重和输入激活进行乘法操作后加到当前的部分和上,用以得到更新的部分和。通过在整个执行过程中反复进行这一操作,我们将获得最终的总和,即最终的输出激活值,然后遍历所有不同的输出维度,以获取所有最终的激活结果。因此对于每次 MAC 操作都要进行 3 次读取和 1 次写入操作。如果所有的存储器的 R/W 都由 DRAM 处理,例如 AlexNet 拥有 724 兆次 MACs 操作,也就是 2896 兆次 DRAM 访存,这将导致整个系统的功耗特别高。因此,局部性的目标是探究我们是否能够实际增添一个中间存储结果,以便能够尽早存储至少部分结果,从而减少对 DRAM 的访存次数。这也是为什么局部性在深度学习系统中也扮演着极其重要的角色。
Parallelism
深度学习中的并行性可以分为两种,一种是时间复用(Temporal Reuse),另外一种是空间复用(Spatial Reuse)。时间复用是指在整个计算过程中,相同的数据将被相同的功能模块多次使用。这句话的意思是同一份数据可能会被实际访问多次,因此这种时候我们就希望能在计算单元和 DRAM 之间设置 Buffer 甚至寄存器组,以便能更加快速的获取可复用的数据;另一种复用是空间复用,它指的是同一段数据被多个 MAC 单元重复利用,某种意义上,这是一种广播模式,为此设计者需要配置相应的布线或者网络。
目前,大量的优化工作本质上是在尝试增加内存或者构建缓存层次结构,以便我们能够实现更快的访问速度,直接触及接近实际使用位置的内存,而无需访问距离较远的 DRAM。在当今硬件设计中,除了缓存结构层次化设计,我们还希望不是仅仅考虑单一的 MAC 计算单元,而是设想拥有所有并行的 MAC 单元来支持硬件系统提供更高的吞吐量。这种情况实际上更为常见,尤其是对于深度学习这类需要并行计算的场景,因其内部蕴含大量并行性,尤其是在各个不同的循环维度上。我们需要构建并行执行单元并高效地组织它们,以便我们能充分利用其吞吐量,发挥最大能效并确保它们能得到充分利用。
Dataflow Taxonomy
大多数人在深度学习领域中描述数据流(Dataflow) 的时候,指的是模型在硬件中执行的特定顺序。这个顺序既包括计算顺序,也涵盖数据移动的顺序。我们采用数据流作为区分手段,首先针对不同的执行顺序,不同的执行顺序又可能导致不同的硬件实现顺序,即不同类型的硬件在执行卷积运算的时候可能存在不同的执行顺序。我们在深度学习硬件背景下,运用 LoopNest 的方法来描述数据流。但是需要注意两点,首先这种描述方式是对数据流的一种非常有限的定义,我们在这里讨论的并非通用型数据流架构,我们讨论的是 DNN 流水线中数据流的执行;其次,每当探讨数据流的时候,我们将采用循环表示法来讨论数据流,以获得更为直观的表现形式,避免过于抽象。
Shao 通过 1D Conv 举例,在这里我们用循环表示法。左侧的循环表示以输出激活中的 Q 作为外层循环,权重中的 S 作为内层循环,我们称这种为固定输出数据流(Output Stationary Dataflow, OS Dataflow);另外一种是以权重中的 S 作为外层循环,输出激活中的 Q 作为内层循环,我们称这种为固定权重数据流(Weight Stationary Dataflow, WS Dataflow)。图中的 for 循环代表一种时间,通过执行顺序,它告诉我们哪些数据片段或操作数实际上保持静态,即在此期间不发生改变。在固定输出数据流中,实际变化的是权重,而在固定权重数据流中,实际变化的是输出激活。
论述完毕!