2019年9月8日日曜日

Jetson NanoでTF-TRTを試す(Image Classification)


(2019.10.16 MobileNet v1, v2のFP16の処理時間を最適化する記事を書いた

目的


TF-TRT(TensorFlow integration with TensorRT)を使ってFP16に最適化したモデルを生成し、NVIDIA GPU、Jetson Nanoでどの程度最適化の効果ががあるのかを確認する。
今回は、Image Classificationのモデルについて確認する。


動機


NVIDIA公式のGithubリポジトリにJetxon(TX, TX2向け)のTF-TRTモデルが公開されている。


TF-TRTはJetson Nanoでも有効であるはずなのだが、ベンチマークなどの情報がすくない。
また、TensorFlow1.14とTensorRT5.1.5では、TF-TRTモデルに変換する際のIFが変更になったことと、Jetson NanoのSDKのバージョンが上がったことから、新しいIFでモデルを作成し、ベンチマークすることにした。




まとめとか


  • TF-TRTをつかうことで、モデルの推論時間を短縮することができることが確認できた(今回はFP16による最適化)。
  • GTX1070, Jetson Nanoで効果が確認できた。
    Jetson NanoはMobileNet v1, v2がImage Classificationのモデルで使用できそう。
  • TF-TRTモデルを生成する側と推論を行う側でTensorRTのバージョンが一致していないとNG(回避方法あり)。


TF-TRTを使うメリット


TensorRTではなく、TF-TRTを使うメリットとしては以下と考えている。
  • TensorFlowで学習したモデル(Frozen Graph or Saved Model)が流用できる。
  • 学習・推論のコードはTensorFlowのコードがほぼそのまま流用できる。


参考ドキュメント


モデルの作成から推論まで


大きな流れとして、
  1. ホスト環境(PC: Ryzen1700+GTX1070)でNGCコンテナーの立ち上げ。
  2. ホスト環境でpretrained modelを読み込み->TF-TRTモデルに変換。
  3. ホスト環境、JetsonNanoで推論を行う。
作成するモデルはTensorFlow/models/slimで用意されているpretrained modelを使用する。

NGCコンテナーの立ち上げ


TF-TRTを使うにはTensorRTが必要である。TensorRTがなくてもモデル生成や実行ができるが、最適化されていないため注意が必要。
ホスト環境にNGCコンテナーを立ち上げる。まずは、DockerをインストールしてGPUを使えるようにセットアップする(前回の記事を参照)。
NVIDIA/TensorFlowから19.08-py3(2019/9/8時点の最新)をPullし、起動する。

sudo docker pull nvcr.io/nvidia/tensorflow:19.08-py
sudo docker run -it --gpus all -p 8888:8888 nvcr.io/nvidia/tensorflow:19.08-py3


TF-TRTリポジトリの取得

TF-TRTモデルを作成するためのリポジトリは本家からForkしている。



モデルの変換はnotebookで用意している。


TF-TRTでFP16のモデルを生成


TF-TRTでpretrained modelを変換し、FP16で最適化する流れを説明する。


pretrained modelの読み込み


pretrained modelのcheckpointファイルからモデルをロード・リストアする。
実際のコードはここ。一般的な方法であるので説明は省略する。
ただし、Relu6についてはTensorRTで最適化するためにrelu(x) - relu(x - 6)に置き換えている(TensorFlow Container 18.11-19.01 (TensorFlow 1.12)でRelu6がサポートされているので置き換えの必要がないかもしれないが今回は未検証)。


TF-TRTモデルの変換


TrtGraphConverterを使ってモデルを変換する(古いAPIはcreate_inference_graph)。
convertメソッドによって最適化されたモデルを得ることができる。
converter = trt.TrtGraphConverter(
    input_graph_def=frozen_graph,
    nodes_blacklist=output_names, #output nodes
    max_batch_size=1,
    is_dynamic_op=True,
    max_workspace_size_bytes=trt.DEFAULT_TRT_MAX_WORKSPACE_SIZE_BYTES,
    precision_mode=trt.TrtPrecisionMode.FP16,
    minimum_segment_size=50)
trt_graph = converter.convert()

TrtGraphConverterの引数には
  • input_graph_def:  ロードしたGraphDefを指定。
  • nodes_blacklist:  最適化しないnode。
    Output nodeを指定(create_inference_graphではoutputs)。
  • is_dynamic_op:  推論の実行時にTensorRTの最適化を行う。
    最適化の際は入力のサイズが予め決まっている必要がある。入力サイズが決まっていないモデルの場合は、TrtGraphConverterによって最適化できない。
    Trueにすることによって、推論時に最適化を行うことができる。
    しかし、初回の推論時に最適化が行われるため処理時間が増加する。
    また、ホストとJetson NanoのTensorRTのバージョンが不一致の場合は、最適化により動作することができる。
  • max_workspace_size_bytes:  TensorRTエンジンが実行できる最大のGPUメモリサイズ。
    DEFAULT_TRT_MAX_WORKSPACE_SIZE_BYTES は新しく追加された定義。
  • precision_mode:  量子化のモード。
    FP16とINT8が選択できる。今回はFP16を選択。
  • minimum_segment_size:  subgraphをTRTEngineOpで置き換える最小のノード数。

最適化が行われると、いくつかのSubGraphがTensorRTEngineOpで置き変わる。
どの程度、置き換わったかは以下のコードで確認できる。
trt_engine_opts = len([1 for n in trt_graph.node if str(n.op) == 'TRTEngineOp'])
print("trt_engine_opts = {}".format(trt_engine_opts))


TF-TRTモデルの保存


モデルの保存は、SerializeToStringを使用する。
base_name = os.path.splitext(os.path.basename(checkpoint_path))[0]
save_model_file_name = base_name + '_frozen_fp16.pb'
with open(os.path.join(MODEL_DIR, save_model_file_name), 'wb') as f:
    f.write(trt_graph.SerializeToString())


ベンチマーク


GTX1070とJetson Nanoでベンチマークを行う。
推論時間のベンチマークに使用したスクリプトはここ
TF-TRTのモデルを扱うためには、以下のインポートが必要。

from tensorflow.python.compiler.tensorrt import trt

推論(session.run)前後の処理時間を100回計測した平均とする。
ただし、初回はモデルの構築を行うため省く。

ファイルサイズ


変換前後でのファイルサイズを比較する。
驚いたことに、TF-TRTモデルのほうがオリジナルの倍になっていた。
ファイルサイズが大きくなるのは組込デバイスには少し負担になる気がするのだが...

ModelOriginal

TF-TRT
(FP16)
MobileNet_v1_1.0_22417M33M
MobileNet_v2_1.0_22414M28M
VGG 16528M1.1G
VGG 19549M1.1G
Inception v126M51M
Inception v243M86M
Inception v392M183M
Inception v4164M327M
ResNet v1 5098M196M
ResNet v1 101171M342M
ResNet v1 152231M461M
ResNet v2 5098M196M
ResNet v2 101171M342M
ResNet v2 152231M462M
Inception Resnet v2214M428M


GTX1070の初回の推論時間


モデル構築を行うため、初回の推論は処理時間が多くかかる。
is_dynamic_opによる違いがあるかを確認した。
  • is_dynamic_opにTrueを指定した場合
    TF-TRTモデルの場合、初回の推論に多く時間がかかっている(約20〜30倍)。
    これは、モデルの最適化が行われるためと思われる。
  • is_dynamic_opにFalseを指定した場合
    TF-TRTモデルのほうがオリジナルより推論時間が短くなっており、最適化の効果が確認できる。


初回の推論時間(is_dynamic_opにTrueを指定)


Model
Inference time [ms]
Original

TF-TRT
(FP16)
MobileNet_v1_1.0_224142322194
MobileNet_v2_1.0_224170532443
VGG 16373745574
VGG 19411752471
Inception v1132124132
Inception v2139227833
Inception v3207830763
Inception v4301946492
ResNet v1 50157029993
ResNet v1 101213854414
ResNet v1 152285977768
ResNet v2 50190130391
ResNet v2 101236054616
ResNet v2 152273877686
Inception Resnet v2339271582

GTX1070 初回の推論時間を比較(is_dynamic_opにTrueを指定)


初回の推論時間(is_dynamic_opにFalseを指定)


Model
Inference time [ms]
Original

TF-TRT
(FP16)
MobileNet_v1_1.0_22414231066
MobileNet_v2_1.0_2241705968
VGG 1637372889
VGG 1941172916
Inception v113211067
Inception v213921043
Inception v320781062
Inception v430191367
ResNet v1 5015701214
ResNet v1 10121381448
ResNet v1 15228591657
ResNet v2 5019011242
ResNet v2 10123601401
ResNet v2 15227381615
Inception Resnet v233921632

GTX1070 初回の推論時間を比較(is_dynamic_opにFalseを指定)


GTX1070の2回目以降の推論時間


TF-TRTモデルのほうが処理時間が短縮している。
FP16の最適化の効果が出ている。is_dynamic_opの違いはなかった。これは、TF-TRTモデルの生成時に最適化が行われているためと思われる。

Model
Inference time [ms]
Original

TF-TRT
(FP16)
MobileNet_v1_1.0_22462
MobileNet_v2_1.0_22473
VGG 16758
VGG 19829
Inception v172
Inception v283
Inception v3188
Inception v43114
ResNet v1 50104
ResNet v1 101177
ResNet v1 1522410
ResNet v2 50136
ResNet v2 1012111
ResNet v2 1522915
Inception Resnet v23814

GTX1070の推論時間(2回目以降、100回の平均)


Jetson Nanoの初回の推論時間


is_dynamic_opにTrueを指定した場合
傾向はGTX1070と同じである。Jetson Nanoではモデルサイズが大きいものは起動できなかった。

is_dynamic_opにFalseを指定した場合、「Segmentation fault (コアダンプ)」が発生し起動できなかった。
ログから、Host側とJetsonNano側のTensorRTのバージョンが一致しておらず、TF-TRTモデルが読み込めなかったと思われる。

2019-09-08 16:24:10.910053: E tensorflow/compiler/tf2tensorrt/utils/trt_logger.cc:41] DefaultLogger The engine plan file is not compatible with this version of TensorRT, expecting library version 5.1.6 got 5.1.5, please rebuild.
Segmentation fault (コアダンプ)


