简介

  • 模型部署相关笔记
  • 模型部署需要的技术栈还是比较复杂的,不仅需要底层并行知识,还需要模型算法知识,不过对于两者的要求都不是很高,只需要能够看的懂最新进展,能够跟着复现就行,模型部署,最重要的还是工程能力

模型有几种形式

  • 模型的形式有很多种,模型部署说到底还是编程任务,使用代码来实现一套函数,只不过这个函数中的参数都被提取出来封装到了一起,称为模型。
  • 也正是因为如此,不同的框架不同的代码生成的模型格式五花八门,从Pytorch到tf到mxnet等训练框架,它们保存的模型各不相同,我们首先要解决的就是统一模型的格式,这里的统一格式并不是指文件的后缀一样就可以了,而是指所要支持的算子要统一。

模型部署是什么

  • 模型部署是指将训练好的机器学习模型部署到生产环境中,以便实际应用中使用。在模型发布之前,需要将模型从训练环境中导出,然后将其部署到生产环境中,通常是作为一个服务或者一个库的形式。

  • 在软件工程中,部署指把开发完毕的软件投入使用的过程,包括环境配置,软件安装等步骤。类似的,对于深度学习模型来说,模型部署指让训练好的模型在特定环境中运行的过程。相比于软件部署,模型部署会面临更多的难题:
    • 运行模型所需要的环境难以配置。深度学习模型通常是由一些框架编写的,例如PyTorch, TensorFlow。由于框架规模,依赖环境的限制,这些框架不适合在手机,开发板等生产环境中安装
    • 深度学习模型的结构通常比较庞大,需要大量的算力才能满足实时运行的需求。模型的运行效率需要优化
  • 因为这些难题的存在,模型部署不能靠简单的环境配置与安装完成。经过工业界和学术界数年的探索,模型部署有了一条流行的流水线:
    • 为了让模型最终能够部署到某一环境上,开发者们可以使用任意一种深度学习框架来定义网络结构,并通过训练确定网络中的参数
    • 之后,模型的结构和参数会被转换成一种只描述网络结构的中间表示,一些针对网络结构的优化会在中间表示上进行
    • 最后,用面向硬件的高性能编程框架(如 CUDA,OpenCL)编写,能高效执行深度学习网络中算子的推理引擎会把中间表示转换成特定的文件格式,并在对应硬件平台上高效运行模型。
  • 这一条流水线解决了模型部署中的两大问题:使用对接深度学习框架和推理引擎的中间表示,开发者不必担心如何在新环境中运行各个复杂的框架;通过中间表示的网络结构优化和推理引擎对运算的底层优化,模型的运算效率大幅提升

如何模型部署

  • 模型部署的过程通常包括以下几个步骤
    • 导出模型: 将训练好的模型导出为可部署的格式,例如PMML, ONNX, TensorFlow等
    • 部署模型: 将导出的模型部署到生产环境中,通常是作为一个服务或者一个库的形式。
    • 测试模型: 在生产环境中对模型进行测试,以确保其能够正常工作,并且输出结果符合预期
    • 监控模型: 在生产环境中对模型进行监控,以便及时发现并解决问题
  • 模型发布是机器学习应用中重要的一环,它可以帮助企业快速将机器学习应用落地,并带来实际的商业价值。

  • 模型的部署方式可以根据具体的应用场景和需求而定,以下是一些常见的模型部署方式
    • WebAPI: 将模型部署为一个Web服务,通过HTTP请求来获取模型预测的结果。可以使用Flask,Django等
    • 嵌入式设备: 将模型部署到嵌入式设备上,例如数梅派,Jetson Nano等。可以使用TensorFlow Lit, Pytorch Mobile等框架来实现
    • 容器化: 将模型打包成docker镜像,然后部署到云服务器上。可以使用Kubernetes, Docker Swarm等容器编排工具来实现
    • 边缘计算: 将模型部署到边缘设备上,例如智能摄像头,智能家居等。可以使用TensorFlow.js, TensorFlow Lite等框架来实现。
  • 无论采用哪种部署方式,都需要考虑模型的性能,安全性,可靠性,可维护性等方面的问题。

