값을 예측할 iOS 앱 만들기

1. 시작하기 전에

이 Codelab에서는 REST 및 gRPC를 사용하여 TensorFlow Serving을 사용하여 iOS 앱에서 회귀 추론을 실행하는 방법을 알아봅니다.

기본 요건

  • Swift를 사용한 iOS 개발 관련 기본 지식
  • 학습 및 배포와 같은 TensorFlow를 활용한 머신러닝에 관한 기본 지식
  • Colaboratory에 대한 기본 지식
  • 터미널, Python, Docker에 관한 기본 지식

실습 내용

  • TensorFlow를 사용하여 회귀 모델을 학습시키는 방법
  • TensorFlow Serving (REST 및 gRPC)을 통해 간단한 iOS 앱을 빌드하고 학습된 모델로 예측하는 방법
  • UI에 결과를 표시하는 방법

준비물

2 설정

이 Codelab의 코드를 다운로드하려면 다음 안내를 따르세요.

  1. 이 Codelab의 GitHub 저장소로 이동합니다.
  2. Code > 다운로드 zip을 클릭하여 이 Codelab의 모든 코드를 다운로드합니다.

A72f2bb4caa9a96.png

  1. 다운로드한 ZIP 파일의 압축을 풀고 필요한 모든 리소스로 codelabs 루트 폴더를 압축 해제합니다.

이 Codelab에서는 저장소의 TFServing/RegressioniOS 하위 디렉터리에 있는 다음 두 폴더만 있는 파일이 필요합니다.

  • starter 폴더에는 이 Codelab을 위해 빌드하는 시작 코드가 포함되어 있습니다.
  • finished 폴더에는 완료된 샘플 앱의 완성된 코드가 포함되어 있습니다.

3. 프로젝트의 종속 항목 다운로드

필수 포드 다운로드

  • 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. 브라우저에서 이 링크를 엽니다.

Colab에서 Python 노트북이 로드됩니다.

  1. Python 메모장에서 TensorFlowNumPy 라이브러리를 가져온 후 xs를 입력으로, ys를 라벨로 사용하여 학습 데이터 쌍 6개를 만듭니다.

이러한 데이터 포인트가 그래프에 표시되면, 그래프는 y = 2x -1 방정식에서 생성되므로 실제로 직선으로 되어 있습니다.

56d05252cfc9df9d.png

  1. Keras API를 사용하여 x 입력에 따라 y 값을 예측하는 간단한 2계층 신경망을 만든 다음 모델을 컴파일하고 컴파일합니다.
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. 탐색 메뉴에서 런타임 > 모두 실행을 클릭하여 노트북을 실행한 다음 실행이 완료될 때까지 기다립니다.
  2. C55600D42359f901.png Files를 클릭한 다음 regression.zip 파일을 다운로드합니다.

bceda15d86571583.png

6. TensorFlow Serving으로 회귀 모델 배포하기

  • TensorFlow Serving으로 모델을 배포하려면 다운로드한 regression.zip 파일을 7-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. TensorFlow Serving에 REST 요청을 보냅니다.
  3. REST 응답에서 예측 결과를 추출하고 UI를 렌더링합니다.

이 단계는 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

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 (Remote Procedure Call) 프레임워크입니다. 부하 분산, 추적, 상태 확인, 인증을 지원하며 데이터 센터 안팎에서 서비스를 효율적으로 연결할 수 있습니다. gRPC가 실제로 REST보다 성능이 더 좋은 것으로 관찰되었습니다.

gRPC로 요청 보내기 및 응답 수신

다음의 간단한 4단계가 있습니다.

  1. 선택사항: gRPC 클라이언트 스텁 코드를 생성합니다.
  2. gRPC 요청을 만듭니다.
  3. TensorFlow Serving에 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을 사용하여 앱에 회귀 기능을 추가했습니다.

자세히 알아보기