このブログは、株式会社フィックスターズのエンジニアが、あらゆるテーマについて自由に書いているブログです。
遠藤です。
先日、ニューラルネットワークをフレームワーク間でやり取りするフォーマットである 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
今回試したのは以下の4つのフレームワークで、それぞれについてスクリプトファイルを作成しました。
各スクリプトでは、 (1) モデルの読み込み、 (2) ONNX モデルへの変換、 (3) 変換された ONNX モデルの検査を行っていて、最終的にモデルを 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 に定義されている 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
実験では、まずは公開されている ResNet-152 や AlexNet を変換しようとしましたが、下記のエラーが発生しました。原因は未対応のレイヤが使われているということで、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 に定義されている 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だけ、既知の問題が発生しているようですので、今後の改善に期待です。
今回試したのは以下の4フレームワークです。それぞれのフレームワークに対し、 ONNX Model Zoo より VGG-19 のネットワークと、 Caffe2 からエクスポートした CaffeNet の両方を用いて推論を実行させました。
今回の実験では、以下のかわいらしい(筆者の実家の)猫の写真を、それぞれのネットワークを用いて認識しました。
まずは 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
どちらのモデルでも、正しく猫を猫として認識してくれました!
まずは 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 の挙動に疑問が残る結果となりました。
まずは 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 でも 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 を用いることで、フレームワークの選択肢がデプロイ先の環境に引きずられることなく、使いたい好きなフレームワークを使うことができるようになります。皆さんもぜひ使ってみてください!
keisuke.kimura in Livox Mid-360をROS1/ROS2で動かしてみた
Sorry for the delay in replying. I have done SLAM (FAST_LIO) with Livox MID360, but for various reasons I have not be...
Miya in ウエハースケールエンジン向けSimulated Annealingを複数タイルによる並列化で実装しました
作成されたプロファイラがとても良さそうです :) ぜひ詳細を書いていただきたいです!...
Deivaprakash in Livox Mid-360をROS1/ROS2で動かしてみた
Hey guys myself deiva from India currently i am working in this Livox MID360 and eager to knwo whether you have done the...
岩崎システム設計 岩崎 満 in Alveo U50で10G Ethernetを試してみる
仕事の都合で、検索を行い、御社サイトにたどりつきました。 内容は大変参考になりま...
Prabuddhi Wariyapperuma in Livox Mid-360をROS1/ROS2で動かしてみた
This issue was sorted....