模型部署的应用场景

  • 图像识别:将图像识别模型部署到智能摄像头、安防监控系统等设备上,实现人脸识别、车牌识别等功能。
  • 自然语言处理:将自然语言处理模型部署到智能客服、聊天机器人等应用中,实现自然语言理解、情感分析等功能。
  • 推荐系统:将推荐系统模型部署到电商、社交网络等应用中,实现商品推荐、好友推荐等功能。
  • 工业控制:将模型部署到工业生产线上,实现质量检测、故障预测等功能。
  • 金融风控:将模型部署到银行、保险等金融机构中,实现风险评估、欺诈检测等功能。
  • 医疗诊断:将模型部署到医疗设备中,如医学影像诊断、病理分析等应用中,实现疾病诊断、治疗方案推荐等功能。
  • 智能交通:将模型部署到交通系统中,实现交通流量预测、智能路灯等功能。
  • 物联网:将模型部署到物联网设备中,如智能家居、智能城市等应用中,实现环境监测、交通管理等功能。
  • 游戏开发:将模型部署到游戏中,实现智能 NPC、游戏推荐等功能。

模型部署的注意事项

  • 确保模型的正确性:在部署模型之前,需要对模型进行充分的测试,以确保其能够正确地工作,并且输出结果符合预期。
  • 选择适合的部署方式:不同的应用场景需要不同的部署方式,需要根据具体的需求选择适合的部署方式。
  • 考虑性能和资源消耗:在部署模型时需要考虑模型的性能和资源消耗,以确保模型能够在生产环境中高效地运行。
  • 考虑安全性和隐私保护:在部署模型时需要考虑安全性和隐私保护,以确保模型不会被恶意攻击或者泄露用户隐私。
  • 建立监控和反馈机制:在部署模型后,需要建立监控和反馈机制,及时发现并解决模型出现的问题,以确保模型能够持续地稳定运行。

模型部署模型的实现类库

  • Python中有很多方法可以实现模型部署,以下是一些常见的方法:
    • Flask:Flask是一个轻量级的Web框架,可以用来搭建Web API。通过Flask,可以将模型部署为一个Web服务,通过HTTP请求来获取模型预测结果。
    • Django:Django是一个功能强大的Web框架,可以用来搭建Web应用程序。通过Django,可以将模型部署为一个Web应用程序,实现更复杂的业务逻辑。
    • FastAPI:FastAPI是一个高性能的Web框架,可以用来搭建Web API。与Flask相比,FastAPI具有更高的性能和更好的类型注释支持。
    • TensorFlow Serving:TensorFlow Serving是一个专门用于模型部署的框架,可以快速部署TensorFlow模型,并提供高性能的预测服务。
    • ONNX Runtime:ONNX Runtime是一个高性能的推理引擎,可以用于部署ONNX格式的模型。ONNX Runtime支持多种硬件平台和操作系统,包括CPU、GPU、FPGA等。
    • PyTorch Serving:PyTorch Serving是一个专门用于PyTorch模型部署的框架,可以快速部署PyTorch模型,并提供高性能的预测服务。
    • AWS Lambda:AWS Lambda是亚马逊云提供的一种无服务器计算服务,可以用来运行代码。通过AWS Lambda,可以将模型部署为一个无服务器应用程序,实现高可用性和低成本。

