手把手教你使用TF服务将TensorFlow模型部署到生产环境
2019獨角獸企業重金招聘Python工程師標準>>>
介紹
將機器學習(ML)模型應用于生產環境已成為一個火熱的的話題,許多框架提供了旨在解決此問題的不同解決方案。為解決這一問題,谷歌發布了TensorFlow(TF)服務,以期待解決將ML模型部署到生產中的問題。
本文提供了一個關于服務于預先訓練的卷積語義分割網絡的實踐教程。閱讀本文后,你將能夠使用TF服務來部署和向TF訓練的深度CNN發出請求等操作。另外,本文將概述TF服務的API及其工作原理。如果你想學習本教程并在計算機上運行示例,請完整了解本文。但是,如果你只想了解TensorFlow服務,你可以專注于前兩部分。
TensorFlow服務庫-概述
首先我們需要花一些時間來了解TF Serving如何處理ML模型的整個生命周期。在這里,我們將介紹TF服務的主要構建塊,本部分的目標是提供TF服務API的介紹。如需深入了解,請訪問TF服務文檔頁面。
TensorFlow服務由一些抽象組成,這些抽象類用于不同任務的API,其中最重要的是Servable,Loader,Source和Manager,讓我們來看看他們之間是如何互動的:
簡單來說,當TF Serving識別磁盤上的模型時,Source組件就開始工作啦,整個服務生命周期也算開始了,Source組件負責識別應加載的新模型。實際上,它會密切關注文件系統,以確定新模型版本何時到達磁盤。當它看到新版本模型時,它會為該特定版本的模型創建一個Loader。
總之,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.py和deeplab_client.ipynb。
在進一步研究之前,請務必下載Deeplab-v3預訓練模型。前往上面的GitHub存儲庫,單擊checkpoints鏈接,你應該有一個名為tboard_logs /的文件夾,其中包含16645 /文件夾。
現在,我們需要創建兩個Python虛擬環境,一個用于Python 3,另一個用于Python 2,請確保安裝必要的依賴項。你可以在serving_requirements.txt和client_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_base和model_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.name和model_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()現在我們可以獲取并享受結果。
原文鏈接
本文為云棲社區原創內容,未經允許不得轉載。
轉載于:https://my.oschina.net/u/1464083/blog/3028461
總結
以上是生活随笔為你收集整理的手把手教你使用TF服务将TensorFlow模型部署到生产环境的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Vue事件源码分析
- 下一篇: Cable:360实现的新虚拟网络架构