《Tensorflow For Machine Intelligence》第七章的翻譯解讀(一)

《Tensorflow For Machine Intelligence》第七章是關於Tensorflow模型部署的內容,雖然該書已有中文版,但一些技術部分感覺翻譯的不到位,沒闡述透徹,因此在閱讀英文原版的同時順便整理了一下自己的理解。    


正文


爲了使用Docker鏡像,我們可用https://github.com/tensorflow/serving/blob/master/tensorflow_serving/tools/docker/Dockerfile.devel文件,這是創建本地鏡像的配置文件,所以爲了使用該文件我們用下面的命令:

docker build --pull -t $USER/tensorflow-serving-devel \
https://raw.githubusercontent.com/tensorflow/serving/master/tensorflow_serving/tools/docker/Dockerfile.devel


注意上面這條命令需要一段時間來下載所有的依賴。


現在,爲了用鏡像運行容器並在上面開展工作,我們用下面的命令:

docker run -v $HOME:/mnt/home -p 9999:9999 -it $USER/tensorflow-serving-devel


該命令將你的home目錄掛載到容器的/mnt/home路徑,並使你在容器內的終端上工作。這樣做是非常有用的,因爲你可以(在容器外)直接使用趁手的IDE/編輯器來編寫代碼,只是用容器來運行編譯工具。爲了讓你的宿主機能夠使用

後面將要創建的server,我們開放了容器的9999端口讓宿主機訪問。


用命令exit能夠退出容器終端,停止容器運行。用上面介紹的命令可以多次再啓動容器。



Bazel workspace 


Tensorflow Serving程序是用C++代碼編寫的,因此應該用Google的Bazel編譯工具來編譯。我們將在最近創建的容器內運行Bazel。


Bazel在代碼層面管理第三方依賴,只要依賴也是用Bazel編譯,就下載並編譯它們。爲了定義我們的工程將支持哪些第三方依賴,必須在工程庫的根目錄定義WORKSPACE文件。


我們需要的依賴是Tensorflow Serving庫,對於我們的例子,還需要包含Inception model代碼的Tensorflow Models庫。


不幸的是在本書撰寫之際,Tensorflow Serving並不支持作爲Git庫通過Bazel直接引用,所以我們必須把它作爲Git子模塊包含在工程中:

# 在本機上
mkdir ~/serving_example
cd ~/serving_example
git init
git submodule add https://github.com/tensorflow/serving.git tf_serving
git submodule update --init --recursive

現在我們用WORKSPACE文件中的local_repository規則將第三方依賴定義爲本地存儲文件。此外,也必須用工程中導入的tf_workspace規則來初始化Tensorflow依賴:

# Bazel WORKSPACE file
workspace(name = "serving")
local_repository(
name = "tf_serving",
path = __workspace_dir__ + "/tf_serving",
)
local_repository(
name = "org_tensorflow",
path = __workspace_dir__ + "/tf_serving/tensorflow",
)
load('//tf_serving/tensorflow/tensorflow:workspace.bzl', 'tf_workspace')
tf_workspace("tf_serving/tensorflow/", "@org_tensorflow")
bind(
name = "libssl",
actual = "@boringssl_git//:ssl",
)
bind(
name = "zlib",
actual = "@zlib_archive//:zlib",
)
# only needed for inception model export
local_repository(
name = "inception_model",
path = __workspace_dir__ + "/tf_serving/tf_models/inception",
)


最後一步我們必須在容器內爲Tensorflow運行./configure

# 在docker容器上
cd /mnt/home/serving_example/tf_serving/tensorflow
./configure



Exporting trained models 導出訓練模型


一旦訓練好模型,我們很樂於評估它的性能。我們需要導出模型的計算圖和變量值,便於生產時可用。


與訓練版本相比,模型的計算圖應該做一些修改,因爲它必須從佔位符接收輸入,在模型上運行單步推斷來計算輸出結果。以Inception model爲例,一般來說,對於任何圖像識別模型,我們希望輸入是代表圖像(JPEG編碼)的單個

字符串,這樣我們就能夠很容易地從客戶app上發送它。這與從TFRecords文件讀取訓練輸入不同。


定義輸入的一般形式應該像這樣:

def convert_external_inputs(external_x):
# transform the external input to the input format required on inference
def inference(x):
# from the original model…
external_x = tf.placeholder(tf.string)
x = convert_external_inputs(external_x)
y = inference(x)


