手把手教你使用TF服務將TensorFlow模型部署到生產環境

摘要: 訓練好的模型不知道如何佈置到生產環境?快來學習一下吧!

介紹

將機器學習(ML)模型應用於生產環境已成爲一個火熱的的話題,許多框架提供了旨在解決此問題的不一樣解決方案。爲解決這一問題,谷歌發佈了TensorFlow(TF)服務,以期待解決將ML模型部署到生產中的問題。python

本文提供了一個關於服務於預先訓練的卷積語義分割網絡的實踐教程。閱讀本文後,你將可以使用TF服務來部署和向TF訓練的深度CNN發出請求等操做。另外,本文將概述TF服務的API及其工做原理。若是你想學習本教程並在計算機上運行示例,請完整了解本文。可是,若是你只想瞭解TensorFlow服務,你能夠專一於前兩部分。git

TensorFlow服務庫-概述

首先咱們須要花一些時間來了解TF Serving如何處理ML模型的整個生命週期。在這裏,咱們將介紹TF服務的主要構建塊,本部分的目標是提供TF服務API的介紹。如需深刻了解,請訪問TF服務文檔頁面。程序員

TensorFlow服務由一些抽象組成,這些抽象類用於不一樣任務的API,其中最重要的是Servable,Loader,Source和Manager,讓咱們來看看他們之間是如何互動的:github

簡單來講,當TF Serving識別磁盤上的模型時,Source組件就開始工做啦,整個服務生命週期也算開始了,Source組件負責識別應加載的新模型。實際上,它會密切關注文件系統,以肯定新模型版本什麼時候到達磁盤。當它看到新版本模型時,它會爲該特定版本的模型建立一個Loader。api

總之,Loader幾乎瞭解模型的全部內容,包括如何加載以及如何估計模型所需的資源,例如請求的RAM和GPU內存。Loader還有一個指向磁盤上模型的指針以及用於加載它的全部必要的元數據。可是有一個問題:加載器不容許加載模型。建立Loader後,Source會將其做爲Aspired Version發送給Manager。安全

收到模型的Aspired Version後,Manager繼續執行服務過程。這裏有兩種可能性,一個是推送第一個模型版本進行部署,在這種狀況下,Manager將確保所需的資源可用,完成後,Manager會授予Loader加載模型的權限;第二是咱們推出現有模型的新版本,在這種狀況下,管理員必須先諮詢版本策略插件,而後再繼續操做,版本策略肯定如何進行加載新模型版本的過程。服務器

具體來講,在第一種狀況下,咱們能夠確保咱們的系統始終可用於傳入客戶的請求。此時,咱們同時加載了兩個模型版本,只有在加載完成後,Manager纔會卸載舊版本,而且能夠安全地在模型之間切換。另外一方面,若是咱們想經過不使用額外緩衝區來節省資源,咱們能夠選擇保留數據。最後,當客戶端請求模型的handle時,管理器返回Servable的handle。網絡

在接下來的部分中,咱們將介紹如何使用TF服務提供卷積神經網絡(CNN)。框架

導出服務模型

爲TensorFlow構建的ML模型提供服務的第一步是確保它的格式正確,爲此,TensorFlow提供了SavedModel類。機器學習

SavedModel是TensorFlow模型的通用序列化格式,若是你熟悉TF,則可使用TensorFlow Saver來保留模型的變量。

TensorFlow Saver提供了將模型的檢查點文件保存到磁盤或從磁盤恢復的功能。實際上,SavedModel包裝了TensorFlow Saver,它是導出TF模型進行服務的標準方式。

SavedModel object有一些很好的功能。首先,它容許你將多個元圖保存到單個SavedModel對象,換句話說,它容許咱們爲不一樣的任務提供不一樣的圖表。例如,假設你剛剛完成了模型的訓練。在大多數狀況下,要執行推理,你的圖表不須要某些特定於訓練的操做。這些操做可能包括優化器的變量,學習速率調度張量,額外的預處理操做等。此外,你可能但願爲移動部署提供量化版本的圖形。

在此環境中,SavedModel容許你使用不一樣的配置保存圖形。在咱們的例子中,咱們有三個不一樣的圖形和相應的標籤,如「訓練」、「推理」和「移動」。此外,這三個圖形爲了提高內存效率還共享相同的變量集。

就在不久前,若是咱們想在移動設備上部署TF模型時,咱們須要知道輸入和輸出張量的名稱,以便向模型提供數據或從模型獲取數據。這須要強制程序員在圖的全部張量中搜索他們所需的張量。若是張量沒有正確命名,那麼任務可能很是繁瑣。

爲了簡化操做,SavedModel提供對SignatureDefs的支持,SignatureDefs定義了TensorFlow支持的計算的簽名。它肯定了計算圖的正確輸入和輸出張量,也就是說使用這些簽名,你能夠指定用於輸入和輸出的確切節點。要使用其內置的服務API,TF Serving要求模型包含一個或多個SignatureDefs。

