程式碼研究室簡介
1. 事前準備
在本程式碼研究室中,您將瞭解如何使用包含搭配 REST 和 gRPC 的 TensorFlow Serving,在 iOS 應用程式中執行迴歸推論。
必要條件
- 與 Swift 進行 iOS 開發作業的基本知識
- TensorFlow 機器學習基本知識,例如訓練和部署
- Colaboratory 基本知識
- 終端機、Python 和 Docker 的基本知識
課程內容
- 如何使用 TensorFlow 訓練迴歸模型。
- 如何透過 TensorFlow Serving (REST 和 gRPC) 使用經過訓練的模型建構簡單的 iOS 應用程式及進行預測。
- 如何在使用者介面中顯示結果。
軟硬體需求
- Colab 的存取權
- 最新版本的 Xcode
- CocoaPods
- Docker
- 現金
- 通訊協定緩衝區編譯器 (除非您想要重新產生 gRPC 存根) 才適用。
- gRPC-swift code-generator 外掛程式 (除非您想要自行產生 gRPC 存留,否則需要)
2. 做好準備
若要下載此程式碼研究室的程式碼:
- 前往這項程式碼研究室的 GitHub 存放區。
- 按一下 [程式碼 >下載 zip],即可下載這個程式碼研究室的所有程式碼。
- 將下載的 ZIP 檔案解壓縮,解壓縮您需要的所有
codelabs
根資料夾。
在這個程式碼研究室中,您只需要存放區的 TFServing/RegressioniOS
子目錄中的檔案,其中包含兩個資料夾:
starter
資料夾包含您為這個程式碼研究室建立的範例程式碼。finished
資料夾包含已完成範例應用程式的範例程式碼。
3. 下載專案的依附元件
下載必要的 pod
- 在
starter/iOS
資料夾中執行下列指令:
pod install
Cocoapods 會安裝所有必要的資料庫,並產生新的 regression.xcworkspace
檔案。
4. 執行啟動應用程式
- 按兩下
regression.xcworkspace
檔案即可開啟 Xcode。
執行並探索應用程式
- 將裝置目標變更為任何 iPhone,例如 iPhone 13。
- 按一下
[執行],然後等待 Xcode 編譯專案,並在模擬工具中開啟啟動應用程式。
使用者介面相當簡單,這裡有一個文字方塊,您可以輸入數值。系統會將這個數字傳送到包含 REST 或 gRPC 的 TensorFlow Serving 後端。後端會對輸入值執行迴歸,並將預測值傳回用戶端應用程式,讓應用程式重新顯示在使用者介面中。
如果您輸入數字並點選 [推測],應用程式就不會與後端通訊,因此不會發生任何狀況。
5. 使用 TensorFlow 訓練簡單的迴歸模型
迴歸是最常見的機器學習工作之一。目的是根據輸入內容預測單一的連續數量。例如,根據今天的天氣狀況,預測明天最高溫度。
訓練迴歸模型
- 在瀏覽器中開啟這個連結。
Colab 會載入 Python 筆記本。
- 在 Python 筆記本中,匯入
TensorFlow
和NumPy
程式庫,然後建立六組訓練資料,其中xs
為輸入資料,ys
為標籤。
如果您把這些資料點放到圖表上,其實實際上是直線,因為這些項目是由 y = 2x -1 方程式所產生。
- 使用 Keras API 建立一個簡單的雙層類神經網路,根據
x
輸入內容預測y
值,然後編譯並調整模型。
xs = np.array([-1.0, 0.0, 1.0, 2.0, 3.0, 4.0], dtype=float)
ys = np.array([-3.0, -1.0, 1.0, 3.0, 5.0, 7.0], dtype=float)
model = tf.keras.Sequential([
tf.keras.layers.Dense(units=10, input_shape=[1]),
tf.keras.layers.Dense(units=1),
])
model.compile(optimizer='sgd',
loss='mean_squared_error')
history = model.fit(xs, ys, epochs=500, verbose=0)
print("Finished training the model")
print(model.predict([10.0]))
模型訓練需要幾秒鐘的時間,因此 10
輸入內容的預測值為 18.999996
,這是非常好的預測結果,因為真值為 2 * 10 -1 = 19。
- 匯出模型:
model_dir = './regression/'
version = 123
export_path = os.path.join(model_dir, str(version))
model.save(export_path, save_format="tf")
print('\nexport_path = {}'.format(export_path))
!ls -l {export_path}
- 將匯出的 SavedModel 壓縮成單一
regression.zip
檔案:
!zip -r regression.zip ./regression
- 按一下導覽選單中的 [Run &&t; Run all] (執行時間> Run all) 以執行筆記本,然後等待執行作業完成。
- 按一下 [
檔案],然後下載
regression.zip
檔案。
6. 使用 TensorFlow Serving 部署迴歸模型
- 如要使用 TensorFlow Serving 部署模型,請使用壓縮工具 (例如 7-Zip) 將下載的
regression.zip
檔案解壓縮。
資料夾結構應如下列所示:
regression
資料夾可視為 SavedModel
資料夾。123
是範例版本號碼。如有需要,您可以挑選其他號碼。
啟動 TensorFlow Serving
- 在終端機中,以 Docker 啟動 TensorFlow Serving,但將
PATH/TO/SAVEDMODEL
預留位置改成您電腦上regression
資料夾的絕對路徑。
docker pull tensorflow/serving docker run -it --rm -p 8500:8500 -p 8501:8501 -v "PATH/TO/SAVEDMODEL:/models/regression" -e MODEL_NAME=regression tensorflow/serving
Docker 會先自動下載 TensorFlow Serving 映像檔,這可能需要一分鐘的時間。之後,TensorFlow Serving 會隨即啟動。記錄應如下所示:
2022-02-25 06:01:12.513231: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:206] Restoring SavedModel bundle. 2022-02-25 06:01:12.585012: I external/org_tensorflow/tensorflow/core/platform/profile_utils/cpu_utils.cc:114] CPU Frequency: 3000000000 Hz 2022-02-25 06:01:13.395083: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:190] Running initialization op on SavedModel bundle at path: /models/ssd_mobilenet_v2_2/123 2022-02-25 06:01:13.837562: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:277] SavedModel load for tags { serve }; Status: success: OK. Took 1928700 microseconds. 2022-02-25 06:01:13.877848: I tensorflow_serving/servables/tensorflow/saved_model_warmup_util.cc:59] No warmup data file found at /models/ssd_mobilenet_v2_2/123/assets.extra/tf_serving_warmup_requests 2022-02-25 06:01:13.929844: I tensorflow_serving/core/loader_harness.cc:87] Successfully loaded servable version {name: regression version: 123} 2022-02-25 06:01:13.985848: I tensorflow_serving/model_servers/server_core.cc:486] Finished adding/updating models 2022-02-25 06:01:13.985987: I tensorflow_serving/model_servers/server.cc:367] Profiler service is enabled 2022-02-25 06:01:13.988994: I tensorflow_serving/model_servers/server.cc:393] Running gRPC ModelServer at 0.0.0.0:8500 ... [warn] getaddrinfo: address family for nodename not supported 2022-02-25 06:01:14.033872: I tensorflow_serving/model_servers/server.cc:414] Exporting HTTP/REST API at:localhost:8501 ... [evhttp_server.cc : 245] NET_LOG: Entering the event loop ...
7. 透過 REST 將 iOS 應用程式與 TensorFlow Serving 連結
後端已就緒,您可以傳送用戶端要求至 TensorFlow Serving 進行預測。向 TensorFlow Serving 傳送要求的方法有兩種:
- REST
- gRPC
透過 REST 傳送要求及接收回應
這裡有三個簡單的步驟:
- 建立 REST 要求。
- 將 REST 要求傳送至 TensorFlow Serving。
- 從 REST 回應中擷取預測結果並顯示 UI。
您可以在 iOS/regression/ViewController.swift
檔案中完成這些步驟。
建立 REST 要求
doInference()
函式目前無法向 REST Serving 傳送 REST 要求。您必須導入這個 REST 分支來建立 REST 要求:
if (connectionMode[picker.selectedRow(inComponent: 0)] == "REST") {
print("Using REST")
// TODO: Add code to send a REST request to TensorFlow Serving.
}
TensorFlow Serving 的 POST 要求包含單一值,因此您必須將輸入值嵌入 JSON 中,也就是要求的要求的酬載。
- 將下列程式碼新增至 REST 分支版本:
//Create the REST request.
let json: [String: Any] = ["signature_name" : "serving_default", "instances" : [[value]]]
let jsonData = try? JSONSerialization.data(withJSONObject: json)
let url = URL(string: "http://localhost:8501/v1/models/regression:predict")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
// Insert JSON data into the request.
request.httpBody = jsonData
將 REST 要求傳送至 TensorFlow Serving
- 將下列程式碼加入 REST 分支之後的程式碼中:
// Send the REST request.
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else {
print(error?.localizedDescription ?? "No data")
return
}
// TODO: Add code to process the response.
}
task.resume()
處理 TensorFlow Serving 中的 REST 回應
- 請將這段程式碼加進
TODO: Add code to process the response.
註解的上述程式碼片段:
// Process the REST response.
let results: RESTResults = try! JSONDecoder().decode(RESTResults.self, from: data)
DispatchQueue.main.async{
self.txtOutput.text = String(results.predictions[0][0])
}
現在,後續處理函式會從回應中擷取預測值,並在使用者介面中顯示結果。
執行
- 按一下
[執行],然後等待 Xcode 在模擬工具中啟動應用程式。
- 在文字方塊中輸入數字,然後按一下 [推測]。
現在,您會在使用者介面中看到預測值。
8. 透過 gRPC 將 iOS 應用程式與 TensorFlow Serving 連結
除了 REST 以外,TensorFlow Serving 也支援 gRPC。
gRPC 是現代化、開放原始碼的高效能遠端程序呼叫 (RPC) 架構,可在任何環境中執行。這項服務具備可連接的負載平衡、追蹤、健康狀態檢查和驗證等功能,可透過高效率的方式連結資料中心內外的服務。發現在實驗中,gRPC 的成效比 REST 更好。
使用 gRPC 傳送要求及接收回應
這裡有四個簡單的步驟:
- 選用:產生 gRPC 用戶端 stub 程式碼。
- 建立 gRPC 要求。
- 將 gRPC 要求傳送至 TensorFlow Serving。
- 從 gRPC 回應中擷取預測結果,然後顯示 UI。
您可以在 iOS/regression/ViewController.swift
檔案中完成這些步驟。
選用:產生 gRPC 用戶端 stub 程式碼
如要搭配 TensorFlow Serving 使用 gRPC,您需要遵守 gRPC 工作流程。如要進一步瞭解相關細節,請參閱 gRPC 說明文件。
TensorFlow Serving 和 TensorFlow 會為您定義 .proto
檔案。自 TensorFlow 和 TensorFlow Serving 2.8 起,下列 .proto
檔案為必要檔案:
tensorflow/core/example/example.proto
tensorflow/core/example/feature.proto
tensorflow/core/protobuf/struct.proto
tensorflow/core/protobuf/saved_object_graph.proto
tensorflow/core/protobuf/saver.proto
tensorflow/core/protobuf/trackable_object_graph.proto
tensorflow/core/protobuf/meta_graph.proto
tensorflow/core/framework/node_def.proto
tensorflow/core/framework/attr_value.proto
tensorflow/core/framework/function.proto
tensorflow/core/framework/types.proto
tensorflow/core/framework/tensor_shape.proto
tensorflow/core/framework/full_type.proto
tensorflow/core/framework/versions.proto
tensorflow/core/framework/op_def.proto
tensorflow/core/framework/graph.proto
tensorflow/core/framework/tensor.proto
tensorflow/core/framework/resource_handle.proto
tensorflow/core/framework/variable.proto
tensorflow_serving/apis/inference.proto
tensorflow_serving/apis/classification.proto
tensorflow_serving/apis/predict.proto
tensorflow_serving/apis/regression.proto
tensorflow_serving/apis/get_model_metadata.proto
tensorflow_serving/apis/input.proto
tensorflow_serving/apis/prediction_service.proto
tensorflow_serving/apis/model.proto
如要產生 gRPC 用戶端 stub 程式碼:
- 在終端機中,前往
starter/src/proto/
資料夾並產生 stub:
bash generate_grpc_stub_swift.sh
starter/src/proto/generated/import
資料夾中會產生多個 .swift
檔案。
- 如果他們尚未複製到您的專案中,請將所有產生的
.swift
檔案拖曳至 Xcode 的專案。
建立 gRPC 要求
與 REST 要求類似,您會在 gRPC 分支中建立 gRPC 要求。
if (connectionMode[picker.selectedRow(inComponent: 0)] == "REST") {
}
else {
print("Using gRPC")
// TODO: add code to send a gRPC request to TF Serving
}
- 如要建立 gRPC 要求,請將此程式碼新增至 gRPC 分支版本:
//Create the gRPC request.
let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
let channel = ClientConnection.insecure(group: group).connect(host: "localhost", port: 8500)
let stub = Tensorflow_Serving_PredictionServiceClient(channel: channel)
var modelSpec = Tensorflow_Serving_ModelSpec()
modelSpec.name = "regression"
modelSpec.signatureName = "serving_default"
// Prepare the input tensor.
var batchDim = Tensorflow_TensorShapeProto.Dim()
batchDim.size = 1
var inputDim = Tensorflow_TensorShapeProto.Dim()
inputDim.size = 1
var inputTensorShape = Tensorflow_TensorShapeProto()
inputTensorShape.dim = [batchDim, inputDim]
var inputTensor = Tensorflow_TensorProto()
inputTensor.dtype = Tensorflow_DataType.dtFloat
inputTensor.tensorShape = inputTensorShape
inputTensor.floatVal = [Float(value)]
var request = Tensorflow_Serving_PredictRequest()
request.modelSpec = modelSpec
request.inputs = ["dense_input" : inputTensor]
let callOptions = CallOptions(timeLimit: .timeout(.seconds(15)))
將 gRPC 要求傳送至 TensorFlow Serving
- 將下列程式碼加入前一個程式碼片段中緊接在 gRPC 分支的後方:
// Send the gRPC request.
let call = stub.predict(request, callOptions: callOptions)
處理 TensorFlow Serving 中的 gRPC 回應
- 加入以下程式碼,緊接在前一個程式碼片段中的程式碼後面:
// Process the response.
call.response.whenSuccess { response in
let result = response.outputs["dense_1"]?.floatVal[0]
DispatchQueue.main.async{
self.txtOutput.text = String(describing: result!)
}
}
call.response.whenFailure { error in
print("Call failed with error\n\(error)")
}
現在,後續處理函式會從回應中擷取預測值,並在使用者介面中顯示結果。
執行
- 在導覽選單中按一下
[執行],然後等待 Xcode 在模擬工具中啟動應用程式。
- 在文字方塊中輸入數字,然後按一下 [推測]。
現在,您會在使用者介面中看到預測值。