Article

2018年4月25日

遠藤です。

先日、ニューラルネットワークをフレームワーク間でやり取りするフォーマットである NNEF と ONNX を紹介いたしました。今回のブログ記事では、それらのうちの ONNX を実際に利用してみて、実際の使用感をレポートしたいと思います。

使用したコードについて

今回の実験で使用したコードは、すべて以下のリポジトリにコミットされています。

fixstars / blog / source / onnx_sample / onnx – Bitbucket

null

上記のコードを実行する際に使用した Docker コンテナのビルドスクリプトは、以下のとおりです。nvidia-docker  を使って実行してください。

fixstars / blog / source / onnx_sample / docker / Dockerfile – Bitbucket

FROM nvidia/cuda:9.1-cudnn7-devel # install dependent Ubuntu packages RUN apt-get update -y && \ apt-get install -y –no-install-recommends \ build-essential \ ca-certificates \ cmake \ git \ python3-dev \ python3-pip \ \ libgoogle-glog-dev \ libgtest-dev \ libiomp-dev \ libleveldb-dev \ liblmdb-dev \ libopencv-dev \ libopenmpi-dev \ libsnappy-dev \ libprotobuf-dev \ openmpi-bin

各フレームから ONNX への出力 (エクスポート)

今回試したのは以下の4つのフレームワークで、それぞれについてスクリプトファイルを作成しました。

  • Caffe2
  • PyTorch
  • CNTK
  • Chainer

各スクリプトでは、 (1) モデルの読み込み、 (2) ONNX モデルへの変換、 (3) 変換された ONNX モデルの検査を行っていて、最終的にモデルを ONNX ファイルに書き出します。それぞれの実験結果を次にまとめます。

Caffe2 to ONNX

Caffe Model Zoo より CaffeNet を ONNX に変換した例です。変換中に若干ワーニングが出てしまっているものの、正常に変換処理が完了しました。

root@0e3e7fb05598:/yendo/codes/tech-research/nnframework/onnx# time python3 caffe2onnx.py --predict in/caffenet/predict_net.pb --init in/caffenet/init_net.pb -o out/test.onnx
loading predict_net.pb
loading init_net.pb
converting to ONNX
WARNING: Logging before InitGoogleLogging() is written to STDERR
W0306 05:32:26.226122   189 conv_pool_op_base.h:653] You are hitting a case where Caffe's legacy padding calculation is hit. This leads to inefficient and sometimes incorrect results. We are keeping this behavior for backward compatibility, but you are strongly recommended to move away from it.
W0306 05:32:26.226179   189 conv_pool_op_base.h:653] You are hitting a case where Caffe's legacy padding calculation is hit. This leads to inefficient and sometimes incorrect results. We are keeping this behavior for backward compatibility, but you are strongly recommended to move away from it.
WARNING:caffe2.python.onnx.frontend:Converting legacy padding to explict padding.
WARNING:caffe2.python.onnx.frontend:Converting legacy padding to explict padding.
WARNING:caffe2.python.onnx.frontend:Converting legacy padding to explict padding.
checking converted model
saving ONNX model

real    0m46.375s
user    0m33.760s
sys     0m12.040s

PyTorch to ONNX

PyTorch に定義されている VGG-16 with Batch Normalization を変換した例です。こちらも若干のワーニングが発生しましたが、正常に変換処理が完了しました。

root@0e3e7fb05598:/yendo/codes/tech-research/nnframework/onnx# time python3 pytorch2onnx.py -o out/vgg16bn.onnx
Downloading: "https://download.pytorch.org/models/vgg16_bn-6c64b313.pth" to /root/.torch/models/vgg16_bn-6c64b313.pth
100.0%
converting to ONNX
checking converted model
[libprotobuf WARNING google/protobuf/io/coded_stream.cc:537] Reading dangerously large protocol message.  If the message turns out to be larger than 1073741824 bytes, parsing will be halted for security reasons.  To increase the limit (or to disable these warnings), see CodedInputStream::SetTotalBytesLimit() in google/protobuf/io/coded_stream.h.
[libprotobuf WARNING google/protobuf/io/coded_stream.cc:78] The total number of bytes read was 553537618

