TensorFlowでInceptionを動かす(2)

概要

前回の続きですよ。
結果から先に言うとワタクシのiPhone6では正常動作しなかった。
メモリが足りなくてすぐ落ちるんだよ。
Inception-v1ならかろうじて動くんだけど前回も述べた通りv1で転移学習する方法が分からなかった。
今回の方法もiPhone6sとかならRAMが2GBあるからイケるはずなので一応コトの顛末を載せておこうか。

iOSで転移学習済みモデルを実行する

まずはiOSでのビルドから説明していこう。
基本的には前回落としたソースの中にiOS用のビルドスクリプトが含まれているのでこれを実行することから始めよう。

tensorflow/contrib/makefile/build_all_ios.sh

上記は30分ぐらいかかる。
上記が終わった後に以下のパスにあるプロジェクトをXcodeで開いてみる。

tensorflow/contrib/ios_examples/camera

Xcodeを開いたらまずはBuild SettingsのHeader Search PathsやLibrary Search Pathsなどを調べてリンク先が存在しているか確かめる。
俺の場合は下記画像のようになっていて特に問題なかった。

headersearchpaths

最も重要なのはOther Linker Flagsの

$(SRCROOT)/../../makefile/gen/lib/libtensorflow-core.a

であろう。これがTensorFlow本体だと思われる。
これがちゃんと生成されているか確かめる。
生成されてなかったら前回のビルドをやり直すといいんじゃないかな。

次に標準Frameworkを手動で追加しなおした(俺のケースでは下記のようにFoundationとCoreGraphics以外のリンクが切れていたため)。ダルい。

xcode1

ここで、libprotobuf-lite.aとlibprotobuf.aは以下のパスにあるのでAdd Other…で追加する必要がある。

tensorflow/contrib/makefile/gen/protobuf_ios/lib

これで多分ちゃんとiOSビルドするようになった。

次にInception-v3はv1とモデルが微妙に異なるため、CameraExampleViewController.mmの以下のように修正する(コメントアウトしてるのがInception-v1用コード)。

//  const int wanted_width = 224;
//  const int wanted_height = 224;
//  const int wanted_channels = 3;
//  const float input_mean = 117.0f;
//  const float input_std = 1.0f;

  const int wanted_width = 299;
  const int wanted_height = 299;
  const int wanted_channels = 3;
  const float input_mean = 128.0f;
  const float input_std = 128.0f;

あとこっちも。

//  std::string input_layer = "input";
//  std::string output_layer = "output";

  std::string input_layer = "Mul";
  std::string output_layer = "final_result";

で、最後にXcodeプロジェクト以下のdataディレクトリに前回転移学習して得られたpbファイルとラベルファイルを突っ込んでリネームする。
以下。

cp /tmp/output_graph.pb ./tensorflow/contrib/ios_examples/camera/data/tensorflow_inception_graph.pb
cp /tmp/output_labels.txt ./tensorflow/contrib/ios_examples/camera/data/imagenet_comp_graph_label_strings.txt

シミュレータ上で動かすとsetupAVCaptureのところでエラーになるので(カメラがないから?)、実機iPhone6上でビルドしてみよう。
あれ?ビルドは出来たけど起動直後にエラーが出るな・・・。
これが以下のエラーである。

Running model failed:Invalid argument: No OpKernel was registered to support Op 'DecodeJpeg' with these attrs
	 [[Node: DecodeJpeg = DecodeJpeg[acceptable_fraction=1, channels=3, fancy_upscaling=true, ratio=1, try_recover_truncated=false](DecodeJpeg/contents)]]

クソ、何なんだこれ。全く意味不明。
早速ググる。

then i copy and rename the new graph.pb + labels.txt to the examples/android/assets folder

the model works fine on desktop by testing it with “label_image”, but i cannot load this model in the android demo app without getting the ‘DecodeJpeg’ Error

i hope someone has some hints how i can solve this problem.
Loading the newer inception model in Android demo example and No OpKernel was registered to support Op error #1269

この人はAndroidみたいだけど同じエラーっぽいね。
さらにググる。

