TI开发版Tensorflow转onnx模型
由 Facebook 和 Microsoft 创建的开放格式神经网络交换格式 ONNX,是一种用于表示机器学习模型。
TF2ONNX 将 TensorFlow 模型转换为 ONNX,从而将 TensorFlow 训练后的模型引入支持 ONNX 的系统。
然而 TF2ONNX 有一些局限(v1.5.5,即开始开发 TFLite2ONNX 的时候),例如不支持 TensorFlow 2.0 或量化。将_易变_的 TensorFlow 模型转换为 ONNX 的工作量很大。并且,由于量化在深度学习部署中扮演着越来越重要的角。
另一方面,TFLite 的模型表示相对稳定,并且由 Google 统一维护的 TensorFlow 和 TFLite 的模型转换器足够健壮。该转换器通过包括批量归一化折叠和激活函数融合在内的图转换简化了 TensorFlow 模型。还可以处理在 TensorFlow Quantization-aware Training(QAT)期间生成的 FakeQuantWithMinMaxVars 节点。
此外,尽管某些模型由 TensorFlow 构建,但仅发布时只有 TFLite 格式的模型,例如 Google
MediaPipe 。ONNX 生态系统无法使用这类模型。
TFLite2ONNX 可以将 TFLite 模型转换为 ONNX。截至 v0.3,TFLite2ONNX 支持 TensorFlow 2.0(感谢 TFLite 转换器)和量化。本文介绍了 TFLite2ONNX 为缩小 TFLite 与 ONNX 模型表示之间的语义差异的背景和实现。
数据布局语义转换
最明显的差距是数据布局问题—— TFLite 模型是 NHWC 格式,而 ONNX 是NCHW,在本文中被称为布局语义差异。
问题与 TF2ONNX
TFLite 的数据布局格式在文档或模型表示中均未提及,但在 TFLite 转换器(TensorFlow 模型需要为 NHWC)和内核有隐式协议。ONNX 则在算子表示和文档(由算子表示生成)中明确声明它使用NCHW。
TF2ONNX 将内部算子和张量转换为 NCHW 数据布局,并允许用户通过 --inputs-as-nchw
选择是否需要将图的输入和输出转换为 NHWC 数据布局。默认情况(未指定 NCHW)会插入 Transpose 算子以桥接 NHWC 和 NCHW 子图。上面的图2是一个使用 TF2ONNX 将 MobileNetV2 TensorFlow模型转换为 ONNX 的实例。(有关 TF2ONNX 处理数据布局的更多描述,请参见 GitHub issue。)
在 TFLite2ONNX 的开发过程中,我们尝试了两种方法:
基于转换的方法—— v0.1启用,v0.3 删除。
基于传播的方法—— v0.2引入并作为默认方法。
基于转换的方法
一个关于布局语义差异的事实是,某些算子具有隐式数据布局,如 Conv;而其他则不是,如 Add。
TFLite2ONNX 的基于转置的方法在算子有布局语义差异的地方插入一个转置模式。转置模式是用一个 Transposetensorflow版本选择 算子将源布局(TFLite)和目标的布局(ONNX)连接起来。
例如,将 TFLite 模式 ⟨ ℎ ⟩→[ ]⟨Datanhwc⟩→[Conv] 转换为 ⟨ ℎ ⟩→[ ]→⟨ ℎ ⟩→[ ]⟨Datanhwc⟩→[Transpose]→⟨Datanchw⟩→[Conv]。(在这篇文章中,⟨ ⟩⟨TensorName⟩ 和 [ ][Operator] 分别表示张量和算子。图3是转换MobileNetV2 的第一个 Conv 的示例。
图3:通过 TFLite2ONNX 的基于转置方法转换的 ONNX 模型
使用这种方法,我们只需要处理一组有限的算子,例如 Conv 和 Pooling。其他的算子和张量转换都是平凡的——没有布局语义上的差异。
基于传播的方法
尽管基于转换的方法可以处理布局语义差异,但由于添加了太多的算子和张量(即转换模式),因此生成的 ONNX 模型太大且复杂。基于传播的方法可以在整个图中传播布局语义差异来解决这个问题。
默认情况下(对于大多数情况),对于给定的图,某些张量具有隐式布局语义,例如直接连接到 Conv 的张量,而其他张量则没有,例如 Abs 和 Add。后着的对布局是透明的,这意味着连接到算子的所有张量都必须具有相同的布局语义或不具有这种语义。
因此,当布局透明的算子连接到具有隐式布局张量的算子时,透明算子的所有张量都具有与连接这两个算子的张量相同的布局语义。这便是传播的含义。
例如,在转换 TFLite 图(省略了kernel和 bias) ⟨ ℎ ⟩→[ ]→⟨ ℎ ⟩→[ ]→⟨ ?⟩⟨Anhwc⟩→[Conv]→⟨Bnhwc⟩→[Abs]→⟨C?⟩ 到 ONNX 时,张量 ⟨ ℎ ⟩⟨Anhwc⟩ 变成 ⟨ ℎ ⟩⟨Anchw⟩ ,⟨ ℎ
⟩⟨Bnhwc⟩ 变成 ⟨ ℎ ⟩⟨Bnchw⟩。因此 [ ][Abs] 的输出⟨ ⟩⟨C⟩ 应该与其输入 ⟨ ⟩⟨B⟩ 具有相同的格式。基于传播的方法会将 ⟨ ⟩⟨B⟩ 的格式传播给 ⟨ ⟩⟨C⟩。因此我们得到 ONNX 图 ⟨ ℎ ⟩→[ ]→⟨ ℎ ⟩→[ ]→⟨ ℎ ⟩⟨Anchw⟩→[Conv]→⟨Bnchw⟩→[Abs]→⟨Cnchw⟩,这其中未引入其他算子或张量。
在布局传播中,如果张量是 activations,则布局变换会置换张量的形状(即 ONNX 中的 value info),如果是权重的数据(即 ONNX 中的 initializer),还要转换数据。
在实践中,算子分为四类(如图5所示):
Implicit:算子在布局语义上有分歧,例如 Conv 。它们是布局语义差异的来源。
Transparent:对布局不敏感的算子,例如 Abs。如果任何张量具有布局语义差异,则将其传播到连接到此类算子的所有张量。
Attribute:可以想 Transparent 那样传播布局语义差异的算子,但需要特殊处理处理敏感属性,例如 Concat 的 axis 属性。传播后需要额外通过以调整这些属性。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论