[Amazon SageMaker] DLR(Deep Learning Runtime)の最新バージョン(1.4.0)をJetson Nanoで使用してみました

2020.10.17

1 はじめに

CX事業本部の平内(SIN)です。

Jetson nano + Sagemaker Neoでの推論は、今まで色々試しているのですが、最新版では少し要領が変わっている部分もあり、ちょっと手間取ったので、改めて、今回作業した手順を纏めさせて頂きました。

2 JetPack 4.2

Jetpackの最新は4.4となってますが、2020/10/17現在、手元では、ライブラリの依存関係からDLRが動作できなかったため、アーカイブから4.2を使用しました。


https://developer.nvidia.com/jetpack-4_2

3 DLR 1.4

SageMaker Neoでコンパイルされたモデルで推論するためには、DLRが必要です。
参考:https://github.com/neo-ai/neo-ai-dlr

x86_64CPU向けは、pipで最新リリースのインストールが可能になっていますが、GPUで使用する場合などは、別途インストールが必要です。

DLRのインストールを案内するページでは、ビルド済みのバイナリリリース(1.3)も公開されていますが、今回は、最新版をコンパイルすることにしました。
参考:Installing DLR

コンパイルの手順については、以下のとおりです。
参考:Building for NVIDIA GPU on Jetson Devices

(1) pip及び、setuptools

最後に使用するインストールスクリプト(python/setup.py)で、「No module named 'setuptools'」となるので、予め、pip及び、setuptoolsをインストールします。

$ sudo apt-get update
$ sudo apt-get install python3-pip
$ pip3 install setuptools

(2) cmake 3.17.2

DLRの構築には、cmake 3.13以降が必要とのことで、最初にcmakeをインストールします。

$ sudo apt-get install libssl-dev
$ wget https://github.com/Kitware/CMake/releases/download/v3.17.2/cmake-3.17.2.tar.gz
$ tar xvf cmake-3.17.2.tar.gz
$ cd cmake-3.17.2
$ ./bootstrap
$ make -j4
$ sudo make install
$ cmake --version
cmake version 3.17.2

(3) DLR 1.4

DLRは、最新版をgitからダウンロードしてコンパイル・インストールします。

$ git clone --recursive https://github.com/neo-ai/neo-ai-dlr
$ cd neo-ai-dlr
$ mkdir build
$ cd build
$ cmake .. -DUSE_CUDA=ON -DUSE_CUDNN=ON -DUSE_TENSORRT=ON
$ make -j4
$ cd ../python
$ python3 setup.py install --user

(4) 確認

python3でimportして、バージョンを確認しています。

Ver1.4では、パフォーマンスの向上のためのメトリックを収集に関する同意を求めるメッセージと、それを処理する案内が表示されるようになっています。

