สร้างแอป iOS เพื่อคาดการณ์ค่า

1. ข้อควรทราบก่อนที่จะเริ่มต้น

ใน Codelab นี้ คุณดูวิธีเรียกใช้การถดถอยจากแอป iOS โดยใช้ TensorFlow Serving ที่มี REST และ gRPC

สิ่งที่ต้องมีก่อน

  • ความรู้เบื้องต้นเกี่ยวกับการพัฒนาบน iOS ด้วย Swift
  • ความรู้เบื้องต้นเกี่ยวกับแมชชีนเลิร์นนิงด้วย TensorFlow เช่น การฝึกอบรมและการทําให้ใช้งานได้
  • ความรู้พื้นฐานเกี่ยวกับ Colaboratory
  • ความรู้เบื้องต้นเกี่ยวกับเทอร์มินัล, Python และ Docker

สิ่งที่คุณจะได้เรียนรู้

  • วิธีฝึกโมเดลการถดถอยด้วย TensorFlow
  • วิธีสร้างแอป iOS แบบง่ายๆ และสร้างการคาดการณ์ด้วยโมเดลที่ฝึกผ่าน TensorFlow Serving (REST และ gRPC)
  • วิธีแสดงผลลัพธ์ใน UI

สิ่งที่ต้องมี

2. ตั้งค่า

วิธีดาวน์โหลดโค้ดสําหรับ Codelab นี้

  1. ไปที่ที่เก็บ GitHub สําหรับ Codelab นี้
  2. คลิก Code > Download ZIP เพื่อดาวน์โหลดโค้ดทั้งหมดสําหรับ Codelab นี้

a72f2bb4caa9a96.png

  1. แตกไฟล์ ZIP ที่ดาวน์โหลดเพื่อคลายโฟลเดอร์ราก codelabs ที่มีทรัพยากรทั้งหมดที่คุณต้องการ

Codelab นี้ต้องการเฉพาะไฟล์ในไดเรกทอรีย่อย TFServing/RegressioniOS ในที่เก็บ ซึ่งมี 2 โฟลเดอร์ดังนี้

  • โฟลเดอร์ starter มีโค้ดเริ่มต้นที่คุณสร้างสําหรับ Codelab นี้
  • โฟลเดอร์ finished มีโค้ดที่สมบูรณ์สําหรับแอปตัวอย่างที่สมบูรณ์

3. ดาวน์โหลดทรัพยากร Dependency สําหรับโปรเจ็กต์

ดาวน์โหลดพ็อดที่จําเป็น

  • ในโฟลเดอร์ starter/iOS ให้เรียกใช้
pod install

Cocoapods จะติดตั้งไลบรารีที่จําเป็นทั้งหมดและสร้างไฟล์ regression.xcworkspace ใหม่

4. เรียกใช้แอปเริ่มต้น

  • ดับเบิลคลิกไฟล์ regression.xcworkspace เพื่อเปิด Xcode

เรียกใช้และสํารวจแอป

  1. เปลี่ยนเป้าหมายของอุปกรณ์เป็น iPhone เช่น iPhone 13

a57198a4f21f970.png

  1. คลิก cacc15c5638260ed.png "Run' แล้วรอให้ Xcode คอมไพล์โปรเจ็กต์และเริ่มแอปเริ่มต้นในเครื่องจําลอง

UI มีความตรงไปตรงมา จะมีกล่องข้อความสําหรับพิมพ์ตัวเลขซึ่งจะส่งไปที่แบ็กเอนด์ TensorFlow Serving ที่มี REST หรือ gRPC แบ็กเอนด์จะดําเนินการรีเซ็ตค่าที่ป้อน และจะส่งคืนค่าที่คาดการณ์ไว้ไปยังแอปไคลเอ็นต์ซึ่งจะแสดงผลใน UI อีกครั้ง

D2976072474ce0b1.png

หากคุณป้อนตัวเลขแล้วคลิกอนุมาน ไม่มีอะไรเกิดขึ้นเนื่องจากแอปยังสื่อสารกับแบ็กเอนด์ไม่ได้

