吹拉弹唱


  • Home
  • Archive
  • Categories
  • Tags
  • Books
  •  

© 2022 Kleon

Theme Typography by Makito

Proudly published with Hexo

机器学习系统 2-1 - 推理优化总览

Posted at 2021-02-05Updated at 2021-02-05 机器学习  机器学习 推理优化 

模型推理服务是在业务关键路径上的,其性能足以收到高度重视。如何优化训练后的模型,提高性能,降低资源占用,维持推理精度,优化方法的通用化与自动化,这些是模型推理优化的核心目标。

说起性能优化,软件的性能优化应该是必备功课,通过profiling定位bottleneck,再针对性优化,可以说具备一定程度通用的方法论,但实际业务上也要随机应变。

模型性能的优化也属于软件性能优化,特点是模型的运行调度框架已经确定,并且由社区平衡通用性和性能做了不少的优化。

模型性能的问题主要在低效的计算密集型算子,不合理的子图结构,复杂的动态运行环境,冗余的模型结构和数据宽度。

针对这些问题,模型推理优化方法主要分为:

# 计算图结构优化

主要是清理冗余节点,一般的训练节点,框架在开始运行前会根据输入和输出裁剪无关节点,通常对性能影响不大。

但部分嵌入计算图的节点就很难被去除,Tensorflow引入的控制结构就是是图结构优化点之一,比如训练时需要的随机dropout结构等,用户可能在构建模型时把标记是否是训练的变量加入到计算图,在清理时就需要将对应placeholder转变为常量,提取子图。

# 算子融合

主要是融合子图,比如对于Feature Embedding这类常用的特征预处理API,通常为了灵活性,生成的子图比较复杂,算子数量极多,框架调度算子的开销远远超过算子实际执行的开销。因此可以匹配对应子图,使用一个算子代替,大大提高执行效率。高频模型中Tensorflow引入的循环控制结构也可以被优化,比如LSTM结构。

通常需要匹配识别的子图较复杂,依赖人工发现模式并编写匹配逻辑,基本纯手工,开发验证工作量大,由于引入了控制节点和Dynamic Shape,目前编译优化方法也没有很好的解法。因此,通常只针对成熟模型做此类定制优化,比如Transformer结构,Embedding结构等。

# 硬件计算库

底层硬件开发者出于安全或其他原因考虑,不会开放底层实现细节,也不会开放精细控制接口,但为了迎合市场需求,会推出计算库。

计算库内提供了粗粒度的算子接口,内置了不少汇编层级的优化经验,实现方法至于是if else还是JIT codegen就不得而知了。

计算库内的核心是如何高效利用硬件执行矩阵并行计算,通常基于经验,比如考虑Cache大小,内存带宽,数据位宽等,利用一个“经验性”公式计算出合理的矩阵分块配置,比较重要也比较让人感到tricky的是公式中不时出现的magic number,很有可能是结合profiling得出的经验。

常见的计算库比如CUDA和MKL-DNN,在单个算子优化的基础上,硬件厂家还会提供子图层面的计算优化库,比如TensorRT等。

# 编译优化

编译优化包含一大类方法,也借鉴了上述方法的思路。主要目标是自动化地、通用化地实现图结构优化、算子融合、以及矩阵分块。

和编译器的优化思路类似,不同框架的计算图经过分图,将可编译的子图会被统一转化为编译优化框架的中间表示(IR),再通过一系列的通用的编译优化pass得到优化后的IR,随后codegen生成执行代码。只要能被划分进编译子图,就能通用化地支持图结构优化和算子融合。

最精华的地方在于codegen,主要思路有:

  • 写入人工调优的经验,生成高效的底层执行代码。一般是社区早期这么做,比如Tensorflow的XLA。
  • 借用硬件计算库生成高效代码。硬件厂商这头大象终于反应过来了,毕竟他们对硬件更了解,不用白不用。
  • 把硬件当作黑盒,构建细粒度矩阵分块方法,通过profiling不断调整分块参数,直到收敛到最优解。比较暴力,分块方法的可调参数越多,Tuning(参数搜索)的时间可能越长,不过也有算法加速收敛,比如TVM。
  • 解析法,比如Ployhedral,把矩阵分块过程描述为一般的空间变换方法,而性能优化问题就转化成了搜索最优整数解的过程。愿景很好,但目标函数本身又很难和实际硬件完全对应,搜索的时间也不小。

编译优化方法在学术届内百花齐放,本质上还是新瓶装旧酒,关键还是在于如何把新问题转化为通用的老问题,并解决新的问题定义边界带来的新的约束带来的corner case。

对于主流模型,硬件厂商的人力可以做到全图支持,定制优化的性能让编译优化难以超越,编译优化更多地是在通用化的corner case场景下发挥作用,与手工方法互补。

同时现有的编译优化方法本身也会遇到比如Dynamic Shape的问题,严重影响分图和codegen,亟待解决。好在如MLIR等编译基础设施工作正在不断发展,解决多层IR信息联合优化,复用优化pass,解决共有的问题。

# 模型压缩

除了编译优化方法之外,模型压缩也被用来从算法层面去除降低数据位宽,冗余图结构,大幅并行化模型结构。

  • 量化

模型推理的性能问题有很多是memory bound(受限于内存带宽),尤其是在高度密集计算(大batch)的场景下,同时推理相对于训练不需要很高的数据精度,可以使用量化方法缩减数据位宽,直接降低所需内存带宽,memory bound问题也迎刃而解。同时原来用于计算32位数据(FP32)的计算单元可以配置为计算2个16位数据(FP16)和4个8位数据(INT8),计算性能也直接翻番。

  • 剪枝

深度学习模型并不是要求精确计算,而是计算概率分布,因此在实践中,将模型中权重较小的连接直接置为0或去除,对模型的整体精度影响不大,反而有效降低了总体计算量。

  • 蒸馏

出于直观,可能使用串行结构(数据依赖)模型,导致计算效率不高,或者使用超大模型,模型参数非常多。但模型训练本质上是拟合数据的目标概率分布,那么就可以使用另一个并行模型“学习”串行模型的概率分布,或者使用一个小模型“学习”大模型的概率分布。只要模型精度控制在预期范围内,那么所需的计算量就能大幅下降。

# 推理服务性能优化

这部分和模型关系不大,但也在关键路径上,因此放在这里。服务的性能优化,主要是优化数据链路,比如数据传输协议,Serving框架线程性能,网关性能等。


Share 

 Previous post: 算法基础 - 优先队列 Next post: 机器学习系统 1-11 - 后端硬件系统 

© 2022 Kleon

Theme Typography by Makito

Proudly published with Hexo