[Object Detection][CoreML][TuriCreate][jupyter notebook][XamariniOS]自作の学習データで物体認識AIを学習させてスマホに移植
タブレット・携帯端末で動く物体認識AIを作りたかったので、色々調べながら泥臭くやりました。途中「神の声」もかりながらやりましたが一応それっぽいものができたと思います。
サンプルAIを動かす
まずはそもそもスマホでAIなるものが動くかわからなかったので、サンプルモデルを探して動かしてみました。
appleのサイトからサンプルコードをクローンして、「BreakfastFinder」をビルドしてみると意外と簡単に動かせました。
AIの起動や処理のコードもそうですが
・カメラの初期化
・撮影画像をリアルタイムで処理をかけて画面に出力する
部分など結構そっちの方が勉強になりました。
ちなみに一行一行デバックで追って解読していったので、、ここだけで3日くらい潰れてます。
サンプルデータでサンプルAIをトレーニングしてみる
AIがスマホで動くことが分かったので、今度はサンプルAIがサンプルデータで学習できるか調べてみます。
appleのサイトの説明にしたがってサンプルデータ(車と自転車)をダウンロードします。これは下記の通りにやればダウンロードできました。
$ mkdir -p ~/Downloads/ig02
$ cd ~/Downloads/ig02
$ curl https://lear.inrialpes.fr/people/marszalek/data/ig02/ig02-v1.0-bikes.zip > bikes.zip
$ curl https://lear.inrialpes.fr/people/marszalek/data/ig02/ig02-v1.0-cars.zip > cars.zip
$ unzip bikes.zip
$ mv readme.txt readme-bikes.txt
$ unzip cars.zip
$ rm bikes.zip cars.zip
Turi Createをインストールしておきます
pip install turicreate
Turi Createでサンプルデータの準備をします。基本的な流れはサイトの手順に従えばいけるとは思いますが、サイトのままではエラーが起きたので私の環境でうまく行ったコードを貼り付けておきます。
ちなみにjupyter notebookで実行するとやりやすいと思います。
import turicreate as tc
import osig02_path = 'Downloads/ig02'raw_sf = tc.image_analysis.load_images(ig02_path, recursive=True,
random_order=True)info = raw_sf['path'].apply(lambda path: os.path.basename(path).split('.')[:2])info = info.unpack().rename({'X.0': 'name', 'X.1': 'type'})raw_sf = raw_sf.add_columns(info)raw_sf['label'] = raw_sf['name'].apply(lambda name: name.split('_')[0])del raw_sf['path']sf_images = raw_sf[raw_sf['type'] == 'image']
sf_masks = raw_sf[raw_sf['type'] == 'mask']def mask_to_bbox_coordinates(img):
import numpy as np
mask = img.pixel_data
if mask.max() == 0:
return None
x0, x1 = np.where(mask.max(0))[0][[0, -1]]
y0, y1 = np.where(mask.max(1))[0][[0, -1]] return {'x': (x0 + x1) / 2, 'width': (x1 - x0),
'y': (y0 + y1) / 2, 'height': (y1 - y0)}sf_masks['coordinates'] = sf_masks['image'].apply(mask_to_bbox_coordinates)sf_masks = sf_masks.dropna('coordinates')sf_masks = sf_masks.pack_columns(['label', 'coordinates'],
new_column_name='bbox', dtype=dict)sf_annotations = sf_masks.groupby('name',
{'annotations': tc.aggregate.CONCAT('bbox')})sf = sf_images.join(sf_annotations, on='name', how='left')sf['annotations'] = sf['annotations'].fillna([])del sf['type']sf.save('ig02.sframe')
準備ができたので、トレーニングさせてみます。
トレーニングはサイトどおりに進めればできるはずです。
短いコードなので一応貼り付けておきます。
import turicreate as tcdata = tc.SFrame('ig02.sframe')train_data, test_data = data.random_split(0.8)model = tc.object_detector.create(train_data)predictions = model.predict(test_data)metrics = model.evaluate(test_data)
model.save('ig02.model')
model.export_coreml('Car_Bike_Detector.mlmodel')
作成されたCar_Bike_Detecter.mlmodelをBreakfastFinderのObjectDetecter.mlmodelと入れ替えてビルドします。
カメラで撮影すると車と自転車を認識できるようになりますが、精度は微妙です。もう少しちゃんと学習が必要のようです。この辺は1日もかからず突破しました。
自分で画像にラベル付けして学習データを準備する
何はともあれデータがあればAIを学習されられることが分かったので、自分で学習データを準備します。
画像のラベル付けが一番迷いました。アノテーションツールのLabelImageは使ったことがありますが、、アノテーションしたデータをどのような出力形式でエクスポートして.sframeにすればいいのか道筋がなかなか見えませんでした。
迷走していると「VoTTを使いなさい」という神のお告げがあったため、使ってみるとまあー使いやすいこと使いやすいこと。
ここからVoTTをインストールして、操作説明はこのサイトかこのサイトを見れば誰でも使えます。
アノテーション後、”エクスポート設定”でプロバイダーを”コンマ区切り値(CSV)”に設定してCSVを出力します。
先程のappleのサイトの手順に従ってCSVのデータを.sframeにしていきますが、サイトのコードだとうまく動かなかったので下記にコードを載せておきます。
import turicreate as tc
import osIMAGES_DIR = '' //画像がある場所
csv_path = '' //CSVがある場所csv_sf = tc.SFrame.read_csv(csv_path)def row_to_bbox_coordinates(row):
"""
Takes a row and returns a dictionary representing bounding
box coordinates: (center_x, center_y, width, height) e.g. {'x': 100, 'y': 120, 'width': 80, 'height': 120}
"""
return {'x': row['xmin'] + (row['xmax'] - row['xmin'])/2,
'width': (row['xmax'] - row['xmin']),
'y': row['ymin'] + (row['ymax'] - row['ymin'])/2,
'height': (row['ymax'] - row['ymin'])}csv_sf['coordinates'] = csv_sf.apply(row_to_bbox_coordinates)del csv_sf['xmin'], csv_sf['xmax'], csv_sf['ymin'], csv_sf['ymax']csv_sf = csv_sf.rename({'image': 'name'})sf_images = tc.image_analysis.load_images(IMAGES_DIR, recursive=True,random_order=True)info = sf_images['path'].apply(lambda path: os.path.basename(path).split('/')[:1])info = info.unpack().rename({'X.0': 'name'})sf_images = sf_images.add_columns(info)del sf_images['path']csv_sf = csv_sf.pack_columns(['label', 'coordinates'], new_column_name='bbox', dtype=dict)sf_annotations = csv_sf.groupby('name',
{'annotations': tc.aggregate.CONCAT('bbox')})sf = sf_images.join(sf_annotations, on='name', how='left')sf['annotations'] = sf['annotations'].fillna([])sf.save('mydata.sframe')
mydata.sframeを使ってモデルをトレーニングしてみる
ここはサイトのコードをそのまま使えばいけますが、短いので貼り付けておきます。
import turicreate as tcdata = tc.SFrame('mydata.sframe')train_data, test_data = data.random_split(0.8)model = tc.object_detector.create(train_data)predictions = model.predict(test_data)metrics = model.evaluate(test_data)model.save('mymodel.model')model.export_coreml('MyObjectDetector.mlmodel')
先程と同じ要領で、作成されたMyObjectDetecter.mlmodelをBreakfastFinderのObjectDetecter.mlmodelと入れ替えてビルドします。
これで自作したAIモデルをタブレット・携帯端末で動かせるはずです。