real    1m11.513s
user    0m39.516s
sys     0m15.656s

CNTK to ONNX

実験では、まずは公開されている ResNet-152AlexNet を変換しようとしましたが、下記のエラーが発生しました。原因は未対応のレイヤが使われているということで、ImageNet を分類するモデルは同様の問題を踏んでしまうことが分かりました。

root@0e3e7fb05598:/yendo/codes/tech-research/nnframework/onnx# time python3 cntk2onnx.py -i in/ResNet152_ImageNet_CNTK.model -o out/resnet152.onnx
converting to ONNX
Traceback (most recent call last):
  File "cntk2onnx.py", line 15, in <module>
    model.save(args.output, format=C.ModelFormat.ONNX)
  File "/usr/local/lib/python3.5/dist-packages/cntk/internal/swig_helper.py", line 69, in wrapper
    result = f(*args, **kwds)
  File "/usr/local/lib/python3.5/dist-packages/cntk/ops/functions.py", line 1504, in save
    return super(Function, self).save(filename, format.value)
  File "/usr/local/lib/python3.5/dist-packages/cntk/cntk_py.py", line 2028, in save
    return _cntk_py.Function_save(self, *args)
RuntimeError: Node 'Combine: Output('ce', [], []), Output('errs', [], []), Output('top5Errs', [], []), Output('z', [#, ], [1000]) -> Output('ce', [], []), Output('errs', [], []), Output('top5Errs', [], []), Output('z', [#, ], [1000])': Unsupported node.

[CALL STACK]
[0x7fc8894dc099]                                                       + 0x88d099
[0x7fc88970f3d6]    CNTK::CNTKToONNXHelper::  CreateNode  (std::shared_ptr<CNTK::Function> const&,  ONNXIR::Graph*,  std::unordered_map<std::shared_ptr<CNTK::Function>,ONNXIR::Node*,std::hash<std::shared_ptr<CNTK::Function>>,std::equal_to<std::shared_ptr<CNTK::Function>>,std::allocator<std::pair<std::shared_ptr<CNTK::Function> const,ONNXIR::Node*>>>&,  std::unordered_map<CNTK::Variable,ONNXIR::Node*,std::hash<CNTK::Variable>,std::equal_to<CNTK::Variable>,std::allocator<std::pair<CNTK::Variable const,ONNXIR::Node*>>>&,  std::unordered_map<CNTK::Variable,CNTK::Variable,std::hash<CNTK::Variable>,std::equal_to<CNTK::Variable>,std::allocator<std::pair<CNTK::Variable const,CNTK::Variable>>> const&) + 0x12e6
[0x7fc88970f81b]    CNTK::CNTKToONNXHelper::  Copy  (std::shared_ptr<CNTK::Function> const&,  ONNXIR::Graph*) + 0x17b
[0x7fc88970fb75]    CNTK::CNTKToONNX::  CreateModel  (std::shared_ptr<CNTK::Function> const&) + 0x135
[0x7fc8897290e6]    CNTK::ONNXFormat::  Save  (std::shared_ptr<CNTK::Function> const&,  std::__cxx11::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t>> const&) + 0x36
[0x7fc8894e0f6d]    CNTK::Function::  Save  (std::__cxx11::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t>> const&,  CNTK::ModelFormat) + 0x3d
[0x7fc88a217bfb]                                                       + 0x1b7bfb
[0x4e9b7f]          PyCFunction_Call                                   + 0x4f
[0x53dbbb]          PyEval_EvalFrameEx                                 + 0x6edb
[0x540199]
[0x53c1d0]          PyEval_EvalFrameEx                                 + 0x54f0
[0x5416ea]          PyEval_EvalCodeEx                                  + 0x88a
[0x4ebe37]
[0x5c1797]          PyObject_Call                                      + 0x47
[0x53920b]          PyEval_EvalFrameEx                                 + 0x252b
[0x5406df]
[0x53c1d0]          PyEval_EvalFrameEx                                 + 0x54f0
[0x540199]
[0x540e4f]          PyEval_EvalCode                                    + 0x1f
[0x60c272]
[0x60e71a]          PyRun_FileExFlags                                  + 0x9a
[0x60ef0c]          PyRun_SimpleFileExFlags                            + 0x1bc
[0x63fb26]          Py_Main                                            + 0x456
[0x4cfeb1]          main                                               + 0xe1
[0x7fc897f6b830]    __libc_start_main                                  + 0xf0
[0x5d6049]          _start                                             + 0x29