模型部署后的性能评价

  • 模型部署后,我们需要对模型的性能进行评价,以确保模型能够在生产环境中高效地运行。以下是一些常见的模型性能评价方法:
    • 响应时间:响应时间是指从接收请求到返回结果所需的时间。在模型部署后,我们需要对模型的响应时间进行评价,以确保模型能够在实时应用中快速响应。
    • 吞吐量:吞吐量是指在单位时间内处理的请求数量。在模型部署后,我们需要对模型的吞吐量进行评价,以确保模型能够在高并发场景下处理大量请求。
    • 准确率:准确率是指模型在测试集上的分类准确率。在模型部署后,我们需要对模型的准确率进行评价,以确保模型在生产环境中能够保持良好的预测性能。
    • 内存占用:内存占用是指模型在运行时所占用的内存大小。在模型部署后,我们需要对模型的内存占用进行评价,以确保模型能够在生产环境中高效地利用资源。
    • CPU和GPU利用率:CPU和GPU利用率是指在模型运行时,CPU和GPU的利用率。在模型部署后,我们需要对CPU和GPU的利用率进行评价,以确保模型能够充分利用硬件资源。

Pytorch

  • 保存为pt文件或者pth文件,pytorch导出的模型有两种方案
    • 第一种是不仅仅包含参数,还包含了模型结构,读取的时候不需要预先建立模型
    • 第二种是仅仅包含参数,读取之前需要将模型建立好,通过pth文件往里面填参数。
  • 但是第一种方案也不是在任何地方都可以直接使用,还是要有模型定义代码,也就是说这两种模型都只能由pytorch使用。

  • pytorch还支持另一种导出
    • 导出成torchscript格式,这种确确实实是将模型结构和参数都保存了,不仅可以用在pytorch,还可以用在C++推理上,不过C++推理依旧需要torchlib,相当于还是离不开torch

ONNX

  • onnx是一套开放的中间格式标准,目前大部分深度学习相关的工具都支持onnx。onnx与训练框架,推理框架都无关
  • onnx模型由三部分组成
    • 节点Node: 就是神经网络中一层
    • 输入Input: 存储了输入矩阵的维度信息
    • 初始化器Initializer: 存储了权重和参数
  • 三种之间互关联,相互依赖,很难修改

  • 在编译器领域,为了支持不同的编程语言和不同的运行平台,提出了一种与编程语言和运行平台无关的中间表达语言称为IR,onnx也可以看作是深度学习领域的IR

推理系统

  • 有了通用的模型格式,深度学习要融入到整个产品中需要设计成一个单独的系统,响应处理外界请求,这就是推理系统的工作。
  • 推理系统需要考虑包括模型管理,服务接口设计,系统检测,系统调度等。推理系统更多的是将推理工作包装成一个服务,供外界使用,推理系统需要考虑的事情
    • 吞吐量
    • 响应效率
    • 扩展性
    • 灵活性

推理引擎

  • 推理引擎主要做的是: 优化模型,实现核心算子,开发目标平台调度引擎。
  • 本来像OpenVINO和TensorRT这种是厂家做出来的,支持的平台也只有它们自己的平台。
  • 随着深度学习大火,各种推理框架都发展了起来。

