TensorFlow : Mobile : TensorFlow ライブラリを統合する (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 12/13/2017
* 本ページは、TensorFlow の本家サイトの Mobile – Integrating TensorFlow libraries を翻訳した上で
適宜、補足説明したものです:
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
解決しようとしている問題を扱うモデル上で何某かの進捗を得たのであれば、直ちにアプリケーション内部でそれを実際に試してみることは重要です。訓練データと現実世界でユーザが実際に遭遇するものとの間には予期せぬ違いがしばしばあり、そしてできる限り早くギャップの明確な構図を得ることは製品体験を改良します。
このページは、TensorFlow モバイルデモ・アプリケーションを既に成功的にビルドして配備できているものとして、TensorFlow ライブラリを貴方自身のモバイル・アプリケーションにどのように統合するかについて説明します。
ライブラリをリンクする
何とかサンプルをビルドした後には、次に、貴方の既存のアプリケーションの一つから TensorFlow を呼び出すことを多分望むでしょう。これを行なうための非常に最も簡単な方法は ここで 説明される Pod インストレーションを使用することですが、 (例えば演算子が含まれるものをカスタマイズするために) TensorFlow をソースからビルドすることを望む場合には、フレームワークとしての TensorFlow から抜け出して、正しいヘッダファイルを含み、そしてビルドされたライブラリと依存に対してリンクする必要があるでしょう。
Android
Android のためには、libandroid_tensorflow_inference_java.jar と呼ばれる JAR ファイルに含まれる Java ライブラリ内でリンクすることが必要なだけです。貴方のプログラムでこの機能を含めるためには3つの方法があります :
- それを含む jcenter AAR を含めます、この example app のように。
- nightly に事前コンパイルされたバージョンを ci.tensorflow.org からダウンロードします。
- JAR ファイルを貴方自身でビルドします、Android Github レポジトリ の手順を使用して。
iOS
※ 省略 (原文 を参照してください)
グローバル・コンストラクタ・マジック
貴方自身のアプリケーションから TensorFlow を呼び出そうとするときに、貴方が遭遇するかもしれない微妙な問題の一つは “No session factory registered for the given session options” エラーです。これが何故起きるのかそしてそれをどのように修正するかを理解するためには、TensorFlow のアーキテクチャについて少し知る必要があります。
フレームワークは非常にモジュール的に設計されていて、薄いコアと独立的な非常に多くの特定のオブジェクトを持ち、それらは必要に応じてミックスしてマッチさせることができます。これを可能にするために、C++ でのコーディング・パターンはモジュールにそれらが提供するサービスについてフレームワークに通知させなければなりません。各実装から別々に更新されなければならない中心的なリストを要求することなしにです。それはまたそれら自身の実装を追加するためにコアの再コンパイルを必要とすることなしに別々のライブラリを許容しなければなりません。
この能力を達成するために、TensorFlow は多くの場所で登録パターン (= registration pattern) を使用しています。コード内では、それはこのように見えます :
class MulKernel : OpKernel {
Status Compute(OpKernelContext* context) { … }
};
REGISTER_KERNEL(MulKernel, “Mul”);
これは、カーネルの主要セットの一部か別々のカスタムライブラリとして、貴方のアプリケーションにリンクされたスタンドアローンな .cc ファイル内にあるでしょう。マジック・パートは REGISTER_KERNEL() マクロが TensorFlow のコアにそれは Mul 演算の実装を持つことを知らせることができることで、その結果それを必要とする任意のグラフで呼び出すことができます。
プログラミング視点からは、このセットアップは非常に便利です。実装と登録コードは同じファイル内に存在し、そして新しい実装の追加はそれをコンパイルしてリンクするほどに単純です。難しいパートは REGISTER_KERNEL() マクロが実装される方法に由来します。C++ はこの類の登録を行なうための良いメカニズムを提供しませんので、何某かのトリッキーなコードに頼らなければなりません。内部では、マクロはそれがこのようなものを生成するように実装されています :
class RegisterMul {
public:
RegisterMul() {
global_kernel_registry()->Register(“Mul”, [](){
return new MulKernel()
});
}
};
RegisterMul g_register_mul;
これは 、global kernel registry に、誰か (何か) が “Mul” カーネルをどのように作成するかそれに尋ねたときにどの関数を呼び出すかを教えるコンストラクタを持つ、RegisterMul クラスをセットアップします。そしてそのクラスのグローバル・オブジェクトがあり、そしてコンストラクタは任意のプログラムのスタート時に呼び出されるべきです。
これが賢明であるように思える一方で、不運なパートは、定義されたグローバル・オブジェクトはどのような他のコードからも使用されません、そのためこれを考慮して設計されているわけではないリンカはそれが削除できると決定するでしょう。その結果、コンストラクタは決して呼び出されることなく、そしてクラスは決して登録されません。総ての類のモジュールは TensorFlow でこのパターンを使用し、そしてコードが実行されるとき Session 実装が最初に探されるということが起きます、これが、この問題が発生するときにそれが特徴的なエラーとして何故現れるかの理由です。
解法は、リンカに対してライブラリからどのようなコードも、それが使用されないとリンカが信じる場合でさえも、ストリップしないように強制することです。iOS では、そのステップは -force_load フラグでライブラリパスを指定して達成されます。そして Linux 上では –whole-archive が必要です。これらはリンカにストリッピングについて積極的でないように説得し、そしてグローバルを保持すべきです。
各種の REGISTER_* マクロの実際の実装は実際にはもう少し複雑ですが、それらは総て同じ根本的な問題を被ります。それらがどのように動作するか興味があれば、op_kernel.h は調査を始めるのに良い場所です。
Protobuf 問題
TensorFlow は一般的には protobuf として知られる、Protocol Buffers ライブラリに依存しています。このライブラリはデータ構造の定義を取って、シリアライゼーションとそれらのためのアクセスコードを各種言語で生成します。トリッキーなパートはこの生成されたコードは、ジェネレータのために使用されたフレームワークの正確に同じバージョンのための共有ライブラリに対してリンクされる必要があることです。(コードを生成するために使用されたツール) protoc が標準リンクとインクルードパスにあるライブラリとは異なるバージョンの protobuf からのものであるとき、これは問題となります。例えば、貴方は ~/projects/protobuf-3.0.1.a にあるローカルでビルドされた protoc のコピーを使用しているかもしれません、しかし 3.0.0 からの /usr/local/lib と /usr/local/include にインストールされたライブラリを持ちます。
この問題の症状はコンパイルまたは protobuf とのリンクの段階の間のエラーです。通常は、ビルドツールがこれをケアしますが、makefile を使用している場合には、この Makfile で示されるように、protobuf ライブラリをローカルでビルドしてそれを使用することを確実にしてください。
問題を引き起こす他の状況は、protobuf ヘッダとソースファイルがビルドプロセスの一部として生成される必要があるときです。このプロセスはビルドをより複雑にします。最初のフェーズは必要な総てのコードファイルを作成するために protobuf 定義に渡るパスでなければなりませんので、その後にのみ続行してライブラリコードのビルドを行なうことができます。
同じアプリケーションにおける protobuf の複数バージョン
protobuf は TensorFlow ライブラリ全体への C++ インターフェイスの一部として必要なヘッダを生成します。これはスタンドアロン・フレームワークとしてライブラリを使用することを複雑にします。
もし貴方のアプリケーションが既に protocol buffers ライブラリのバージョン 1 を使用している場合には、TensorFlow の統合はトラブルを持つかもしれません、何故ならばそれはバージョン 2 を要求するからです。もし両者のバージョンを同じバイナリにリンクしようとするならば、幾つかのシンボルの衝突でリンクエラーを見るでしょう。この特定の問題を解決するために、rename_protobuf.sh に実験的なスクリプトを持ちます。
総ての依存をダウンロードした後に、makefile ビルドの一部としてこれを実行する必要があります :
tensorflow/contrib/makefile/download_dependencies.sh tensorflow/contrib/makefile/rename_protobuf.sh
TensorFlow API を呼び出す
フレームワークを利用可能にしたのであれば、次にそれを呼び出す必要があります。通常のパターンは最初に数値計算の調整されたセットを表すモデルをロードして、それからそのモデルを通して入力 (例えば、カメラからの画像) を走らせてそして出力 (例えば、予測されたラベル) を受けとります。
Android 上では、このユースケースに丁度フォーカスされた Java Inference Library (推論ライブラリ) を提供します、その一方で iOS と Raspberry Pi 上では C++ API を直接的に呼び出します。
Android
これは Android 上の典型的な推論ライブラリ・シークエンスがどのようなものかを示します :
// Load the model from disk. TensorFlowInferenceInterface inferenceInterface = new TensorFlowInferenceInterface(assetManager, modelFilename); // Copy the input data into TensorFlow. inferenceInterface.feed(inputName, floatValues, 1, inputSize, inputSize, 3); // Run the inference call. inferenceInterface.run(outputNames, logStats); // Copy the output Tensor back into the output array. inferenceInterface.fetch(outputName, outputs);
このコードのソースは Android examples で見つけられます。
iOS と Raspberry Pi
これは iOS と Raspberry Pi のための同等なコードです :
// Load the model.
PortableReadFileToProto(file_path, &tensorflow_graph);
// Create a session from the model.
tensorflow::Status s = session->Create(tensorflow_graph);
if (!s.ok()) {
LOG(FATAL) << "Could not create TensorFlow Graph: " << s;
}
// Run the model.
std::string input_layer = "input";
std::string output_layer = "output";
std::vector outputs;
tensorflow::Status run_status = session->Run({ {input_layer, image_tensor}},
{output_layer}, {}, &outputs);
if (!run_status.ok()) {
LOG(FATAL) << "Running model failed: " << run_status;
}
// Access the output data.
tensorflow::Tensor* output = &outputs[0];
これは総て iOS sample code を基にしていますが、iOS-特有のものは全くありません; 同じコードは C++ をサポートする任意のプラットフォーム上で利用できるはずです。
Raspberry Pi のための固有のサンプルはまた ここで 見つけられます。
以上