GoogleのAutoML(機械学習)でレゴのフィギュアのオブジェクト検出を行う | そう備忘録

GoogleのAutoML(機械学習)でレゴのフィギュアのオブジェクト検出を行う

AutoMLのオブジェクト検出

GoogleのCloud AutoML(Machine Learning)は自分で用意した画像を読み込ませてアノテーション(ラベル・意味付け)を行うことによりノンプログラミングで機械学習の学習済みモデルをクラウド上で作成してくるサービスだ。

本来であれば必要なハイパーパラメータの設定などをGoogleがクラウド上で自動的に”適当”にやってくれるので専門的な知識がなくてもそれなりの精度のモデルが作製できてしまう。

それなりと言っても下手な機械学習のプログラミングレベルの自分がPythonで学習させたものよりも、余程精度が良いので「下手な運転のマニュアル車よりも優秀なオートマチック車の方が燃費が良い」みたいな事が起きているのだと思う。

以前の記事で画像分類を試してみたので今回はオブジェクト検出を試してみた。

尚、オブジェクト検出とは画像から複数のオブジェクトを検出してバウンディングボックス(四角い枠)で囲むと同時にそのオブジェクトの名称を予測スコア(確率)で表してくれるモデルになる。

以下の動画を見てもらえるとイメージが湧くと思う。

動画

オブジェクト検出の動画(ラズパイ+カメラ+Coral USB Accelerator)

ラズパイなのでカメラを動かした時のオブジェクトへの追従が若干遅れ気味なのはご容赦願いたい。

料金について

Google Cloud AutoML Visionは有料のサービスだが無料トライアルが可能になっている。

トレーニングとオンライン予測それぞれに40時間の無料ノード時間が使える。

5000枚の画像のトレーニングには8ノード時間あれば十分とあるので今回用意した画像(451枚)を複数回トレーニングする分には十分な無料枠が用意されていた。

またオンライン予測とはトレーニング後にモデルをダウンロードしてEdgeで動かすのではなく”モデルをデプロイ”をクリックしてクラウド上で検証できるサービスの事。

このオンライン予測はうっかり動かしっぱなしにしてしまうと思わぬ請求が来てしまうので注意したい。

終了したらこまめに”モデルのデプロイ解除”を行ったほうが良い。

オンライン予測については後述する。

手順について

大まかな手順は以下の通り

  1. 学習用の画像を用意する
  2. 画像をCloud AutoMLにアップロードする
  3. 画像に対してラベル付けをする
  4. モデルをトレーニング(学習)させる
  5. 学習済みモデルをラズパイにエクスポートする
  6. ラズパイにカメラを取り付けてカメラに写ったレゴフィギュアを学習済みモデルでオブジェクト検出する

学習用の画像を用意する

画像の撮影

娘からレゴのフィギュアを借りてカメラでフィギュアの写真を撮影した。

撮影したフィギュアは左からWoman(レゴクリエイターの女性)、Olivia(レゴフレンズ サマーキャンピングからオリビア)、Rapunzel(ディズニープリンセスからラプンツェル)、Stephanie(レゴフレンズからステファニー)の4種類。

撮影したフィギュア

撮影する写真の種類は何回かトレーニングを繰り返して以下の知見を得られた。

  1. 正面以外の横向き、後ろ向き、斜め上、下からの画像
  2. 様々な距離(近め、遠目)の画像
  3. 一枚に複数のフィギュアが写っている画像
  4. 様々な背景が写り込んだ画像

上記は「あったほうが精度があがる」レベルなので例えば1.の写真だけでも判別はできた。

しかし様々な条件(昼、夜、違う場所)で試した所、2~4の画像を学習させた方が良い結果を得られた。

特に4.の背景に関しては最初は壁をバックにしたシンプルな背景のフィギュアの写真を撮影していたが、実行時に何らかの背景が写り込んでしまった時に精度が悪くなってしまった。

ふと思いついて色々な背景が写り込んでいる写真を学習させた所、精度が良くなった。

学習用の画像にあえてノイズを乗せた方が実践に強いモデルができるのかも知れない。

背景が写り込んでいる

背景が写り込んでいるフィギュア

画像の切り出し

撮影は1枚1枚シャッターを切って撮影をすると時間と手間がかかるので動画で撮影をして、mp4をフレーム毎に切り出してjpgにしている。