5. ฝึกโมเดลการถดถอยแบบง่ายด้วย TensorFlow

การถดถอยเป็นหนึ่งในงาน ML ที่พบบ่อยที่สุด โดยมีเป้าหมายเพื่อคาดการณ์ปริมาณอย่างต่อเนื่องรายการเดียวโดยพิจารณาจากอินพุต ตัวอย่างเช่น โดยอิงตามสภาพอากาศวันนี้ คาดการณ์ว่าอุณหภูมิสูงสุดในวันพรุ่งนี้

ฝึกโมเดลการถดถอย

  1. เปิดลิงก์นี้ในเบราว์เซอร์

Colab โหลดสมุดบันทึก Python

  1. นําเข้าไลบรารี TensorFlow และ NumPy ในสมุดบันทึก Python แล้วสร้างข้อมูลการฝึก 6 คู่ที่มี xs เป็นอินพุต และ ys เป็นป้ายกํากับ

หากคุณพล็อตจุดข้อมูลเหล่านี้บนกราฟ จริงๆ แล้วข้อมูลเหล่านั้นจะอยู่ในเส้นตรง เนื่องจากข้อมูลเหล่านี้สร้างขึ้นจากสมการ y = 2x -1

56d05252cfc9df9d.png

  1. ใช้ Keras API เพื่อสร้างเครือข่ายโครงข่ายระบบประสาทเทียมแบบ 2 ชั้นเพื่อคาดการณ์ค่า y โดยอิงตามอินพุต x จากนั้นคอมไพล์และจําลองโมเดล
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]))

โมเดลนี้ใช้เวลาเพียง 2-3 วินาทีในการฝึก และคุณจะเห็นค่าที่คาดการณ์ของอินพุต 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. บีบอัด ระบบจะบันทึก Model Model ที่ส่งออกเป็นไฟล์ regression.zip ไฟล์เดียว ดังนี้
!zip -r regression.zip ./regression
  1. คลิก Runtime > Run all ในเมนูการนําทางเพื่อเรียกใช้สมุดบันทึกและรอให้การเรียกใช้เสร็จสิ้น
  2. คลิก c55600d42359f901.png Files แล้วดาวน์โหลดไฟล์ regression.zip

bcaa15d86571583.png

6. ทําให้โมเดลการเกิดปัญหาซ้ําใช้งานได้ด้วยการแสดงผล TensorFlow

  • หากต้องการทําให้โมเดลใช้งานได้ด้วยการแสดงผล TensorFlow ให้ยกเลิกการบีบอัดไฟล์ regression.zip ที่ดาวน์โหลดด้วยเครื่องมือขยายข้อมูลที่บีบอัดไว้ เช่น 7-Zip

โครงสร้างโฟลเดอร์ควรมีลักษณะเช่นนี้

7faeb4f03af39646.png

คุณจะอ้างอิงโฟลเดอร์ regression ว่าเป็นโฟลเดอร์ SavedModel ก็ได้ 123 เป็นหมายเลขเวอร์ชันตัวอย่าง คุณเลือกหมายเลขอื่นได้หากต้องการ

เริ่มแสดงโฆษณา TensorFlow

  • ในเทอร์มินัล ให้เริ่มแสดงโฆษณา TensorFlow ด้วย 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 โดยอัตโนมัติก่อน ซึ่งจะใช้เวลา 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. เชื่อมต่อแอป iOS กับ TensorFlow Serving ผ่าน REST

แบ็กเอนด์พร้อมให้บริการแล้ว คุณจึงส่งคําขอไคลเอ็นต์ไปยังบริการ TensorFlow เพื่อทําการคาดการณ์ได้ คุณส่งคําขอไปยังการแสดง TensorFlow ได้ 2 วิธีดังนี้

  • REST
  • gRPC

ส่งคําขอและรับการตอบกลับด้วย REST

