AI模型互通的新开放生态系统ONNX介绍与实践

2017/12/29 张永超 机器学习

导读

不久前,Facebook与微软联合推出了一种开放式的神经网络切换格式——ONNX,它是一种表征深度学习模型的标准,可以实现模型在不同框架之间进行迁移。

ONNX 的全称为“Open Neural Network Exchange”,即“开放的神经网络切换”。顾名思义,该项目的目的是让不同的神经网络开发框架做到互通互用。目前,ONNX 已经得到 PyTorch、Caffe2、CNTK、MXNet 以及包括 Intel、ARM、Huawei、高通、AMD、IBM 等芯片商的支持。

按照该项目的设想,ONNX 的推出主要是为了解决当下AI生态系统的关键问题之一:开发框架的碎片化。现在有大量的不同框架用来构建和执行神经网络,还有其他的机器学习系统,但是它们之间不尽相同,而且无法协同运行。

AI的开发技术主要是深度学习神经网络,而神经网络训练和推理通常采用一种主流的深度学习框架。目前,主流的框架有如下这些:

  • TensorFlow(Google)
  • Caffe/Caffe2(Facebook)
  • CNTK(Microsoft)
  • MXNet(Amazon主导)
  • PyTorch(Facebook主导)

不同的框架有不同的优势,但是框架之间的互通性并不好,甚至没有互通性。开发AI模型时,工程师和研究人员有很多的AI框架可以选择,但是并不能“一次开发,多处直接使用”。面对不同的服务平台,往往需要耗费大量的时间把模型从一个开发平台移植到另一个,让开发者苦不堪言。因此增强框架之间的互通性变的非常重要,例如:

  • 情况1:某个框架的某个模型不管是准确度还是性能都非常好,但是和软件整体的架构不相符;
  • 情况2:由于框架A表现很好,用它训练了一个神经网络,结果生产环境中使用的是框架B,这就意味着你可能需要将框架A训练的神经网络移植到框架B支持的格式上。

但是如果使用ONNX,就可以消除这种尴尬,例如ONNX可以导出用PyTorch构建的训练模型,并将它与Caffe2结合起来用于推理(详细教程)。这对于研究阶段使用PyTorch构建模型,正式环境使用Caffe2的结构非常合适,且省去了模型移植的时间成本和人力成本等。

目前支持ONNX的框架如下:

详细支持情况,可查看http://onnx.ai/getting-started中各项详细说明。

ONNX 技术概括

根据官方介绍中的内容,ONNX 为可扩展的计算图模型、内部运算器(Operator)以及标准数据类型提供了定义。每个计算图以节点列表的形式组织起来,构成一个非循环的图。节点有一个或多个的输入与输出。每个节点都是对一个运算器的调用。图还会包含协助记录其目的、作者等元数据信息。运算器在图的外部实现,但那些内置的运算器可移植到不同的框架上。每个支持 ONNX 的框架将在匹配的数据类型上提供这些运算器的实现。

概括的说就是,ONNX是在跟踪分析AI框架所生成的神经网络在运行时是如何执行的,然后会利用分析的信息,创建一张可以传输的通用计算图,即符合ONNX标准的计算图。虽然各个框架中的计算表示法不同,但是生成的结果非常相似,因此这样的方式也是行得通的。

那么ONNX标准的推出,为研究者、给开发者带来的意义是什么呢?

ONNX标准的意义

  • 首当其冲就是框架之间的互通性

    开发者能够方便的在不同框架之间切换,为不同的任务选择最优的工具。往往在研发阶段需要的模型属性和产品阶段是不同的,而且不同的框架也会在特定的某个属性上有所优化,例如训练速度、对网络架构的支持、是否支持移动设备等等。如果不在研发阶段更换框架或者将研发阶段的模型进行移植,可能造成项目延迟等。ONNX解决了这个难题,使用ONNX支持的AI框架,则可以灵活选择AI框架,方便的进行模型切换。

  • 共享优化

    芯片制造商不断地推出针对神经网络性能有所优化的硬件,如果这个优化频繁发生,那么把优化整合到各个框架是非常耗时的事。但是使用ONNX标准,开发者就可以直接使用优化的框架了。

这次没有Google

