Intelligent Technology's Technical Blog

株式会社インテリジェントテクノロジーの技術情報ブログです。

キン消しディープラーニング~DeepBeliefSDKの学習機能を試す~

こんにちは。中山です。

前回、「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」フォルダに格納されていました。

f:id:IntelligentTechnology:20150813175252p:plain

「学習機能」のサンプルプロジェクトは、「examples」フォルダの中の「LearningExample」が、これに該当します。
念のため、「LearningExample」フォルダごと別の場所にコピーして、その中の、

f:id:IntelligentTechnology:20150813175323p:plain

LearningExample.xcodeproj」をダブルクリックします。

Xcode でプロジェクトが開かれた直後は、以下のように「Frameworks」フォルダ内のファイルが見つからない、という状態になっているかもしれません。

f:id:IntelligentTechnology:20150813175352p:plain

このような場合は、プロジェクトの「Linked Frameworks and Libraries」から、対象のフレームワークを追加し直します。

f:id:IntelligentTechnology:20150715184135p:plain

見つからないフレームワークファイルのうち、「DeepBelief.framework」は、「DeepBeliefSDK」フォルダ内の「iOSLibrary」フォルダに格納されているものを指定します。(「XCTest.framework」は、無視しました。)

f:id:IntelligentTechnology:20150715184553p:plain

サンプル実行

iPhone を Mac に USB 接続して、サンプルアプリを実行します。
以下のような画面が表示されますので、「Start Learning」ボタンをタップして、画像認識の学習を開始します。

f:id:IntelligentTechnology:20150811182732j:plain

今回認識させるのは、

f:id:IntelligentTechnology:20150813180709j:plain:w320

そう、「キン消し」こと「キン肉マン消しゴム」です。
今回はこの中の「ペンタゴン」を「キン消し代表」として「学習」させてみます。

先ほどの画面の「Start Learning」ボタンをタップするとすぐ、以下のような画面に切り替わります。
認識させたいものがiPhoneのカメラに映るようにして、画像を認識させます。
このとき、認識対象のものをいろいろな角度から認識できるように、iPhoneを動かしながら撮影します。

f:id:IntelligentTechnology:20150811182820j:plain

画面上部のゲージがいっぱいになると、以下のような画面に切り替わります。

f:id:IntelligentTechnology:20150811182914j:plain

今度は、認識させたい対象「ではない」ものを「学習」させます。
「Continue Learning」ボタンをタップして、

f:id:IntelligentTechnology:20150811183004j:plain

認識させたい対象「ではない」ものを、iPhoneのカメラに映します。
できるだけ多くのものを撮影すると良いようです。
ゲージがいっぱいになると、「学習」完了です。

学習完了後、すぐに画像認識機能を試せるようになっています。
先ほど認識させた「ペンタゴン」を映してみると、

f:id:IntelligentTechnology:20150811183158j:plain

画面上部のゲージが増えていきました。今回認識させたものとの「一致度」が高い、と判断したようです。
ちなみに、関係ないものだと、

f:id:IntelligentTechnology:20150811183257j:plain

このように、画面上部のゲージが伸びません。

では、ほかの超人はいかがでしょうか。
「ブラックホール」は、

f:id:IntelligentTechnology:20150811183225j:plain

ものすごいマッチ度!ダテに四次元殺法コンビを結成しているわけではないようです。
それでは「悪魔将軍」はどうなるかというと、

f:id:IntelligentTechnology:20150811183234j:plain

こちらも一致度が高い。
では「アシュラマン」はどうでしょう。アシュラマンは腕が多いので、果たして?

f:id:IntelligentTechnology:20150811183206j:plain

これもやっぱり「キン消し」だと認識できたようです。
「キン消し」って、

  • 「薄いオレンジ色の単色で」
  • 「人のような形をしている」
  • 「消しゴム」

ということで、実はコンピュータにとっては認識は簡単なのかもしれません。
今回はこのような結果となりましたが、この「学習」のさせかたによって、精度もいろいろと異なってくるのだと思われます。

学習結果の確認

このサンプルアプリで画像認識の学習を行った結果は、Xcodeのコンソールに、以下のようにテキストで出力されます。

f:id:IntelligentTechnology:20150814144838p:plain

この出力結果をファイルに保存して、他のアプリから読み込むことで、画像認識の機能を他のアプリからも利用できるようになります。
この機能の検証については、また別の機会に試してみたいと思います。

どんな処理をやっているのか?

では実際には、このサンプリアプリはどのような実装になっているのでしょうか。
「学習」を開始すると、まず以下のように、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」を使って簡単に実現することができました。
次は、今回独自に「学習」した結果を、別のアプリに組み込んでみて、画像認識の機能を利用できるようにしてみたいと思います。