现在比较热门的是近存或存内计算设计,想法也非常简单,既然瓶颈来源于数据间流动,尤其是存储空间到计算空间的流动,能不能想一些办法让计算跟存储发生在同一个地方?这实际上正好和冯诺依曼体系相对,后者是将两者分开,这个要合在一起。 为什么可以这么做?因为新型计算,比如神经网络或图计算,经常有一方不变,另外一方不断变化的情况。比如 A 乘上 B,A 不断变化而 B 不变,这种情况下,可以在存储 B 的地方进行计算,不需要把数据挪来挪去。 大家进行了非常多尝试,比如用 DRAM 来做计存。最近,阿里好像也写了一篇文章讲了存内计算,把存储器设计成可以有计算单元,直接在里面进行相应计算,这都是一些有益的尝试。与在从外面拿到数据过来进行计算相比,这个计算能效强几千倍,是非常有希望的未来发展方向。
如果考虑到每个 PE 都有 9 种方式进行表达,就没有办法通过人工方式进行优化,必须通过自动化方式(比如线性规划)完成整个系统的表达。同时,你也不可能逐个去找这些表达方式,需要一些的层次化的方式。 比如,先有些大的区分,再由小的区分一直到最后单个表达。如果用三种颜色来做表达,会发现即使在一个层次的映射下也有不同 PE 有不同运行表达来满足整体上数据流动的最优。这样,能效就能再推进一倍。
同样的思路不仅仅可以用到深度学习里面。深度学习只是一个图计算特例。而任何可用图方式来表达数据流动的计算都是图计算,深度学习虽然很丰富但仍然只是图计算的一种特殊表达。所以,你可以用存内计算方式来进行图计算。 这是我们另外一个 HPCA 2018 上的工作。我们发现是图计算,尤其是深度优先或者网络优先搜索这类算法,可以把他们在矩阵上的表达,用图计算的方式来实现。相对于在传统 CPU 平台上的计算,能效会有上百倍提高。
另一个常用的神经网络优化是量化. 网络训练需要高精度,但推理时并不需要高精度。这就产生了一个很有趣的事情:究竟什么样的精度,优化是最好的,而且这个精度用什么方式去表达。 传统上,大家可以逐个去找最佳结果。比如,一个 bit、两个 bit、四个 bit...... 去找就对了。但你会发现,还要考虑到这个 bit 怎么在存储进行表达。比如,对这个 layer 来讲,当有某个 bit 于所有数字来说都是零,那就不需要存这一整个 bit。举个例子,只要保证你只要这四个 bit 里的第二个和第四个存在,而不是每个都需要,这就丰富了整个精度的优化。这也是我们第一次将结构稀疏化运用到 bit 水平的稀疏化研究上。
我们用 Group LASSO 的方式,把数据表达里面整个 column 或者整个结构为零的 bit 全部去掉,这样就极大降低存储成本。这是我们 2021 年的一篇文章。
再往下就是训练,这是一个很复杂的事情。我们经常教学生 loss function 要趋近于饱和。但在公司,永远不可能有足够的算力让你算到饱和,基本上给你一百台机器训练 24 小时,无论你训练成什么样,你都得结束,这使得训练本身要非常高效。 传统上我们采取分布式服务器的做法,把模型复制很多遍,但每个复制的模型只用一部分数据来训练。那么怎么保证最后得到的结果考虑到所有的数据?你就需要把这些神经网络在训练中禅城的梯度送到参数服务器里面,做平均之后再发回去来更新本地的神经网络。这就产生了一个问题:当节点服务器特别多的时候,最后整个系统就完全的被梯度传输产生的数据流所占据。 怎么办?我们后来发现,当参数足够多的情况下,产生的梯度会满足某一个分布,根本不需要传输原始数据,只需要算分布的一些参数和一些诸如数据多还是少之类的,把他们传过去,就可以完全在另外一端复制这个分布,得到相应结果。