Taste of Tech Topics

Acroquest Technology株式会社のエンジニアが書く技術ブログ

エッジでLookout for Visionを実行すると爆速だった話

こんにちは、機械学習エンジニアの駿です。

先日庭の花壇にヒマワリの種をまいたのですが、早速芽が出てきました。
夏に黄色い花が咲くのが今から楽しみです。

今回はAWSの外観検査サービスである Amazon Lookout for Vision が、 AWS IoT Greengrass を使ってエッジでの推論が可能になったため、試してみました。

今までは Lookout for Visionクラウド側での推論(判定)しかできず、画像をネットワーク越しに送るため、オーバーヘッドが発生していました。
そのため、例えば工場の生産ラインで外観検査をしていたとすると、製品の撮影・検査をした後で、異常なモノを仕分けをしたりしますが、それに時間がかかってしまう、といった状況が発生することになってしまいます。

しかし、 Lookout for Vision のモデルを Greengrass でパッケージ化しエッジデバイスに配置して検査を行うことで、画像を送信するオーバーヘッドがなくなり、リアルタイムで処理ができるようになります。

Amazon Lookout for Vision

Lookout for Visionは画像が正常か異常を判定する、異常検知のサービスです。
教師画像は最低30枚と少ない枚数で始められますが、非常に高い精度で異常を判定することができます。

詳細については以下の記事があるので、そちらをご覧ください。

acro-engineer.hatenablog.com

AWS IoT Greengrass

Greengrass は Raspberry Piなどのエッジデバイス上にIoTアプリケーションを構築、デプロイ、管理するためのクラウドサービスです。
Lambda関数、Dockerコンテナなどをコンポーネントとしてパッケージ化し、エッジデバイスにデプロイ、実行することができます。

そんな Greengrass が2021年末に Lookout for Vision モデルのコンポーネントに対応し、
GPUを備えたエッジデバイスにデプロイすることで、クラウドに画像を送ることなく、オンプレミスかつリアルタイムの外観検査ができるようになりました。

構成

今回はエッジデバイスにJetsonNanoを使用し、 Greengrass サービスを通じて、 Lookout for Vision モデルコンポーネントと、 それを利用するための EdgeAgent コンポーネントをデプロイします。

今回の構成図(公式ドキュメントより)

手順

大まかな流れは下記のようになります。

  1. JetsonNano 上で Greengrass のサービスを起動する
  2. Lookout for Vision モデルを Greengrass 用にコンポーネント化する
  3. モデルコンポーネントAWSが提供している EdgeAgent を JetsonNano にデプロイする
  4. Python スクリプトからデプロイされたモデルを起動し、検査を実行する

早速、詳細の説明に入ります。

1. JetsonNano 上で Greengrass のサービスを起動する

公式ドキュメントでは動作環境としてJetson Xavierを推奨していますが、すぐに用意できなかったので、今回は JetsonNano を使ってみました。
ただし、性能を考慮した場合、実際の運用などでは Jetson Xavier などを利用するほうが安全だと思われます。

(1) JetsonNano に JetPack をインストール

エッジデバイス上で Lookout for Vision のモデルを使用するためには、GPUとCUDA、TensorRT などのライブラリが必要です。
JetPack を使うと、これらのライブラリをひとつひとつインストールする必要がなく、 既に整った環境を作ることができます。

JetPack4.5.1 のSDカードイメージをダウンロードし、公式ドキュメント に従ってセットアップを行います。

なお、Greengrass でデプロイする Lookout for Vision モデルが対応しているのが、バージョン4.4と4.5系のみのため、間違えて最新バージョンを入れないように注意が必要です。

(2) Greengrass サービスおよび Lookout for Vision モデルの起動に必要なライブラリのインストール

Greengrass サービスの起動に Java が必要になります。
今回はAWSが提供する Corretto をインストールしました。

また、Lookout for Vision のモデルの起動に Python3.8 もしくは 3.9 が必要です。
今回は 3.8 をインストールしました。

(3) クライアントアプリに必要なライブラリのインストール

公式ドキュメント に従って、 grpc をインストールし、サービス定義ファイルからクライアントインターフェイスを生成します。

また、画像読み込みのための Pillow もインストールします。

(4) Greengrass サービスのダウンロードおよび起動

