こんにちは。中山です。
「ディープラーニング」。
私自身はまだしっかり理解できているわけではないのですけれども、日ごと、この単語を目にする機会が多くなっているように感じます。
この「ディープラーニング」、しっかり理解しようとすると、やはり専門的な知識が必要になってきます。
しかしそれでも、まずは気軽に体験してみたい、その入り口に立ってみたい、という要望をかなえてくれそうなのが、今回ご紹介する「DeepBeliefSDK」です。
DeepBeliefSDK とは
「DeepBeliefSDK」とは、iOS、Android、LinuxやMac OS Xで利用できる、ディープラーニング(深層学習)された、画像認識のためのフレームワークです。
この「DeepBeliefSDK」については、以下の記事などで紹介されていました。
当ブログ記事では、「DeepBeliefSDK」を、AndroidスマートフォンとiPhoneで、実際に動かしてみるまでの、具体的な手順を紹介してみたいと思います。
サンプルを動かしてみる
今回は、
- Mac OS X(10.10.3)
- Android Studio(1.2.2)
- Xcode(6.4)
- Nexus7 2013(Android OS 5.1)
- iPhone5S(iOS 8.4)
という環境で試しました。
Android用、iOS用の各サンプルプロジェクトも「DeepBeliefSDK」のリポジトリに含まれていますので、まずは「DeepBeliefSDK」の Github リポジトリからソースファイル一式を取得します。
$ git clone https://github.com/jetpacapp/DeepBeliefSDK.git
Android用、iOS用のサンプルプロジェクトは以下のように、「examples」フォルダに格納されていました。
まず、Android用のサンプルプロジェクトをインポートします。
Android Studio を起動して「Import project」をクリックします。
「examples」フォルダの中の「AndroidExample」フォルダを選択して、インポートを行います。
おそらく何も調整せずに、そのまま正常にインポートできると思います。
インポート完了後、AndroidスマートフォンをUSB接続して、そのサンプルアプリを実行します。
サンプルアプリを起動すると、カメラのプレビューが表示されます。
プレビュー画面の左上隅に、画像解析結果がリアルタイムで表示されていきます。(表示される文字の色・サイズは少し調整しています。)
上のようにMacのキーボードを映したところ、解析結果として
- computer keyboard(コンピュータのキーボード)
- space bar(スペースキー)
が表示されました(右側に表示されている数値は各解析結果の「予測値」のようです)。キーボードついてはかなりの精度で解析できているようです。
もうひとつ、ピンクのカバの消しゴムの場合は、
このような解析結果でした。「予測値」はどれも低いですが、意外といい線いってる?
- rubber eraser(消しゴム)
- diaper(おむつ)
- studio couch(ソファーベッド)
- piggy bank(ブタの貯金箱)
- ice lolly(棒つきのアイスキャンディ)
一方、iOSのサンプルプロジェクトは、「examples」フォルダの中の「SimpleiOS」が、これに該当します。
念のため、「SimpleiOS」フォルダごと別の場所にコピーして、その中の、
「SimpleExample.xcodeproj」をダブルクリックします。
Xcode でプロジェクトが開かれた直後は、以下のように「Frameworks」フォルダ内のファイルが見つからない、という状態になっているかもしれません。
このような場合は、プロジェクトの「Linked Frameworks and Libraries」から、対象のフレームワークを追加し直します。
見つからないフレームワークファイルのうち、「DeepBelief.framework」は、「DeepBeliefSDK」フォルダ内の「iOSLibrary」フォルダに格納されているものを指定します。(「XCTest.framework」は、とりあえず無視しました。)
iPhone を Mac に USB 接続して、サンプルアプリを実行します。
Android版のサンプルアプリと同じように、画像解析結果がリアルタイムで表示されます。
ボールペンを写してみましたが、解析結果は
- Penlight(ペンライト)
- Drunstick(ドラムスティック)
- Letter Opener(レターオープナー)
- Ballpoint(ボールペン)
となり、なかなか精度も良いのでは、と思いました。
どんな処理をやっているのか?
少し長いですが、実際に画像解析処理を行っている部分のソースコードを抜き出してみます。
以下はAndroid版のソースですが、iOS版でも、だいたい同様のことを行っているようです。
void classifyBitmap(Bitmap bitmap) { final int width = bitmap.getWidth(); final int height = bitmap.getHeight(); final int pixelCount = (width * height); final int bytesPerPixel = 4; final int byteCount = (pixelCount * bytesPerPixel); ByteBuffer buffer = ByteBuffer.allocate(byteCount); bitmap.copyPixelsToBuffer(buffer); byte[] pixels = buffer.array(); Pointer imageHandle = JPCNNLibrary.INSTANCE.jpcnn_create_image_buffer_from_uint8_data(pixels, width, height, 4, (4 * width), 0, 0); PointerByReference predictionsValuesRef = new PointerByReference(); IntByReference predictionsLengthRef = new IntByReference(); PointerByReference predictionsNamesRef = new PointerByReference(); IntByReference predictionsNamesLengthRef = new IntByReference(); long startT = System.currentTimeMillis(); // 画像を解析して、その結果を文字列(のリスト)として返す JPCNNLibrary.INSTANCE.jpcnn_classify_image( networkHandle, imageHandle, 0, 0, predictionsValuesRef, predictionsLengthRef, predictionsNamesRef, predictionsNamesLengthRef); long stopT = System.currentTimeMillis(); float duration = (float)(stopT-startT) / 1000.0f; System.err.println("jpcnn_classify_image() took " + duration + " seconds."); JPCNNLibrary.INSTANCE.jpcnn_destroy_image_buffer(imageHandle); Pointer predictionsValuesPointer = predictionsValuesRef.getValue(); final int predictionsLength = predictionsLengthRef.getValue(); Pointer predictionsNamesPointer = predictionsNamesRef.getValue(); final int predictionsNamesLength = predictionsNamesLengthRef.getValue(); System.err.println(String.format("predictionsLength = %d", predictionsLength)); float[] predictionsValues = predictionsValuesPointer.getFloatArray(0, predictionsLength); Pointer[] predictionsNames = predictionsNamesPointer.getPointerArray(0); ArrayList<PredictionLabel> foundLabels = new ArrayList<PredictionLabel>(); for (int index = 0; index < predictionsLength; index += 1) { final float predictionValue = predictionsValues[index]; if (predictionValue > 0.05f) { String name = predictionsNames[index].getString(0); System.err.println(String.format("%s = %f", name, predictionValue)); PredictionLabel label = new PredictionLabel(name, predictionValue); foundLabels.add(label); } } Collections.sort(foundLabels); String labelsText = ""; for (PredictionLabel label : foundLabels) { labelsText += String.format("%s - %.2f\n", label.name, label.predictionValue); } labelsView.setText(labelsText); }
「キモ」となるのは、「jpcnn_classify_image」メソッドです。
このメソッドが、渡された画像オブジェクト(カメラのプレビューをキャプチャしたもの)を解析し、その結果を文字列(のリスト)として返しています。
「ディープラーニング」って、なんか敷居が高そう、などと思っていたわけなのですが、実はもう、このようにメソッドひとつ呼び出すだけで、すぐにその機能を試せてしまう、ということになっていたのでした。
次は何を試す?
「DeepBeliefSDK」を使うことで、あらかじめ「ディープラーニングされた」画像解析の処理を簡単に試すことができました。この次は、同じく「DeepBeliefSDK」を使って、独自の画像を「学習」させてみて、その効果を検証したいと考えています。