mp4からjpgファイルへの切り出しは自作のPythonのプログラムを使用したので、その時の記事についてはこちらを参照

上記のプログラムで出来る主なことは以下の通り。

  1. 指定したフレーム間隔ごとにjpgに保存できる
  2. フレーム番号の始まりが指定可能できる(例えば3フレーム目から抽出する)
  3. 画像を指定したサイズでリサイズしてリサイズ後に正方形にトリミングできる
  4. ピンぼけ画像は出力しない

高精度の画像で学習させれば精度が良くなるわけでも無いらしいので機械学習で良く使われている250✕250ピクセルのサイズで切り出した。

用意した画像

画像をCloud AutoMLにアップロードする

Googe Cloud Platformにログイン

Google Cloud Platformログインをしてコンソールにてプロジェクトを作成した後、ダッシュボードでAutoMLを検索してVisionに遷移する。

初めて使用するときは必要なAPIの許可や請求情報の登録(無料枠を使う場合でも)が必要になる。

上記については以前の記事のプロジェクトの作成~AutoML Visionまでを参照のこと。

Visionが表示されたら”データセット”を選択する。

Visionでデータセットをクリック

新しいデータセットの作成

新しいデータセットをクリックする。

新しいデータセット

データセット名に”lego_figure_ObjectDetect20191126”を入力して”オブジェクト検出”を選択して「データセットを作成」ボタンをクリックする。

データセットを作成

画像のインポート

インポートタブが開いているので”パソコンから画像をアップロード”を選択して「ファイルを選択」ボタンをクリックして作成した画像を複数選択する。

続いて”BROWSE”をクリックする。

パソコンから画像をアップロード

画像はまとまったフォルダに格納したいので”Lego-Figure”フォルダを作成して「SELECT」ボタンをクリックする。

Lego-Figureディレクトリの作成、選択

先程の画面に戻るので「続行」ボタンをクリックする。

続行をクリックする

選択された画像がアップロードされた。

画面ショットでは枚数が356枚になっているが最終的には451枚の画像をアップロードしている。

画像がアップロードされた

ラベル付け

ラベルの作成

”イメージタブ”を選択したら最初にラベルを作成する。

”新規ラベルを追加”をクリックして新しく現れた枠に”Rapunzel”と入力する。

合計、Olivia、Rapunzel、Stephanie、Womanの4種類のラベルを作成した

ラベルの新規作成

表示枚数の変更

デフォルトでは一度に表示する画像の枚数が50枚なので200枚に変更する。

そうしないと作業効率が悪い。

表示枚数を200枚に変更する

アノテーション(ラベル付け)

1枚目の画像をクリックして拡大表示させたらフィギュアが写っている所をマウスで選択して四角い枠を表示させたら左から対象のラベルを選択する。

この様に1枚1枚の画像の対象物に対してラベル付けを行う。

画像にラベル付け

1枚に複数のオブジェクトが写っている時も同様に行う。

正直この作業が一番大変だった。

これこそ「AIで自動化できれば良いのに」、「しかしその為にはラベル付けが必要だし」と矛盾を抱えながら時間をかけてアノテーションを行った。

複数のオブジェクトが写っている例

モデルのトレーニング(学習)

トレーニングタブ

全ての画像へのラベル付けが終了したらトレーニングタブに移動する。

Oliviaを例にすると97枚の画像にラベル付けされた。

尚、トレーニング、検証、テストの数値(枚数)の意味は以下の通り。

トレーニング

全体の80%は学習する為の画像

この画像を使ってCloud AutoML Vsionはレゴフィギュアを学習する

検証

全体の10%はハイパーパラメータ(学習前に設定するパラメータ)の調整とトレーニングを停止するタイミングの決定に利用される

通常ハイパーパラメータは”人が手動”で設定するのだがCloud AutoML Visionでは画像から自動で設定する仕様になっている

またトレーニングをし過ぎると過学習と言ってかえって精度が悪くなるのでトレーニングを停止するタイミングは重要になる

Cloud AutoMLでは検証データから停止タイミングを自動で決めている模様

テスト

残りの10%は上記データでトレーニングをした後にモデルの評価(どれぐらい分類・予測が成功するか)に使用される

トレーニング、検証、テストはそれぞれランダムに分割されて同じ画像を使わないことによって汎用性を高めている

学習をした画像で予測をしたら精度が高いのは当然なので違う画像で予測テストを行っている

