Day 5面向非专业人士的神经网络加速方法科普。我们目前已考察了两种手段:一是使用稀疏参数矩阵,二是将参数量化成为整型数据(若不理解术语,请从Day 1开始读)。
两种方法的相同点:都只做很小的合成质量牺牲,来换取大幅度的加速;同时由于要从普通的矩阵乘法进行变形,也必然会带来一些额外的运算消耗。
我们想要两者一起应用,以达成更高的提速。那将是一个所有参数都以小整数存储的稀疏矩阵。然而,这究竟能不能提速,还得看程序到底跑在什么硬件上。
之前提过,很旧很旧的老电脑在量化情形下跑得很好;2021年你手头上典型的“老电脑”可能没有量化反而跑得快;然后又然后,刚出炉的新设备量化之后性能更佳。
稀疏MVM是同样的情形,但这次还不是单纯的硬件。对于删减不足一半的不那么稀疏的矩阵,还不如把它们当作密集矩阵来处理。
纵使是对于那些非常稀疏的矩阵,我们也需要选择如何将数据存储在运存上,以及以何种顺序将它们加载至CPU还有如何处理它们。每种方式都有其优点和缺点,并会影响一些种类的稀疏矩阵。
Synthesizer V AI所使用的神经网络有许多不同大小。有些可以变形成稀疏的,有些则不行。我们的软件可以运行在自Pentium 4(2004)之后的所有x86处理器上。这些硬件搭配矩阵的类型和大小,有无数种排列组合。
我们该如何编程神经网络(或者精确说,MVM子程序)来支持所有这些情况?我们真的要写100个或者1000个版本?不。我们不写程序本身,我们写一个程序,让它去写另一个程序来做工。
更有趣的是——所有这些都在运行中完成。我们并不发出一个预构造(预编译)好的程序,让它包含所有的情形(程序将会大的离谱)。我们发出一个“程序构造器”,它接收一个神经网络,并生成运行神经网络的代码。
这儿的代码是什么呢?如果你了解编程,你或许知道C、C++和Fortran是性能最好的编程语言。比这些还快的就是汇编语言了,汇编会直接翻译成CPU可理解的一串0和1。
我们的程序构造器是用C写的。它首先扫描CPU支持的特性及它所接收的矩阵种类;然后它将0和1转存进内存并告诉CPU,“这儿有些新鲜出炉的代码,请运行它们!”
这个方法叫做即时(JIT)编译,也不是什么新东西了。同样的方法被用在现代网络浏览器里,来加速Javascript的运行。只是直到最近我们才发现它在机器学习方面的应用。
(给进阶读者)你可能想过FBGEMM。是的,就是它的灵感。我们的大概是一个GeMV/SpMV版本的FBGEMM,并添加了基于AsmJit的旧处理器的支持。