欢迎关注我的公众号 [极智视界],获取我的更多经验分享
大家好,我是极智视界,本文来谈谈 AI推理框架,是谈谈AI发展系列的第二篇。
本文是我写的谈谈 AI 发展系列的第二篇,另外篇章可以自行查阅:
- 谈谈 AI 发展第一篇:AI 训练框架 ==> 访问方式:链接;
- 谈谈 AI 发展第三篇:AI 训练算力 ==> 敬请期待;
- 谈谈 AI 发展第四篇:AI 推理算力 ==> 敬请期待;
- 谈谈 AI 发展第五篇:AI 编译框架 ==> 敬请期待;
时光斗转星移,AI 飞速发展。
满打满算,我真正进入到 AI 这个行业差不多五年的样子,这段时间其实是正处于 AI 框架和 AI 算法本身快速发展的黄金阶段。也很有幸见证了一些 “古早” 的框架、算法,以及从 “古早” 到 “现代” 快速进化的过程,感慨良多。我决定在这几篇文章中谈谈我这个阶段对于 AI 的一些基础设施,如框架、算力、算法等的思考、感受和想法,作为一种输出,也作为一种记录,可能几年后回过头再来看,会别有一番滋味。
本文所讨论的推理框架可能会涉及英伟达的 TensorRT、NCNN、Tengine 以及一些国产硬件厂商的配套推理引擎。在众多的推理引擎中,英伟达的 TensorRT 可谓是翘楚中的翘楚,标杆中的标杆。标杆到什么程度呢,举个例子,咱们国产 AI 芯片厂商的新贵天数智芯、登临,他们的推理引擎都是完全对标 TensorRT 的,意思是一些接口、推理流程的组织方式都是力求和 TensorRT 保持一致,目的是为了借用英伟达的生态,降低用户层的迁移成本。
和训练框架一样,推理框架同样生态繁多,但当你接触过的框架较多之后,就应该能够发现他们之间的一些共同点:推理流程都是一致的。我画了下面这个图来进行展示和说明,这里的每一个模块的重要性都很高。拿前端解析来说就很难,因为训练框架太多了,同一个模型可能会用不同的框架进行训练,那就需要支持从不同的框架往 ONNX 或者往你自定义的模型结构进行转换和收敛,这个过程是复杂的。这个复杂性一方面来自于不同框架之间的区别,算子粒度、算子描述、算子参数等可能都会有所区别;另一方面来自于 ONNX 本身,ONNX 本身具有一些问题,比如 ONNX opset 版本之间的不统一,比如胶水算子比较多导致对部署不友好等。接着是模型优化,模型优化是推理引擎中最重要的模块,里面的内容也为最多。对于模型优化,我觉得大致可以分为上层优化和底层优化两个部分。上层优化或者又可称为图优化、IR 优化,包括像耳熟能详的量化、算子融合、部署不友好算子的替换等都属于这个层级;而底层优化会更加多的涉及软硬件协同的优化,比如会借助指令集的加速做一些向量化的实现,比如会更多考虑访存方面的问题来调节算子的调度,比如采用循环拆分来更好地使用并行计算等。正是由于模型优化这一块过于复杂,过于博大精深,所以衍生出了编译框架来对专门对这一块进行优化,对于编译框架后面我会专门写文章进行讨论。一个推理框架的性能怎么样,往往在于它模型优化这一块做的怎么样,如果模型优化这一块做的足够好,那么整个推理框架的性能也会比较突出。在完成了前端模型解析和模型优化后,会进入到常规的模型推理流程,即 前处理 -> 模型推理 -> 后处理。在实际业务中,对于推理框架来说,并不会在前处理和后处理中做太丰富的支持,前处理和后处理模块大多会由用户来主导,比如会使用一些如 OpenCV、npp 之类的通用库来进行处理。一些急于推广的推理框架,为了降低适配难度以及提高整体 pipeline 的性能,另有些情况是需要自己实现一些常用算子以适配自家的芯片,这样可能会在自己的推理引擎中以自定义算子 plugin 的形式集成一些前后处理的典型算子,比如 yolo、比如 resize。
推理框架按使用场景、使用类型来说,大致可以分为两类,一类是自用的,一类是通用的。显然,自用的大多其实是芯片厂商的推理框架,为了让自家的芯片可用,所以推出了适配自家芯片的推理框架。自用的推理框架在市场上的占用率应该是占主导的,其中为代表的肯定还是 TensorRT,另外还有像英特尔的 OpenVino 以及我国各国产芯片厂商的 sdk。自用推理框架的特点十分明显,即在自家的芯片上适配的很好而且效率很高,但不同芯片之间不能通用。自用推理框架的这个毛病让用户很是痛苦,拿我来说,我至少接触和开发过 TensorRT、Openvino、昇腾的推理框架、寒武纪的推理框架、算能的推理框架、登临的推理框架、曙光的推理框架、昆仑的推理框架、海思的推理框架、全志的推理框架等,很多时候都会觉得麻烦。而通用的推理框架就是为了解决这个痛点,通用的推理框架中,NCNN 主要还是面向移动端,覆盖的通用范畴并不够算广;而 Tengine 则明显后劲不足,推理框架大一统方面可能最有希望的还是如 TVM 的编译框架了。所以从某种角度来说,对于 TVM 的期望,其实跟对 ONNX 的期望是一样的。
对于 TensorRT 来说,很多同学可能会有这种观点,由于 TensorRT 和英伟达 GPU 的强绑定,因为英伟达 GPU 的流行,所以 TensorRT 自然也就流行。我倒觉得这种观点有点以偏概全,以 TensorRT 为代表的软件栈和 GPU 硬件之间的关系更像是相辅相成,相互成就。有一种更加科学的说法,叫做软硬件协同,意思是单飞并不好,组团价更高。TensorRT 是推理框架中的神,在我多年的使用过程中,并没有发现它明显的缺点,反而是越用越好用。在 TensorRT 中,构建模型的方式主要有三种,分别是 ONNX Parser、TF Parser、API (Python or C++),而我会习惯用 C++ API 的形式来构建模型。和大多数推理框架一样,TensorRT 的模型支持序列化和反序列化,序列化的意思是将 TensorRT 的 IR 保存为一个称为 Plan 的离线模型,便于实际工程部署,而反序列化的意思是加载这个 Plan 离线模型并还原为 TensorRT IR 的过程,所以模型的序列化和反序列化在流程上要有则必然成对出现。当然,TensorRT 完全可以跳过序列化和反序列化的过程,直接使用 TensorRT IR 来进行推理。这一点在曙光 DCU 的推理引擎 MIGraphX 中的示例工程中也有所体现,它的做法就是直接加载 ONNX 模型,然后就做推理了,当然这并不是好的做法或者说并不是正常的做法。
对于英伟达来说,它在推理领域更加强的原因不止于 TensorRT。配套 TensorRT,往下还有更加成熟的 CUDA,往上还有考虑整体 Pipeline 性能优化的 DeepStream,配合 NvProfiler、Nsight 性能分析工具等,这里的每一个拿出来都很能打,况且都是互相配合着的,一整套东西自成体系。
国产的推理引擎在市场上的众多推理框架中一定占据着重要戏份,国产推理引擎的繁荣很大程度上得益于芯片热。国产推理框架的上手难度其实普遍较高,上手难度以华为昇腾最甚,这一点接触过昇腾的同学应该有所了解。昇腾的开发难度高很大程度上是因为昇腾把更加多底层优化开放出来所导致,你可以很轻松地在网上搜到很多关于昇腾 CANN 的教学教程。说到 CANN 就不得不说昇腾的算子开发,昇腾的算子开发方式主要有 DSL 和 TIK,由于整个推理体系是基于 TVM 的,所以算子开发的方式也和 TVM 的类似,做了计算和调度的分离。所以不管是 DSL 还是 TIK,都会有计算和调度的概念,区别在于在 DSL 中只需要关注计算而把调度隐藏,而在 TIK 中既要关心计算也要关心调度。调度涉及硬件特性,所以比较难写,这也是为什么说 DSL 属于入门级的,TIK 属于高手级的。说到昇腾参考 TVM,其实这个现象不止于昇腾,咱们国产推理引擎的设计大多都会有个自己的参考,下面列出了一些我的了解。
对于寒武纪,比较有特色的可能是 BANG C,它的定位就跟英伟达的 CUDA C 类似。所以在寒武纪中你要自定义算子的话,一般就需要用 BANG C 来实现,这一点很像在 TensorRT 中自定义 Plugin 的时候需要去写 CUDA (当然这不是绝对,TensorRT 中还可以使用组合基础算子的方式来自定义 Plugin)。寒武纪做的比较好的地方在于,寒武纪的产品有卡有盒子,它的推理框架是通的,这对于不同场景的之间的推理覆盖切换成本很低。
在移动端,NCNN 应该算相当出色。NCNN 的模型转换做的很好,模型结构的设计遵循清晰、易于修改的原则,不像 ONNX 那样必须要用代码才能去修改网络的节点,而是比如使用一个文档编辑器就能方便地修改网络的参数,这一点我觉得做的很好。NCNN 的模型转换之所以好,是因为它一方面对于前端训练框架支持的够全,另一方面对于模型也支持的够全。特别的,对于模型解析的老大难问题 – Pytorch 的模型解析来说,NCNN 就提供了两套方案:(1) Pytorch -> ONNX (常规款);(2) PNNX。PNNX 是 NCNN 新提供的替代 Pytorch 导出 ONNX 的针对 Pytorch 模型解析的工具,做法是通过 Torch 来做桥梁,通过解析 Torch 转换到 NCNN IR。很有特色的地方在于整套工具采用 C++ 来实现,这在众多的模型解析工具中也是比较少见,因为训练框架大多还是用 Python 的。当然,NCNN 也有一些明显的局限性,正是 NCNN 致力于服务移动端,而手机一般只有一个摄像头,所以 NCNN 在框架架构设计之初就天然省略了 Batch 维度,只是保留了 CHW 的三维数据结构,这在一众推理框架中其实比较少见,当然这对于更加通用一些的多 Batch 场景,NCNN 的适用性就大大降低。Tengine 是基于 NCNN 更加新一些的推理框架,曾经我还是对 Tengine 抱有一些希望,并也向其仓库贡献了一些代码,但最近来看,明显后劲不足。
还有一些不温不火的推理框架没有提及,比如 MNN、TNN、OpenPPL 等,另外还有一些看起来会比较小众,比如地平线的工具链,工具这些大都不是因为不够优化,而是可能生态不够。还有一些过于碎片化可能只关注某个模块,比如商汤的量化,其实也很优秀。还有像 OnnxRuntime、TF-Lite 之类的我就不多说了,实际工程中应该用的人比较少吧。
这些应该就是我目前对于 AI 推理框架的一些理解,如有不切实际之处,还望不吝指正。
好了,以上分享了 谈谈 AI 发展第二篇:AI 推理框架。希望我的分享能对你的学习有一点帮助。
后面几篇的预告:
- 谈谈AI发展第三篇:AI 训练算力
- 谈谈AI发展第四篇:AI 推理算力
- 谈谈AI发展第五篇:AI 编译框架