Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Let's use TensorFlow on Android!

Let's use TensorFlow on Android!

2016/11/19 ABC 2016 Autumnにて発表した「TensorFlowをAndroidで使おう!」の資料です。

Arata Furukawa

November 19, 2016
Tweet

More Decks by Arata Furukawa

Other Decks in Technology

Transcript

  1. Overview 1. 実行フローの説明 2. TensorFlow C++ APIの解説 3. ビルド方法 →理論の話はしません。

    理論については私のブログか、 DevFestでの発表資料を参考にしてください。 古川のブログ:http://ornew.net/ DevFestでの資料:https://speakerdeck.com/ornew/tensorflow-on-android
  2. About JNI JNIの指定するインターフェイスに準じたABIを持つネイティブコードを Javaから実行することができる仕組み。 例)以下のようなコードをC++で書きます。 JNIEXPORT jbyteArray JNICALL Java_com_example_app_Class_method (JNIEnv*

    env, jobject instance, jstring param){ // native code } →バイト配列を戻り値とし、引数として文字列を受け取る  Classクラスのメソッドmethodとして、Javaで使用できる ※C++のABIは処理系依存であるため、宣言はextern “C”内で行い、Cのマングルを使用する。
  3. About Session このSessionの作成自体には何も難しいことはありませんが、 Sessionはシングルスレッド前提であることに注意し、厳重に管理する 必要があります。 Androidアプリではあらゆるところで非同期処理が使われており、意 図しない形で別スレッドから操作が走る可能性があります。なるべく、 Sessionを作成するために使ったJavaのスレッドからSessionへの操 作を行う必要があります。 また、ネイティブコードの実行であるため、間違えて別スレッドからアク

    セスしたりして何かが起きてしまった場合、セグメンテーション違反で 問答無用でアプリが落ちます。しかも、エラーハンドリングをきちんとし ていないと落ちた理由すらわからない状況になる可能性もあります。 Pythonとは異なりC++は全て自分で管理する必要がありますので、 このSessionの管理にはとりわけ注意してください。
  4. Session - Create Session作成は以下のようになります。今回は簡単のため、作成する Sessionは1つとします。 SessionOptions options; std::unique_ptr<Session> session(NewSession(options)); //

    使用後 session->Close(); SessionOptionsをNewSessionに渡すことで新たなSessionを作 成できます。ここで、スマートポインタを用いていることに気をつけてく ださい。 Sessionはあらゆるリソースを持つことになりますので、破棄するとき は一度だけClose()を実行する必要があります。 ここで、C++に中途半端に知識がある方は、スマートポインタのカスタ ムデリータでCloseすればいいと考えるでしょう。 これは、ほぼ間違いなくやっては駄目です。
  5. Tensor - Flat auto view = tensor.flat<float>(); ここでTensorのViewを取得しています。このViewに対し操作を加え ると、元となったTensorに反映されます。このような間接的にオブジェ クトを操作する事ができるようなデザインをViewと呼ぶことがありま

    す。 このViewはフラットとして取得したので、一次元配列のようにアクセス できます。 view(i) = i; 更に詳しく言えばViewの関数オブジェクトとしてデザインされており、 関数呼び出し演算子にインデクスを指定することで要素にアクセスで きます。この戻り値はmutableなため、左辺値として機能します。
  6. Tensor - Shaped 形状を変形して取得したい場合は以下のようになります。 auto view = tensor.shaped<float,2>({12, 10}); view(0,0)

    = 0; これは、12x5x2だったTensorを12x10の2次元Tensorとして取得し ています。 こんなこともできちゃいます。 auto view = tensor.shaped<float,5>({2,2,2,3,5}); view(0,0,0,0,0) = 0; より複雑な操作についてはAPIリファレンスを読んで下さい。 https://www.tensorflow.org/versions/r0.11/api_docs/cc/ClassTensor.html
  7. Run graph - inputs 第1引数 inputs 型 vector<pair<string,Tensor>> const& これは簡単ですね。入力となるTensorです。

    Python APIのfeed_dictと等価です。使い方もそっくりです。 例) session->Run({ {"x", x}, {"labels", labels}, }, ...);
  8. Run graph - output_tensor_names 第2引数 output_tensor_names 型 vector<string> const& これが一番厄介です。名前から察するに、出力となるTensorの名前

    ですよね? 実は、これが先程言った、オペレーションの指定なのです。 TensorFlowはこの出力(=エンドポイント)が満たされるまで実行され ます。よって、ここにオペレーションの名前を指定することで、オペレー ションを実行できます。 例) session->Run({}, {"train_op:0"}, ...
  9. Run graph - target_node_names 第3引数 target_node_names 型 vector<string> const& これもちょっと厄介です。これは、「実行されるが出力に含まれないノー

    ド」を指定します。繰り返し言っているように、TensorFlowは出力が 満たされた時点で実行を終了します。しかし、これでは出力はないが 変数を変更するなどの副作用を持つ動作をするオペレーションが実行 できません。そういったノードを指定します。 私は使ったことがありませんが、サンプルによっては使い分けている ので、意味を理解しておかないと混乱します。
  10. Run graph - node name ちなみに、output_tensor_namesに指定する名前ですが、 これはグラフ定義の中に定義されている名前である必要が あります。この名前は、Pythonでグラフを作る時にちゃんと 名前をつけることをおすすめします。 y

    = tf.nn.softmax(tf.matmul(x, W) + b, name='name') ほとんどのオペレーション、関数にname引数が存在しており、 省略すると自動的に付きます。これはTensorBoardで使われる だけでなく、C++から使うときにも必要になりますので、 重要なオペレーションには必ず名前をつけましょう。
  11. Bazel - Options もう一つ気をつけないといけないのが、ビルドオプションです。 クロスコンパイルツールとターゲットABIの明示的な指定が 必要になります。 bazel build libexample.so --crosstool_top=//external:android/crosstool

    --cpu=armeabi-v7a --host_crosstool_top=@bazel_tools//tools/cpp:toolchain これを指定しないとAndroidでは実行できません。 尚、これらの依存ツール群は、TensorFlowのbzlファイルを 読み込むことでBazelにより自動解決されます。
  12. CREDITS Special thanks to all the people who made and

    released these awesome resources for free: ◦ Presentation template by SlidesCarnival ◦ Photographs by Unsplash