[Amazon SageMaker] 組み込みアルゴリズム(ObjectDetection)で作成したモデルをOpencVINOで使用してみました
1 はじめに
CX事業本部の平内(SIN)です。
OpenVINO ツールキットでは、モデルをIRと呼ばれる中間表現フォーマットにして利用します。そして、同ツールキットには、各種のフレームワークで作成したモデルを、IRに変換するツールが含まれています。
以前、Amazon SageMaker(以下、SageMaker)の組み込みアルゴリズム(画像分類)で作成したモデルを変換して使用してみました。
今回、同じ要領で、組み込みアルゴリズム(物体検出)で作成したモデルを同じ要領で変換しようとした際に、下記のエラーとなってしまいました。
[ ERROR ] Exception occurred during running replacer "REPLACEMENT_ID" \ (<class 'extensions.load.mxnet.loader.MxNetLoader'>): \ Unexpected exception happened during extracting attributes for node multibox_target. Original exception message: Operation '_contrib_MultiBoxTarget'\ not supported. Please register it as custom op.
以前作業した時より、OpenVINOのバージョンも上がり、最新では、少しパスも変わっていたので、改めて手順をまとめました。
% ls -la /opt/intel | grep openvino lrwxr-xr-x 1 root wheel 19 5 12 02:35 openvino_2021 -> openvino_2021.3.394 drwxrwxr-x 11 root wheel 352 5 12 02:35 openvino_2021.3.394
2 モデル
変換に使用したモデルは、下記で作成したものです。
出力先に格納されている、model.tar.gzを解凍すると、3つのファイルが格納されています。
こちらは、一見、MXNet形式のモデルですが、MXNetフレームワークで使用するためには変換が必要です。
下記は、その手順です。
変換すると、以下のようになります。
この形式であれば、OpenVINOのModel Optimizerによる変換が可能です。
3 Model Optimizer
Model Optimizerは、OpenVINOツールキットをインストールする手順の中で展開されるようになっています。 参考:Model Optimizer Developer Guide
変換作業は、Model Optimizerがインストールされてたディレクトリの、mo_mxnet.py(MXNet用)を実行するだけです。
最初に、modelの中に、MXNetのモデルを置き、FP16用とFP32用を作成するためディレクトリを作成しておきます。
% tree . -L 2 . ├── FP16 ├── FP32 └── model ├── deploy_model_algo_1-0000.params └── deploy_model_algo_1-symbol.json
以下は、FP32で作成している例です。
最初に、INTEL_OPENVINO_DIRという環境変数に、最新のOpenVINOのパスを設定しています。
% export INTEL_OPENVINO_DIR=/opt/intel/openvino_2021 % echo $INTEL_OPENVINO_DIR /opt/intel/openvino_2021 % $INTEL_OPENVINO_DIR/deployment_tools/model_optimizer/mo.py --input_model model/deploy_model_algo_1-0000.params --input_shape "[1,3,512,512]" --data_type FP32 --output_dir FP32 % tree . -L 2 . ├── FP16 ├── FP32 │ ├── deploy_model_algo_1-0000.bin │ ├── deploy_model_algo_1-0000.mapping │ └── deploy_model_algo_1-0000.xml └── model ├── deploy_model_algo_1-0000.params └── deploy_model_algo_1-symbol.json
出力先に、.xmlと .bin が作成されていますが、これが、IRフォーマットのモデルです。
4 コード
以下は、生成したOpenVINOを使用して、MacBook Pro (2.3 GHz クアッドコアIntel Core i7) 上で推論しているコードです。
推論時間は、0.5sec程度となっていました。
processing_time 0.50sec processing_time 0.48sec processing_time 0.50sec processing_time 0.51sec
import numpy as np import cv2 import time from openvino.inference_engine import IECore class Model: def __init__(self, ie, device, model): net = ie.read_network(model=model+'.xml', weights=model+'.bin') self.__input_blob_name = list(net.inputs.keys())[0] self.__output_blob_name = list(net.outputs.keys())[0] self.__exec_net = ie.load_network(network=net, device_name=device, num_requests=1) self.input_size = net.inputs[self.__input_blob_name].shape def infer(self, data): prob = self.__exec_net.infer(inputs={self.__input_blob_name: data}) return prob[self.__output_blob_name] class ProductDetector(Model): def __init__(self, ie, device, model_path): super().__init__(ie, device, model_path) # super().input_sizeから取得可能ですが、分かりやすいようにSHAPE=512で表現しています self.__SHAPE = 512 # 入力インターフェースへの画像変換 def __prepare_frame(self, frame): img = cv2.resize(frame, (self.__SHAPE, self.__SHAPE)) # 800*600 -> 512*512 img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # BGR -> RGB img = img.transpose((2, 0, 1)) # 512,512,3 -> 3,512,512 return img[np.newaxis, :] # 3,512,512 -> 1,3,512,512 def infer(self, frame): height, width = frame.shape[:2] frame = self.__prepare_frame(frame) detections = [] result = super().infer(frame) for obj in result[0][0]: _, clsid, conf, x1, y1, x2, y2 = obj x1 = int(x1 * width/self.__SHAPE * self.__SHAPE) y1 = int(y1 * height/self.__SHAPE * self.__SHAPE) x2 = int(x2 * width/self.__SHAPE * self.__SHAPE) y2 = int(y2 * height/self.__SHAPE * self.__SHAPE) detections.append([int(clsid), conf, x1, y1, x2, y2]) return detections def main(): CLASSE_NAMES=["ChipStar","Curry","Pringles","SeaFood","Butamen"] COLORS = [(0,0,175),(0,200,200),(0,175,0),(175,0,0),(0,100,255)] # OpenVINO DEVICE = "CPU" MODEL = "./FP32/deploy_model_algo_1-0000" ie = IECore() person_detector = ProductDetector(ie, DEVICE, MODEL) # OpenCV WIDTH = 640 HEIGHT = 480 DEVICE_ID = 0 FPS = 24 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) # フォーマット・解像度・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 start = time.time() # 時間計測 detections = person_detector.infer(frame) processing_time = time.time() - start print("processing_time {:.2f}sec".format(processing_time)) for detection in detections: clsid, conf, x1, y1, x2, y2 = detection if(conf < 0.6): continue print("{} {:.1f} ({},{})-({},{})".format(CLASSE_NAMES[clsid], conf, x1, y1, x2, y2)) color = COLORS[clsid] cv2.rectangle(frame, (x1, y1), (x2, y2), color, 2) text = "{} {:.2f}".format(CLASSE_NAMES[clsid], conf) cv2.putText(frame, text, (x1, y1), 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() main()
5 最後に
今回は、SageMakerで作成した物体検出モデルをOpenVINOツールキットで利用してみました。MXNetのままで、実行した時は、推論に2.0sec近くかかってしまっていたので、OpenVINOに変換する価値はあったと思います。
モデルの利用方法を検討する場合など、結構時間がかかると思うのですが、SageMakerのエンドポイントだと、課金がかさんでしまうので、ちょっとぐらい遅くても、ローカルデバイスで実行できるのは助かります。
なお、OpenVINOツールキットでは、モデルごとにCPU、GPUを振り分けて使用したり、USBで追加可能なNCSなどがあります。