本次课程内容主要是AI模型(如NLP BERT, LSTM等)、框架(如TensorFlow,PyTorch等)在IA架构上的优化理论,技术要点以及真实案例分享。
【课程目标】
了解应用性能优化的理论基础
掌握AI应用的优化方法
进行优化技术探讨
了解优化案例
英特尔是半导体行业和计算创新领域的全球领先厂商,以智能互联技术奠定全球创新基石。英特尔创始于1968年,拥有50余年推动技术创新和耕耘产业生态的成功经验。如今,英特尔正转型为一家以数据为中心的公司。英特尔的战略愿景聚焦于三个方面:一是致力于生产世界上最好的半导体芯片;二是引领人工智能与“自能”革命;三是做全球领先的端到端技术平台提供者。英特尔正与合作伙伴一起,共推人工智能、自动驾驶、 5G及物联网等领域的技术创新与应用突破,驱动智能互联世界,从而解决人类面临的重大挑战。 英特尔于1985年进入中国,是在华高科技领域最大的外国投资企业之一,协议总投入约130亿美元。中国是英特尔全球战略之重点,英特尔在中国拥有美国总部之外最为全面的业务部署,覆盖了前沿研究、产品技术开发、精尖制造、产业生态合作、市场营销、客户服务、风险投资和企业社会责任等。英特尔中国现有员工约9,500人。扎根中国三十四年,英特尔见证并深度参与了中国的改革开放,从浦东开发、西部开发到振兴东北等,英特尔一直跟随改革开放的步伐,积极带动区域经济发展,为高端制造持续注入新动力。 2014年12月,英特尔宣布在未来15年将为成都工厂投资16亿美元,首次引入英特尔最先进的高端测试技术( ATT),使成都工厂在原有的芯片封装与测试业务之外进一步升级为英特尔在美国境外唯一的ATT技术工厂;该技术已于2016年11月18日正式投产。 2015年10月,英特尔宣布投资55亿美元,将英特尔大连工厂升级为非易失性存储技术制造基地;该项目已经于2016年7月正式投产, 2017年发布了两款全新的基于3D NAND的数据中心级固态盘;其二期项目已经在2018年9月投产英特尔最先进的96层3D NAND产品。英特尔中国研究院是英特尔全球五大创新中枢之一,而英特尔亚太研发中心已发展成为英特尔在亚洲最大的综合性研发基地之一。 英特尔中国积极履行企业社会责任,涵盖了人才培养、员工关爱、多元化和包容、环境可持续、供应链建设、志愿服务等领域。英特尔连续20多年被教育部评为最佳合作伙伴。我们持续支持英特尔杯嵌入式大赛、英特尔杯软件创新大赛和人工智能人才培养等项目,开展丰富多彩的校园活动,每年都有上万名学生的直接参与,受益青少年数以十万计。英特尔中国员工在2018年参与志愿活动人数达8,636人,贡献志愿服务时间超过12万小时,参与比例为69%; 10年来累计志愿服务时间超过72 万小时。我们把公司运营与环境可持续发展协调并进,积极减少碳足迹;还和政府、产业链以及公益组织深入合作,共同推动绿色可持续发展。全球独立机构声望研究院发布的“中国最具声望的公司”( RepTrak? 100) 2018年百强排行榜中,英特尔荣登榜首。
阿姆达尔定律对于一个固定负债的情况下,整个任务在加速了其中某部分功能的处理速度之后,延迟的理论加速比S阿姆达尔给出了如下公式:
其中p为采取了增强或加速的某部分功能在整个任务中所占的比例,而s为该部分收益的倍数,这样当s趋近于无穷时,整个任务的极限加速比S能够趋近于1-p分之1,这也是加速比的上限。
例如做并行处理时,若串行代码占整个代码的25%的比例,那么可并行部分占75%,即p等于75%,那么并行处理的总体性能的上限为4倍。
冯诺伊曼模型是一种将程序指令计算器和数据寄存器合并在一起的计算机设计概念结构。计算机硬件由运算器、控制器、存储器、输入设备和输出设备五大部分组成,它的主要特点是以运算单元为中心,IO设备与存储器间的数据传输都要经过运算器,存储与计算相分离,并且CPU的运算速度可以达到内存的200多倍。为了提高性能,我们应该尽可能对数据进行重用,通过软件的方法实现对存储访问的减少,从而整体上提高性能。
CPU的流水线技术,可以将指令分解为多部,并且让不同的指令各部操作,相互重叠,从而实现几条指令的高效并行处理,加速程序的运行过程。现在高性能CPU流水线非常复杂,我们就简单来讲。从简单来看,CPU流水线在概念上可以分为两个部分,前端和后端。前端主要负责获取程序代码指令,将其分解为一个或多个可以称为微操作的底层硬件指令。微操作被分配给后端进行执行,后端负责监控微操作的数据核实可用,并且在可用的执行单元中执行微操作。微操作执行完成称为退役,执行结果提交并且返回到CPU寄存器里或者写回内存当中。
Intel数学核心函数库MKL包含了高度线程化和矢量化后的函数,以发挥Intel处理器的最优性能。并且它同时具有C和Fortran的API接口,是一套高度优化线程安全的数学例程和函数。MKL-DNN是Intel开源的面向深度学习的函数库。它是高度优化的适用于深度学习和深度神经网络等计算密集型应用的构建模块,适用于Caffe、TensorFlow和Torch等等。它本身由C++实现,同时提供了C++和C语言的API接口,方便Python及Java等高级语言进行调用。
Intel MKL库支持常见的线性代数函数,例如BLAS、LAPACK函数等等。它还支持快速傅里叶变换,例如多维傅立叶变换。还提供了与FFTW相兼容的函数,让FFTW的用户可以用MKL FFT函数代替原有程序的FFT变换,获得更高的性能。MKL还支持矢量数学常用的三角函数、双曲线函数、指数、对数、幂函数等等函数。还有统计学常用的风度函数、差异系数、方差、协方差等等。除此之外,还有样条函数、新预算法、快速泊松解法等等。MKL-DNN对于深度神经网络常用的卷积、池化、规划、激活函数,例如ReLU、Softmax等等都有很好的支持。
MKL-DNN重点面向深度神经网络,它是一个开源项目,在GitHub上可以免费下载使用(github.com/01org/mkl-dnn)。它的函数实现了高度的矢量化和线程化,从而在英特尔平台上获得了最高的性能。对于英特尔各种硬件资源,MKL-DNN都提供了统一的DNN API接口,方便函数的调用。而且MKL-DNN的更新发布速度很快,能够及时的对主流新函数提供支持。MKL-DNN支持的计算和数据操作,包括卷积、池化、规划等等。它能够借助英特尔体系架构处理器的特性,发挥出硬件的最优性能,并且它支持Float32、Int8、Float16、Bfloat16几种数据类型,同时提供了C和C++的API接口,使用起来非常的用户友好。
模型的融合层面,也就是通常提到的Fusion。Fusion的意义在于减少内存或减少计算。
在图执行器层面,还可以通过调整调度机制和修改运行方式开展优化。相关例子如图所示。
在人工智能的很多常用结构里都会经常用到矩阵乘法的计算,通常这部分会比较耗时,所以也是优化的一个重点。计算矩阵乘法的函数之一就是MKL库中的cblas_sgemm,使用单精度实数。另外还有对应双精度实数、单精度浮数和双精度浮数的函数。
其中,Layout表示二维矩阵存储,是按行优先还是按列优先。transa、transb表示矩阵是否需要做转至。mn分别表示矩阵a的行数和列数。nk表示矩阵b的行数和列数。mk分别代表了矩阵c的行数和列数。alpha和beta是计算公式中的两个参数值。
并行化编程是为了使计算机各路线能够并行工作,多道程序能够更全面的利用计算机资源,从而提高系统效率,减少开销。OpenMP是一种常用的并行化编程API,尤其是和多核CPU上的并行程序开发设计。它支持的语言,包括C、C++和Fortran。同时,适用于绝大多数的平台架构和操作系统,比如Linux、MacOS、Windows。它由一组影响运行时行为的编译器指令、库例程和环境变量组成。
在用OpenMP进行程序开发时,并没有什么需要特别关注的地方,因为现如今大多数编译器已经支持了OpenMP。在编程使我们只需要在特定的源代码片段的前面加入OpenMP专用的预编译指令就可以通知编译器将该个程序自动进行并行化处理,并且在必要的时候加入线程同步及通信机制,当编译器选择忽略预处理指令时,或者编译器不支持OpebMP时,程序又退化为一般的通用串行程序,此时代码依然可以正常运行,只是不能利用多线程和多核CPU来加速程序的执行而已。
Code is copied from github.com/BVLC/caffe/blob/master/src/caffe/layers/conv_layer.cpp
Code is copied from github.com/intel/caffe/blob/master/src/caffe/layers/conv_layer.cpp
假共享是对称多处理器系统,简称SMP上的一种常见性的问题。在SMP系统中,每个处理器均有一个本地高速缓存。内存系统必须保质高速缓存的一致性。当不同处理器上的线程修改驻留在同一高速缓存行中的变量时,就会发生假共享,结果将导致高速缓存行无效,并强制执行更新,进而影响系统的性能。示例如图所示。
灰色箭头表示高速缓存行被加载到CPU0和CPU1的Cache当中。在并行执行时,尽管这些线程修改的是不同的变量,分别是红色和蓝色的箭头,但是元素相邻,高速缓存行仍然会无效,并强制内存更新,以维持高速缓存的一致性。这种现象之所以被称为假共享,是因为每个线程并非真正共享相同变量的访问权,访问同一变量或真正共享要求编程是同步结构,以确保有序的数据访问。要确保多个高速缓存中的数据一致性,英特尔遵循的是MESI协议,即修改独占共享无效协议。另外,编译器可以感知假共享。例如当使用优化选项编译下方代码时,编译器会利用现成专有临时变量消除以上的假共享。
从程序员的角度,避免假共享的主要方式是进行代码检查,潜在的假共享主要出现在线程访问全局或动态分配共享数据结构的例程中。
向量化编程是提高算法速度的一种有效方法,简单来说,目标就是生成SIMD指令。向量化能够很好的提升一些数值运算操作,如矩阵乘、矩阵加、矩阵向量乘法等等的速度。因为对于标量来说,一条指令只执行一个数学运算,但是当对矢量进行计算时,一条指令却可以同时执行多个数学运算。随着各行各业对更高性能的需求持续增强,Intel的指令集架构也在不断的演进中,功能也在不断地拓展完善。
英特尔提供了多种寄存器和指令来支持单指令多数据操作,按时间的先后顺序包括支持64位寄存器的MMX系列、支持128位寄存器的SSE系列、支持256位寄存器的AVX系列和支持512位寄存器的AVX512系列。
为了实现代码的向量化主要有以下这几种方法:
Example is from https://software.intel.com/en-us/articles/using-avx-without-writing-avx-code
一个标准的BERT模型行层数有12层,其中的处理运算也是整个模型最耗时的部分,所以这也是我们优化的重点。针对BERT我们采取的主要优化方法是模型的变换。从模型层面来讲,原模型中的OP太多太散,调度上面的开销和内存分配上的开销比较大,所以我们把12个层里所有的OP通过手写C++代码进行了对应函数和功能的实现,并且在实现的过程中,调用了很多高效的Intel MKL核心函数库里的函数,从而提高了计算的效率。并且通过调整内存访问方式,移除了不必要的Transpose操作,也就是矩阵的转置操作。然后通过向TensorFlow注册新OP来实现对手写Bert OP的调用,从而在整体上获得了BERT模型的性能提升。
采取这种优化方法优点在于它可以很快的得到实现与部署并且性能提升非常显著,通过向TensorFlow注册新OP的方式,我们也无需重新编译安装向TensorFlow,只需要加载新OP的动态库,即可对新的Bert OP实现轻松的调用,并且也同时适用于向TensorFlow Serving。但是,这种方法也还存在着一些不足之处。
如何调整内存访问的方式来避免Transpose操作如图所示。
二位矩阵Reshape成三维过后,它的内存访问顺序是按照图中黑色框图从框图左下角开始往右往前延伸,然后框图整体从上往下走的。Reanspose之后,第一二个维度互换,它的内存访问顺序就变成了绿色框图,从左上角向右向下延展,框图整体上市从左向右走的。其实我们并不需要真正调用Transpose转置函数来改变它在内存中的存储方式,只需要把它看成12个矩阵,每一个带768的步长,那么就可以在768的步长之后访问到内存中相应的元素,从而移除了实际上冗余的转置操作,获得了性能上的提升。
模型案例结合Wavenet的Tacotron-2。Tacotron-2是谷歌推出的直接从文本中合成语音的神经网络结构,它结合了Wavenet和Tacotron的优势,不需要任何语法知识即可直接输出文本对应的语音,左图为Tacotron-2的详细架构。
LSTM是一种时间循环神经网络,它可以避免常规RNN梯度消失的问题,适合于处理和预测时间序列中间隔和延迟非常长的时间。计算公式里面的原本的方式是横向按顺序存储这些weights,这样虽然实现起来很简单也比较通俗易懂,但是每次当我们进入网络来计算四条公式的时候,需要4次访问W数组,4次访问U数组。
Intel所采用的优化方式是将这些weights按纵向存储,那么每次我们只需要访问一次W数组机可以对四条公式进行计算。这种方式下就减少了对内存的访问时间,提高了执行效率,性能达到了1.5倍的提升。
课程学习交流
请扫码关注腾讯产业互联网学堂微信公众号