Google 是 TensorFlow 框架的核心主导者,而 TensorFlow 目前是业界的主流,是 GitHub 最受欢迎、生态体系健全度较高的框架。但是目前看来,TensorFlow 官方并没有支持ONNX。但是社区已经有了非官方的ONNX版TensorFlow,详情可参考 https://github.com/tjingrant/onnx-tf

如何使用

以下是根据ONNX官方教程,进行的实践和遇到的具体问题。Getting Started。 ONNX 的使用总体分为两步:

  1. 模型的导出:将使用支持ONNX标准的AI框架训练的模型导出为ONNX格式;

  2. 模型的导入:将ONNX格式的模型导入到支持ONNX的另一个AI框架中。

这里我选择模型导出的AI框架是:PyTorch,导出的模型是Apple Core ML所支持的mlmodel格式。

1. ONNX 安装

本地环境:

  • macOS High Sierra v10.13.1
  • Python 2.7
  • Xcode 9.1 command line tools
  • conda 4.3.30

ONNX的安装支持Binaries、Docker、Source三种方式,由于我本地常使用Anaconda,因此这里我使用Binaries方式 进行安装:

conda install -c conda-forge onnx

安装完成后如下显示:

2. coremltools 安装

coremltools 的安装较为简单,如果本地环境无误,运行如下指令:

pip install coremltools

即可完成安装。

3. PyTorch 安装

PyTorch ONNX支持的版本目前仅支持从源码Master分支安装,因此如果你本机的环境中已经安装了PyTorch,可能需要重新安装,安装方法可见:From Source

需要注意的是,在进行安装时,需要将指令 MACOSX_DEPLOYMENT_TARGET=10.9 CC=clang CXX=clang++ python setup.py install 中的MACOSX_DEPLOYMENT_TARGET部分修改为当前你设备安装的macOS版本号。

截止发稿时,PyTorch已经更新了官网版本,最新版本为 0.3.0,已经支持了ONNX,因此你可以直接使用官网的方式安装了。【注意:如果本地已经安装过了,使用此方法安装后,需要重新安装TorchVision (conda install torchvision -c pytorch),否则部分功能可能不能使用。】

4. 模型算法选择

这里选择的是PyTorch中的一个示例模型算法Super-resolution 进行实践。

Super-resolution(超分辨率)技术是指由低分辨率(LR, low-resolution)的图像或图像序列恢复出高分辨率(HR, high-resolution)的技术,其原理可参考图像超分辨率技术(Image Super Resolution)中的介绍。 Super-resolution(超分辨率)核心思想是用时间分辨率(同一场景的多帧图像序列)换成更高的空间分辨率,实现时间分辨率向空间分辨率的转换,论文地址

在本文中,将使用一个具有虚拟输入的小型超分辨率模型。

本文主要是讲解如何使用ONNX进行模型转换,对于具体的技术原理可参考文中链接。

5. 模型构建

了解了相关的软件安装和模型之后,我们开始构建Super-resolution模型网络,此部分内容借鉴PyTorch examples中的实现。

# Super Resolution model definition in PyTorch
import torch.nn as nn
import torch.nn.init as init

class SuperResolutionNet(nn.Module):
	def __init__(self, upscale_factor, inplace=False):
    	super(SuperResolutionNet, self).__init__()

    	self.relu = nn.ReLU(inplace=inplace)
    	self.conv1 = nn.Conv2d(1, 64, (5, 5), (1, 1), (2, 2))
    	self.conv2 = nn.Conv2d(64, 64, (3, 3), (1, 1), (1, 1))
    	self.conv3 = nn.Conv2d(64, 32, (3, 3,), (1, 1), (1, 1))
    	self.conv4 = nn.Conv2d(32, upscale_factor ** 2, (3, 3), (1, 1), (1, 1))
    	self.pixel_shuffle = nn.PixelShuffle(upscale_factor)

    	self._initialize_weights()

	def _initialize_weights(self):
    	init.orthogonal(self.conv1.weight, init.calculate_gain('relu'))
    	init.orthogonal(self.conv2.weight, init.calculate_gain('relu'))
    	init.orthogonal(self.conv3.weight, init.calculate_gain('relu'))
    	init.orthogonal(self.conv4.weight)


	def forward(self, x):
    	x = self.relu(self.conv1(x))
    	x = self.relu(self.conv2(x))
    	x = self.relu(self.conv3(x))
    	x = self.pixel_shuffle(self.conv4(x))
    	return x

以上模型网络构件完成后,我们就可以创建一个模型了,例如:

