値を予測する iOS アプリを作成する

1. 始める前に

この Codelab では、TensorFlow Serving と REST および gRPC を使用して iOS アプリから回帰推論を実行する方法を学びます。

前提条件

  • Swift による iOS 開発の基本的な知識
  • トレーニングやデプロイなど、TensorFlow による機械学習に関する基本的な知識
  • Colaboratory の基本的な知識
  • ターミナル、Python、Docker の基本的な知識

学習内容

  • TensorFlow で回帰モデルをトレーニングする方法
  • 簡単な iOS アプリを作成し、TensorFlow Serving(REST と gRPC)を使用してトレーニング済みモデルを使用して予測を行う方法。
  • 結果を UI に表示する方法。

必要なもの

2. 設定する

この Codelab のコードをダウンロードするには:

  1. この Codelab の GitHub リポジトリに移動します。
  2. [Code] > [Download zip] をクリックして、この Codelab のすべてのコードをダウンロードします。

a72f2bb4caa9a96.png

  1. ダウンロードした zip ファイルを解凍して、必要なリソースがすべて揃った codelabs ルートフォルダを展開します。

この Codelab では、リポジトリの TFServing/RegressioniOS サブディレクトリ内のファイルのみが必要です。このサブディレクトリには 2 つのフォルダが含まれています。

  • starter フォルダには、この Codelab で構築するスターター コードが含まれています。
  • finished フォルダには、完成したサンプルアプリの完成したコードが含まれています。

3. プロジェクトの依存関係をダウンロードする

必要な Pod をダウンロードする

  • starter/iOS フォルダで、次のコマンドを実行します。
pod install

CocoaPods により、必要なライブラリがすべてインストールされ、新しい regression.xcworkspace ファイルが生成されます。

4.スターター アプリを実行する

  • regression.xcworkspace ファイルをダブルクリックして Xcode を開きます。

アプリを実行して操作する

  1. デバイス ターゲットを iPhone 13 などの iPhone に変更します。

a57198a4f21f970.png

  1. cacc15c5638260ed.png [Run] をクリックし、Xcode がプロジェクトをコンパイルし、シミュレータでスターター アプリを起動するのを待ちます。

UI は非常にシンプルです。テキスト ボックスには数値を入力できます。この数値は、REST または gRPC で TensorFlow Serving バックエンドに送信されます。バックエンドは、入力値に対して回帰を行い、予測値をクライアント アプリに返します。クライアント アプリはその結果を UI に再度表示します。

d2976072474ce0b1.png

数値を入力して [推論] をクリックすると、アプリがまだバックエンドと通信できないため、何も起こりません。

5. TensorFlow を使用して単純な回帰モデルをトレーニングする

回帰は ML タスクの中でも最も一般的なものの 1 つです。目標は、入力に基づいて単一の連続数量を予測することです。たとえば、今日の天気に基づいて明日の最高気温を予測します。

回帰モデルのトレーニング

  1. ブラウザで、このリンクを開きます。

Colab が Python ノートブックを読み込みます。

  1. Python ノートブックで TensorFlow ライブラリと NumPy ライブラリをインポートし、入力として xs、ラベルとして ys を使用して、6 組のトレーニング データを作成します。

これらのデータポイントをグラフでプロットすると、データポイントは y = 2x -1 式から生成されるため、実際には直線上にあります。

216-05252cfc9df9d.png

  1. Keras API を使用して、単純な 2 層ニューラル ネットワークを作成し、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 を 1 つの regression.zip ファイルに圧縮します。
!zip -r regression.zip ./regression
  1. ナビゲーション メニューで [ランタイム] > [すべて実行] をクリックしてノートブックを実行し、実行が完了するまで待ちます。
  2. [c55600d42359f901.png Files] をクリックし、regression.zip ファイルをダウンロードします。

bceda15d86571583.png

6. TensorFlow Serving を使用して回帰モデルをデプロイする

  • TensorFlow Serving を使用してモデルをデプロイするには、ダウンロードした regression.zip ファイルを 7-Zip などの解凍ツールで解凍します。

フォルダ構造は次の画像のようになります。

7faeb4f03af39646.png

regression フォルダは、SavedModel フォルダとして参照できます。123 はバージョン番号の例です。別の数字を選択することもできます。

TensorFlow サービスの提供を開始

  • ターミナルで、TensorFlow Serving と Docker を起動します。ただし、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 の画像が自動的にダウンロードされます。これには 1 分ほどかかります。その後、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 にリクエストを送信する方法は 2 つあります。

  • REST
  • gRPC

REST でリクエストを送信してレスポンスを受信する

手順は次の 3 つです。

  1. REST リクエストを作成します。
  2. TensorFlow Serving に REST リクエストを送信します。
  3. REST レスポンスから予測結果を抽出し、UI をレンダリングします。

以下の手順は iOS/regression/ViewController.swift ファイルで行います。

REST リクエストを作成する

  1. 現時点では、doInference() 関数は TensorFlow 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 に埋め込む必要があります。

  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

TensorFlow 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])
}

これで、後処理関数によってレスポンスから予測値が抽出され、その結果が UI に表示されます。

実行

  1. cacc15c5638260ed.png [Run] をクリックし、Xcode がシミュレータでアプリを起動するのを待ちます。
  2. テキスト ボックスに数値を入力し、[推論] をクリックします。

これで、UI に予測値が表示されます。

df9bcb9aa21bb30e.png

8. gRPC を使用して iOS アプリと TensorFlow Serving を接続する

TensorFlow Serving は REST に加えて、gRPC もサポートします。

b6f4449c2c850b0e.png

gRPC は、最新のオープンソースで高性能なリモート プロシージャ コール(RPC)フレームワークです。あらゆる環境で実行できます。負荷分散やトレース、ヘルスチェック、認証に対応するプラグイン可能なサポートにより、データセンター内やデータセンター間でサービスを効率的に接続できます。実際には、gRPC は REST よりもパフォーマンスが高いことがわかっています。

gRPC でリクエストを送信してレスポンスを受信する

次の 4 つの簡単な手順に沿ってください。

  1. (省略可)gRPC クライアント スタブコードを生成します。
  2. gRPC リクエストを作成する。
  3. gRPC サービングに gRPC リクエストを送信します。
  4. gRPC レスポンスから予測結果を抽出し、UI をレンダリングします。

以下の手順は iOS/regression/ViewController.swift ファイルで行います。

省略可: gRPC クライアント スタブコードを生成する

TensorFlow Serving で gRPC を使用するには、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

複数の .swift ファイルが starter/src/proto/generated/import フォルダに生成されます。

  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)")
}

これで、後処理関数によってレスポンスから予測値が抽出され、その結果が UI に表示されます。

実行

  1. ナビゲーション メニューで cacc15c5638260ed.png [Run] をクリックし、Xcode がシミュレータでアプリを起動するのを待ちます。
  2. テキスト ボックスに数値を入力し、[推論] をクリックします。

これで、UI に予測値が表示されます。

9. 完了

TensorFlow Serving を使用して、アプリに回帰機能を追加しました。

詳細