模型转换

  • 不同的训练框架,如Tensorflow、PyTorch、MindSpore、MXNet、CNTK等,都定义了自己的模型的数据结构,推理系统需要将它们转换到统一的一种数据结构上。开发神经网络交换协议(Open Neural Network Exchange,ONNX)正是为此目的而设计的。ONNX支持广泛的机器学习运算符集合,并提供了不同训练框架的转换器,例如TensorFlow模型到ONNX模型的转换器、PyTorch模型到ONNX模型的转换器等。
  • 模型转换本质上是将模型这种结构化的数据,从一种数据结构转换为另一种数据结构的过程。进行模型转换首先要分析两种数据结构的异同点,然后针对结构相同的数据做搬运;对于结构相似的数据做一一映射;对于结构差异较大的数据则需要根据其语义做合理的数据转换;更进一步如果两种数据结构上存在不兼容,则模型转换无法进行。
  • ONNX的一个优势就在于其强大的表达能力,从而大多数业界框架的模型都能够转换到ONNX的模型上来而不存在不兼容的情况。

  • 模型可以抽象为一种图,从而模型的数据结构可以解构为以下两个要点:
    • 模型拓扑连接:从图的角度来说,就是图的边;从AI模型的角度来说,就是AI模型中的数据流和控制流等。模型数据流和控制流的定义又可以引申出子图的表达形式、模型输入输出的表达形式、控制流结构的表达形式等。比如Tensorflow1.x中的控制流表达为一种有环图,通过Enter、Exit、Switch、LoopCond、NextIteration等算子来解决成环,而ONNX通过Loop,If等算子来表达控制流,从而避免引入了有环,所以在将Tensorflow1.x的控制流模型转化为ONNX模型时,需要将Tensorflow模型中的控制流图结构融合成ONNX的While或者If算子。
    • 算子原型定义:从图的角度来说,就是图的顶点;从AI模型角度来说,就是AI模型中的数据处理节点或者控制流节点。算子原型包括但不限于算子类型、算子输入输出的定义、算子属性的定义等。比如Caffe的slice算子和ONNX的slice算子的语义其实是不一致的,Caffe的slice算子应该映射到ONNX的Split算子,所以在将Caffe模型转换成ONNX模型时,需要将Caffe的Slice算子映射到ONNX的Split算子。比如Tensorflow中的中的FusedBatchNorm算子在Caffe中找不到相同语义的算子,需要将Caffe的BatchNorm算子和Scale算子组合起来才能表达相同的语义。
  • 在完成模型转换之后,通常地,框架会将一些不依赖于输入的工作提前去完成。这些工作包括了如常量折叠、算子融合、算子替换、算子重排等一些优化手段。这些优化手段的概念在前面的章节其实已经提及到,比如在编译器前端阶段,通常也会做常量折叠;在编译器后端阶段,通常会根据后端的硬件支持程度,对算子进行融合和拆分。但是有些优化工作只有在部署阶段才能进行或者彻底进行

模型部署场景

  • 这个问题主要源于中心服务器云端部署和边缘部署两种方式的差异
    • 云端部署常见的模式是模型部署在云端服务器,用户通过网页访问或者 API 接口调用等形式向云端服务器发出请求,云端收到请求后处理并返回结果。
    • 边缘部署则主要用于嵌入式设备,主要通过将模型打包封装到 SDK,集成到嵌入式设备,数据的处理和模型推理都在终端设备上执行。

模型部署方式

  • 针对中心服务器云端部署方式和边缘部署方式,分别有两种不同的部署方案,Service部署和SDK部署。
    • Service 部署:主要用于中心服务器云端部署,一般直接以训练的引擎库作为推理服务模式。
    • SDK 部署:主要用于嵌入式端部署场景,以 C++ 等语言实现一套高效的前后处理和推理引擎库(高效推理模式下的 Operation/Layer/Module 的实现),用于提供高性能推理能力。此种方式一般需要考虑模型转换(动态图静态化)、模型联合编译等进行深度优化。
  SDK 部署 Service 部署
部署环境 SDK 引擎 训练框架
模型语义转换 需要进行前后处理和模型的算子重实现 一般框架内部负责语义转换
前后处理对齐算子 训练和部署对应两套实现,需要进行算子数值对齐 共用算子
计算优化 偏向于挖掘芯片编译器的深度优化能力 利用引擎已有训练优化能力