トレーニングの開始

「トレーニングを開始」ボタンをクリックする。

トレーニングの開始

右側にウィンドウが現れるのでモデルの定義を選択する。

今回はラズパイにモデルをダウンロードして使いたいので”Edge”を選択する。

モデルの定義はEdgeを選択

モデルの最適化オプションは”Best trade-off”を選択して「続行」をクリックした。

その他には高精度・低速の”Higher accuracy”、低精度・高速なFaster predictionsがある。

モデルの最適化オプション

予算を24に設定して「トレーニングを開始」した。

こちらはデフォルトで24が表示されていたのでそのままにしたが、もう少し少ない数字でもOK(のはず)

予算の設定

トレーニング中

トレーニング中の画面が表示される。

この処理は時間がかかるのでこのまま画面を閉じてしまっても大丈夫。

451枚だと数十分で処理が終了した。

トレーニング中

トレーニングが終了するとメールが届くのでリンクをクリックすると先程の画面に戻る。

トレーニング終了メール

トレーニング結果

トレーニング結果が表示される。

適合率

予測が正しかった割合

例えばOliviaと予測した画像がOliviaだった時の正解率

極端な例だと1枚だけOliviaと予測してそれがOliviaだった場合、他のOliviaが写った全ての画像をOliviaと判定できなくても(Oliviaの)適合率は1.0(100%)になってしまう

再現率

正解に対して正しい予測を行った割合

Oliviaの画像(全97枚)のうち、何枚をOliviaと予測できたかの割合。

極端な例だと451枚全てOliviaと予測しても(Oliviaの)再現率は1.0(100%)になってしまう(他はゼロだが)

トレーニング結果

評価タブで再現率と適合率との関係がグラフで表されている。

適合率と再現率とはお互いトレードオフの関係にあるので、お互い1.0(100%)に近い形でグラフが描かれているのでトレーニング(学習)は上手くいっている。

評価タブ

学習済みモデルのエクスポート

モデルの選択

次にテストと使用タブで出力するモデルを選択するのだが、オンライン予測をすることもできる。

その際は右側にある”モデルをデプロイ”をクリックするとオンラインでの予測が可能になる。

予測したい写真をクラウドにアップロードするとリアルタイムで予測結果を表示してくれる。

オンライン予測に注意!

しかしこの時に注意しなければいけない事がある!

それはデプロイしたモデルは削除しない限り時間単位での課金(従量課金ではない)になるので、そのまま放置すると使用していないのにも関わらず1日数千円の課金が積み重なり、月初に10万超えの請求を受けることになってしまう。

ネットで「AutoML 請求」で検索すると、うっかりそのままにしてしまい多額の請求を受けてしまった方達の記事が散見されるので十分に注意したい。

交渉して減額してもらった方達も居る模様

デプロイしたモデルは検証が終わったらこまめに削除を行うようにしたい。

尚、今回はオンライン予測は行わずにモデルの出力を行う。

出力可能なモデルの一覧が”Use Your Model”に表示されるのでTF Liteを選択する。

以前の単一ラベル分類を行ったときにはCoralというモデルがあったが今回は無いので”TF Lite”(TensorFlow Lite)を選択する。

出力モデルの選択

出力先の指定

右側にウィンドウが開くのでBROWSEをクリックしてモデルの出力先を指定する。

モデルの出力先の設定

モデル出力先のバケットを選択して「SELECT」ボタンをクリックする。

必要に応じてフォルダを作成する

バケットを選択する

Export

「EXPORT」ボタンをクリックしてモデルをGoogleのストレージにエクスポートする。

尚、ボタンの下にクラウド上のストレージからモデルを取得するコマンドが表示されるのだが、gsutilコマンドはラズパイにはデフォルトではインストールされていない。

ラズパイにgsutilをインストールする手間を鑑み手動でダウンロード、ラズパイにアップロードを行った。

モデルのEXPORT

ストレージ

暫く待つと”export operation finished”と表示されるのでメニューからStorageー>ブラウザを選択する。

storage、ブラウザ

エクスポートしたストレージを選択する。

ストレージの選択

モデルのダウンロード

model-export/iod/配下のExport先のフォルダーを指定すると、

  • dict.txt
  • model.tflite
  • tflite_metadata.json

の3つのファイルが作成されているのでそれぞれダウンロードしてラズパイにコピーする。