要建立此類簽名,咱們須要提供輸入,輸出和所需方法名稱的定義,輸入和輸出表示從字符串到TensorInfo對象的映射。在這裏,咱們定義了默認張量,用於向圖表輸入數據和從圖表接收數據。

目前,有三種服務API:分類,預測和迴歸。每一個簽名定義都與特定的RPC API相匹配,Classification SegnatureDef用於Classify RPC API,Predict SegnatureDef用於Predict RPC API等等依此類推。

對於分類簽名,必須有輸入張量(接收數據)和兩個可能的輸出張量中的至少一個:類或分數。Regression SignatureDef只須要一個張量用於輸入,另外一個用於輸出。最後,Predict signature容許動態數量的輸入和輸出張量。此外,SavedModel支持數據存儲,以用於ops初始化依賴於外部文件的狀況,它還具備在建立SavedModel以前清除設備的機制。

如今,讓咱們看看咱們如何在實踐中作到這一點。

設置環境

在開始以前,咱們須要從Github克隆此TensorFlow DeepLab-v3。DeepLab是谷歌最好的語義分割ConvNet,網絡能夠將圖像做爲輸入並輸出相似掩模的圖像,該圖像將某些對象與背景分開。

該版本的DeepLab在Pascal VOC分段數據集上進行了訓練,所以,它能夠分割和識別多達20個類。若是你想了解有關語義分段和DeepLab-v3的更多信息,請查看深刻深度卷積語義分段網絡和Deeplab_V3

與服務相關的全部文件都存在於:./deeplab_v3/serving/。在那裏,你會發現兩個重要的文件:deeplab_saved_model.pydeeplab_client.ipynb

在進一步研究以前,請務必下載Deeplab-v3預訓練模型。前往上面的GitHub存儲庫,單擊checkpoints連接,你應該有一個名爲tboard_logs /的文件夾,其中包含16645 /文件夾。

如今,咱們須要建立兩個Python虛擬環境,一個用於Python 3,另外一個用於Python 2,請確保安裝必要的依賴項。你能夠在serving_requirements.txtclient_requirements.txt文件中找到它們。

你可能很好奇爲何須要兩個Python env,由於咱們的模型DeepLab-v3是在Python 3下開發的,而TensorFlow Serving Python API僅針對Python 2發佈。所以,要導出模型並運行TF服務,咱們使用Python 3 env 。

請注意,你可使用bazel中的Serving API放棄Python 2 env。有關更多詳細信息,請參閱TF服務實例。完成這一步後,讓咱們從真正重要的事情開始吧。

實例教程

TensorFlow提供了一個易於使用的高級實用程序類使用SavedModel,類名爲SavedModelBuilder。SavedModelBuilder類提供了保存多個元圖,關聯變量和數據的功能。讓咱們來看一個如何導出Deep Segmentation CNN模型進行服務的運行示例。

如上所述,要導出模型,咱們使用啦SavedModelBuilder類。它將生成SavedModel協議緩衝區文件以及模型的變量和資源。

讓咱們剖析一下代碼:

# Create SavedModelBuilder class
# defines where the model will be exported
export_path_base = FLAGS.export_model_dir
export_path = os.path.join(
    tf.compat.as_bytes(export_path_base),
    tf.compat.as_bytes(str(FLAGS.model_version)))
print('Exporting trained model to', export_path)
builder = tf.saved_model.builder.SavedModelBuilder(export_path)

SavedModelBuilder接收(做爲輸入)保存模型數據的目錄。這裏,export_path變量是爲了鏈接export_path_basemodel_version。所以,不一樣的模型版本將保存在export_path_base文件夾內的單獨目錄中。

假設咱們在生產中有咱們模型的基礎版本,但咱們想要部署它的新版本。由於咱們已經提升了模型的準確性,並但願爲咱們的客戶提供這個新版本。要導出同一模型的不一樣版本,咱們只需將FLAGS.model_version設置爲更高的整數值便可。而後將在export_path_base文件夾中建立一個不一樣的文件夾(保存咱們模型的新版本)。

如今,咱們須要指定模型的輸入和輸出Tensors。爲此,咱們使用SignatureDefs,簽名定義了咱們要導出的模型類型。它提供了從字符串(邏輯Tensor名稱)到TensorInfo對象的映射。咱們的想法是,客戶端能夠引用簽名定義的邏輯名稱,而不是引用輸入/輸出的實際張量名稱。

爲了服務語義分段CNN,咱們將建立一個預測簽名。請注意,build_signature_def()函數採用輸入和輸出張量的映射以及所需的API。

SignatureDef須要指定:輸入,輸出和方法名稱,咱們指望輸入有三個值一圖像,另外兩個張量指定其尺寸(高度和寬度)。對於輸出,咱們只定義了一個結果-分段輸出掩碼。

# Creates the TensorInfo protobuf objects that encapsulates the input/output tensors
tensor_info_input = tf.saved_model.utils.build_tensor_info(input_tensor)
tensor_info_height = tf.saved_model.utils.build_tensor_info(image_height_tensor)
tensor_info_width = tf.saved_model.utils.build_tensor_info(image_width_tensor)