nvidia@nvidia-desktop:~$ python3
Python 3.6.9 (default, Oct  8 2020, 12:12:24)
[GCC 8.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import dlr

 CALL HOME FEATURE ENABLED


 You acknowledge and agree that DLR collects the following metrics to help improve its performance.
 By default, Amazon will collect and store the following information from your device:

 record_type: <enum, internal record status, such as model_loaded, model_>,
 arch: <string, platform architecture, eg 64bit>,
 osname: <string, platform os name, eg. Linux>,
 uuid: <string, one-way non-identifable hashed mac address, eg. 8fb35b79f7c7aa2f86afbcb231b1ba6e>,
 dist: <string, distribution of os, eg. Ubuntu 16.04 xenial>,
 machine: <string, retuns the machine type, eg. x86_64 or i386>,
 model: <string, one-way non-identifable hashed model name, eg. 36f613e00f707dbe53a64b1d9625ae7d>

 If you wish to opt-out of this data collection feature, please follow the steps below:
	1. Disable it with through code:
		 from dlr.counter.phone_home import PhoneHome
		 PhoneHome.disable_feature()
	2. Or, create a config file, ccm_config.json inside your DLR target directory path, i.e. python3.6/site-packages/dlr/counter/ccm_config.json. Then added below format content in it, {"enable_phone_home" : false}
	3. Restart DLR application.
	4. Validate this feature is disabled by verifying this notification is no longer displayed, or programmatically with following command:
		from dlr.counter.phone_home import PhoneHome
		PhoneHome.is_enabled() # false as disabled
>>> print(dlr.__version__)
1.4.0

4 SWAP

使用するモデルによりますが、今回使用したモデルでは、デフォルト(メモリ4G SWAP 2G)では、不足して動作できないため、6GByteの /swapfile を作成し、SWAPを拡張しました。

$ sudo dd if=/dev/zero of=/swapfile bs=1G count=6
$ sudo swapoff -a
$ sudo mkswap /swapfile
$ sudo swapon /swapfile
$ free -h
              total        used        free      shared  buff/cache   available
Mem:           3.9G        1.3G        1.4G         48M        1.2G        2.3G
Swap:          6.0G          0B        6.0G

5 Sagemaker Neo

元となるモデルは、Amazon SageMakerの組み込みアルゴリズム(Image Classification)で作成したものです。

SageMaker Neoで、以前に作成したモデルは、DLR 1.4では、そのまま利用できなかったため、コンパイルし直しました。

データ入力設定は、{"data":[1,3,224,224]} 、機械学習フレームワークは、MXNet、ターゲットデバイスは、jetson_nanoを設定しています。

作成されたモデルのファイル構造は、以下のとおりでした。 以前のものとは、ファイル数が変わっています。(dlr.h及び、libdlr.soが増えている)

6 推論動作の確認

ブランク画像で問題なく推論できるかを確認します。Neoで作成したモデルは、解凍して./modelの下に配置しています。

.
├── model
│   ├── compiled.meta
│   ├── compiled_model.json
│   ├── compiled.params
│   ├── compiled.so
│   ├── dlr.h
│   ├── libdlr.so
│   └── model-shapes.json
└── run.py

下記のコードを実行することで動作の確認ができます。

PhoneHome.disable_feature()は、メトリック収集に関する記述です。

また、TVM_TENSORRT_CACHE_DIR=',' の指定でカレントディレクトリにキャッシュを作成します。

以下のコードでは、無効化されていますが、TVM_TENSORRT_USE_FP16='1' を有効にすると、16ビット浮動小数点精度に変換し、高速化が図れます。
参考:Additional Options for TensorRT Optimized Models

run.py

import numpy as np
import time
import os
import dlr
from dlr.counter.phone_home import PhoneHome
PhoneHome.disable_feature()

os.environ['TVM_TENSORRT_CACHE_DIR'] = '.'
# os.environ['TVM_TENSORRT_USE_FP16'] = '1'

model = dlr.DLRModel('model/', 'gpu', 0)
img = np.random.rand(1, 3, 224, 224)

for _ in range(10):
    start = time.time() 
    model.run({'data': img})
    processing_time = time.time() - start
    print("{:.2f} sec".format(processing_time))

以下のような出力であれば問題なしです。(モデルがキャッシュされていない場合、1回目の推論にかなり時間がかかります)

$ python3 run.py
2020-10-17 14:10:29,074 INFO Found libdlr.so in model artifact. Using dlr from model/libdlr.so
[14:11:08] /home/nvidia/neo-ai-dlr/3rdparty/tvm/src/runtime/contrib/tensorrt/tensorrt_module.cc:261: Loading cached TensorRT engine from ./8507350289998171460.plan
[14:11:29] /home/nvidia/neo-ai-dlr/src/dlr_tvm.cc:62: Loading metadata file: model/compiled.meta
0.71 sec
0.17 sec
0.17 sec
0.17 sec
0.17 sec
0.17 sec
0.17 sec
0.17 sec
0.17 sec
0.17 sec

キャシュが有効になると、xxx.meta及びxxx.planファイルができています。

.
├── 8507350289998171460.meta
├── 8507350289998171460.plan
├── model
│   ├── compiled.meta
│   ├── compiled_model.json
│   ├── compiled.params
│   ├── compiled.so
│   ├── dlr.h
│   ├── libdlr.so
│   └── model-shapes.json
└── run.py

ちなみに、下記は、os.environ['TVM_TENSORRT_USE_FP16'] = '1' を有効にして、実行した1回目(キャッシュなし)と2回目のログです。推論自体は、倍近く早くなってるようです。(0.17sec -> 0.1sec)

$ python3 run.py
2020-10-17 14:13:28,225 INFO Found libdlr.so in model artifact. Using dlr from model/libdlr.so
[14:14:07] /home/nvidia/neo-ai-dlr/src/dlr_tvm.cc:62: Loading metadata file: model/compiled.meta
[14:14:10] /home/nvidia/neo-ai-dlr/3rdparty/tvm/src/runtime/contrib/tensorrt/tensorrt_module.cc:80: Building new TensorRT engine for subgraph tensorrt_0
[16:31:41] /home/nvidia/neo-ai-dlr/3rdparty/tvm/src/runtime/contrib/tensorrt/tensorrt_module.cc:296: Caching TensorRT engine to ./8507350289998171460_fp16.plan
[16:31:57] /home/nvidia/neo-ai-dlr/3rdparty/tvm/src/runtime/contrib/tensorrt/tensorrt_module.cc:90: Finished building TensorRT engine for subgraph tensorrt_0
8269.84 sec
0.20 sec
0.10 sec
0.10 sec
0.10 sec
0.10 sec
0.10 sec
0.10 sec
0.10 sec
0.10 sec
$ python3 run.py
2020-10-17 16:32:24,707 INFO Found libdlr.so in model artifact. Using dlr from model/libdlr.so
[16:33:04] /home/nvidia/neo-ai-dlr/3rdparty/tvm/src/runtime/contrib/tensorrt/tensorrt_module.cc:261: Loading cached TensorRT engine from ./8507350289998171460_fp16.plan
[16:33:21] /home/nvidia/neo-ai-dlr/src/dlr_tvm.cc:62: Loading metadata file: model/compiled.meta
0.28 sec
0.10 sec
0.10 sec
0.10 sec
0.10 sec
0.10 sec
0.10 sec
0.10 sec
0.10 sec
0.10 sec

F16のキャッシュは、別に作られています。

$ ls -la
total 418668
drwxrwxr-x  3 nvidia nvidia      4096 10月 17 16:31 .
drwxr-xr-x 19 nvidia nvidia      4096 10月 17 13:12 ..
-rw-rw-r--  1 nvidia nvidia       111 10月 17 16:31 8507350289998171460_fp16.meta
-rw-rw-r--  1 nvidia nvidia 194759992 10月 17 16:31 8507350289998171460_fp16.plan
-rw-rw-r--  1 nvidia nvidia       111 10月 17 13:34 8507350289998171460.meta
-rw-rw-r--  1 nvidia nvidia 233920904 10月 17 13:34 8507350289998171460.plan
drwxrwxr-x  2 nvidia nvidia      4096 10月 17 12:55 model
-rw-r--r--  1 nvidia nvidia       455 10月 17 14:13 run.py

7 Webカメラの画像推論

最後に、Webカメラの動画を約10FPSで推論しているサンプルコードです。

import cv2
import numpy as np
import dlr
import time
import os
from dlr.counter.phone_home import PhoneHome
PhoneHome.disable_feature()

os.environ['TVM_TENSORRT_CACHE_DIR'] = '.'
os.environ['TVM_TENSORRT_USE_FP16'] = '1'
SHAPE = 224
MODEL_PATH = './model'
CLASSES = ["ポリッピー(GREEN)","OREO","カントリーマム","ポリッピー(RED)","柿の種(わさび)","通のとうもろこし","CHEDDER_CHEESE","ピーナッツ","ストーンチョコ","PRETZEL(YELLOW)","海味鮮","柿の種","カラフルチョコ","フルグラ(BROWN)","NOIR","BANANA(BLOWN)","チーズあられ","俺のおやつ","PRIME","CRATZ(RED)","CRATZ(GREEN)","揚一番","ポリッピー(YELLOW)","こつぶっこ","アスパラガス","海苔ピーパック","いちご","梅しそチーズあられ","通のえだ豆","柿の種(梅しそ)","PRETZEL(BLACK)","辛子明太子","CRATZ(ORANGE)","チョコメリゼ","フライドポテト(じゃがバター味)","BANANA(BLUE)","でん六豆","パズル","フルグラ(RED)","PRETZEL(GREEN)","フライドポテト(しお味)",]

DEVICE_ID = 0
WIDTH = 800
HEIGHT = 600
GST_STR = ('v4l2src device=/dev/video{} ! video/x-raw, width=(int){}, height=(int){} ! videoconvert ! appsink').format(DEVICE_ID, WIDTH, HEIGHT)

def main():

    PhoneHome.disable_feature()
    cap = cv2.VideoCapture(GST_STR, cv2.CAP_GSTREAMER)
    model = dlr.DLRModel('model/', 'gpu', 0)

    while(True):

        _, frame = cap.read()
        if(frame is None):
            continue
        frame = frame[0 : int(HEIGHT), 0 : int(HEIGHT)] # 横長の長方形 => 正方形

        # 入力画像生成
        img = cv2.resize(frame, dsize=(SHAPE, SHAPE)) # height * height => 224 * 224
        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # BGR => RGB
        img = img.transpose((2, 0, 1)) # 224,244,3 => 3,224,224
        img = img[np.newaxis, :] # 3,224,224 => 1,3,224,224
        print("img.shape: {}".format(img.shape))

        # 推論
        start = time.time() # 時間計測
        out = model.run({'data': img})
        processing_time = time.time() - start
        print(processing_time)

        # 表示
        prob = np.max(out)
        index = np.argmax(out[0])
        print("{} {:.2f} {:.2f}sec".format(CLASSES[index], prob, processing_time))

main()

8 最後に

今回は、DLR(Deep Learning Runtime)の最新バージョン(1.4.0)をJetson Nanoで使用してみました。

キャッシュする時に、非常に時間がかかりますが、2回目以降は、起動自体も以前のものより早くなっている気がしました。(すいません、個人的体感です)

なお、DLRでは、モデルのバージョン互換が必要なので、以前に作成したモデルは注意が必要です。