Sorry you’re hitting problems! Since DecodeJpeg isn’t supported as part of the core, you’ll need to strip it out of the graph first. I’m working on a more user-friendly approach, but you should be able to run the strip_unused script on it
iOS example DecodeJpeg issue with Image Retraining model #2883

お、これiOSだしドンピシャじゃね?
DecodeJpegが動かないからグラフからこの部分を取り除いてみようってことらしい。
そんなことして大丈夫なんかいな。
まぁとにかくこのコメント通りやれば動きそうだね。やってみよう。

bazel build tensorflow/python/tools:strip_unused

bazel-bin/tensorflow/python/tools/strip_unused \
--input_graph=/tmp/output_graph.pb \
--output_graph=/tmp/output_graph_stripped.pb \
--input_node_names=Mul \
--output_node_names=final_result \
--input_binary=true

上記コードが正常に動けば/tmp/output_graph_stripped.pbが得られるはずだ。
これをまたさっきのようにリネームしてXcode配下にぶち込んでみよう。

cp /tmp/output_graph_stripped.pb ./tensorflow/contrib/ios_examples/camera/data/tensorflow_inception_graph.pb

そんでもう一回実機iPhone6でビルドだ!
おーエラー出ない!動いた!よっしゃあああああああああああああああああああああ!
と思ったら速攻アプリが落ちました。
エヨーン・・・・なんで・・・・?

調べたらメモリ不足で落ちてたっぽい。
ふざけんなーーファック!

メモリ使用量を減らせないか

で、またググりマンボウの旅へ。

Sorry you’re hitting problems! We actually have a few different ways to reduce memory usage. One of them is to run tensorflow/contrib/quantization/tools/quantize_graph with –mode=weights to shrink the size of the model on disk by quantizing the weights to eight bits. I’m also working on folding in the batch normalization ops into the weights, which will reduce memory pressure.
iOS example Retrained Stripped model memory usage causes app to crash without warning #2927

これだな。間違いない。
一つ目の打開策はquantizeか。なるほど。
Inception-v3って22層ぐらいあるんだっけ?要するにだから重みが膨大な数あるから、その全ての重みを8 bitに圧縮しても大して結果変わらないしいいんじゃね?って話?
ここらへんの話(↓)は前に読んだことあるからそれに近い話かな??と思った(全然違ったらゴメン)。

3.14 (こっちの方がFPが低い)
3.141592653 (こっちの方が高い)

前者より後者の方が精度が高いです。だから半精度(FP16)、単精度(FP32)、倍精度(FP64)と呼ばれている。数字はbit数です。
倍精度は主にスパコンレベルで使われていてディープラーニングでは長らく単精度が主流でしたが、段々と「半精度で十分」という考えが主流になってきているようです。
次期GPUのGTX 1080正式発表(再追記:半精度アクセラレーション対応してなかった)

とりあえずそれでいいや。やってみよう!

bazel build tensorflow/contrib/quantization/tools:quantize_graph
bazel-bin/tensorflow/contrib/quantization/tools/quantize_graph --input=/tmp/output_graph.pb --output_node_names="final_result" --output=/tmp/output_graph_quantized.pb --mode=eightbit

よっしゃーこれでquantizedなグラフモデルが得られたのでこれをXcode配下にまたコピーして実機ビルドしてみよう!
・・・・・・・・・

ios_examples/camera/CameraExampleViewController.mm:375] Couldn't load model: Not found: Op type not registered 'Dequantize'

あーーまたエラーだあああ!
なんと・・・モデルがロード出来ないと・・・。
しかもこのエラーはXcodeがフリーズしてしまって強制終了しないとアカンっていうヘビーなやつだ!

要はDequantizeなんて名前のOpは登録されていないと。Opってオペレーションか。
つまりiOS用にビルドしたtensorflowのコアにはDequantizeのための関数が含まれていないってことか・・・。
困ったねえ。ググるか。

How to Quantize Neural Networks with TensorFlow

