この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
1 はじめに
CX事業本部の平内(SIN)です。
最近、棚に陳列された商品を確認する作業を進めています。
ここまで、商品を検出するためのモデル作成には、Amazon SageMaker(以下、SageMaker)のビルトインアルゴリズムである物体検出(object-detection)を利用してきました。
マーカーなどで、商品の写っている部分を特定することで、ある程度の精度を上げることは可能なのですが、要求に耐えるモデルを作成するためには、一定量の精度の高いデータセットが必要であり、アノテーション作業を伴うデータセット作成のコストを削減することは、なかなか難しいと感じていました。
今回は、アルゴリズムを同じくSageMakerのビルトインであるイメージ分類(image-classification)に変更することで、「データセットの作成コストを大きく下げることができないか」という検討の記録です。
2 image-classification
イメージ分類(image-classification)は、画像自体がどのようなシーンであるかを分類するもので、画像とラベルだけでデータセットが、作成可能です。
ここまで使用してきた物体検出(object-detection)では、ターゲットを囲ったBounding boxが必要であり、これが必要なくなることでデータセット作成のコストは格段に下がります。
※ Ground TruthのJob作成画面より
image-classificationは、本来、画面自体に何が写っているかを分類するもですが、既に、商品の位置を特定し、部分画像として切り出しているので、この切り出した部分を、「全画面」として扱い推論することで、object-detectionの代替とします。
下記は、検出対象を中央の水色枠に固定し、推論を行っている様子です。
3 データセット
準備したデータセットは、白地の上に商品を置いて、800*600で撮影した画像です。 各ラベルごと80枚用意しましたが、フォルダ名をラベル名にして、その下に画像を置いて、プログラムで処理すれば、あっという間に作成できます。
4 学習
5種類のラベル各80枚(計400枚)の画像を8:2(320:80)に分けて学習用と検証用に使用しました。
設定したパラメータは、以下のようになっています。
ml.p2.xlargeでepoch=5で回して143秒でした。
以下は、ログの抜粋ですが、Validation-accuracyが、あっという間に1.0になっているのに驚きます。
Epoch[0] Train-accuracy=0.521875
Epoch[0] Train-top_k_accuracy_5=1.000000
Epoch[0] Time cost=6.138
Epoch[0] Validation-accuracy=0.962500
Epoch[1] Train-accuracy=0.984375
Epoch[1] Train-top_k_accuracy_5=1.000000
Epoch[1] Time cost=2.210
Epoch[1] Validation-accuracy=0.912500
Epoch[2] Train-accuracy=0.993750
Epoch[2] Train-top_k_accuracy_5=1.000000
Epoch[2] Time cost=2.168
Epoch[2] Validation-accuracy=1.000000
Epoch[3] Train-accuracy=1.000000
Epoch[3] Train-top_k_accuracy_5=1.000000
Epoch[3] Time cost=2.167
Epoch[3] Validation-accuracy=1.000000
Epoch[4] Train-accuracy=1.000000
Epoch[4] Train-top_k_accuracy_5=1.000000
Epoch[4] Time cost=2.166
Epoch[4] Validation-accuracy=1.000000
5 動画での利用
以下は、Webカメラの画像を、推論しているコードです。
from boto3.session import Session
import json
import cv2
profile = 'developer'
endPoint = 'sampleEndPoint'
classes = ['ASPARA','CRATZ','OREO','PRETZEL','PRIME']
deviceId = 3 # Webカメラ
height = 600
width = 800
class SageMaker():
def __init__(self, profile, endPoint):
self.__endPoint = endPoint
session = Session(profile_name = profile)
self.__client = session.client('sagemaker-runtime')
def invoke(self, image):
data = self.__client.invoke_endpoint(
EndpointName = self.__endPoint,
Body = image,
ContentType='image/jpeg'
)
results = data['Body'].read()
return json.loads(results)
sageMake = SageMaker(profile, endPoint)
cap = cv2.VideoCapture(deviceId)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, width)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height)
fps = cap.get(cv2.CAP_PROP_FPS)
width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
print("FPS:{} WIDTH:{} HEIGHT:{}".format(fps, width, height))
# 切り出す部分の定義
x1 = int(width/2-80)
x2 = int(width/2+80)
y1 = int(height/2-120)
y2 = int(height/2+120)
while True:
# カメラ画像取得
ret, frame = cap.read()
if(frame is None):
continue
# 部分画像の切り出し
img = frame[y1: y2, x1: x2]
# 推論
_, jpg = cv2.imencode('.jpg', img)
results = sageMake.invoke(jpg.tostring())
# 結果表示
probabilitys = {}
for i,result in enumerate(results):
probabilitys[classes[i]] = result
probabilitys = sorted(probabilitys.items(), key = lambda x:x[1], reverse=True)
(name, probability) = probabilitys[0]
str = "{} {}".format(name, probability)
cv2.putText(frame,str,(20, int(height)-20), cv2.FONT_HERSHEY_SIMPLEX, 2, (255,255,255), 2, cv2.LINE_AA)
# 水色枠の描画
frame = cv2.rectangle(frame,(x1, y1), (x2, y2), (255,255,0),3)
cv2.imshow('frame', frame) # カメラ画像
cv2.imshow('img', img) # 切り出し画像
cv2.waitKey(1)
cap.release()
cv2.destroyAllWindows()
6 最後に
イメージ分類(image-classification)は、物体検出(object-detection)に比較して、非常に軽量です。そして、データセットの作成コストは、比較にならないぐらい低いと思います。
運用場面をよく考察し、うまく適用していければと考えています。