# output tensor info
tensor_info_output = tf.saved_model.utils.build_tensor_info(predictions_tf)

# Defines the DeepLab signatures, uses the TF Predict API
# It receives an image and its dimensions and output the segmentation mask
prediction_signature = (
    tf.saved_model.signature_def_utils.build_signature_def(
        inputs={'images': tensor_info_input, 'height': tensor_info_height, 'width': tensor_info_width},
        outputs={'segmentation_map': tensor_info_output},
        method_name=tf.saved_model.signature_constants.PREDICT_METHOD_NAME))

請注意,字符串‘image',‘height',‘width'和‘segmentation_map'不是張量。相反,它們是引用實際張量input_tensor,image_height_tensor和image_width_tensor的邏輯名稱。所以,它們能夠是你喜歡的任何惟一字符串。此外,SignatureDefs中的映射與TensorInfo protobuf對象有關,而與實際張量無關。要建立TensorInfo對象,咱們使用實用程序函數:tf.saved_model.utils.build_tensor_info(tensor)

如今咱們調用add_meta_graph_and_variables()函數來構建SavedModel協議緩衝區對象,而後咱們運行save()方法,它會將模型的快照保存到包含模型變量和資源的磁盤。

builder.add_meta_graph_and_variables(
    sess, [tf.saved_model.tag_constants.SERVING],
    signature_def_map={
        'predict_images':
            prediction_signature,
    })

# export the model
builder.save(as_text=True)
print('Done exporting!')

如今咱們能夠運行deeplab_saved_model.py來導出咱們的模型。

若是一切順利,你將看到文件夾./serving/versions/1,請注意,「1」表示模型的當前版本。在每一個版本子目錄中,你將看到如下文件:

·saved_model.pb或saved_model.pbtxt這是序列化的SavedModel文件。它包括模型的一個或多個圖形定義,以及簽名定義。

·變量,該文件夾包含圖形的序列化變量。

如今,咱們已準備好啓動咱們的模型服務器。爲此,請運行:

$ tensorflow_model_server --port=9000 --model_name=deeplab --model_base_path=<full/path/to/serving/versions/>

model_base_path指的是輸出模型保存,另外,咱們不在路徑中指定版本文件夾,模型版本控制由TF服務處理。

生成客戶端請求

客戶端代碼很是簡單,看一下:deeplab_client.ipynb。首先,咱們讀取要發送到服務器的圖像並將其轉換爲正確的格式。接下來,咱們建立一個gRPC存根,存根容許咱們調用遠程服務器的方法。爲此,咱們將實例化prediction_service_pb2模塊的beta_create_PredictionService_stub類。此時,存根保持調用遠程過程的必要邏輯,就像它們是本地的同樣。

如今,咱們須要建立和設置請求對象。因爲咱們的服務器實現了TensorFlow Predict API,所以咱們須要解析Predict請求。要發出Predict請求,首先,咱們從predict_pb2模塊中實例化PredictRequest類。咱們還須要指定model_spec.namemodel_spec.signature_name參數。該名稱參數是當咱們推出的服務器定義的「模型名稱」的說法,而signature_name是指分配給邏輯名稱signature_def_map()的參數add_meta_graph()函數。

# create the RPC stub
channel = implementations.insecure_channel(host, int(port))
stub = prediction_service_pb2.beta_create_PredictionService_stub(channel)

# create the request object and set the name and signature_name params
request = predict_pb2.PredictRequest()
request.model_spec.name = 'deeplab'
request.model_spec.signature_name = 'predict_images'

# fill in the request object with the necessary data
request.inputs['images'].CopyFrom(
  tf.contrib.util.make_tensor_proto(image.astype(dtype=np.float32), shape=[1, height, width, 3]))

request.inputs['height'].CopyFrom(tf.contrib.util.make_tensor_proto(height, shape=[1]))
request.inputs['width'].CopyFrom(tf.contrib.util.make_tensor_proto(width, shape=[1]))

接下來,咱們必須提供服務器簽名中定義的輸入數據。請記住,在服務器中,咱們定義了一個Predict API來預期圖像以及兩個標量(圖像的高度和寬度)。爲了將輸入數據提供給請求對象,TensorFlow提供了實用程序tf.make_tensor_proto(),此方法是從Python/numpy建立的TensorProto對象,咱們可使用它將圖像及其尺寸提供給請求對象。

看起來咱們已經準備好調用服務器了。爲此,咱們調用Predict()方法(使用存根)並將請求對象做爲參數傳遞。gRPC支持:同步和異步調用。所以,若是你在處理請求時想要作一些工做,咱們能夠調用Predict.future()而不是Predict()

# sync requests
result_future = stub.Predict(request, 30.)

# For async requests
# result_future = stub.Predict.future(request, 10.)
# Do some work...
# result_future = result_future.result()

如今咱們能夠獲取並享受結果。



本文做者:【方向】

閱讀原文

本文爲雲棲社區原創內容,未經容許不得轉載。

相關文章
相關標籤/搜索