is_dynamic_opにTrueを指定した場合は、初回の推論時にモデルの最適化が行われるため、事象が発生しなかったと思われる。

Jetson NanoのTensorRTのバージョンは5.1.6、Host側のNGCコンテナーは5.1.5なので、確かにバージョンは不一致。でも、TensorRTのリリースノートにはバージョン5.1.5まで、NGCコンテナーにもない... これはどういうことだろうか?
5.1.6がリリースされたら、is_dynamic_opにFalseを指定した場合も検証することとする。


初回の推論時間(is_dynamic_opにTrueを指定)


Model
Inference time [ms]
Original

TF-TRT
(FP16)
MobileNet_v1_1.0_22427551129589
MobileNet_v2_1.0_22421224172359
VGG 16強制終了起動せず
VGG 19強制終了起動せず
Inception v1994418599
Inception v21735827673
Inception v3強制終了48400
Inception v4強制終了強制終了
ResNet v1 5016651OOM
ResNet v1 101強制終了
ResNet v1 152
ResNet v2 5032912起動せず
ResNet v2 101強制終了
ResNet v2 152
Inception Resnet v2

※強制終了: "強制終了"を表示してスクリプトが終了した。
※OOM: "OutOfMemory"を表示してスクリプトが終了した。
※起動せず: 5分以上経過しても初回の推論が終わらないので諦めた。
※ー: 計測していない。

