创建一个 iOS 应用以预测值

1. 准备工作

在此 Codelab 中,您将学习如何将 TensorFlow Serving 与 REST 和 gRPC 结合使用,从 iOS 应用运行回归推断。

前提条件

  • 具备使用 Swift 进行 iOS 开发的基础知识
  • 具备使用 TensorFlow 进行机器学习的基础知识,例如训练和部署
  • 了解 Colaboratory 基础知识
  • 具备终端、Python 和 Docker 方面的基础知识

学习内容

  • 如何使用 TensorFlow 训练回归模型。
  • 如何构建一个简单的 iOS 应用,并通过 TensorFlow Serving(REST 和 gRPC)使用经过训练的模型进行预测。
  • 如何在界面中显示结果。

所需物品

2. 进行设置

如需下载此 Codelab 的代码,请执行以下操作:

  1. 转到此 Codelab 的 GitHub 代码库
  2. 依次点击 Code > Download zip,下载此 Codelab 的所有代码。

a72f2bb4caa9a96.png

  1. 解压缩下载的 ZIP 文件,将包含您需要的所有资源的 codelabs 根文件夹解压缩。

在本 Codelab 中,您只需要代码库 TFServing/RegressioniOS 子目录中的文件,其中包含两个文件夹:

  • starter 文件夹包含您基于此 Codelab 构建的起始代码。
  • finished 文件夹包含完成后的示例应用的完整代码。

3.下载项目的依赖项

下载所需的 pod

  • starter/iOS 文件夹中,运行以下命令:
pod install

Cocoapods 将安装所有必要的库并生成新的 regression.xcworkspace 文件。

4.运行入门级应用

  • 双击 regression.xcworkspace 文件以打开 Xcode。

运行和探索应用

  1. 将设备目标更改为任何 iPhone,如 iPhone 13。

a57198a4f21f970.png

  1. 点击 cacc15c5638260ed.png 'Run',然后等待 Xcode 编译项目并在模拟器中启动入门应用。

界面非常简单。有一个文本框可供您输入数字,并通过 REST 或 gRPC 将其发送到 TensorFlow Serving 后端。后端对输入值执行回归,并将预测值返回给客户端应用,客户端应用会再次在界面中显示结果。

d2976072474ce0b1.png

如果您输入数字并点击 Infer(推断),则应用还没有与后端通信,因此系统不会执行任何操作。

5. 使用 TensorFlow 训练简单的回归模型

回归是最常见的机器学习任务之一。其目标是根据输入预测单个连续数量。例如,根据今天的天气预报明天的最高温度。

训练回归模型

  1. 在浏览器中打开此链接

Colab 加载 Python 笔记本。

  1. 在 Python 笔记本中,导入 TensorFlowNumPy 库,然后创建六对训练数据,其中 xs 作为输入,ys 作为标签。

如果您在图表上绘制这些数据点,它们实际上位于一条直线中,因为它们是根据 y = 2x -1 的等式生成的。

56d05252cfc9df9d.png

  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。

  1. 导出模型:
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}
  1. 将导出的 SavedModel 压缩到一个 regression.zip 文件中:
!zip -r regression.zip ./regression
  1. 点击导航菜单中的 Runtime > Run all 以运行笔记本,然后等待运行完成。
  2. 点击 c55600d42359f901.png 文件,然后下载 regression.zip 文件。

bceda15d86571583.png

6.使用 TensorFlow Serving 部署回归模型

  • 如需使用 TensorFlow Serving 部署模型,请使用解压缩工具(例如 7-Zip)解压缩下载的 regression.zip 文件。

文件夹结构应如下图所示:

7faeb4f03af39646.png

您可以将 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 发送请求和接收响应

有三个简单的步骤:

  1. 创建 REST 请求。
  2. 将 REST 请求发送到 TensorFlow Serving。
  3. 从 REST 响应中提取预测结果,并呈现界面。

您可以在 iOS/regression/ViewController.swift 文件中完成以下步骤。

创建 REST 请求

  1. 目前,doInference() 函数不会将 REST 请求发送到 TensorFlow Serving。您需要实现此 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(即请求的载荷)中。

  1. 将以下代码添加到 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 Serving 发送 REST 请求

  • 将以下代码添加到 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])
}

现在,后处理函数从响应中提取预测值,并在界面中显示结果。

运行应用

  1. 点击 cacc15c5638260ed.png Run,然后等待 Xcode 在模拟器中启动该应用。
  2. 在文本框中输入一个数字,然后点击推断

现在,您会在界面中看到预测值。

df9bcb9aa21bb30e.png

8. 通过 gRPC 将 iOS 应用与 TensorFlow Serving 连接

除了 REST 之外,TensorFlow Serving 还支持 gRPC

b6f4449c2c850b0e.png

gRPC 是一种现代的高性能远程过程调用 (RPC) 框架,可以在任何环境中运行。它通过可插入式支持负载均衡、跟踪、健康检查和身份验证,高效地连接数据中心内和各个数据中心内的服务。我们发现,在实践中,gRPC 的性能比 REST 更高。

使用 gRPC 发送请求和接收响应

有四个简单的步骤:

  1. 可选:生成 gRPC 客户端桩代码。
  2. 创建 gRPC 请求。
  3. 将 gRPC 请求发送到 TensorFlow Serving。
  4. 从 gRPC 响应中提取预测结果,并呈现界面。

您可以在 iOS/regression/ViewController.swift 文件中完成以下步骤。

可选:生成 gRPC 客户端桩代码

如需将 gRPC 与 TensorFlow Serving 搭配使用,您需要遵循 gRPC 工作流。如需了解详情,请参阅 gRPC 文档

a9d0e5cb543467b4.png

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 客户端桩代码,请执行以下操作:

  1. 在终端中,转到 starter/src/proto/ 文件夹,然后生成桩:
bash generate_grpc_stub_swift.sh

系统会在 starter/src/proto/generated/import 文件夹中生成许多 .swift 文件。

  1. 如果尚未将其复制到您的项目中,请拖动生成的所有.swift添加到项目中的 Xcode。

9e65705cf6be7aac.png

创建 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)))

向 TensorFlow Serving 发送 gRPC 请求

  • 将以下代码添加到 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)")
}

现在,后处理函数从响应中提取预测值,并在界面中显示结果。

运行应用

  1. 点击导航菜单中的 cacc15c5638260ed.png Run,然后等待 Xcode 在模拟器中启动应用。
  2. 在文本框中输入一个数字,然后点击推断

现在,您会在界面中看到预测值。

9. 恭喜

您已使用 TensorFlow Serving 为应用添加回归功能!

了解详情