torch_model = SuperResolutionNet(upscale_factor=3)

此模型的结构如下:

SuperResolutionNet(
  		(relu): ReLU()
  		(conv1): Conv2d (1, 64, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
  		(conv2): Conv2d (64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
 		(conv3): Conv2d (64, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
  		(conv4): Conv2d (32, 9, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
  		(pixel_shuffle): PixelShuffle(upscale_factor=3)
)

如果你不想自己再去训练模型,可以下载已经构建好的预训练模型权重文件pretrained model weights,进行模型的构建,例如:

import torch.utils.model_zoo as model_zoo

# 加载预训练模型权重文件
model_url = 'https://s3.amazonaws.com/pytorch/test_data/export/superres_epoch100-44c6958e.pth'
batch_size = 1    # 这里是一个随机数(这了仅仅是为了演示使用,实际项目中需要调整)

# 使用权重文件初始化模型
torch_model.load_state_dict(model_zoo.load_url(model_url))

# 设置训练模式为False,因为我们仅使用正向传播的方式
torch_model.train(False)

训练后,得到的模型结构同上。

6. 模型导出为ONNX格式

上面通过构建模型的过程,我们已经得到了模型,为了能够将此模型使用在Core ML框架中,首先需要将此模型导出为ONNX标准格式的模型文件。关于PyTorch中如何导出ONNX格式的模型文件,可以参考torch.onnx中的说明和示例。

简单的说,在PyTorch中要导出模型是通过跟踪其工作的方式进行的。要导出模型需要调用 torch.onnx._export() 函数,此函数将会记录模型中的运算符用于计算输出的轨迹,另外此函数需要一个输入张量 x,可以是一个图像或一个随机张量,只要它是正确的大小即可。

import torch.onnx
from torch.autograd import Variable

# 张量 `x` 大小确定
x = Variable(torch.randn(batch_size, 1, 224, 224), requires_grad=True)

# 导出模型
torch_out = torch.onnx._export(
	torch_model,   # 模型对象
	x,     # 模型输入(或多元输入的元组) 
	"onnx-model/super_resolution.onnx",  # 保存文件的路径(或文件对象) 
	export_params=True)  # 是否存储参数(True:将训练过的参数权重存储在模型文件中;False:不存储)

torch_out 是执行模型后的输出。通常情况下可以忽略这个输出,但是在这里我们将使用它来验证在Core ML中运行导出的模型是否和此值具有计算相同的值。

7. 转换ONNX格式到Core ML支持的mlmodel格式

7.1 安装转换工具onnx-coreml

首先我们要安装能够将ONNX标准格式的模型转换为Apple Core ML格式的工具onnx-coreml。安装指令如下:

pip install  onnx-coreml

安装完成后,就可以使用此工具将ONNX标准格式的模型转换为Apple Core ML格式了。

7.2 加载ONNX格式模型文件

在开始之前,需要先使用ONNX加载之前导出的ONNX格式模型文件到对象中:

import onnx

model = onnx.load('super_resolution.onnx')
7.3 转换模型到CoreML

由于我们安装了ONNX 到Core ML的转换工具,因此我们可以直接使用其中的 convert() 函数转换模型:

import onnx_coreml

cml = onnx_coreml.convert(model)
print type(cml)
cml.save('coreml-model/super_resolution.mlmodel')

这里的cml是格式转换后的coreml model对象,调用其save()方法即可将转换后的对象存储文件,即得到Core ML支持的模型文件。

有了这个mlmodel格式的模型文件后,我们就可以将其应用在 Core ML中进行推理了,这里不再进行此部分的描述,具体使用方法可参考Apple官方给出的mlmodel模型文件相关的使用示例,Integrating a Core ML Model into Your App

总结

目前,ONNX所支持的AI框架和工具还较少,整个项目还处于初级阶段,但是已经能够看出,ONNX的推出无疑是对整个AI研究和开发领域极大的福祉。不远的将来,当大多数的AI框架和工具都支持ONNX标准的时候,就不会发生为了选择生产环境的AI框架而重新学习、为了能够生产环境应用模型而耗时移植模型等等类似的事件了。减少了开发人员消耗在移植过程中的时间,增加了钻研算法和开发更令人兴奋的AI应用的时间,相信每个开发者和研究者都盼望着这个时期的到来。

参考资料

Search

    欢迎下载我们的APP

    Table of Contents