Jetson Nano 初回の推論時間を比較(is_dynamic_opにTrueを指定)


Jetson Nanoの2回目以降の推論時間


MobileNet v1, v2に限定すると、TF-TRTによる最適化の効果が出ている。
Inception v1, v2は差がない。おそらくGPUのメモリサイズが不足していると思われる。
ここから、TF-TRTで使用できるモデルはMobileNet v1, v2になる。


Model
Inference time [ms]
Original

TF-TRT
(FP16)
MobileNet_v1_1.0_2243425
MobileNet_v2_1.0_2243729
VGG 16強制終了起動せず
VGG 19強制終了起動せず
Inception v14949
Inception v27176
Inception v3強制終了191
Inception v4強制終了強制終了
ResNet v1 50161OOM
ResNet v1 101強制終了
ResNet v1 152
ResNet v2 50212起動せず
ResNet v2 101強制終了
ResNet v2 152
Inception Resnet v2

※青色セル: メモリ不足と思われる警告が出力
(The caller indicates that this is not a failure, but may mean that there could be performance gains if more memory were available.)

Jetson nanoの推論時間(2回目以降、100回の平均)


その他


  • 次はObject Detection モデルについて確認してみる。
    NGCコンテナーのTensorRTのバージョンがアップデートされてからか?
  • tf.kerasで作成したモデルもTF-TRTモデルに変換して実行することができる。
    notebookはここ。
    ただし、推論の際はtf.kerasではなく、session.runの方式になってしまう。
    変わってしまうのであれば、TensorRTで実行したほうがよいかもしれない。
    (TF2.0でTF-TRTがtf.kerasで推論できるようになればかなり強力)
  • Jetson NanoでTF-TRTモデルの作成を行ったが、ベンチマークした結果、最適化されなかった。
    おそらく、GPUの世代やメモリサイズが影響していると思われる。



0 件のコメント:

コメントを投稿