部署的核心优化指标

  • 部署的核心目标是合理把控成本、功耗、性价比三大要素

  • 成本问题是部署硬件的重中之重,AI 模型部署到硬件上的成本将极大限制用户的业务承受能力。
  • 成本问题主要聚焦于芯片的选型,比如,对比寒武纪 MLU220 和 MLU270,MLU270 主要用作数据中心级的加速卡,其算力和功耗都相对于边缘端的人工智能加速卡MLU220要低。至于 Nvida 推出的 Jetson 和 Tesla T4 也是类似思路,Tesla T4 是主打数据中心的推理加速卡,而 Jetson 则是嵌入式设备的加速卡。对于终端场景,还会根据对算力的需求进一步细分,比如表中给出的高通骁龙芯片,除 GPU 的浮点算力外,还会增加 DSP 以增加定点算力,篇幅有限,不再赘述,主要还是根据成本和业务需求来进行权衡。

  • 在数据中心服务场景,对于功耗的约束要求相对较低;在边缘终端设备场景,硬件的功耗会影响边缘设备的电池使用时长。因此,对于功耗要求相对较高,一般来说,利用 NPU 等专用优化的加速器单元来处理神经网络等高密度计算,能节省大量功耗

  • 不同的业务场景对于芯片的选择有所不同,以达到更高的性价比。 从公司业务来看,云端相对更加关注是多路的吞吐量优化需求,而终端场景则更关注单路的延时需要。在目前主流的 CV 领域,低比特模型相对成熟,且 INT8/INT4 芯片因成本低,且算力比高的原因已被广泛使用;但在NLP或者语音等领域,对于精度的要求较高,低比特模型精度可能会存在难以接受的精度损失,因此 FP16 是相对更优的选择。在 CV 领域的芯片性价比选型上,在有 INT8/INT4 计算精度的芯片里,主打低精度算力的产品是追求高性价比的主要选择之一,但这也为平衡精度和性价比提出了巨大的挑战

部署流程

  • 上面简要介绍了部署的主要方式和场景,以及部署芯片的选型考量指标,接下来以 SDK 部署为例,给大家概括介绍一下 SenseParrots 在部署中的整体流程。SenseParrots 部署流程大致分为以下几个步骤:模型转换、模型量化压缩、模型打包封装 SDK。

模型转换

  • 模型转换主要用于模型在不同框架之间的流转,常用于训练和推理场景的连接。目前主流的框架都以 ONNX 或者 caffe 为模型的交换格式,SenseParrots 也不例外。SenseParrots 的模型转换主要分为计算图生成和计算图转换两大步骤,另外,根据需要,还可以在中间插入计算图优化,对计算机进行推理加速(诸如常见的 CONV/BN 的算子融合)
  • 计算图生成是通过一次 inference 并追踪记录的方式,将用户的模型完整地翻译成静态的表达。在模型 inference 的过程中,框架会记录执行算子的类型、输入输出、超参、参数和调用该算子的模型层次,最后把 inference 过程中得到的算子信息和模型信息结合得到最终的静态计算图
  • 在计算图生成之后与计算图转换之前,可以进行计算图优化,例如去除冗余 op,计算合并等。SenseParrots 原生实现了一批计算图的精简优化 pass,也开放接口鼓励用户对计算图进行自定义的处理和优化操作
  • 计算图转换是指分析静态计算图的算子,对应转换到目标格式。SenseParrots 支持了多后端的转换,能够转换到各个 opset 的 ONNX、原生 caffe 和多种第三方版本的 caffe。框架通过算子转换器继承或重写的方式,让 ONNX 和 caffe 的不同版本的转换开发变得更加简单。同时,框架开放了自定义算子生成和自定义算子转换器的接口,让第三方框架开发者也能够轻松地自主开发实现 SenseParrots 到第三方框架的转换