มี 3 ขั้นตอนง่ายๆ ดังนี้

  1. สร้างคําขอ REST
  2. ส่งคําขอ REST ไปยังการแสดงผล TensorFlow
  3. ดึงผลลัพธ์ที่คาดการณ์ไว้จากการตอบกลับของ REST และแสดง UI

คุณจะทําขั้นตอนเหล่านี้ได้ในไฟล์ iOS/regression/ViewController.swift

สร้างคําขอ REST

  1. ขณะนี้ฟังก์ชัน doInference() ไม่ได้ส่งคําขอ REST ไปยังการแสดงผล TensorFlow คุณต้องใช้สาขา REST นี้เพื่อสร้างคําขอ REST ดังนี้
if (connectionMode[picker.selectedRow(inComponent: 0)] == "REST") {
    print("Using REST")
    // TODO: Add code to send a REST request to TensorFlow Serving.
    
}

การแสดงผล TensorFlow ต้องการคําขอ 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 ไปยังการแสดงโฆษณา TensorFlow

  • เพิ่มรหัสนี้ทันทีหลังจากรหัสในสาขา 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()

ประมวลผลการตอบกลับ REST จาก TensorFlow Serving

  • เพิ่มโค้ดนี้ในข้อมูลโค้ดก่อนหน้าหลังความคิดเห็น 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. เชื่อมต่อแอป iOS กับ TensorFlow Serving ผ่าน gRPC

นอกจาก REST แล้ว TensorFlow Serving ยังรองรับ gRPC อีกด้วย

b6f4449c2c850b0e.png

gRPC เป็นเฟรมเวิร์ก Call Procedure Call (RPC) แบบโอเพนซอร์สที่มีประสิทธิภาพสูงและใช้งานได้กับทุกสภาพแวดล้อม เครื่องมือนี้จะเชื่อมบริการอย่างมีประสิทธิภาพทั่วทั้งศูนย์ข้อมูลต่างๆ ที่รองรับความสามารถในการจัดสรรภาระงาน การติดตาม การตรวจสอบประสิทธิภาพการทํางาน และการตรวจสอบสิทธิ์แบบเสียบปลั๊กได้ พบว่า gRPC มีประสิทธิภาพมากกว่า REST ในทางปฏิบัติ

ส่งคําขอและรับการตอบกลับด้วย gRPC

มี 4 ขั้นตอนง่ายๆ ดังนี้

  1. ไม่บังคับ: สร้างโค้ดไคลเอ็นต์ gRPC
  2. สร้างคําขอ gRPC
  3. ส่งคําขอ gRPC ไปยัง TensorFlow Serving
  4. ดึงผลลัพธ์ที่คาดการณ์ไว้จากการตอบกลับของ gRPC และแสดง UI

คุณจะทําขั้นตอนเหล่านี้ได้ในไฟล์ iOS/regression/ViewController.swift

ไม่บังคับ: สร้างโค้ดไคลเอ็นต์ gRPC

หากต้องการใช้ gRPC กับการให้บริการ TensorFlow คุณต้องทําตามเวิร์กโฟลว์ 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

คุณสร้างคําขอ gRPC ใน gRPC ได้เช่นเดียวกับคําขอ REST

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

  • เพิ่มโค้ดนี้ลงในสาขา gRPC ทันทีหลังจากโค้ดในข้อมูลโค้ดก่อนหน้า
// Send the gRPC request.
let call = stub.predict(request, callOptions: callOptions)

ประมวลผลการตอบสนอง gRPC จาก TensorFlow Serving

  • เพิ่มโค้ดนี้หลังโค้ดในข้อมูลโค้ดก่อนหน้านี้
// 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 "เรียกใช้' ในเมนูการนําทาง แล้วรอให้ Xcode เปิดแอปในเครื่องจําลอง
  2. ป้อนตัวเลขในกล่องข้อความ แล้วคลิกอนุมาน

ตอนนี้คุณจะเห็นค่าที่คาดการณ์ใน UI

9. ยินดีด้วย

คุณใช้ TensorFlow Serving เพื่อเพิ่มความสามารถในการถดถอยลงในแอปแล้ว

ดูข้อมูลเพิ่มเติม