閱讀1010 返回首頁    go 阿裏雲 go 技術社區[雲棲]


TensorFlow在iOS和Mac上的使用

一、環境


1首先你得安裝好Xcode 8,確定開發者目錄指向你安裝Xcode的位置並且已經被激活。(如果你在安裝Xcode之前已經安裝了Homebrew,這可能會指向錯誤的地址,導致TensorFlow安裝失敗):


sudo xcode-select -s /Applications/Xcode.app/Contents/Developer


2、安裝Homebrewhttps://brew.sh/index_zh-cn.html


3Homebrew安裝其他軟件


brew install python3

brew cask install java

brew install bazel

brew install automake

brew install libtool

brew install wget


4、命令:brew list 查看已安裝的軟件

FBD439D4-C8D0-4DF5-8DF3-FFAFF50AEAEE.png


5、成功安裝了python3,那pip3也安裝,使用python3 的包管理器pip3來安裝所需要的包。


pip3 install numpy

pip3 install scipy

pip3 install scikit-learn

pip3 install pandas

pip3 install tensorflow


6、命令:pip3 list 查看已安裝的包

5fc5acfe44cdb3c50c9fa399db4daa95d6699128


這些包會安裝在/usr/local/lib/python3.6/site-packages目錄下。


7、下載TensorFlow源碼,需要使用源碼文件編譯模型,並且使用官方iOSDemo

克隆TensorFlow GitHub倉庫。注意,一定要保存在沒有空格的路徑下,否則bazel會拒絕構建。我是克隆到我的主目錄下:


cd /Users/matthijs

git clone https://github.com/tensorflow/tensorflow -b r1.0

  

-b r1.0表明克隆的是r1.0分支。當然你也可以隨時獲取最新的分支或者主分支。


Note:MacOS Sierra 上,運行下麵的配置腳本報錯了,我隻能克隆主分支來代替。在OS X EI Caption 上使用r1.0分支就不會有任何問題。


