Jetson Nano Developer KitでAWS IoT GreengrassのML Inferenceを試す(GPU編)

前回の記事では、Jetson Nano上でAWS IoT GreengrassのML Inferenceを試しました。
[Jetson Nano Developer KitでAWS IoT GreengrassのML Inferenceを試す](https://qiita.com/sparkgene/items/425d310c1d6c9158f896)

ただし、前回はCPUを利用した推論が行われていました。今回は、GPUを使った推論を実行するための方法について紹介します。（この記事は前回の記事の環境ができている前提での設定となります）

## MXNetをソースコードからビルド

apt-getを利用した方法だと、CUDA9向けのMXNetがインストールされるため、JetsonNanoのGPUを使いたい時に動きません。（JetsonNanoだとCUDA10の為）
そこで、同じスレッドで紹介されている通り、ソースコードからのビルドが必要になります。

[I was unable to compile and install MXNET on the jetson nano，Is there an official installation tutorial?](https://devtalk.nvidia.com/default/topic/1049293/jetson-nano/i-was-unable-to-compile-and-install-mxnet-on-the-jetson-nano-is-there-an-official-installation-tutorial-/post/5326119/#5326119)

細かいコマンドは、このスレッドに書かれている内容(1. Allocate swap 〜 4. Install MXNet)を参照してほしいのですが、注意点として2つあります。

* Swapfileは8Gのサイズが必要
  * 手持ちが16GのSDカードだった為、空き領域が少なく試しに4GでSwapfileを作ったらビルドの途中でコケました
  * 32GのSDカードを手に入れてビルドしました
* make -j3のコマンドがすごく長い
  * ビルドにめっちゃ時間が掛かります。寝る前に実行して朝確認するぐらいの感じでやるといいでしょう
  * 誤ってターミナルを閉じたらコマンドがコケるので、 `nohup make -j3 &` で、ターミナル落としても大丈夫なようにしましょう

## pythonから確認

pythonのコマンドでインタプリタを開いて、GPUが利用可能か確認します

```
nvidia@nvidia-jetson:~$ python
Python 2.7.15+ (default, Nov 27 2018, 23:36:35)
[GCC 7.3.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import mxnet as mx
>>> a = mx.nd.ones((2,3), mx.gpu())
>>> print((a*2).asnumpy())
[[2. 2. 2.]
 [2. 2. 2.]]
```

うまくインストールできたようです！

## GPUのモニター

[jtop](https://github.com/rbonghi/jetson_stats)というコマンドがあるので、これをインストールして使えるようにします。(READMEのインストールを参考にしてください）

`sudo jtop` コマンドを実行すると以下のような画面で確認できます。

<img width="997" alt="スクリーンショット 2019-09-05 15.35.48.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36184/5cb4a4a9-058a-dca9-65b7-e5b80b3ced5e.png">

## Lambdaの修正

[28行目](https://github.com/sparkgene/greengrassObjectClassification/blob/master/greengrassObjectClassification.py#L28)の引数に `context='GPU0'` を追加します。

```
global_model = load_model.ImagenetModel(model_path, 'MXNET', 'synset.txt', 'squeezenet_v1.1')
↓
global_model = load_model.ImagenetModel(model_path, 'MXNET', 'synset.txt', 'squeezenet_v1.1', context='GPU0')

```
新しいバージョンをデプロイし、エイリアスの参照バージョンを変更します。

## Greengrass上のLambdaの修正

### Lambdaの設定変更

メモリを2GBに変更するのと、/sys ディレクトリへの読み込みアクセスを有効化する

<img width="508" alt="スクリーンショット_2019-09-05_14_57_03.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36184/f98de39c-775c-dbaf-1d4f-149f0c2432a0.png">

### ローカルリソースを追加

GPUを利用する場合は、LambdaからGPUリソースにアクセスする必要があるため、以下のローカルリソースの追加を行います。

|リソース名|リソースタイプ|デバイスパス/ソースパス|送信先パス|Lambda 関数の関連|
|----|----|----|----|----|
|nvhost-ctrl-gpu|デバイス|/dev/nvhost-ctrl-gpu|-|読み取りと書き込みアクセス|
|shm|ボリューム|/dev/shm|/dev/shm|読み取りと書き込みアクセス|
|nvhost-ctrl|デバイス|/dev/nvhost-ctrl|-|読み取りと書き込みアクセス|
|nvhost-prof-gpu|デバイス|/dev/nvhost-prof-gpu|-|読み取りと書き込みアクセス|
|nvmap|デバイス|/dev/nvmap|-|読み取りと書き込みアクセス|
|nvhost-dbg-gpu|デバイス|/dev/nvhost-dbg-gpu|-|読み取りと書き込みアクセス|

設定例
<img width="510" alt="スクリーンショット 2019-09-05 14.58.41.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36184/545c4692-e4dc-d527-83da-83ae4b8fe1e8.png">

最終的にローカルリソースはこのような設定

<img width="805" alt="スクリーンショット 2019-09-05 16.13.25.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36184/14c97676-afc2-5b65-97c4-7ee91a5970f7.png">

## 動作確認

マネージメントコンソールからGreengrassのデプロイを実行します。

`jtop`でみると、GPUの使用率が上がって、使われているのがわかります。

<img width="991" alt="スクリーンショット 2019-09-05 16.15.09.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36184/7dc227a3-470c-37dc-de0d-502f9be0b8ae.png">

Lambdaのコードを以下のように修正して、カメラから画像を取り出して推論する時間を出力するようにしました

``` greengrassObjectClassification.py
42    start = time.time()
43    predictions = global_model.predict_from_cam()
44    elapsed_time = time.time() - start
45    print("elapsed_time:{}".format(elapsed_time))
```

GPUの場合

```
[2019-09-05T16:22:55.017+09:00][INFO]-elapsed_time:0.0266320705414
[2019-09-05T16:22:55.018+09:00][INFO]-[(0.11811499, 'n04372370 switch, electric switch, electrical switch'), (0.07899597, 'n03476991 hair spray'), (0.067904234, 'n04485082 tripod'), (0.066124074, 'n02783161 ballpoint, ballpoint pen, ballpen, Biro'), (0.057746205, 'n03017168 chime, bell, gong')]
```

CPU利用の場合

```
[2019-09-05T16:23:43.222+09:00][INFO]-elapsed_time:0.319680929184
[2019-09-05T16:23:43.223+09:00][INFO]-[(0.1585841, 'n04372370 switch, electric switch, electrical switch'), (0.07223587, 'n02783161 ballpoint, ballpoint pen, ballpen, Biro'), (0.059364777, 'n03476991 hair spray'), (0.056694258, 'n03876231 paintbrush'), (0.051916137, 'n03804744 nail')]
```

GPUだと推論にかかった時間が`elapsed_time:0.0266320705414`ですが、CPU利用の場合は`elapsed_time:0.319680929184`と、10倍ぐらい早くなっているのが確認できました。

## まとめ

GPUを使うとかなり早くなりますね。

[Greengrassのドキュメントに書かれているリソース](https://docs.aws.amazon.com/ja_jp/greengrass/latest/developerguide/ml-dlc-console.html#jetson-lambda-dlc-config)とは違ったので、指定するローカルリソースを調べるのはなかなか大変ですが、今回は`strace`コマンドを使って、何にアクセスしているかを確認して設定しました。

## 免責

発言内容は個人的な意見であり、所属する企業を代表するものではありません。
掲載しているソースコードは、サンプルレベルの物ですので動作を保証するものではありません。