Greengrass のコンソールから 「1つのCoreデバイスをセットアップ」を選択します。

「1つのCoreデバイスをセットアップ」

コアデバイス名など必要な情報を入力すると、インストーラのダウンロードコマンドおよび実行コマンドが生成されるので、エッジデバイス上でコマンドを実行します。

インストールが終わると、自動で Greengrass サービスの実行が始まります。

また、インストール中に Greengrass ユーザ ggc_user が作成されています。
そのままでは Greengrass でデプロイされたコンポーネントGPU にアクセスできないため、 ggc_user を video グループに追加します。

sudo usermod -a -G video ggc_user

Greengrass コンソールから追加したエッジデバイスが確認でき、ステータスが「正常」となっていたら Greengrass の準備は完了です。

JetsonNanoがCoreデバイスとして設定できた

(5) DLR のインストール

Lookout for Vision が libdlr.so を必要とするのですが、 pip でインストールできるDLRには .so ファイルが含まれていないようです。

ggc_user として whl からインストールすることで、 モデルコンポーネントが読み込めるようになります。
下記コマンドを ggc_user として実行してください。

curl -O https://neo-ai-dlr-release.s3-us-west-2.amazonaws.com/v1.10.0/jetpack4.5/dlr-1.10.0-py3-none-any.whl
python3.8 -m pip install dlr-1.10.0-py3-none-any.whl

2. Lookout for Vision モデルを Greengrass 用にコンポーネント化する

既に学習済みのモデルがあるものとします。

プロジェクトのページに遷移し左のメニューで「モデルのパッケージ」を選択します。
「モデルパッケージングジョブを作成」ボタンを押し、「モデルの選択」など必要な項目を埋めていきます。

「モデルパッケージングジョブを作成」

  • ターゲットハードウェア設定

    2022/05/07現在、プリセットの設定は Jetson Xavier 用しかないため、JetsonNano を使用する際は「ターゲットプラットフォーム」を選択して、設定を行います。

    項目
    オペレーティングシステム LINUX
    アーキテクチャ ARM64
    アクセラレーター NVIDIA
    コンパイラオプション {"gpu-code": "sm_53", "trt-ver": "7.1.3", "cuda-ver": "10.2"}

    コンパイラオプションは使用するデバイス、インストールしたライブラリのバージョンによって異なります。
    GPUコード、TensorRT バージョン、CUDA バージョンをそれぞれ指定します。
    上記値は JetsonNano+JetPack4.5.1の場合の値です。

    ターゲットハードウェアの設定

最後に「モデルパッケージングジョブを作成」ボタンを押して、パッケージングを開始します。
コンソール上で、「成功」ステータスになったら完了です。

Greengrass コンソールのコンポーネントページからも作成したモデルコンポーネントを確認することができます。

3. モデルコンポーネントAWSが提供している EdgeAgent を JetsonNano にデプロイする

Greengrass コンソールのデプロイページからデプロイを作成します。

今回はデプロイターゲットにコアデバイスを選択し、上で登録した JetsonNano にのみデプロイします。

コンポーネントの選択」画面で、パッケージした Lookout for Vision モデルコンポーネントを選択します。
このコンポーネントAWSが提供する aws.iot.lookoutvision.EdgeAgent に依存しているため、 そちらも自動でデプロイされます。

コンポーネントの選択」

そのほかの設定を変更する必要は必要ありません。
「デプロイ」を選択して、 JetsonNano にデプロイします。

デプロイのステータスが「完了」になったら成功です。

4. Pythonスクリプトからデプロイされたモデルを起動し、検査を実行する

エッジデバイス上で ggc_user としてログインします。

Pythonインタプリタを起動し、モデルの起動と検査を試してみます。

(1) モデルの起動

import grpc
from edge_agent_pb2_grpc import EdgeAgentStub
import edge_agent_pb2 as pb2
channel = grpc.insecure_channel("unix:///tmp/aws.iot.lookoutvision.EdgeAgent.sock")
stub = EdgeAgentStub(channel)
model_component_name = "lfv_component_aarm"

# まずはモデルが止まっていることを確認します。
model_description_response = stub.DescribeModel(pb2.DescribeModelRequest(model_component=model_component_name))
model_description_response.model_description.status == pb2.STOPPED
# -> True