real    0m10.880s
user    0m11.380s
sys     0m1.796s

そこでネットワークを変えて CIFAR-10 向けの ResNet-20 を試してみたところ、未対応レイヤの問題を踏まずに実行できることが分かったので試してみました。

root@0e3e7fb05598:/yendo/codes/tech-research/nnframework/onnx# time python3 cntk2onnx.py -i in/ResNet20_CIFAR10_CNTK.model -o out/resnet20_cifar10.onnx
converting to ONNX
checking converted model
Traceback (most recent call last):
  File "cntk2onnx.py", line 19, in <module>
    onnx.checker.check_model(onnx_model)
  File "/usr/local/lib/python3.5/dist-packages/onnx/checker.py", line 32, in checker
    proto.SerializeToString(), ir_version)
onnx.onnx_cpp2py_export.checker.ValidationError: Field 'type' of attr is required but missing.

==> Context: Bad node spec: output: "Parameter5454" name: "Parameter5454" op_type: "Constant" attribute { name: "value" t { dims: 1 dims: 10 data_type: FLOAT float_data: -0.070361428 float_data: -0.357194 float_data: 0.37219584 float_data: 0.47041565 float_data: 0.071708754 float_data: -0.092652842 float_data: -0.015438936 float_data: -0.084205344 float_data: -0.074094325 float_data: -0.22061794 } } doc_string: "" domain: ""

real    0m2.108s
user    0m1.876s
sys     0m0.348s

ONNX への変換処理自体はうまく通っているように見えますが、変換したモデルを検査すると、ONNX モデルに問題があるというエラーが出てしまいました。この件について CNTK 開発元に問い合わせたところこれは Known Issue だとのことなので、将来の CNTK リリースで改善されると期待されます。

Chainer to ONNX

Chainer に定義されている ResNet-152 を ONNX に変換しました。一切のワーニングやエラーなく、正常に ONNX への変換処理が完了しました。

root@0e3e7fb05598:/yendo/codes/tech-research/nnframework/onnx# time python3 chainer2onnx.py -o out/resnet152.onnx
Now loading caffemodel (usually it may take few minutes)
converting to ONNX
checking converted model

real    0m28.132s
user    0m23.660s
sys     0m7.384s

実験結果としては、それぞれのネットワークから ONNX にネットワークを出力することが確認できました。CNTKだけ、既知の問題が発生しているようですので、今後の改善に期待です。

ONNX モデルを用いた各フレームワークでの推論 (インポート)

今回試したのは以下の4フレームワークです。それぞれのフレームワークに対し、 ONNX Model Zoo より VGG-19 のネットワークと、 Caffe2 からエクスポートした CaffeNet の両方を用いて推論を実行させました。

  • Caffe2
  • CNTK
  • MXNet
  • TensorFlow

今回の実験では、以下のかわいらしい(筆者の実家の)猫の写真を、それぞれのネットワークを用いて認識しました。

Caffe2 の実験結果

まずは Model Zoo の VGG-19 を用いた際の認識結果です。見事、61% の確率で tabby cat と認識されました!識別結果2位も猫なので、両方合わせると非常に高い確率で猫であると認識されました。