一旦GitHub倉庫克隆完畢,你就需要運行配置腳本(configure script:


cd tensorflow

./configure


這裏有些地方可能需要你自行配置,比如:


Please specify the location of python. [Default is /usr/bin/python]:


我寫的是/usr/local/bin/python3,因為我使用的是Python 3.6。如果你選擇默認選項,就會使用Python 2.7來創建TensorFlow


Please specify optimization flags to use during compilation [Default is 

-march=native]:


這裏隻需要按Enter鍵。後麵兩個問題,隻需要選擇n(表示 no)。當詢問使用哪個Python庫時,按Enter鍵選擇默認選項(應該是Python 3.6 庫)。剩下的問題都選擇n。隨後,這個腳本將會下載大量的依賴項並準備構建TensorFlow所需的一切。


8、構建靜態庫

有兩種方法構建TensorFlow1.Mac上使用bazel工具;2.IOS上,使用Makefile。我們是在IOS上構建,所以選擇第2種方式。不過因為會用到一些工具,也會用到第一種方式。


TensorFlow的目錄中執行以下腳本:


tensorflow/contrib/makefile/build_all_ios.sh


這個腳本首先會下載一些依賴項,然後開始構建。一切順利的話,它會創建三個鏈入你的app的靜態庫:libtensorflow-core.a libprotobuf.a libprotobuf-lite.a

還有另外兩個工具需要構建,在終端運行如下兩行命令:


bazel build tensorflow/python/tools:freeze_graph

bazel build tensorflow/python/tools:optimize_for_inference


Note: 這個過程至少需要20分鍾,因為它會從頭開始構建TensorFlow(本次使用的是bazel)。如果遇到問題,請參考官方指導



9iOS工程配置引用參考:


9.1Other Linker Flags : 

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

如圖:

8b6d1f87e498c091361659e199c46660b9380dc3


9.2、Header Search Paths : 

$(SRCROOT)/../../makefile/gen/proto $(SRCROOT)/../../makefile/downloads/eigen $(SRCROOT)/../../makefile/downloads $(SRCROOT)/../../makefile/downloads/protobuf/src/ $(SRCROOT)/../../../..


9.3、Libaray Search Paths :

$(SRCROOT)/../../makefile/gen/lib $(SRCROOT)/../../makefile/gen/protobuf_ios/lib


如圖:

fc8f8800da4e2bbc7c51127bebc9405c6cd5ec31


9.4、Demo工程與makefile目錄相對關係如圖:

ef15fed656748c65635bedec2d2184e1208473ff


10、庫的引用如圖:

6ab50187f6d9616aed236634a119de2df2160085


二、模型訓練


1.下載數據


cd /Users/javalong/Desktop

mkdir Test

cd Test

curl -O https://download.tensorflow.org/example_images/flower_photos.tgz

tar xzf flower_photos.tgz


進入flower_photos,可以看到5個文件夾,daisy dandelion roses sunflowers tulips


d9bb3338f89439ed8cf5711a521e3e62e364c955



2. 利用預訓練模型訓練數據


模型是穀歌的Inceptionv3https://arxiv.org/abs/1512.00567)。在2012年的imageNet上進行訓練,並在2012ImageNet上取得了3.4%top-5準確率(人類的隻有5%

這麼一個複雜的網絡若是直接自己訓練,起碼需要幾天甚至十幾天的時間。所以這裏我采用複用深度學習的方法。即模型前麵的層的參數都不變,而隻訓練最後一層的方法。最後一層是一個softmax分類器,這個分類器在原來的網絡上是1000個輸出節點(ImageNet1000個類),所以需要刪除網絡的最後的一層,變為所需要的輸出節點數量,然後再進行訓練。

tensorflow/examples/image_retraining/retrain.py 中采用的方法是這樣的:將自己的訓練集中的每張圖像輸入網絡,最後在瓶頸層(bottleneck),就是倒數第二層,會生成一個2048維度的特征向量,將這個特征保存在一個txt文件中,再用這個特征來訓練softmax分類器。


進入tensorflow源代碼目錄


2.1

cd tensorflow


2.2

python3 tensorflow/examples/image_retraining/retrain.py \

--bottleneck_dir=/Users/javalong/Desktop/Test/ \

--how_many_training_steps 100000 \

--model_dir=/Users/javalong/Desktop/Test/inception2 \

--output_graph=/Users/javalong/Desktop/Test/retrained_graph2.pb \

--output_labels=/Users/javalong/Desktop/Test/retrained_labels.txt \

--image_dir=/Users/javalong/Desktop/Test/flower_photos


腳本會下載Inceptionv3模型,80多兆,可以提前下載好放到Testinception2目錄下

模型路徑:https://download.tensorflow.org/models/image/imagenet/inception-2015-12-05.tgz

訓練完成後,在Test目錄下看到模型文件retrained_graph.pb和標簽retrained_labels.txt


3. 模型驗證


新建文件label_image.py,注意格式,代碼縮進使用tab


#-*- coding:utf-8 -*-

import os

import numpy as np

import tensorflow as tf

from sklearn import metrics



# 隨便一張玫瑰花圖片

image_path = "test1.jpg"


# read in the image_data

image_data = tf.gfile.FastGFile(image_path, "rb").read()


# Loads label file, strips off carriage return

label_lines = [line.strip() for line in tf.gfile.GFile("retrained_labels.txt")]


# Unpersists graph from file

with tf.gfile.FastGFile("retrained_graph2.pb", "rb") as f:

    graph_def = tf.GraphDef()

    graph_def.ParseFromString(f.read())

    _ = tf.import_graph_def(graph_def, name="")


with tf.Session() as sess:

# Feed the image_data as input to the graph and get first prediction

    softmax_tensor = sess.graph.get_tensor_by_name("final_result:0")

    preditions = sess.run(softmax_tensor, {"DecodeJpeg/contents:0":image_data})

# sort to show labels of first prediction in order of confidence

    top_k = preditions[0].argsort()[-len(preditions[0]):][::-1]

    for node_id in top_k:

        human_string = label_lines[node_id]

        score = preditions[0][node_id]

        print('%s (score = %.5f' %(human_string, score))


執行文件

python label_image.py 


得到類似下圖結果

1d9742b24d5ccd4af906516a34476abf074b7bb9




、編譯模型


將模型文件轉化成ios可以上可以運行的文件


1、首先編譯tensorflow源碼示例中的label_image來測試retrained_graph2.pb模型

1.1

cd tensorflow


1.2

bazel build tensorflow/examples/label_image:label_image


1.3

bazel-bin/tensorflow/examples/label_image/label_image \

--output_layer=final_result \

--labels=/Users/javalong/Desktop/Test/retrained_labels.txt \

--image=/Users/javalong/Desktop/Test/flower_photos/daisy/5547758_eea9edfd54_n.jpg \

--graph=/Users/javalong/Desktop/Test/retrained_graph2.pb


如圖所示:

9455ebd24166d4b52bb2ce935fcd7c2476a703a2


2、優化模型並去掉ios不支持的算子

翻譯文章中說明如下:

由於移動設備內存有限,並且app需要下載,因此TensorFlowiOS版本隻支持那些推理中常見的算子(ops,提供英文原文以防翻譯錯誤),沒有大量支持擴展的依賴項。你可以在tensorflow/contrib/makefile/tf_op_files.txt這個文件中找到可以使用的算子列表。

文章中提到,DecodeJpeg是不能使用的算子之一,因為其實現方法依賴於libjpeg,而它在iOS上運行非常麻煩(painful to support),並且增加binary footprint(不懂。。。)。盡管可以用iOS原生的圖像庫實現,大部分應用其實直接圖像buffer而不需要對jpeg進行編碼。

問題麻煩的地方在於,我們使用的Inception模型包括了DecodeJpeg算子。我們通過直接將圖片數據傳給Mul結點繞過DecodeJpeg操作。盡管如此,圖被加載的時候,如果平台不支持,即使算子沒有被調用,還是會報錯,因此我們利用optimize_for_inference去掉所有沒有被輸入和輸出結點用到的結點。這個腳本同時會完成一些優化來加速,例如將batch normalization轉換成卷積權重(the convolutional weights)來減少計算次數。


2.1、

bazel build tensorflow/python/tools:optimize_for_inference


2.2

bazel-bin/tensorflow/python/tools/optimize_for_inference \

--input=/Users/javalong/Desktop/Test/retrained_graph2.pb \

--output=/Users/javalong/Desktop/Test/optimized_graph.pb \

--input_names=Mul \

--output_names=final_result


如圖所示:

0c845d2225e22821a527441f525fdd09a6df48f5


同樣可以通過label_image驗證optimized_graph.pb模型的有效性。


3、將模型的權重變成256之內的常數

優化前模型87.5M,優化後的模型還是有87.2M,可以損失一定的精確度,將模型的權重從浮點型轉化成整數。

3.1、 

bazel build tensorflow/tools/quantization:quantize_graph


3.2

bazel-bin/tensorflow/tools/quantization/quantize_graph --input=/Users/javalong/Desktop/Test/optimized_graph.pb \

--output=/Users/javalong/Desktop/Test/rounded_graph.pb \

--output_node_names=final_result \

--mode=weights_rounded


如圖所示:

6c877e5ebcd31fd41e8d6a6cadd19b025a5e092e


4、驗證rounded_graph.pb模型

4.1、

bazel-bin/tensorflow/examples/label_image/label_image \

--output_layer=final_result \

--labels=/Users/javalong/Desktop/Test/retrained_labels.txt \

--image=/Users/javalong/Desktop/Test/flower_photos/daisy/5547758_eea9edfd54_n.jpg \

--graph=/Users/javalong/Desktop/Test/rounded_graph.pb


如圖所示:

458b3b4b19f5db9305eaefa7864c189ecda05e04


5、內存映射(memory mapping

由於app87M的模型權值加載到內存中,會對RAM帶來壓力導致穩定性問題,因為OS可能會殺死占用內存太多的應用。幸運的是,這些緩衝區的內容是隻讀的,能以os在內存遇到壓力的時候很容易釋放的方式將模型映射到內存中。為此,我們將模型轉換成可以從GraphDef分別加載到形式。

5.1、

bazel build tensorflow/contrib/util:convert_graphdef_memmapped_format


5.2

bazel-bin/tensorflow/contrib/util/convert_graphdef_memmapped_format \

--in_graph=/Users/javalong/Desktop/Test/rounded_graph.pb \

--out_graph=/Users/javalong/Desktop/Test/mmapped_graph.pb


需要注意的是,此時模型文件已經不是一般的GraphDef protobuf,所以如果還按照以前的方法加載會遇到錯誤。


5.3、

bazel-bin/tensorflow/examples/label_image/label_image \

--output_layer=final_result \

--labels=/Users/javalong/Desktop/Test/retrained_labels.txt \

--image=/Users/javalong/Desktop/Test/flower_photos/daisy/5547758_eea9edfd54_n.jpg \

--graph=/Users/javalong/Desktop/Test/mmapped_graph.pb


如圖所示:

5213021205abcf7851bf3b6b60567baf8d300fdd


四、使用iOS Demo工程


1demo是在tensorflow官方提供的ios示例代碼camera的基礎上進行的修改,工程路徑:tensorflow/contrib/ios_examples/camera

2、將rounded_graph.phretrained_labels.txt導入到data目錄下。

3、修改CameraExampleViewController.mm中要加載到文件,圖片等尺寸,結點的名字和如何縮放像素值。


a47c2f0423974ca3963a820bc43c84da87f699bd


4、最後程序運行的結果如下:


b39adfa86faae031e8e48c6618f39204d0467ed1




最後更新:2017-07-13 13:32:30

  上一篇:go  深入分析synchronized的實現原理
  下一篇:go  Vim技能修煉教程(11) - 代碼折疊