深度学习模型转换技术
目前的转换技术在设计思路上主要存在两种差异,一种是直接将模型从现有框架转换为适合目标框架使用的格式,我们在这称此技术为直接转换技术;另外一种是针对深度学习设计一种开放式的文件规范,而主流深度学习框架最终都能实现对这种规范标准的支持,这种技术的代表是开放式神经网络切换框架——ONNX技术。
2.1 直接转换技术
转换器实现模型文件转换的基本原理涉及一下几步:
读取载入A框架生成的模型文件,读取并识别模型网络中的张量数据的类型/格式、运算单元的类型和参数、计算图的结构和命名规范,以及它们之间的其他关联信息。
将第一步识别得到的模型结构和模型参数信息翻译成B框架支持的代码格式,比如B框架指Pytorch时,relu激活层(运算单元)这一信息可翻译为torch.nn.ReLu()。当然,运算单元较为复杂时(特别是带较多参数的情况),可在转换器中封装一个对应的运算单元转换函数来实现B框架的运算单元骨架。
在B框架下保存模型,即可得到B框架支持的模型文件。
转换器如能将现有模型文件直接转换为生产环境支持的格式,这在操作上会给使用者带来很大的便利。但因市面上存在的深度学习框架众多(超过10种),目前还没有一种转换器能够实现所有模型文件之间的转换。我在这列出部分可实现不同框架迁移的模型转换器,如图7所示。更完整的列表请参考:https://github.com/jasonaidm/deep-learning-model-convertor
可以看出,目前既有机构,也有个人开发一些特定场景的模型转换器。其中最有名的模型转换器当属微软于2018年开源的MMdnn框架。下面我将多花点篇幅来介绍这个框架。
图7 不同模型文件的转换器
MMdnn实质上是一套用于转换、可视化深度神经网络模型的综合性解决方案。MMdnn中的「MM」代表模型管理,「dnn」的意思是深度神经网络,它能够通过中间表征格式让训练模型在Caffe、Keras、MXNet、TensorFlow、CNTK、Pytorch和CoreML等深度学习框架之间转换(如图8所示),帮助开发者实现模型在不同框架之间的交互。MMdnn主要有以下特征:
模型文件转换器,不同的框架间转换DNN模型
模型代码片段生成器,生成适合不同框架的代码
模型可视化,DNN网络结构和框架参数可视化
模型兼容性测试(正在进行中)
图8 MMdnn系统目前支持的深度学习框架
需要强调的是,强如背靠微软的MMdnn转换器,也仅支持部分模型的转换,具体原因我们在3.3节有作较为详细阐述。MMdnn在一些ImageNet模型上有做测试(如图9所示),但官方没有提及NLP项目上的测试情况。阅读一些模型转换器文档列出的运算单元支持表发现, MMdnn尚不支持一些较新的运算单元,比如PReLu、Bottleneck、BatchNormalization等,而诸如BiLSTM、Mask等自然语言常用的operators更是缺乏,可以推断,仅仅依靠MMdnn的原始骨架,只能完成小部分的模型转换。
图9 MMdnn在一些ImageNet模型上测试的结果
2.2 ONNX技术
2.2.1简介
ONNX是一种针对机器学习所设计的开放式的文件格式,用于存储训练好的模型。它使得不同的人工智能框架(如Pytorch, MXNet)可以采用相同格式存储模型数据并交互。 ONNX的规范及代码主要由微软,亚马逊 ,Facebook 和 IBM 等公司共同开发,形成强大的深度学习开源联盟,并将源代码托管在Github上(地址: https://github.com/ONNX),谷歌一直在围绕TensorFlow和谷歌云的深度学习开发自己的独立生态,所以暂时不太会加入到这个联盟中来。目前官方支持加载ONNX模型并进行推理的深度学习框架有: Caffe2, Pytorch, MXNet,ML.NET,TensorRT 和 Microsoft CNTK, TensorFlow 也有非官方的支持ONNX,目前处于实验阶段。
ONNX 定义了一种可扩展的计算图模型、一系列内置的运算单元(OP)和标准数据类型。每一个计算流图都定义为由节点组成的列表,并构建有向无环图。其中每一个节点都有一个或多个输入与输出,每一个节点称之为一个 OP。这相当于一种通用的计算图,不同深度学习框架构建的计算图都能转化为它。事实上,上节介绍的一些模型文件转换器其内部实现机制也借用了ONNX技术。目前ONNX支持的框架和基于ONNX的转换器如下图10所示。具体可参考此链接:http://ONNX.ai/supported-tools
图10 ONNX支持的框架和转换器
2.2.2 ONNX结构规范
(此部分内容大部分直译官方文档:https://github.com/onnx/onnx/blob/master/docs/IR.md)
模型结构的主要目的是将元数据(meta data)与图形(graph)相关联,图形包含所有可执行元素。首先,读取模型文件时需使用元数据,实现提供所需的信息,以确定它是否能够执行模型、生成日志消息、错误报告等功能。此外元数据对工具很有用,例如IDE和模型库,它需要它来告知用户给定模型的目的和特征。
每个model具有以下组件:
2.2.2.1 ONNX运算单元集
每个模型必须明确命名运算单元,命名方式依赖于运算单元的功能。运算单元集定义可用的操作符、版本和状态。所有模型都隐式导入默认的ONNX运算单元集。
运算单元集的属性:
2.2.2.2 ONNX运算单元
运算单元定义的属性:
2.2.2.3 ONNX序列化图(Graph)
序列化图由一组元数据字段(metadata),模型参数列表(a list of model parameters,)和计算节点列表组成(a list of computation nodes)。每个计算数据流图被构造为拓扑排序的节点列表,这些节点形成图形,其必须是无循环的。 每个节点代表对运算单元的调用。 每个节点具有零个或多个输入以及一个或多个输出。
图表具有以下属性:
每个图形必须定义其输入和输出的名称和类型,它们被指定为“值信息”结构,具有以下属性:
2.2.2.4 图的命名规范
所有名称必须遵守C标识符语法规则。节点,输入,输出,初始化器和属性的名称被组织到多个命名空间中。在命名空间内,每个给定图形的每个名称必须是唯一的。
2.2.2.5 标准化数据类型
数据类型存在两种官方的ONNX变体,两者在支持的数据类型和支持的运算单元中存在区别。对于支持的数据类型,ONNX定义只识别张量作为输入和输出类型。而经典的机器学习扩展——ONNX-ML,还可识别序列和maps。对于计算图graph和节点node的输入和输出、计算图的初始化,ONNX支持 原始数字、字符串和布尔类型,但必须用作张量的元素。
张量元素类型:
其他规范如Input / Output Data Types、Attribute Types等都在官网上有申明,这里就不再罗列了。
2.2.3 ONNX支持的运算单元(Operator)
ONNX拥有非常明确的、严格的神经网络框架标准,并且拥有非常详细的官方文档。此外,它还支持非常多的运算单元,而且还在高频地增加新的operators。目前官网列出的operators已达134种,具体名单请参考:https://github.com/onnx/onnx/blob/master/docs/Operators.md
2.2.4 新增operator
ONNX支持用户新增operator,以解决特殊场景特殊模型的转换,并给出新增operator的规范步骤:https://github.com/onnx/onnx/blob/master/docs/AddNewOp.md