Jetson NanoでDeep Learning
前回の記事でJetson Nano 2GBをセットアップするところまで書きました。
今回は、カメラからの映像に対して、GPUを使ったDeep Learningによる映像分析を試してみます。
NVIDIAの公式WebサイトにあるJetson AI Courses and Cerificationの中の、Section 1 – NVIDIA Deep Learning Institute’s Getting Started with AI on Jetson Nano courseに従ってやってみました。
事前準備
コースに入る前に、幾つか準備をしておきます。特にswapについては、拡張しないと途中でフリーズしてしまうことがありますので、必須です。
1. カメラの動作確認
- カメラデバイスが認識されると、/dev/video0が作成されるようです。
- nvgstcaptureコマンドで、X Window上にカメラのキャプチャ画像が表示されればOK。
2. swapの拡張
- NVIDIAの動画では、swapを4GBに拡張するように言っていましたが、4GBだと後述のImage Regressionでフリーズします。
- さらに4GB追加して合計8GBで問題なく動くようになりました。フォーラムで12GBまで増やしたというコメントもあったので、動作を試しながら必要に応じて増やすと良さそうです。
- swapを増やす方法は、こちらの記事を参考に。
https://forums.developer.nvidia.com/t/rpi-v2-csi-camera-freezes-with-jetson-nano-2gb/159173
3. モニターに接続しなくてもWi-FiをOnにする
- デフォルトの状態では、電源を入れたときにすぐにWi-Fiに接続されず、一度モニター画面上でログイン操作をする必要があります。
- 以下の記事を参考に、ログインしなくてもWi-Fiに接続されるようにしました。これで、HDMIとUSBキーボード、マウスを取り外してよくなりました。
Jetson Nano 2GB (Ubuntu) でログインしなくても無線LAN に接続できるようにする
- JetsonのIPアドレスを毎回調べずに済むよう、Wi-Fiルータで固定IPアドレスを割り当てるように設定しました。
- ログインするマシンの/etc/hostsにホスト名jetsonを追加しました。これで、ssh yzlab@jetson のようにログイン出来るようになります。
4. X Window Systemの無効化
- ターミナルしか使わないので、リソースを節約するために以下のコマンドでターミナルで起動するように変更しました。コマンド実行後、再起動が必要です。実行前後で使用メモリを600MB以上減らすことが出来ました。
$ sudo systemctl set-default multi-user.target
JupyterLab
コースの指示に従い、Dockerコンテナを起動します。
ただし、起動オプションに少し手を加えています。
- USBカメラではなく、CSI経由で映像を入力する場合、–volume /tmp/argus_socket:/tmp/argus_socketをつけます。
- 先程増やしたswapのサイズを指定します。これをやらないと、後の検出フェーズでフリーズしてしまいます。
$ mkdir nvdli-data
$ sudo docker run --runtime nvidia -it --rm --network host \
--volume ~/nvdli-data:/nvdli-nano/data \
--volume /tmp/argus_socket:/tmp/argus_socket \
--memory=500M --memory-swap=8G \
--device /dev/video0 \
nvcr.io/nvidia/dli/dli-nano-ai:v2.0.1-r32.5.0
Webブラウザで http://jetson:8888/lab
のようにアクセスすると、JupyterLabのログイン画面になります。
(jetsonはJetson Nanoのホスト名)
Image Classification
CNN(Convolutional Neural Network)モデルを用いて、入力映像を幾つかの種類に分類するというものです。
CNNは、人間の脳細胞のニューロンのネッワークを模して作られた人工的なニューラルネットワークの一つです。ニューラルネットワークは、複数のレイヤーに分かれており、入力データを変換して次のレイヤーに渡すことを繰り返し、それらを最終レイヤーで組み合わせて推論を行います。
CNNモデルのトレーニングや、トレーニング後のモデルを使った推論には多量の演算処理が必要となります。
多数のコアを持つGPUを用いることで、並列に演算が出来るため、高速に処理を行うことが出来ます。
サンプルとして、以下のプロジェクトが入っています。
- Thumbs Project : 手の映像を thumbs-up / thumbs-down の2つに分類する。
- Emotions Project : 顔の映像を none / happy / sad / angry の4つに分類する。
- Fingers Project : 手の映像から、何本の指が立っているかで1〜5に分類する。
Thumbs Projectを例にとると、以下の手順で映像の分析を行うことが出来ます。
- JupyterLabを起動し、classification_interactive.ipynbを開く。
- Code blocksを上から順に実行していく。最後のブロックを実行すると、インタラクティブツールが立ち上がる。
- カメラから親指を上げた状態と下げた状態の画像を、角度を変えながら複数取り込み、教師データ(訓練用データ)を作成する。
- 教師データを用いて学習(deep learning)を行い、モデルを作成する。
- 作成されたモデルに基づいてリアルタイム検出を行う。
下のスクリーンショットは、実際に検出させている様子です。
- 親指が上向きと判定されると、thumbs_upのスライダーが1に近付きます。下向きだと判定されると、thumbs_downのスライダーが1に近付きます。
- 使用しているモデルは、ResNet-18(Residual neural network)で、代表的なDeep Learningのモデルです。224 x 224の解像度で処理を行っています。
- 教師データとして、角度や位置を変えながら、手の画像を撮影します。上向き15枚、下向き15枚の合計30枚の画像を撮影しました。
- ただし、カメラの向きを変えて背景が変わると、精度が下がります。色々な状況に対応するためには、データ質と量を改善する必要があります。
Image Regression
Image Classificationが離散的な出力を行うのに対し、Image Regressionは、連続的な出力を行います。
サンプルの Face XY Project では、顔の映像を入力すると、左右の眼や鼻の座標を出力します。
- 映像の中で右目(または左目/鼻)の位置を検出し、その座標に青い○印を表示します。
- 使用しているモデルと画像の解像度は、Image Classificationと同様です。ただしResNet-18の最終レイヤーが異なっており、X,Yの2つ値を出力するようになっています。
- カメラにうつる顔の位置や角度を少しずつ変えながら、右目(または左目/鼻)の位置を指定し、教師データを作成します。右目/左目/鼻、それぞれ20個ずつデータを作成しました。
ちなみにこの作業、カメラではなく画面に顔を向けないと操作が出来ないため、苦労しました。
カメラをPCの画面に近付けてやったのですが、どうしても目線が変な方向にいってしまいます。そのためか、検出の精度も悪く、顔を少し傾けたり、位置が悪いと、全然違う場所を指し示してしまいます。
今回のようなデータを作成するときは、二人以上で作業を行い、カメラにうつる人と、作成操作を行う人を分けないと難しいです。
最後に
今回はJetson Nanoを使って、Deep Learningによる映像解析を試してみました。
- 小さいながらパワーがあり、リアルタイムで映像の解析を行うことが出来ます。ResNet18を使った960×544ピクセルの人物検出で14FPS出るそうです。
- ドキュメントも豊富で、開発者向けのフォーラムもあります。しかし、まだ発展途上のためか、情報の通りにやっても動かないことがあり、試行錯誤しました。
- 少ないサンプルでもそれなりに動いてくれましたが、カメラの向きを変えるなど、条件を少し変えると検出しづらくなります。よく言われるようにAI系の開発ではデータが肝で、高い精度を出そうとするとデータの質と量を高める必要があります。
上記の他に、物体や人物の検出、顔の検出、自動車の検出、骨格検出など、様々なことが出来るようなので、今後試していきたいと思います。