# モデルを起動します
stub.StartModel(pb2.StartModelRequest(model_component=model_component_name))
# -> status: STARTING
# 起動するのを待ってから
model_description_response = stub.DescribeModel(ob2.DescribeModelRequest(model_component=model_component_name))
model_description_response.model_description.status == pb2.RUNNING
# -> True

(2) 検査実行

モデルが起動したら画像に対して検査を実行できます。

エッジデバイスに画像を用意して、Pillow で読み込んだものをモデルに送ります。

from PIL import Image

image = Image.open(image_path)
image = image.convert("RGB")
detect_anomalies_response = stub.DetectAnomalies(
    pb2.DetectAnomaliesRequest(
        model_component=model_component_name,
        bitmap=pb2.Bitmap(
            width=image.size[0],
            height=image.size[1],
            byte_data=bytes(image.tobytes())
        )
    )
)

is_anomalous = detect_anomalies_response.detect_anomaly_result.is_anomalous
confidence = detect_anomalies_response.detect_anomaly_result.confidence
print(f"Image is anomalous - {is_anomalous}")
print(f"confidence - {confidence:.2}")
# -> Image is anomalous - True
# -> confidence - 0.97

(3) モデルの停止

stub.StopModel(StopModelRequest(model_component=model_component_name))
model_description_response = stub.DescribeModel(ob2.DescribeModelRequest(model_component=model_component_name))
model_description_response.model_description.status == pb2.STOPPED
# -> True

channel.close()

結果

精度

前述の投稿で使用している Metal Nut 画像から正常を5枚、異常を5枚使って検査を実行したところ、すべて正しく判定することができました。

クラウド側で判定した場合と比較して、エッジデバイス上で判定しても同等の精度が出ることがわかりました。

実行時間

下記の関数を作り、画像1枚を推論するのにかかる時間を計測してみました。

def timeit():
    start_time = time.time()
    stub.DetectAnomalies(...)
    print(f"took {time.time() - start_time} seconds")

上記関数を10回実行した結果は下記のようになり、平均で0.25秒/枚で検査ができることになります。

# 所要時間(ms)
1 399.8
2 374.2
3 212.9
4 216.0
5 217.8
6 212.5
7 215.5
8 212.8
9 218.3
10 216.5
平均 249.6

awscli でクラウド側の Lookout for Vision モデルを使用した場合はネットワークの往復も含めて約3秒でした。

awscli の場合は画像変換の時間も含まれるため単純な比較はできませんが、エッジで実行することで10倍以上早くなっています。
Jetson Xavier などより計算力のあるデバイスを用いることで、さらにリアルタイム性のある外観検査ができるようになりますね。

オレゴンリージョンの Lookout for Vision を使用しているため、東京リージョンのものを用いるよりもさらに伝送時間がかかっていると思われます。)

料金

Greengrass を使ってエッジデバイスにデプロイした Lookout for Vision モデルを使用する場合、エッジ推論ユニットに基づいて月額料金がかかります。
1デバイス上で120検査/分までの検査は1エッジ推論ユニットとして扱われ、1エッジ推論ユニットは月100USD かかります。

個人で実施するにはちょっとお高めになっているので、工場など大規模で検査を行う必要があるユースケースを想定しているのがわかります。

まとめ

今回はGreengrassを使ってLookout for VisionのモデルをJetson Nanoにデプロイし、外観検査を行いました。
普段エッジデバイスを使うことがあまりないこともあり、最初の環境構築でバージョン不整合などでつまづいてしまいました。

実際に動かしてみて、エッジでの検査は画像をネットワーク越しに送信する必要がない分、10倍も早く実行できることがわかりました。
それでいてクラウドと同じモデルを使っているため、精度は同等です。

また、今回は Pythonインタプリタを使って検査を実行しましたが、クライアントアプリを作成して Greengrass コンポーネントとしてデプロイすることもできます。

今度は実際にカメラを繋いで、どれくらいのFPSが出せるのか、なども試してみたいです。

Acroquest Technologyでは、キャリア採用を行っています。
  • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
  • Elasticsearch等を使ったデータ収集/分析/可視化
  • マイクロサービス、DevOps、最新のOSSを利用する開発プロジェクト
  • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長
  少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。 www.wantedly.com