尚、コピーする際に他のモデルファイルと区別する為にファイルのプリフィックスに”lego_figure_OD_”を付加した。

モデルがExport先されいてるフォルダーを選択する

lego_figure_OD_dict.txt

ラベルファイル

1行目に何故か”background”(背景)が作成されている

色々な背景が写り込んだ画像を学習させたからかも知れない

後はラベルを作成したフィギュア名が並んでいる

ラベルファイル

lego_figure_OD_model.tflite

TensorFlow Liteのモデルファイル

このファイルを使ってオブジェクト検出を行う

lego_figure_OD_tflite_metadata.json

メタデータ(JSON形式)

このファイルはプログラムの動作には直接関係が無いがモデルと同一ディレクトリにコピーをしておいた

メタデータ(JSON形式)

inputShapeの2、3つ目の値(320、320)はこのモデルの画像の高さ、幅をピクセルで表している

プログラム中ではライズパイのカメラから読み込まれた画像を上記のサイズにリサイズしてオブジェクト検出を行っている

学習させた画像のサイズは250✕250なのだが若干大きなサイズになっている

もしかしたら320のサイズの画像を学習させた方が精度が上がったのかも知れない(未検証)

outputTensorRepresentationではTensorFlow Liteのインタプリタを呼び出した時に返される項目を表している

bounding_boxes

オブジェクトを検出した画像の領域をバウンディングボックス(四角い枠)の左上の座標(ymin, xmin)右下の座標(ymax, xmax)で返す

class_labels

検出されたクラスのラベル(の番号)

上記の例だとbackgroundが0、Womanが1、Oliviaが2となる

class_confidences

信頼スコア

検出されたオブジェクトの信頼度を確率(0~1の数値)で返す

num_of_boxes

検出数

画像中に何個のオブジェクトが検出されたかを数値で返す

ラズパイでのオブジェクト検出

実行環境の構築

ラズパイにカメラの接続や必要なランタイムのインストール等、実行環境を整える

そしてサンプルプログラムをダウンロードを行う。

手順は以下の通りなので、それぞれ過去記事を参考にして欲しい。

  1. ラズパイにカメラを接続
  2. カメラを有効化
  3. Edge TPUランタイムのインストール
  4. ラズパイとCoral USB Acceleratorを接続
  5. 最大クロック周波数で動作させる為のランタイムをインストール(オプション)
  6. TensorFlow Liteインタープリターのインストール
  7. git cloneコマンドでサンプルプログラム類をダウンロード
  8. ディレクトリをexamples/lite/examples/object_detection/raspberry_pi/ へ移動
  9. プログラム(detect_picamera.py)の修正(Coral USB Acceleratorを使用する場合)

となる。

モデルのコピー

上記手順の6(サンプルプログラムのダウンロード)を行うとホームディレクトリ配下にexamples/lite/examples/object_detection/raspberry_pi/ ディレクトリが作成される。

更にdownloadsディレクトリを作成してモデルファイル類をコピーする。

ディレクトリ構造

ディレクトリ構造は以下の通り。

├─examples
│  │      
│  ├─lite
│  │  │      
│  │  ├──examples
│  │  │  │
│  │  │  ├──object_detection
│  │  │  │  │
│  │  │  │  ├──raspberry_pi
│  │  │  │  │  detect_picamera.py
│  │  │  │  │  │
│  │  │  │  │  └──downloads
│  │  │  │  │     lego_figure_OD_dict.txt
│  │  │  │  │     lego_figure_OD_model.tflite
│  │  │  │  │     lego_figure_OD_tflite_metadata.json
│  │  │  │  │

実行

以下のコマンドでプログラムを実行する。

python3 detect_picamera.py \
--model ./downloads/lego_figure_OD_model.tflite \
--label ./downloads/lego_figure_OD_dict.txt \
--threshold 0.4

実行結果

右側にあるのがGoogleのCoral USB Acceleratorを接続したラズパイとカメラ、書籍の上のレゴのフィギュアを撮影している。

左側にあるのがディスプレイ。

カメラに写ったレゴフィギュアをバウンディングボックスで囲んで名称を表示している。

ちょっと見えにくいが予測スコアは80%以上なのでまずまずの予測精度が出ている。

実行結果

カメラを動かすとタイムラグが発生するが追従してくる。

