avatar
tkat0.dev
Published on

いまさらONNXを調べた(v1.4.1)

ONNXは 1 年以上前から使ってるもののちゃんと仕様しらないな〜、だったので調べた。

https://twitter.com/_tkato_/status/1076019658300346368

とか言ってた。

  • 以下、ONNXv1.4.1 を対象
  • ONNX のドキュメントは GitHub にまとまっている
    • Learn about ONNX spec
    • この記事は上記のドキュメントから必要なところをまとめたもの
  • ONNX 自体は Protobuf で定義した仕様で、ランタイム非依存。
  • ONNX と ONNX-ML があるけど、主に DeepLearning 向けに使いたいので ONNX について調べた

onnx.proto

IR の仕様は以下で説明されている。 https://github.com/onnx/onnx/blob/v1.4.1/docs/IR.md

これらは proto ファイルに実体が定義されているので、あわせてみるとわかりやすい。

IR の定義の.proto
https://github.com/onnx/onnx/blob/v1.4.1/onnx/onnx.proto3

例えばModelProtoここのモデル定義 同様に proto とドキュメントが対応付けできる。

また、ONNX でつかえる行列データは Tensor 型があるが、以下の定義。
https://github.com/onnx/onnx/blob/v1.4.1/onnx/onnx.proto3#L281-L314

ポピュラーなのは uint8~float64 かな。 任意精度はなさそうだけど Issue に上がっていた気がする。

続いて IR の上に構築される Operator や OperatorSet の定義
https://github.com/onnx/onnx/blob/v1.4.1/onnx/onnx-operators.proto3

Operator は Conv とか FC とか。OperatorSet はそれらをまとめてバージョン付したもの。 デフォルトでは ai.onnx の opset が使える。これで足りない場合はユーザーが同様に定義できる。 詳細は以下。
https://github.com/onnx/onnx/blob/v1.4.1/docs/IR.md#operator-sets

protobuf は、コンパイルすることで C++や Python から呼び出せるクラスになる。
int64 version = 2; のような右辺は field number で、後方互換性を保ってフィールドを追加したり名前を変えたりするための識別子。

Versioning

https://github.com/onnx/onnx/blob/v1.4.1/docs/Versioning.md

特に道具として ONNX を使おうとするとこのバージョンの理解は必要。 フレームワーク A から B へ ONNX 経由で変換したいときに、バージョン違いで変換できないこともあるので。

IR, model, opset でそれぞれバージョンを定義。使う側として意識するのは opset かな。

  • IR specification
    • Float などの型定義など、Operator などを構築する IR そのもの
    • 最新は v4 で BFLOAT16 型の追加など
  • version of model
    • 学習済みモデルを ONNX として配布する場合のバージョンの付け方(任意)
  • version of operator set
    • conv, fc など operator の集合を定義して、そのバージョン管理をする
    • 例えば opset v9 から XXX は導入されたとかそういう
    • モデルは、自身が依存する opset とそのバージョンを明示する(例: このモデルは ai.onnx v9 の opset を使いますよ)
    • opset 自体もユーザーが定義できる
    • version だけでなく status 属性(EXPERIMENTAL or STABLE)もあり、STABLE になったら変更してはいけない

あとは ONNX 自体も semantic versioning でバージョン付されている。

opset のバージョンが違う場合に変換するための Version Converter がある。 だけど、opset v9 には対応していないのかな。issue にも上がっていた気がした。 これらのツールは基本 C++で書いてあるけど Python で呼び出せるように wrap されている。

ONNX Model Opset Version Converter
https://github.com/onnx/onnx/blob/v1.4.1/docs/OpsetVersionConverter.md

ONNX Optimizer

ONNX Optimizer
https://github.com/onnx/onnx/blob/master/docs/Optimizer.md

コンパイラのパスと同じようなもの。 ONNX のバックエンドに実装されるであろう最適化の中でもバックエンド比依存のものは IR でやっていきましょうというよくあるもの。

汎用的な pass は以下に組み込まれている https://github.com/onnx/onnx/tree/master/onnx/optimizer/passes

fuse_bn_into_conv などある。グラフレベルの最適化、ある程度 ONNX でやっちゃっても良さそう。

pass は Python ではかけないみたい。Python でかけると色々やりたいことあるんだけどな。

TypeDenotation

onnx/TypeDenotation.md at master · onnx/onnx
https://github.com/onnx/onnx/blob/master/docs/TypeDenotation.md

onnx/MetadataProps.md at master · onnx/onnx
https://github.com/onnx/onnx/blob/master/docs/MetadataProps.md

モデルのメタデータをどうやって保持するか。これ面白い。

denotation 属性に RGB とか、NHCW のどれかとかを格納するみたい。値が取りうる範囲 0-255 とかも。

モデルの前処理後処理の情報もメタデータとして ONNX に書くのは良いですね。やろうと思えばソフトウェアで処理できる。


これで ONNX の上を歩けるようになったぞ〜。万が一バックエンド実装する機会が来たら、また詳細を調べようか。