こんにちは。中山です。
前回、「DeepBeliefSDK」を使って、AndroidスマートフォンやiPhoneから、簡単に「ディープラーニング」の世界を体験することができました。今回はさらに一歩進んで、その画像認識の「学習」の機能を試してみたいと思います。「キン消し」を使って。
DeepBeliefSDKの、学習機能サンプル
前回と同様に、「DeepBeliefSDK」付属のサンプルプロジェクトを使って、画像認識の学習機能を試してみたいと思います。
「DeepBeliefSDK」付属の、「学習機能」用サンプルプロジェクトはiOS版のみでしたので、今回は、
- Mac OS X(10.10.4)
- Xcode(6.4)
- iPhone5S(iOS 8.4)
という環境で試しています。
まずは「DeepBeliefSDK」のソースファイル一式を、まだ取得済みでなければ、Github リポジトリから取得します。
$ git clone https://github.com/jetpacapp/DeepBeliefSDK.git
iOS用の「学習機能」のサンプルプロジェクトが、以下のように「examples」フォルダに格納されていました。
「学習機能」のサンプルプロジェクトは、「examples」フォルダの中の「LearningExample」が、これに該当します。
念のため、「LearningExample」フォルダごと別の場所にコピーして、その中の、
「LearningExample.xcodeproj」をダブルクリックします。
Xcode でプロジェクトが開かれた直後は、以下のように「Frameworks」フォルダ内のファイルが見つからない、という状態になっているかもしれません。
このような場合は、プロジェクトの「Linked Frameworks and Libraries」から、対象のフレームワークを追加し直します。
見つからないフレームワークファイルのうち、「DeepBelief.framework」は、「DeepBeliefSDK」フォルダ内の「iOSLibrary」フォルダに格納されているものを指定します。(「XCTest.framework」は、無視しました。)
サンプル実行
iPhone を Mac に USB 接続して、サンプルアプリを実行します。
以下のような画面が表示されますので、「Start Learning」ボタンをタップして、画像認識の学習を開始します。
今回認識させるのは、
そう、「キン消し」こと「キン肉マン消しゴム」です。
今回はこの中の「ペンタゴン」を「キン消し代表」として「学習」させてみます。
先ほどの画面の「Start Learning」ボタンをタップするとすぐ、以下のような画面に切り替わります。
認識させたいものがiPhoneのカメラに映るようにして、画像を認識させます。
このとき、認識対象のものをいろいろな角度から認識できるように、iPhoneを動かしながら撮影します。
画面上部のゲージがいっぱいになると、以下のような画面に切り替わります。
今度は、認識させたい対象「ではない」ものを「学習」させます。
「Continue Learning」ボタンをタップして、
認識させたい対象「ではない」ものを、iPhoneのカメラに映します。
できるだけ多くのものを撮影すると良いようです。
ゲージがいっぱいになると、「学習」完了です。
学習完了後、すぐに画像認識機能を試せるようになっています。
先ほど認識させた「ペンタゴン」を映してみると、
画面上部のゲージが増えていきました。今回認識させたものとの「一致度」が高い、と判断したようです。
ちなみに、関係ないものだと、
このように、画面上部のゲージが伸びません。
では、ほかの超人はいかがでしょうか。
「ブラックホール」は、
ものすごいマッチ度!ダテに四次元殺法コンビを結成しているわけではないようです。
それでは「悪魔将軍」はどうなるかというと、
こちらも一致度が高い。
では「アシュラマン」はどうでしょう。アシュラマンは腕が多いので、果たして?
これもやっぱり「キン消し」だと認識できたようです。
「キン消し」って、
- 「薄いオレンジ色の単色で」
- 「人のような形をしている」
- 「消しゴム」
ということで、実はコンピュータにとっては認識は簡単なのかもしれません。
今回はこのような結果となりましたが、この「学習」のさせかたによって、精度もいろいろと異なってくるのだと思われます。
学習結果の確認
このサンプルアプリで画像認識の学習を行った結果は、Xcodeのコンソールに、以下のようにテキストで出力されます。
この出力結果をファイルに保存して、他のアプリから読み込むことで、画像認識の機能を他のアプリからも利用できるようになります。
この機能の検証については、また別の機会に試してみたいと思います。
どんな処理をやっているのか?
では実際には、このサンプリアプリはどのような実装になっているのでしょうか。
「学習」を開始すると、まず以下のように、DeepBeliefSDKが提供する
- 「jpcnn_create_trainer」メソッド
が呼び出されます。
if (trainer != NULL) { jpcnn_destroy_trainer(trainer); } trainer = jpcnn_create_trainer(); // トレーナーオブジェクトへのハンドルを取得
その後は、以下のように「runCNNOnFrame:」メソッドから
- 「jpcnn_create_image_buffer_from_uint8_data」メソッド
- 「jpcnn_classify_image」メソッド
を呼び出して、iPhoneのカメラ画像からキャプチャしたイメージを解析します。
- (void)runCNNOnFrame: (CVPixelBufferRef) pixelBuffer { ・・・ // 画面キャプチャ結果をイメージオブジェクトとして取得する void* cnnInput = jpcnn_create_image_buffer_from_uint8_data( sourceStartAddr, width, height, 4, sourceRowBytes, doReverseChannels, 1); float* predictions; int predictionsLength; char** predictionsLabels; int predictionsLabelsLength; struct timeval start; gettimeofday(&start, NULL); // 画像を解析し、その結果を「predictions」変数にfloatの配列として格納する jpcnn_classify_image( network, cnnInput, JPCNN_RANDOM_SAMPLE, -2, &predictions, &predictionsLength, &predictionsLabels, &predictionsLabelsLength); struct timeval end; gettimeofday(&end, NULL); const long seconds = end.tv_sec - start.tv_sec; const long useconds = end.tv_usec - start.tv_usec; const float duration = ((seconds) * 1000 + useconds/1000.0) + 0.5; jpcnn_destroy_image_buffer(cnnInput); dispatch_async(dispatch_get_main_queue(), ^(void) { // 解析結果を「学習」させる [self handleNetworkPredictions: predictions withLength: predictionsLength]; }); }
この「runCNNOnFrame:」メソッド自体は、「AVCaptureVideoDataOutputSampleBufferDelegate」プロトコルの「captureOutput:didOutputSampleBuffer:fromConnection:」メソッドから呼び出されているので、ビデオフレームが更新されるたびに実行されるようになっています。
上記で画像解析後に呼び出している「handleNetworkPredictions:withLength:」メソッドでは、
- 「jpcnn_train」メソッド
を呼び出して、解析結果を「学習」させています。
jpcnn_train(trainer, 1.0f, predictions, predictionsLength);
最初に行う、認識「対象」のものの学習の場合は、この「jpcnn_train」メソッドの2番目の引数に「1.0f」を指定します。
一方、認識「対象ではない」ものの学習の場合、以下のように「jpcnn_train」メソッドの2番目の引数には「0.0f」を指定しています。
jpcnn_train(trainer, 0.0f, predictions, predictionsLength);
学習完了後は、
- 「jpcnn_predict」メソッド
を呼び出して、iPhoneのカメラに映っているものが、すでに「学習」されたものと一致しているかどうかを判定します。
const float predictionValue = jpcnn_predict( predictor, predictions, predictionsLength);
若干の手順はあるものの、特に難しく考えなくても、このように「DeepBeliefSDK」が提供するメソッドを呼び出していくことで、簡単に「学習機能」を試すことができました。
次は何を試す?
画像の「学習」も、「DeepBeliefSDK」を使って簡単に実現することができました。
次は、今回独自に「学習」した結果を、別のアプリに組み込んでみて、画像認識の機能を利用できるようにしてみたいと思います。