root@91b3c88b45be:/yendo/codes/tech-research/nnframework/onnx# python3 inference_caffe.py -i in/jo7ueb_logo.jpg -m zoo/vgg19/model.onnx
loading image ...
/usr/local/lib/python3.5/dist-packages/skimage/transform/_warps.py:84: UserWarning: The default mode, 'constant', will be changed to 'reflect' in skimage 0.15.
  warn("The default mode, 'constant', will be changed to 'reflect' in "
loading ONNX model ...
running inference ...
[libprotobuf WARNING google/protobuf/io/coded_stream.cc:537] Reading dangerously large protocol message.  If the message turns out to be larger than 1073741824 bytes, parsing will be halted for security reasons.  To increase the limit (or to disable these warnings), see CodedInputStream::SetTotalBytesLimit() in google/protobuf/io/coded_stream.h.
[libprotobuf WARNING google/protobuf/io/coded_stream.cc:78] The total number of bytes read was 574674688
/usr/local/lib/python3.5/dist-packages/caffe2/python/onnx/backend.py:992: UserWarning: This version of onnx-caffe2 targets ONNX operator set version 3, but the model we are trying to import uses version 4.  We will try to import it anyway, but if the model uses operators which had BC-breaking changes in the intervening versions, import will fail.
  warnings.warn("This version of onnx-caffe2 targets ONNX operator set version {}, but the model we are trying to import uses version {}.  We will try to import it anyway, but if the model uses operators which had BC-breaking changes in the intervening versions, import will fail.".format(cls._known_opset_version, imp.version))
[libprotobuf WARNING google/protobuf/io/coded_stream.cc:537] Reading dangerously large protocol message.  If the message turns out to be larger than 1073741824 bytes, parsing will be halted for security reasons.  To increase the limit (or to disable these warnings), see CodedInputStream::SetTotalBytesLimit() in google/protobuf/io/coded_stream.h.
[libprotobuf WARNING google/protobuf/io/coded_stream.cc:78] The total number of bytes read was 574674688
[libprotobuf WARNING google/protobuf/io/coded_stream.cc:537] Reading dangerously large protocol message.  If the message turns out to be larger than 1073741824 bytes, parsing will be halted for security reasons.  To increase the limit (or to disable these warnings), see CodedInputStream::SetTotalBytesLimit() in google/protobuf/io/coded_stream.h.
[libprotobuf WARNING google/protobuf/io/coded_stream.cc:78] The total number of bytes read was 574674688
n02123045 tabby, tabby cat: 0.6092502474784851
n02123159 tiger cat: 0.37426939606666565
n02124075 Egyptian cat: 0.012377521023154259
n02127052 lynx, catamount: 0.0022683446295559406
n03958227 plastic bag: 0.00035553876659832895

次に Caffe2 からエクスポートした CaffeNet を用いた際の認識結果です。こちらは Top-1 と Top-2 の順序が変わっているものの、やはり非常に高い確率で猫として認識されています。

root@91b3c88b45be:/yendo/codes/tech-research/nnframework/onnx# python3 inference_caffe.py -i in/jo7ueb_logo.jpg -m out/test.onnx
loading image ...
/usr/local/lib/python3.5/dist-packages/skimage/transform/_warps.py:84: UserWarning: The default mode, 'constant', will be changed to 'reflect' in skimage 0.15.
  warn("The default mode, 'constant', will be changed to 'reflect' in "
loading ONNX model ...
running inference ...
n02123159 tiger cat: 0.482316792011261
n02123045 tabby, tabby cat: 0.3712765872478485
n02124075 Egyptian cat: 0.12604202330112457
n02127052 lynx, catamount: 0.01987113431096077
n02129604 tiger, Panthera tigris: 0.0001537091302452609

どちらのモデルでも、正しく猫を猫として認識してくれました!

CNTK の実験結果

まずは VGG-19 による認識結果です。同じモデルなので、当然のように同じ結果が出ています。各項目の確率の値までほぼ一致していることが分かります。

root@91b3c88b45be:/yendo/codes/tech-research/nnframework/onnx# python3 inference_cntk.py -i in/jo7ueb_logo.jpg -m zoo/vgg19/model.onnx
loading image ...
/usr/local/lib/python3.5/dist-packages/skimage/transform/_warps.py:84: UserWarning: The default mode, 'constant', will be changed to 'reflect' in skimage 0.15.
  warn("The default mode, 'constant', will be changed to 'reflect' in "
loading ONNX model ...
Selected GPU[0] GeForce GTX TITAN X as the process wide default device.
running inference ...
n02123045 tabby, tabby cat 0.6092488169670105
n02123159 tiger cat 0.374269962310791
n02124075 Egyptian cat 0.01237753126770258
n02127052 lynx, catamount 0.0022683446295559406
n03958227 plastic bag 0.0003555387374944985

次は CaffeNet を用いた認識結果ですが、大まかな順位は同じような結果が出ているものの、各項目の確率の値が大きく変化していることが分かりました。

root@91b3c88b45be:/yendo/codes/tech-research/nnframework/onnx# python3 inference_cntk.py -i in/jo7ueb_logo.jpg -m out/test.onnx
loading image ...
/usr/local/lib/python3.5/dist-packages/skimage/transform/_warps.py:84: UserWarning: The default mode, 'constant', will be changed to 'reflect' in skimage 0.15.
  warn("The default mode, 'constant', will be changed to 'reflect' in "
loading ONNX model ...
Selected GPU[0] GeForce GTX TITAN X as the process wide default device.
running inference ...
n02123159 tiger cat 0.6376021504402161
n02123045 tabby, tabby cat 0.13328386843204498
n02124075 Egyptian cat 0.12346789985895157
n02127052 lynx, catamount 0.10223167389631271
n02123394 Persian cat 0.002654271200299263

どちらのモデルでも正しく猫と認識されましたが、 CaffeNet の挙動に疑問が残る結果となりました。

MXNet の実験結果

まずは VGG-19 による認識結果です。こちらは、Caffe2/CNTK と同様に、確率の値までほぼ一致しました。

root@91b3c88b45be:/yendo/codes/tech-research/nnframework/onnx# python3 inference_mxnet.py -i in/jo7ueb_logo.jpg -m zoo/vgg19/model.onnx
loading image ...
/usr/local/lib/python3.5/dist-packages/skimage/transform/_warps.py:84: UserWarning: The default mode, 'constant', will be changed to 'reflect' in skimage 0.15.
  warn("The default mode, 'constant', will be changed to 'reflect' in "
loading ONNX model ...
running inference ...
n02123045 tabby, tabby cat 0.6092513203620911
n02123159 tiger cat 0.37426936626434326
n02124075 Egyptian cat 0.012377566657960415
n02127052 lynx, catamount 0.0022683527786284685
n03958227 plastic bag 0.00035553969792090356

CaffeNet を用いた認識結果も、Caffe2 を用いて推論した時と確率の値がほぼ一致しました。

root@91b3c88b45be:/yendo/codes/tech-research/nnframework/onnx# python3 inference_mxnet.py -i in/jo7ueb_logo.jpg -m out/test.onnx
loading image ...
/usr/local/lib/python3.5/dist-packages/skimage/transform/_warps.py:84: UserWarning: The default mode, 'constant', will be changed to 'reflect' in skimage 0.15.
  warn("The default mode, 'constant', will be changed to 'reflect' in "
loading ONNX model ...
running inference ...
n02123159 tiger cat 0.4823160767555237
n02123045 tabby, tabby cat 0.37127742171287537
n02124075 Egyptian cat 0.12604232132434845
n02127052 lynx, catamount 0.019871143624186516
n02129604 tiger, Panthera tigris 0.00015371007611975074

MXNet によって推論を行ったところ、Caffe2 と計算結果がほぼ一致することが確認できました。

TensorFlow の実験結果

結論から言うと、TensorFlow でも Caffe2/MXNet とほぼ同じ結果が得られました。

VGG-19 による認識結果です。

root@91b3c88b45be:/yendo/codes/tech-research/nnframework/onnx# python3 inference_tensorflow.py -i in/jo7ueb_logo.jpg -m zoo/vgg19/model.onnx
loading image ...
/usr/local/lib/python3.5/dist-packages/skimage/transform/_warps.py:84: UserWarning: The default mode, 'constant', will be changed to 'reflect' in skimage 0.15.
  warn("The default mode, 'constant', will be changed to 'reflect' in "
loading ONNX model ...
[libprotobuf WARNING google/protobuf/io/coded_stream.cc:537] Reading dangerously large protocol message.  If the message turns out to be larger than 1073741824 bytes, parsing will be halted for security reasons.  To increase the limit (or to disable these warnings), see CodedInputStream::SetTotalBytesLimit() in google/protobuf/io/coded_stream.h.
[libprotobuf WARNING google/protobuf/io/coded_stream.cc:78] The total number of bytes read was 574674688
2018-03-08 22:29:27.055207: I tensorflow/core/platform/cpu_feature_guard.cc:137] Your CPU supports instructions that this TensorFlow binary was not compiled to use: SSE4.1 SSE4.2
/usr/local/lib/python3.5/dist-packages/onnx_tf/backend.py:677: UserWarning: Unsupported kernel_shape attribute by Tensorflow in Conv operator. The attribute will be ignored.
  UserWarning)
WARNING:tensorflow:From /usr/local/lib/python3.5/dist-packages/onnx_tf/backend.py:961: calling softmax (from tensorflow.python.ops.nn_ops) with dim is deprecated and will be removed in a future version.
Instructions for updating:
dim is deprecated, use axis instead
running inference ...
n02123045 tabby, tabby cat 0.6092506647109985
n02123159 tiger cat 0.3742689788341522
n02124075 Egyptian cat 0.0123775415122509
n02127052 lynx, catamount 0.0022683569695800543
n03958227 plastic bag 0.0003555379807949066

CaffeNet による認識結果です。

root@91b3c88b45be:/yendo/codes/tech-research/nnframework/onnx# python3 inference_tensorflow.py -i in/jo7ueb_logo.jpg -m out/test.onnx
loading image ...
/usr/local/lib/python3.5/dist-packages/skimage/transform/_warps.py:84: UserWarning: The default mode, 'constant', will be changed to 'reflect' in skimage 0.15.
  warn("The default mode, 'constant', will be changed to 'reflect' in "
loading ONNX model ...
2018-03-08 22:32:44.154263: I tensorflow/core/platform/cpu_feature_guard.cc:137] Your CPU supports instructions that this TensorFlow binary was not compiled to use: SSE4.1 SSE4.2
/usr/local/lib/python3.5/dist-packages/onnx_tf/backend.py:677: UserWarning: Unsupported kernel_shape attribute by Tensorflow in Conv operator. The attribute will be ignored.
  UserWarning)
WARNING:tensorflow:From /usr/local/lib/python3.5/dist-packages/onnx_tf/backend.py:961: calling softmax (from tensorflow.python.ops.nn_ops) with dim is deprecated and will be removed in a future version.
Instructions for updating:
dim is deprecated, use axis instead
running inference ...
n02123159 tiger cat 0.4823186695575714
n02123045 tabby, tabby cat 0.3712737560272217
n02124075 Egyptian cat 0.12604300677776337
n02127052 lynx, catamount 0.019871098920702934
n02129604 tiger, Panthera tigris 0.0001537106145406142

実験結果としては、それぞれのフレームワークで ONNX のインポートを行い、正しくかわいい猫の認識ができました。CNTK における CaffeNet の例を除き、基本的に同一モデル同一出力となることが確認でき、フレームワーク間の差は基本的には見られないことが分かりました。

CNTK で CaffeNet の出力結果が変わった件については、十分な調査はしていないのですが、おそらく LRN (Local Response Normalization) の実装に差があるのではないかと推測しています。

ONNX を用いたモデルの出力と推論が簡単にできることを、実際に確かめることができました。ONNX を用いることで、フレームワークの選択肢がデプロイ先の環境に引きずられることなく、使いたい好きなフレームワークを使うことができるようになります。皆さんもぜひ使ってみてください!

About Author

yasunori.endo

Leave a Comment

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください

Recent Comments

Social Media