要約
- 本記事では、マイクロソフト社のクラウドサービスであるAzureのCustom Visionを使用して、画像分類モデルを作成します。
- その画像分類モデルをAIエッジコンピューター「AE2100」へデプロイして、推論を実行するまでを説明します。
はじめに
Custom Visionとは、GUI操作だけで簡単にAI学習モデルの作成ができるマイクロソフト社のSaaS型のクラウドサービスで、製造業の検品作業や倉庫の類似部品の識別(管理)などで利用されています。
Custom Visionで学習モデルを作成すると、専用のURLが割り当てられます。Azure上で推論させる場合は、このURLに対し推論させたい画像データをREST APIで送信すると、推論結果を取得することができます。
しかし、Azureは、従量課金制のため、Azure上で推論(API呼び出し)される度にコストがかかりまいます。また、クラウドでのAIの処理は、クラウドに画像データを送信する必要があるため、リアルタイムな推論ができないデメリットもあります。
本記事では、Custom Visionで作成した学習モデルをAE2100へデプロイし、推論実行する方法を解説します。これにより、無料かつリアルタイムで推論することができます。
環境
用意するものは、以下の3つです。
①カメラによる撮影または、kaggle等のオープンデータをダウンロードして、学習させる画像を用意してください。
本記事では、製造業における検品作業を想定して、ゴルフボールの画像を使用したいと思います。
②Custom Visionで学習モデルを作成するために、Azureのアカウントを用意してください。
③AE2100のマニュアル(SDK取扱説明書 DeepLearning編)に従って、AE2100の環境と開発環境ご用意してください。
なお、AE2100および開発環境は以下のものを前提としています。
【開発環境】
OS:Ubuntu20.04 LTS OpenVINO:2021.4.1 LTS
【AE2100】
本体ファーム:V3.6.0(HDDL Daemon:OpenVINO2021.4.1 LTS)
標準コンテナ:OpenVINO有(Ubuntu20.04版)2021年10月版
プロジェクトの作成(Custom Vision)
さっそく、Custom Visionのwebページに移動し、[サインイン]の後、[NEW PROJECT]を選択して、新しいプロジェクトを作成していきます。
Custom Vision サービスを使用するには、Azure で Custom Vision Training リソースと Prediction リソースを作成する必要がありますので、Create new project>Resourceの[Create new]をクリックして、リソースを作成します。
この時、「Name」は、Azure上で一意な名前にしなければならない点と、「pricing Tier」の選択によって、使用できる機能と料金が変化する点を注意してください。今回は、あくまでお試しなので、無料で利用できる「F0」を選択しています。
リソースの作成が完了したら、プロジェクトの詳細を設定します。ここではAIの種類を選択します。
今回、ゴルフボールの正しい並び(OK)と誤った並び(NG)の判定をおこなうために、「Project Type」は”Classification”を選択します。
また、AE2100へデプロイ可能なAIモデルを出力するためには、「Domains」は”General(compact)”もしくは”General(compact)[S1]”を選択する必要があります。今回は、精度の高い”General(compact)[S1]”を選択します。
残りの選択項目は、デフォルトのままで構いません。
プロジェクトの詳細を設定後[Create project]をクリックすると、以下のような画面に遷移します。
学習モデルの作成(Custom Vision)
続いて、公式ドキュメントの通りに、データセット(学習させたい画像)をアップロードしていきます。
公式ドキュメントでは、タグごとに最低30枚の画像を学習することが推奨になりますが、ここではOKとNGの画像をそれぞれ10枚ずつ学習します。
なお、正しい並び(OK)は、左から黄、白、橙、白の順で列毎に同じ色のボールが並んでおり、それ以外の並びの画像は誤った並び(NG)としております。
正しい並び(OK)と誤った並び(NG)の画像(「1_1_OK_images_TRAIN」、「1_2_NG_images_TRAIN」フォルダの画像)に対してタグ付けし、[Train]をクリックして学習を開始します。
3分程度で学習が完了します。その後、テスト画像(「2_1_OK_images_TEST_to_TRAIN」、「2_2_NG_images_TEST_to_TRAIN」フォルダの画像)のアップロードを行い、[Quick Test]でモデルの評価を実施します。
「OK」を正しく「OK」と予測した画像が9/10枚、
「NG」を正しく「NG」と予測した画像が2/10枚でした。。
「NG」の正解率が低いので、「Quick Test」で使用したテスト画像に正しいタグを付けて再度学習します。
2回目は、「OK」を正しく「OK」と予測した画像が10/10枚、「NG」を正しく「NG」と予測した画像が7/10枚でしたので、NGのテスト画像(「3_2_NG_images_TEST_to_TRAIN」フォルダの画像)10枚のみを再度学習させました。
そうすると、「OK」、「NG」の両方のテスト画像を10/10枚正しく予測させることができました!
学習モデルの精度を上げるには、いろいろな方法があるようですので、自作の学習モデルを作成する際は、試行錯誤してみてください。
学習モデルのエクスポート(Custom Vision)
それでは、先ほど作成した学習モデルをエクスポートしていきます。
「performance」>「Export」>「OpenVino」>「Export」の順にクリックし、AE2100で動作させるのに最適なOpenVINO形式で学習モデルをエクスポートします。
ダウンロードして開いたzipファイルが、以下のようになっていれば問題ありません。
AE2100へのデプロイ
エクスポートした学習モデルをAE2100へデプロイします。
学習モデルのzipファイル、テスト画像、サンプルコード(predict.py)をzipに圧縮して、SCPでAE2100へ転送します。
なお、サンプルコード(predict.py)は、OpenVINO形式の学習モデルを読み込み、入力画像に対してラベル毎の推論結果(確率)を表示するプログラムになっています。
サンプルコード(predict.py)の確認は、ここをクリック
import argparse
import pathlib
import numpy as np
import PIL.Image
from openvino.inference_engine import IECore
class Model:
def __init__(self, xml_filepath, bin_filepath):
ie = IECore()
net = ie.read_network(str(xml_filepath), str(bin_filepath))
assert len(net.input_info) == 1
self.exec_net = ie.load_network(network=net, device_name='CPU')
self.input_name = list(net.input_info.keys())[0]
self.input_shape = net.input_info[self.input_name].input_data.shape[2:]
self.output_names = list(net.outputs.keys())
def predict(self, image_filepath):
# The model requires RGB[0-1] NCHW input.
image = PIL.Image.open(image_filepath).resize(self.input_shape)
input_array = np.array(image)[np.newaxis, :, :, :]
input_array = input_array.transpose((0, 3, 1, 2)) # => (N, C, H, W)
input_array = input_array / 255 # => Pixel values should be in range [0, 1]
return self.exec_net.infer(inputs={self.input_name: input_array})
def print_outputs(outputs):
outputs = list(outputs.values())[0]
for index, score in enumerate(outputs[0]):
print(f"Label: {index}, score: {score:.5f}")
def main():
parser = argparse.ArgumentParser()
parser.add_argument('xml_filepath', type=pathlib.Path)
parser.add_argument('bin_filepath', type=pathlib.Path)
parser.add_argument('image_filepath', type=pathlib.Path)
args = parser.parse_args()
model = Model(args.xml_filepath, args.bin_filepath)
outputs = model.predict(args.image_filepath)
print_outputs(outputs)
if __name__ == '__main__':
main()
OpenVINOを利用したプログラムの実装方法につきましては、別の記事で詳しく記載されておりますので、ご参照ください。
続いて、teratermでAE2100へログインし、標準コンテナ上に「CV_Classification」ディレクトリを作成します。
root@ae2100:~# docker start ubuntu-openvino
root@ae2100:~# docker exec -it ubuntu-openvino /bin/bash
root@3a69a45317dd:/opt/intel/openvino_2021.4.689# cd
root@3a69a45317dd:~# mkdir CV_Classification
root@3a69a45317dd:~# exit
exit
ホストからコンテナへzipファイルをコピーします。
root@ae2100:~# docker cp Send_TO_AE2100.zip ubuntu-openvino:/root/CV_Classification
そして、コンテナ内でunzipを実行(解凍)します。
(unzipコマンドがない場合は、インターネットに接続した状態でインストールしてください。)
root@ae2100:~# docker start ubuntu-openvino
root@ae2100:~# docker exec -it ubuntu-openvino /bin/bash
root@3a69a45317dd:/opt/intel/openvino_2021.4.689# cd /root/CV_Classification/
root@3a69a45317dd:~/CV_Classification# apt install unzip
root@3a69a45317dd:~/CV_Classification# unzip Send_TO_AE2100.zip
root@3a69a45317dd:~/CV_Classification# cd Send_TO_AE2100
root@3a69a45317dd:~/CV_Classification/Send_TO_AE2100# unzip ExportFile_OpenVino.zip
#結果の出力
最後に推論を実行していきましょう。
root@3a69a45317dd:~/CV_Classification/Send_TO_AE2100# python3 predict.py model.xml model.bin OK_image.jpg
Label: 0, score: 0.16326
Label: 1, score: 0.83674
root@3a69a45317dd:~/CV_Classification/Send_TO_AE2100# python3 predict.py model.xml model.bin NG_image1.jpg
Label: 0, score: 0.94221
Label: 1, score: 0.05779
root@3a69a45317dd:~/CV_Classification/Send_TO_AE2100# python3 predict.py model.xml model.bin NG_image2.jpg
Label: 0, score: 0.97225
Label: 1, score: 0.02775
Label: 0が誤った並び(NG)、Label: 1が正しい並び(OK)である各確率をscoreで表示しています。
よって、「OK_image.jpg」は、16%の確率でNG、83%の確率でOKと推論しています。
「NG_image1.jpg」については94%でNG、6%でOK、「NG_image2.jpg」については97%でNG、3%でOKということで、正しく推論できていることがわかります。
続いて、viコマンドで「predict.py」の14行目「self.exec_net = ie.load_network(network=net, device_name='CPU')」を「self.exec_net = ie.load_network(network=net, device_name='HDDL')」に変更して、VPUを指定して実行できるか確認してみましょう。
root@3a69a45317dd:~/CV_Classification/Send_TO_AE2100# vi predict.py
サンプルコード(predict.py)の確認は、ここをクリック
import argparse
import pathlib
import numpy as np
import PIL.Image
from openvino.inference_engine import IECore
class Model:
def __init__(self, xml_filepath, bin_filepath):
ie = IECore()
net = ie.read_network(str(xml_filepath), str(bin_filepath))
assert len(net.input_info) == 1
self.exec_net = ie.load_network(network=net, device_name='HDDL')
self.input_name = list(net.input_info.keys())[0]
self.input_shape = net.input_info[self.input_name].input_data.shape[2:]
self.output_names = list(net.outputs.keys())
def predict(self, image_filepath):
# The model requires RGB[0-1] NCHW input.
image = PIL.Image.open(image_filepath).resize(self.input_shape)
input_array = np.array(image)[np.newaxis, :, :, :]
input_array = input_array.transpose((0, 3, 1, 2)) # => (N, C, H, W)
input_array = input_array / 255 # => Pixel values should be in range [0, 1]
return self.exec_net.infer(inputs={self.input_name: input_array})
def print_outputs(outputs):
outputs = list(outputs.values())[0]
for index, score in enumerate(outputs[0]):
print(f"Label: {index}, score: {score:.5f}")
def main():
parser = argparse.ArgumentParser()
parser.add_argument('xml_filepath', type=pathlib.Path)
parser.add_argument('bin_filepath', type=pathlib.Path)
parser.add_argument('image_filepath', type=pathlib.Path)
args = parser.parse_args()
model = Model(args.xml_filepath, args.bin_filepath)
outputs = model.predict(args.image_filepath)
print_outputs(outputs)
if __name__ == '__main__':
main()
root@3a69a45317dd:~/CV_Classification/Send_TO_AE2100# python3 predict.py model.xml model.bin OK_image.jpg
Label: 0, score: 0.25000
Label: 1, score: 0.75000
root@3a69a45317dd:~/CV_Classification/Send_TO_AE2100# python3 predict.py model.xml model.bin NG_image1.jpg
Label: 0, score: 0.93506
Label: 1, score: 0.06494
root@3a69a45317dd:~/CV_Classification/Send_TO_AE2100# python3 predict.py model.xml model.bin NG_image2.jpg
Label: 0, score: 0.95703
Label: 1, score: 0.04312
こちらも問題ないみたいですね!
ターミナル上で出力が少し遅かったですが、GPUやHDDLで実行する際は、モデルのロード時間がかかりますので、その影響かと思います。
また、HDDLのパフォーマンスを最大限に活かすためには、非同期処理のプログラムを作成する必要があります。
別途、参考の記事がありますので、挑戦してみてください。
#参考
念のため、OpenVINOのベンチマークを行ってみます。
(※AE2100上でベンチマークを行うには、OpenVINOのフルパッケージをインストールする必要がありますので、リソースにご注意ください。)
CPUのベンチマーク結果
root@3a69a45317dd:/opt/intel/openvino_2021.4.689/deployment_tools/tools/benchmar
k_tool# python3 benchmark_app.py -d CPU -i /root/CV_Classification/Send_TO_AE210
0/OK_image.jpg -m /root/CV_Classification/Send_TO_AE2100/model.xml
~中略~
Count: 572 iterations
Duration: 60481.09 ms
Latency: 414.16 ms
Throughput: 9.46 FPS
GPUのベンチマーク結果
root@3a69a45317dd:/opt/intel/openvino_2021.4.689/deployment_tools/tools/benchmar
k_tool# python3 benchmark_app.py -d GPU -i /root/CV_Classification/Send_TO_AE210
0/OK_image.jpg -m /root/CV_Classification/Send_TO_AE2100/model.xml
~中略~
Count: 1236 iterations
Duration: 60328.87 ms
Latency: 195.01 ms
Throughput: 20.49 FPS
VPUのベンチマーク結果
root@3a69a45317dd:/opt/intel/openvino_2021.4.689/deployment_tools/tools/benchmar
k_tool# python3 benchmark_app.py -d HDDL -i /root/CV_Classification/Send_TO_AE2100/OK_image.jpg -m /root/CV_Classification/Send_TO_AE2100/model.xml
~中略~
Count: 2432 iterations
Duration: 60212.76 ms
Latency: 197.85 ms
Throughput: 40.39 FPS
ベンチマークの結果を表にすると、以下のようになります。
CPUに対してVPUのスループットが4倍以上の数値であることをご確認いただけるかと思います。
まとめ
Custom VisionとAE2100を使用することで、簡単にAIの学習モデルを作成し、実運用の環境でリアルタイムな推論ができます。
Custom Visionは、GPUサーバーなどのAIの学習環境を構築が不要で、無料枠も充実しているため、気軽に利用を開始することができます。
皆様の現場でどの程度使えそうか(精度)は、やってみないと分からないところもありますので、ぜひ色々な使い方で試してみてください。