物体検知モデルを実用向けに速度チューニングする

ARISE analyticsの秋元です。

画像処理システムの開発では様々な画像処理技術をシステムに組み込んでいきますが、システムの要件に応じて適切なモデルや実行方法を選択する必要があります。今回は画像処理技術の一つである物体検知のモデルを実用向けに速度チューニングするという例を通して、画像処理システム開発の裏側の努力をご紹介します。

物体検知とは

物体検知とは画像の中から「犬」や「自転車」といった特定のオブジェクトを検出する技術です。例えば、下図は有名な物体検知モデルであるYoloの物体検知結果の例です[1]。物体検知モデルは画像を入力としてBounding Boxという物体を囲む矩形とそれに対応するクラスラベルを出力します。物体検知は画像処理技術の中では基本的なタスクの一つで、物体追跡や姿勢検知など様々な応用タスクの土台となる技術です。

Yoloによる物体検知結果

(出典:https://pjreddie.com/darknet/yolo/

近年ではYolo、Faster-RCNN、SSD、RetinaNet、CenterNet等様々な手法が提案されており、日々State-of-the-artを更新しながら多くの研究者が高精度で高速な物体検知モデルを発表しています。物体検知の精度としては既に実用に足る水準に達しつつあり、実際、画像処理技術を応用したソリューションが次々と発表されています。

研究成果と実用化の間の壁

さて、このように進歩が著しい物体検知モデルですが、実際の業務における実用化を考えた場合には研究成果として発表されているモデルの性能と実用レベルの性能との間には高い壁が存在します。特に動画像をリアルタイムで処理するシステムの場合、モデルの「実行速度」が問題となる場合が多く、モデルの推論にかかる時間をどの程度高速化できるかというチューニングが重要になります。ここでいう実行速度とはモデルに画像を投入してから物体検知結果を返すまでの推論にかかる時間をさしますが、なぜこれが高い壁なのかを説明します。

例として、次のようなリアルタイムの画像処理システムを考えます。クラウド上に物体検知モデルを搭載した画像処理システムを構築し、顧客の現場に固定カメラを設置して撮影した動画をクラウドに伝送し物体検知の推論を行います。推論した結果はユーザの手元のタブレット等にリアルタイムで表示させるようにします。

このシステムでは画像送信処理を開始してからユーザに結果が届くまで、図のように「画像送信処理」→「ネットワーク伝送」→「物体検知推論処理」→「ネットワーク伝送」→「結果表示処理」というプロセスを経由します。処理に必要な時間は

となります。システムはリアルタイムを目指しているのでこの処理時間の和をできる限り0に近づけたいところですが、実際にはネットワーク伝送など物理的な距離に依存する部分も存在するため、現実的には画像送信処理から結果表示処理まで数秒以内(例えば3秒)を目標としてみます。画像送信処理やネットワーク伝送には意外と時間がかかる上、物体検知推論処理でも映像のデコードや検知結果の後処理といった複数のプロセスが必要なため純粋な物体検知にかけられる時間はかなり限られます。今回は物体検知にかけられる時間(t2)は画像1枚当たり最大0.5秒を目標としましょう。

目標の推論時間が定まったので、次は画像処理システム上で動作する物体検知推論処理のアーキテクチャを検討します。研究成果として報告されている数値は多くの場合、高性能なGPUを用いた推論結果であることが多く、例えば物体検知モデルの一つであるYolov4の論文では50FPS~100FPS以上という非常に高速な結果が出ると記載されていますが、推論には「GeForce RTX 2080 Ti」というGPUが用いられています[2]。問題は、画像処理システムをソリューションとして提供する場合に推論用のマシンにGPUを使用するとランニングコストが非常に高価になるということです。そのため今回のようなソリューションに組み込む画像処理システムは、多くの場合、より安価なCPUを搭載したマシンで稼働させる必要があり、マシンスペックに制約がある状態で高速な推論を実現しなければなりません。これが、画像処理の実行速度における研究領域と実用化との間の高い壁です。

速度チューニングの前準備

ここからは、実際に最新の物体検知モデルの一つであるYolov4を使ってCPUマシンで速度チューニングをしていきます。チューニングにあたって、まずは速度を検証するための評価データおよび評価方法、CPU環境を確認しておきます。

評価データにはMicrosoft COCOデータセット[3]を使用します。MS COCOデータセットは物体検知など画像処理タスクのために構築されたデータセットで、33万枚もの画像が含まれます。今回はこのうちval_2017の評価データ5000枚の中からランダムに500枚を抽出して速度計測および精度評価を行います。また、精度の評価指標にはAP(Average Precision)と呼ばれる指標を用います(今回の記事では詳しく説明しませんが、物体検知ではよく使われる指標で数値が高いほど精度が良い指標です)。速度のチューニングを行う場合は、最終的には推論精度とのトレードオフになります。つまり、どこかの時点で「これ以上推論速度を上げる場合には推論精度を落とさなくてはならない」というポイントが来ます。今回、推論速度だけではなく推論精度も一緒に計測していくのは上記のような理由によるものです。

次に、評価用のマシンとしてIntelの「Xeon(R) Silver 4116 CPU @ 2.10GHz」を8コア積んだものを使用します。メモリは24GBです。

Yolov4のモデルはこちら(https://github.com/AlexeyAB/darknet)を参考にしてMS COCOで学習済みのものを使用します。そのため、今回の検証ではモデルの構造には手を加えず、モデルのパラメータや実行フレームワークを中心としてチューニングを行っていきます。Yolov4ではよく使われる画像の入力サイズが何種類かありますが、まずは608×608のサイズで検証を行います。

チューニングを始める前に、まずGPUを積んだマシンでYolov4がどの程度の精度・速度を出せるのか確認しておきましょう。Yolov4のモデルはDarknetというフレームワーク上で実行します。Tesla T4を搭載しているAWSのg4dn.2xlargeインスタンスで評価用データ500枚の推論処理を実行し、実行速度と検知精度を算出したところ、下記の結果となりました。

今回のシステムの推論速度の目標値は0.5秒なので、GPUを搭載したマシンを用いてDarknetフレームワーク上で実行すれば軽く目標をクリアできることがわかりました。では、これからYolov4をCPUに載せ替えて推論速度を計測し、チューニングを行っていきます。

まずはシンプルにCPU上で推論を実行

まずは単純にモデルをCPU上で実行してみます。実行フレームワークはPyTorchを用います。実行速度だけを考えるとC言語で実装されたDarknetフレームワーク上で実行することも考えられるのですが、今回ターゲットにしているような画像処理システムは(弊社の場合)Pythonで実装されることが多く、モデルを実行するフレームワークもPythonで取り回せると都合がよいため、まずはPythonベースの深層学習フレームワークであるPyTorchに学習済みのモデルを載せて実行してみます。DarknetのパラメータをPyTorchで実行するやり方はこちら(https://github.com/Tianxiaomo/pytorch-YOLOv4 )を参考にさせていただきました。

評価用データ500枚に対して処理実行したところ、実行結果は

となりました。

推論時間は画像1枚当たり1.118秒となり、目標値0.5秒に対して2倍以上かかってしまっています。単純にCPU上で実行しただけではシステムに搭載できるほどのパフォーマンスが出せないことがわかりました。ここから、試行錯誤を繰り返して目標値の推論時間0.5秒以内を目指します。

推論プラットフォームを変えて最適化①:onnxruntime

学習済みのモデルの推論時間をチューニングする場合、モデルの構造を変更することは基本的にできないので、モデルをより最適な推論プラットフォームにコンバートして各プラットフォーム上で推論を高速化できるようパラメータを調整していく流れになります。

まずはCPU上での推論プラットフォームとしてよく取り上げられるonnxruntimeを試してみます。PyTorchで試したモデルをさらにonnx形式に変換します。PyTorch→onnxへの変換は簡単で、下記のようなスクリプトで変換可能です。

model = Darknet(config.cfg)
model.load_weights(config.darknet)

input_names, output_names = ['input'], ['boxes', 'confs']
x = torch.randn((1, 3, model.height, model.width))

torch.onnx.export(model, x, <PATH TO ONNX FILE>, export_params=True, opset_version=11,
                  do_constant_folding=True, input_names=['input'], output_names=['boxes', 'confs'], dynamic_axes=None)

変換したonnx形式のモデルをonnxurntimeで推論させてみたところ下記のような結果となりました。

PyTorchで推論した場合と比べて0.3秒ほど遅くなっています。モデルによってはonnx形式に変換することで実行速度が速くなるものもあるようですが、Yolov4の場合は逆に遅くなるようです。念のため、onnx形式のデータをonnxoptimizerで最適化したバージョンでも実行速度を計測してみます。

最適化処理は下記のように行います。onnxoptimizerの最適化は計算グラフの中で推論時には冗長になる部分をよりシンプルな形に置き換えるといった最適化処理を行うもので、例えばBatchNormalizationをConvolutionに還元したり、一度だけの計算で済む処理を別のグラフに切り出して計算回数を削減したりといったことを行います。

model = onnx.load(<PATH TO ONNX>)
    OPTIONS = [
        'eliminate_deadend',
        'eliminate_identity',
        'eliminate_nop_dropout',
        'eliminate_nop_monotone_argmax',
        'eliminate_nop_pad',
        'eliminate_nop_transpose',
        'eliminate_unused_initializer',
        'extract_constant_to_initializer',
        'fuse_add_bias_into_conv',
        'fuse_bn_into_conv',
        'fuse_consecutive_concats',
        'fuse_consecutive_log_softmax',
        'fuse_consecutive_reduce_unsqueeze',
        'fuse_consecutive_squeezes',
        'fuse_consecutive_transposes',
        'fuse_matmul_add_bias_into_gemm',
        'fuse_pad_into_conv',
        'fuse_transpose_into_gemm',
        'lift_lexical_references',
        'nop',
    ]
    model = onnxoptimizer.optimize(model, OPTIONS)
    with open(<PATH TO OPEIMIZED ONNX>, 'wb') as f:
        f.write(model.SerializeToString())

onnxoptimizerによる最適化後の推論時間は下記のようになりました。

最適化前に比べて若干速くなってはいますが、それでも目標速度である0.5秒には及びません。

推論プラットフォームを変えて最適化②:OpenVINO

次は、Intelのハードウェアに最適化された推論プラットフォームを持つOpenVINOを試します。記事執筆時点でOpenVINOによる推論を行う方法は、

  1. onnx形式のモデルを読み込ませて推論する方法
  2. モデルをIR形式に変換して推論する方法

の2つの選択肢があります。後者の方法に関してはyolov4のパラメータをIR形式に変換する必要があり、こちら(https://github.com/TNTWEN/OpenVINO-YOLOV4)を参考にしてモデルの変換を行いました。

2つの方法でそれぞれOpenVINOによる推論を実行したところ、処理時間は下記のようになりました。

IR形式では若干精度が下がるものの、onnx形式に比べると多少は速度が速いようです。しかし、このままでは目標速度である0.5秒以内をクリアするのは難しそうです。

推論精度を犠牲にして速度アップを目指す

ここまでで、精度を落とさずに目標速度0.5秒をクリアするのはかなり難しいことがわかってきました。そこで、次は多少精度を犠牲にしても速度を改善する方法を試していきます。

ここまで使用していたYolov4モデルはインプットの画像サイズが608×608のものでした。これをインプットサイズを416×416に縮小したモデルに切替えます。解像度が低くなり画像の詳細な特徴はとらえられなくなりますが、計算量を削減できるため速度アップが見込めます。

では解像度416×416版のYolov4をPyTorch、onnxruntime、OpenVINOでそれぞれ速度計測してみます。

予想通り推論精度が多少下がっていますが、推論速度を大幅に改善することができました。一番早いものでPyTorchによる0.549秒まで推論速度が向上しています。

インプットの解像度を落とすことで推論速度をかなり向上させることができましたが、目標速度の0.5秒まではもう一歩というところです。そこで次はOpenVINOのフレームワークを使用して、モデルをさらに最適化してみます。

さらに高速化を目指す:INT8 量子化

ここまでOpenVINOでの推論処理で使用していたIR形式のファイルはFP32という形式のもので、FPはFloating Pointの略称です。つまり、推論実行時にデータを浮動小数点で処理しています。OpenVINOにはDeep Learning Workbench[7]という機能があり、これを使うことによってFP32のモデルをIntegerに量子化することができます。推論時の演算をFP32ではなくINT8で行うことによって計算量を大幅に削減することができます。Deep Learningにおける演算ではFP32ほどの値の範囲は必要なく、INT8で表現できる範囲でも十分に計算が可能だそうです[8]。

(出典:https://docs.openvinotoolkit.org/latest/workbench_docs_Workbench_DG_Introduction.html

FP32のモデルをINT8に変換して推論したところ、下記の結果となりました。

 

FP32のモデルからあまり精度を落とすことなく、推論速度を向上させることに成功しました。1枚当たりの推論速度は0.349秒で、目標速度0.5秒をクリアしています。

以上のように、画像処理の推論モデルを実際にソリューションで使える形に落とし込むにはなかなかの手間が必要です。今回は、様々なプラットフォームを試行錯誤し、推論精度を多少犠牲にして目標速度を達成することができました。なお、608×608でINT8化した場合を計測してみると推論時間は1秒前後となります。今回試したYolov4の場合は速度向上効果としては量子化よりも入力データサイズの縮小の方が効果が大きい結果となりました。

おわりに

さて参考までに、今回はIntelの「Xeon(R) Silver 4116 CPU @ 2.10GHz」x8コアというCPU上で推論速度の最適化を行いましたが、では、CPUのコア数やスペックをあげるとどうなるのでしょうか?

OpenVINOによるINT8のモデルをAWSのc5.4xlargeインスタンスで試してみました。スペックは「Xeon(R) Platinum 8275CL CPU @ 3.00GHz」が16コアでメモリは32GBです。

...!!!

やはり高スペックなマシンを使用できればCPUでもかなりの推論速度を出せるようです。ちなみに、最初にGPUで計測した推論時間は0.032秒でした。ただし、このc5.4xlargeインスタンスはCPUではありますがGPUでの計測時に使用したg4dn.2xlargeよりも多少安い程度で、GPUインスタンスに迫る価格になっています。

今回は例として架空のシステムを想定して目標速度に対するチューニングを行いました。実際のシステム開発においては、最終的にはサービスとして提供できる価格や推論精度、推論速度などのバランスを考慮してマシンスやモデルを選定することになるでしょう。

以上、今回の記事では機械学習関連のモデルは開発が完了したものをそのまま実際のシステムに実戦投入できるわけではなく、処理性能やマシンスペック、サービス価格など様々な面でチューニングが必要になるというシステム開発の裏側をご紹介しました。

参考文献・URL

[1] https://pjreddie.com/darknet/yolo/

[2] Alexey Bochkovskiy, Chien-Yao Wang, Hong-Yuan Mark Liao. YOLOv4: Optimal Speed and Accuracy of Object Detection. arXiv preprint arXiv:2004.10934. 2020.4

[3] Tsung-Yi Lin, Michael Maire, Serge Belongie, James Hays, Pietro Perona, Deva Ramanan, Piotr Dollar, and C Lawrence Zitnick. Microsoft COCO: Common objects in context. In Proceedings of the European Conference on Computer Vision (ECCV), pages 740–755, 2014. 5

[4] AlexeyAB/darknet

[5] Tianxiaomo/pytorch-YOLOv4

[6] TNTWEN/OpenVINO-YOLOV4

[7] https://docs.openvinotoolkit.org/latest/workbench_docs_Workbench_DG_Introduction.html

[8] https://www.xlsoft.com/jp/products/intel/openvino/document/enhanced-low-precision-pipeline.html

関連記事