在上面的代碼中我們爲輸入定義佔位符,調用函數將佔位符表示的外部輸入轉化爲原始模型推斷方法要求的格式。例如我們將JPEG字符串轉化爲推斷要求的圖像格式。最後我們對轉化後的輸入調用原始模型推斷方法。


例如,對於Inception模型我們應該有這樣的方法:

import tensorflow as tf
from tensorflow_serving.session_bundle import exporter
from inception import inception_model
def convert_external_inputs(external_x):
# transform the external input to the input format required on inference
# convert the image string to a pixels tensor with values in the range 0,1
image = tf.image.convert_image_dtype(tf.image.decode_jpeg(external_x, channels=3), tf.float32)
# resize the image to the model expected width and height
images = tf.image.resize_bilinear(tf.expand_dims(image, 0), [299, 299])
# Convert the pixels to the range -1,1 required by the model
images = tf.mul(tf.sub(images, 0.5), 2)
return images
def inference(images):
logits, _ = inception_model.inference(images, 1001)
return logits


推斷方法要求參數賦值,我們將從訓練checkpoint恢復這些參數值。你可以回想在基礎章節裏,我們週期性保存模型的訓練checkpoint。這些文件包含了當時學習到的參數值,所以即使發生災難我們也不會丟失訓練進度。


當訓練結束時,最後保存的訓練checkpoint將包含最新的模型參數,這些是我們希望投入生產的。


爲了恢復checkpoint,代碼應該是:

saver = tf.train.Saver()
with tf.Session() as sess:
# Restore variables from training checkpoints.
ckpt = tf.train.get_checkpoint_state(sys.argv[1])
if ckpt and ckpt.model_checkpoint_path:
saver.restore(sess, sys.argv[1] + "/" + ckpt.model_checkpoint_path)
else:
print("Checkpoint file not found")
raise SystemExit


對Inception模型,可以從http://download.tensorflow.org/models/image/imagenet/inception-v3-2016-03-01.tar.gz下載預先訓練好的checkpoint。

# 在docker容器上

cd /tmp
curl -O http://download.tensorflow.org/models/image/imagenet/inception-v3-2016-03-01.tar.gz
tar -xzf inception-v3-2016-03-01.tar.gz


最後,我們用tensorflow_serving.session_bundle.exporter.Exporter類導出模型。

我們創建該類的實例來傳遞saver實例,然後用exporter.classification_signature方法創建模型的signature。signature說明了哪個是input_tensor,哪些是輸出張量。輸出由classes_tensor和scores_tensor組成,其中classes_tensor包含輸出類名的列表,scores_tensor包含模型分配給每個類的得分/概率。


通常模型中有大量的類別,可以用tf.nn.top_k配置僅返回選擇的類別,這些類別是模型分配得分最高的k個類別。


最後一步應用signature是調用exporter.Exporter.init方法,用export方法導出模型。export方法接收輸出路徑,模型版本號和會話。

scores, class_ids = tf.nn.top_k(y, NUM_CLASSES_TO_RETURN)
# for simplification we will just return the class ids, we should return the names instead
classes = tf.contrib.lookup.index_to_string(tf.to_int64(class_ids),
mapping=tf.constant([str(i) for i in range(1001)]))
model_exporter = exporter.Exporter(saver)
signature = exporter.classification_signature(
input_tensor=external_x, classes_tensor=classes, scores_tensor=scores)
model_exporter.init(default_graph_signature=signature, 
                    init_op=tf.initialize_all_tables())
model_exporter.export(sys.argv[1] + "/export", 
                      tf.constant(time.time()), sess)

由於Exporter類代碼中對自動生成代碼的依賴,我們必須在Docker容器內用bazel運行導出器。

爲此,我們在前面啓動的bazel workspace裏把代碼保存爲export.py。需要有像下面的編譯規則的BUILD文件:

# BUILD file
py_binary(
name = "export",
srcs = [
"export.py",
],
deps = [
"//tensorflow_serving/session_bundle:exporter",
"@org_tensorflow//tensorflow:tensorflow_py",
# only needed for inception model export
"@inception_model//inception",
],
)


然後我們在容器內用命令運行導出器:

# 在docker容器上
cd /mnt/home/serving_example
bazel run :export /tmp/inception-v3


基於解壓在/tmp/inception-v3的checkpoint,上述命令將在/tmp/inception-v3/{current_timestamp}/創建導出。


首次運行要花費一些時間,因爲它必須編譯Tensorflow。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章