この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
1 はじめに
CX事業本部の平内(SIN)です。
Amazon SageMake(以下、SageMaker)の組み込みアルゴリズムであるオブジェクト検出は、デバイスにインストールしたMXNetフレームワークの上で利用することが可能です。
今回は、MacOSにセットアップしたMXNetでこれを利用してみました。
最初に、動作している様子です。 ResNet-50をベースネットワークとしたのもで5つの商品だけを検出する小さなモデルですが、比較的高速に動作していると思いました。(Macのファンは、少し唸ってますが・・・)
2 データセット
(1) Ground Truth
元となるデータは、ツールで自動的にアノテーションを付加した、Amazon SageMaker Ground Truth(以下、Ground Truth)形式のものです。
参考:[Amazon SageMaker] 撮影と同時にアノテーションを追加してAmazon SageMaker Ground Truth形式のデータを作成してみました
各商品(5種類)は、それぞれ約80枚撮影し、全部で401枚となっています。
(2) データ増幅
アノテーションがそのまま利用できる範囲で、画像を変換し、データは増幅されています。
参考:[Amazon SageMaker] Amazon SageMaker Ground Truth で作成したデータをOpenCVで増幅してみました
変換に使用したリストは以下の通りで、9種類の変換を追加したことで、データ数は、401件から4011件となりました。
# 変換リスト
convertList = [
{"function": saturation, "param": 0.6}, # 彩度
{"function": saturation, "param": 0.8},
{"function": brightness, "param": 0.8}, # 明度
{"function": contrast, "param": 0.8}, # コントラスト
{"function": contrast, "param": 1.6}, # コントラスト
{"function": contrast, "param": 2.0}, # コントラスト
{"function": mosaic, "param": 0.5},# モザイク
{"function": gaussian, "param": 10.0},# ガウスノイズ
{"function": noise, "param": 0.01}, # ごま塩ノイズ
]
全データ: 401件
増幅後データ: 4011件
(3) RecordIO形式
効率的に学習に回せるように、上記のデータは、RecordIO形式に変換されています。
参考:[Amazon SageMaker] Amazon SageMaker Ground Truth で作成したデータをオブジェクト検出で利用可能なRecordIO形式に変換してみました
学習用と検証用に8対2で分割しています。
全データ: 4010件 [0]ASPARA: 800件 [1]CRATZ: 800件 [2]PRETZEL: 800件 [3]PRIME: 800件 [4]OREO: 810件
ASPARA => 640:160 残り:3210件
CRATZ => 640:160 残り:2410件
PRETZEL => 640:160 残り:1610件
PRIME => 640:160 残り:810件
OREO => 648:162 残り:0件
Train: 3208件
.
├── train.idx
├── train.lst
├── train.rec <= 657.8M
├── validation.idx
├── validation.lst
└── validation.rec <= 171.0M
出力された、train.recとvalidation.recは、S3にアップロードされます。
3 オブジェクト検出
学習は、組み込みアルゴリズムのオブジェクト検出(Object Detection)で行われています。
使用したインスタンスは、ml.p3.8xlarge ✕ 1(ボリュームサイズ 50GB)で、epochを50回して、トレーニング時間は、1622秒でした。
使用したハイパーパラメータは、以下のとおりです。
base_network resnet-50
early_stopping false
epochs 50
image_shape 512
label_width 350
learning_rate 0.001
lr_scheduler_factor 0.1
mini_batch_size 16
momentum 0.9
nms_threshold 0.45
num_classes 5
num_training_samples 3208
optimizer sgd
overlap_threshold 0.5
use_pretrained_model 1
weight_decay 0.0005
学習の経過です。
epoch mAP smooth_l1 cross_entropy
-----------------------------------------
1 0.064 0.54 1.083
10 0.127 0.172 0.731
20 0.307 0.09 0.535
30 0.571 0.06 0.414
40 0.763 0.047 0.325
49 0.807 0.042 0.282
出力されたモデル(model.tar.gz)のサイズは、99.6MByteとなっていました。
4 MXNet用への変換
上記で作成したモデルを、MXNetのフレームワーク上で利用する場合、変換(損失層の削除とNMS層を追加)が必要になります。
変換は、https://github.com/zhreshold/mxnet-ssdのdeploy.pyを利用しました。
また、このツールは、Python2系での実行が必要ということで、作業は、SageMakerのノートブックインスタンス(JupyterNotebook)で行いました。
作業した内容は、以下のとおりです。
# 元モデル(model.tar.gz)を作業フォルダ(trained-model)に展開する
TMP_FOLDER='trained-model'
!tar -xvzf $TMP_FOLDER/model.tar.gz -C $TMP_FOLDER/
# ツールをダウンロードして、ヘルプを確認(動作確認)
!git clone https://github.com/zhreshold/mxnet-ssd.git
!python mxnet-ssd/deploy.py -h
# ハイパーパラメータを確認
!cat $TMP_FOLDER/hyperparams.json | jq
# ツールによる変換
!python mxnet-ssd/deploy.py --network resnet50 --num-class 5 --nms .45 --data-shape 512 --prefix $TMP_FOLDER/model_algo_1
上記により、作業フォルダには、deploy_model_algo_1-0000.params及び、deploy_model_algo_1-symbol.jsonが生成されます。
// 変換前
trained-model/model_algo_1-symbol.json
trained-model/hyperparams.json
trained-model/model.tar.gz
trained-model/model_algo_1-0000.params
// 変換後
trained-model/deploy_model_algo_1-0000.params
trained-model/deploy_model_algo_1-symbol.json
trained-model/model_algo_1-symbol.json
trained-model/hyperparams.json
trained-model/model.tar.gz
trained-model/model_algo_1-0000.params
5 コード
Mac上で実行したコードです。
変換で生成したモデルを、./modelに置き、Webカメラの画像を入力インターフェースに変換して推論にかけています。
こちらは、Python3で動作しています。
import mxnet as mx
import cv2
import numpy as np
from collections import namedtuple
# Webカメラ
DEVICE_ID = 0
WIDTH = 800
HEIGHT = 600
FPS = 24
MODEL_PATH = './model/deploy_model_algo_1'
CLASSES = ['ASPARA','CRATZ','PRETZEL','PRIME','OREO']
COLORS = [(128, 0, 0),(0, 128, 0),(0, 0, 128),(128, 128, 0),(0, 128,128)]
def main():
# Model Initialize
SHAPE = 512
input_shapes=[('data', (1, 3, SHAPE, SHAPE))]
Batch = namedtuple('Batch', ['data'])
sym, arg_params, aux_params = mx.model.load_checkpoint(MODEL_PATH, 0)
mod = mx.mod.Module(symbol=sym, label_names=[], context=mx.cpu())
mod.bind(for_training=False, data_shapes=input_shapes)
mod.set_params(arg_params, aux_params)
# Video Initialize
cap = cv2.VideoCapture (DEVICE_ID)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, WIDTH)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, HEIGHT)
cap.set(cv2.CAP_PROP_FPS, FPS)
width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
fps = cap.get(cv2.CAP_PROP_FPS)
print("fps:{} width:{} height:{}".format(fps, width, height))
while True:
# カメラ画像取得
_, frame = cap.read()
if(frame is None):
continue
# 入力インターフェースへの画像変換
frame = frame[0 : int(height), 0 : int(height)] # 800*600 -> 600*600
frame = cv2.resize(frame, (SHAPE, SHAPE)) # 600*600 -> 512*512
img = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # BGR -> RGB
img = img.transpose((2, 0, 1)) # 512,512,3 -> 3,512,512
img = img[np.newaxis, :] # 3,512,512 -> 1,3,512,512
# 推論
mod.forward(Batch([mx.nd.array(img)]))
prob = mod.get_outputs()[0].asnumpy()
prob = np.squeeze(prob)
index = int(prob[:, 0][0])
confidence = prob[0][1]
x1 = int(prob[0][2] * SHAPE)
y1 = int(prob[0][3] * SHAPE)
x2 = int(prob[0][4] * SHAPE)
y2 = int(prob[0][5] * SHAPE)
# 表示
print("[{}] {}, {}, {}, {}".format(CLASSES[index], x1,y1,x2,y2))
if(confidence > 0.6): # 信頼度
frame = cv2.rectangle(frame,(x1, y1), (x2, y2), COLORS[index],2)
frame = cv2.rectangle(frame,(x1, y1), (x1 + 150,y1-20), COLORS[index], -1)
label = "{} {:.2f}".format(CLASSES[index], confidence)
frame = cv2.putText(frame,label,(x1+2, y1-2), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255,255,255), 1, cv2.LINE_AA)
# 画像表示
cv2.imshow('frame', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
if __name__ == '__main__':
main()
6 最後に
今回は、オブジェクト検出のモデルを、Mac上で利用してみました。 この要領を使用すると、RaspberryPiや、Jetson NanoもMxNetをインストールして利用可能かも知れません。