TensorFlowでのQuantizeのことならここが一番詳しいぜって感じのブログ投稿を見つけた。
つうか何コレ、TensorFlowの中の人の投稿か。
で、全部読んでコメント欄をスクロールしていくと、、、

This is great work. I tried and could able to reduce the size to 23 MB. But am getting an error Not found: Op type not registered ‘Dequantize’, when I’m trying to classify image. Please help.
Ramya says:

あったあった。この人と全く同じエラーだわこりゃ。
で、その下の回答を読むと、、、

Sorry you’re hitting problems, I should have included more information on loading the ops.

Which script are you using to classify the image? If it’s the python one, then you’ll need to include the python quantized ops in the BUILD file, like I do here for quantize_graph:
https://github.com/tensorflow/tensorflow/blob/master/tensorflow/contrib/quantization/tools/BUILD#L29
Then you’ll need to load the ops explicitly in the script, like this:
https://github.com/tensorflow/tensorflow/blob/master/tensorflow/contrib/quantization/tools/quantize_graph.py#L289

If you’re using the C++ label_image example, it’s easier because you should just be able to add tensorflow/contrib/quantization:cc_ops and tensorflow/contrib/quantization/kernels:quantized_ops as dependencies to your cc_binary in the BUILD file.
Pete Warden says:

おおー解決策らしきものが提示されてるぞ。
前半はpythonで実行する話だな。ワタクシはiOSでビルドしてるのでどっちかっていうと後半の話が近いかな。
ビルドする際にtensorflow/contrib/quantization:cc_opsとtensorflow/contrib/quantization/kernels:quantized_opsをBUILDファイルのcc_binaryにdependencyとして書きなさいってことね。
うーんC++でビルドするならそれでいいんやけどワタクシはiOS用の特殊なビルドスクリプト使ってやってるからちょっと違う方法かもなー・・・・ハテ・・・

ってことで色々やってみたんですがそもそもビルドとかあんまりやったことねーし全然わかんねーYO!って感じで頓挫中。
誰かここ分かる人いたら教えて下さいナ。

んでまぁquantizeは諦めたわけよ。で、さっきのコメントで次に言及されてた方法でメモリ使用量を削減しようかと思いましてね。
これだこれ。

We also have memory-mapped constants, which you can try by running this script on your graph:
https://github.com/tensorflow/tensorflow/blob/master/tensorflow/contrib/util/convert_graphdef_memmapped_format.cc

I haven’t tried this myself yet on iOS, but with previous projects it was a great way to reduce memory problems since mapped files don’t seem to count towards your overall usage, and are automatically swapped out when pressure is high.
iOS example Retrained Stripped model memory usage causes app to crash without warning #2927

memory-mapped constantだ!全然こちらは意味わかんなかったけどなんかうまくいきそうじゃん?
ってことで早速やってみるYO!

bazel build tensorflow/contrib/util:convert_graphdef_memmapped_format
bazel-bin/tensorflow/contrib/util/convert_graphdef_memmapped_format --in_graph=/tmp/output_graph.pb --out_graph=/tmp/output_graph_memmapped.pb

はい、またXcode配下にコピーして実機ビルドだ!

ios_examples/camera/CameraExampleViewController.mm:308] Running model failed:Not found: FeedInputs: unable to find feed output Mul

はい、出ましたねエラー。
これはググっても分かんなそうなエラーだなぁ。
Mulって名前が見つからんってことだわなー。
うーんとりあえずさっきCameraExampleViewController.mm中のinput_layerのとこのコードをMulに変えたのをやめてinputにしてみたりMul:0にしてみたりinput:0にしてみたりと色々やったけどダメだったよ。
分かんない。これも挫折。

っていうわけでメモリ使用量を削減することは出来なかった!
だがiPhone6sなら動くと信じてる!
誰か試してみて下さい!
チャオ!

以下は自分用メモです。

# TensorFlowのバージョン確認方法
pythonからだとtf.__version__

# TensorFlow v0.9.0 RC0へのアップグレード方法
sudo pip install --upgrade https://storage.googleapis.com/tensorflow/mac/tensorflow-0.9.0rc0-py2-none-any.whl