タイムラグが発生するのはCoral USB Acceleratorを接続しているとはいえRaspberry Pi 3 B+なので仕方が無い所か。

しかしラプンツェルの予測スコアが58.6%と低目なのが気になる。

カメラを動かす

フィギュアを後ろ向きにした所。

ラプンツェルの予測スコアは上がったがレゴクリエイターの女性(Woman)が下がってしまった。

後ろ向き

最後に横からのオブジェクト検出。

ラプンツェルが認識されない時がある。

横からの映像

恐らくだが認識率が低いのは学習させた画像に偏りがあったせいなのかも知れない。

今までも画像を増やして様々なシーンの画像を追加してゆくと認識率が上がったので例えばレゴクリエイター女性(Woman)の後ろ向きの写真を追加で学習させれば認識率も上がるように思う。

動画

プログラムを動かしながらラズパイカメラでレゴフィギュアを撮影した時の動画。

フィギュアを後ろ向き、横向きに動かした時のバウンディングボックスの追随の様子を撮影している。

カメラを動かした際のバウンディングボックスの追随のタイムラグはRaspberry Pi 3 B+なので仕方が無いが、レゴクリエイターのWomanの後ろ姿やラプンツェルが時々、認識率が下がってしまう件については学習させる画像を工夫すればもう少し精度が上がるように思う。

学習させた画像が基本的に昼間の自然光で撮影したものが中心だったので夜間に電灯及びLED光だと条件が違ってきているのかも知れない。

もう少し色々と試してみたいと思う。

souichirou

やった事を忘れない為の備忘録 同じような事をやりたい人の参考になればと思ってブログにしてます。 主にレゴ、AWS(Amazon Web Services)、WordPress、Deep Learning、RaspberryPiに関するブログを書いています。 仕事では工場に協働ロボットの導入や中小企業へのAI/IoT導入のアドバイザーをやっています。 2019年7月にJDLA(一般社団法人 日本デイープラーニング協会)Deep Learning for GENERALに合格しました。 質問は記事一番下にあるコメントかメニュー上部の問い合わせからお願いします。

おすすめ

4件のフィードバック

  1. mack より:

    souichirou先生、ラズパイで物体検出をする素晴らしい記事を有難うございます。
    ラズパイで物体検出を始めたばかりのど素人で、mackと申します。
    2点教えて頂きたい事がございます。
    まず1点目は、
    Coral USB Acceleratorを使用するためのプログラム修正で、71行目をコメントアウトして以下の行を追加するを行うと
    argsが未定義だとエラーになってしまいAcceleratorを使えません。なぜこうなったのか自分では解からないものですから、
    どうすれば良いのか是非お教えいただきますようお願い致します。
    2点目は、
    実行結果で、バウンディングボックスで囲んで名称を表示している映像を、ラスパイで録画するにはどうしたらよろしいのでしょうか?
    ネットをいろいろ探索しても分からなかったので、是非お教えいただきますようお願い致します。

    • souichirou より:

      mackさん、こんにちは
      1点目ですがリンク先が間違っており、detect_picamera.py の修正では無くてclassify_picamera.pyの修正になっていました。
      混乱させてすいませんでした。先程修正しております。
      正しくは、
      ・33行目にInterpreterの他にload_delegateを追加する
      ・123行目をコメントアウトして以下の行を加える

       interpreter = Interpreter(args.model,experimental_delegates=[load_delegate('libedgetpu.so.1.0')])

      になります。
      詳しくは記事中のリンク先をご確認下さい。
      2点目はちょっと手強いように思います。
      detect_picamera.py では io.BytesIO()でストリームをjpgに変換して、そのjpgにバウンディングボックスを描いていますので、jpgから動画に戻す必要があると思います。
      恐らくffmpeg-pythonで連続したjpg画像 → mpeg に変換すれば出来るように思うのですが実際に試した訳では無いので、その旨ご了承ください。

  2. mack より:

    souichirou先生
    返信がとても遅くなってしまいまして申し訳ございません。
    mackです。
    迅速なご回答を頂きまして誠にありがとうございます。
    先生のお教え通りに行ったところ、無事成功いたしました。
    心から感謝いたします。
    何分素人ですので、またご相談したいことができましたら、
    どうぞ宜しくお願い致します。

質問やコメントや励ましの言葉などを残す

名前、メール、サイト欄は任意です。
またメールアドレスは公開されません。