模型量化压缩

  • 终端场景中,一般会有内存和速度的考虑,因此会要求模型尽量小,同时保证较高的吞吐率。除了人工针对嵌入式设备设计合适的模型,如 MobileNet 系列,通过 NAS(Neural Architecture Search) 自动搜索小模型,以及通过蒸馏/剪枝的方式压缩模型外,一般还会使用量化来达到减小模型规模和加速的目的。
  • 量化的过程主要是将原始浮点 FP32 训练出来的模型压缩到定点 INT8(或者 INT4/INT1) 的模型,由于 INT8 只需要 8 比特来表示,因此相对于 32 比特的浮点,其模型规模理论上可以直接降为原来的 1/4,这种压缩率是非常直观的。 另外,大部分终端设备都会有专用的定点计算单元,通过低比特指令实现的低精度算子,速度上会有很大的提升,当然,这部分还依赖协同体系结构和算法来获得更大的加速
  • 量化的技术栈主要分为量化训练(QAT, Quantization Aware Training)和离线量化(PTQ, Post Training Quantization), 两者的主要区别在于,量化训练是通过对模型插入伪量化算子(这些算子用来模拟低精度运算的逻辑),通过梯度下降等优化方式在原始浮点模型上进行微调,从来调整参数得到精度符合预期的模型。离线量化主要是通过少量校准数据集(从原始数据集中挑选 100-1000 张图,不需要训练样本的标签)获得网络的 activation 分布,通过统计手段或者优化浮点和定点输出的分布来获得量化参数,从而获取最终部署的模型。 两者各有优劣,量化训练基于原始浮点模型的训练逻辑进行训练,理论上更能保证收敛到原始模型的精度,但需要精细调参且生产周期较长;离线量化只需要基于少量校准数据,因此生产周期短且更加灵活,缺点是精度可能略逊于量化训练。 实际落地过程中,发现大部分模型通过离线量化就可以获得不错的模型精度(1% 以内的精度损失,当然这部分精度的提升也得益于优化策略的加持),剩下少部分模型可能需要通过量化训练来弥补精度损失,因此实际业务中会结合两者的优劣来应用
  • 量化主要有两大难点:一是如何平衡模型的吞吐率和精度,二是如何结合推理引擎充分挖掘芯片的能力。 比特数越低其吞吐率可能会越大,但其精度损失可能也会越大,因此,如何通过算法提升精度至关重要,这也是组内的主要工作之一。另外,压缩到低比特,某些情况下吞吐率未必会提升,还需要结合推理引擎优化一起对模型进行图优化,甚至有时候会反馈如何进行网络设计,因此会是一个算法与工程迭代的过程

模型打包封装SDK

  • 实际业务落地过程中,模型可能只是产品流程中的一环,用于实现某些特定功能,其输出可能会用于流程的下一环。因此,模型打包会将模型的前后处理,一个或者多个模型整合到一起,再加入描述性的文件(前后处理的参数、模型相关参数、模型格式和版本等)来实现一个完整的功能。因此,SDK 除了需要一些通用前后处理的高效实现,对齐训练时的前后处理逻辑,还需要具有足够好的扩展性来应对不同的场景,方便业务线同学扩展新的功能。可以看到,模型打包过程更多是模型的进一步组装,将不同模型组装在一起,当需要使用的时候将这些内容解析成整个流程(pipeline)的不同阶段(stage),从而实现整个产品功能
  • 另外,考虑到模型很大程度是研究员的研究成果,对外涉及保密问题,因此会对模型进行加密,以保证其安全性。加密算法的选择需要根据实际业务需求来决定,诸如不同加密算法其加解密效率不一样,加解密是否有中心验证服务器,其核心都是为了保护研究成果

模型推理

  • 实际上,用训练好的模型对新数据进行预测,在机器学习工程上有一个更专业的名词叫做 推理(inference)。
  • 通过训练集构建的神经网络对新输入数据进行预测,就是推理。

  • 一般情况下,推理又分为: 静态推理与动态推理
    • 静态推理很好理解,我们通过集中对批量数据进行推理,并将结果存放在数据表或者数据库中。当有需要的时候,再直接通过查询来获得推理结果。
    • 而动态推理一般表示我们将模型部署到服务器中。当有需要时,通过向服务器发送请求来获得模型返回的预测结果。与静态推理不同的是,动态推理的过程是实时计算的,而静态推理是提前批量处理好的。
  • 当然,静态和动态推理各有优缺点。静态推理适合于对大批量数据进行处理,因为动态推理面对大数据量时非常耗时。但是静态推理无法实时更新,而动态推理的结果是即时计算结果