สร้างแอป Flitter เพื่อจัดประเภทข้อความ

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

ใน Codelab นี้ คุณดูวิธีเรียกใช้การจําแนกประเภทข้อความจากแอป Flwer ที่มี TensorFlow Serving ผ่าน REST และ gRPC ได้

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

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

  • วิธีสร้างแอป Flwer แบบง่ายและจัดประเภทข้อความผ่าน TensorFlow Serving (REST และ gRPC)
  • วิธีแสดงผลลัพธ์ใน UI

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

2. ตั้งค่าสภาพแวดล้อมในการพัฒนาซอฟต์แวร์ Flash

ในการพัฒนาฟลัดไลท์ คุณต้องมีซอฟต์แวร์ 2 ชิ้นที่ต้องใช้ในห้องทดลองนี้ ซึ่งได้แก่ Flutter SDK และตัวแก้ไข

คุณเรียกใช้ Codelab ได้โดยใช้อุปกรณ์ใดก็ได้ต่อไปนี้

  • เครื่องจําลอง iOS (ต้องติดตั้งเครื่องมือ Xcode)
  • โปรแกรมจําลอง Android (ต้องตั้งค่าใน Android Studio)
  • เบราว์เซอร์ (ต้องใช้ Chrome เพื่อแก้ไขข้อบกพร่อง)
  • เป็นแอปพลิเคชันบนเดสก์ท็อป Windows, Linux หรือ macOS คุณต้องพัฒนาบนแพลตฟอร์มที่คุณวางแผนจะทําให้ใช้งานได้ ดังนั้นหากต้องการพัฒนาแอปบนเดสก์ท็อปของ Windows คุณต้องพัฒนาใน Windows เพื่อเข้าถึงเชนบิลด์ที่เหมาะสม มีข้อกําหนดด้านระบบปฏิบัติการที่เฉพาะเจาะจงซึ่งระบุไว้ใน docs.flwer.dev/desktop

3. ตั้งค่า

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

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

ไฟล์ 2cd45599f51fb8a2.png

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

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

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

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

  1. ใน VS Code ให้คลิก File > Openfolder แล้วเลือกโฟลเดอร์ starter จากซอร์สโค้ดที่คุณดาวน์โหลดไว้ก่อนหน้านี้
  2. หากคุณเห็นกล่องโต้ตอบที่แจ้งให้ดาวน์โหลดแพ็กเกจที่จําเป็นสําหรับแอปเริ่มต้น ให้คลิกรับแพ็กเกจ
  3. หากไม่เห็นกล่องโต้ตอบนี้ ให้เปิดเทอร์มินัลแล้วเรียกใช้คําสั่ง flutter pub get ในโฟลเดอร์ starter

7ada07c300f166a6.png

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

  1. ใน VS Code ให้ตรวจสอบว่าตั้งค่าโปรแกรมจําลอง Android หรือ iOS Simulator อย่างถูกต้องและปรากฏในแถบสถานะ

ตัวอย่างเช่น ต่อไปนี้คือสิ่งที่คุณจะเห็นเมื่อใช้ Pixel 5 กับโปรแกรมจําลอง Android

9767649231898791.png

สิ่งที่คุณจะเห็นเมื่อใช้ iPhone 13 กับเครื่องจําลอง iOS มีดังนี้

95529e3a682268b2.png

  1. คลิกa19a0c68bc4046e6.pngเริ่มแก้ไขข้อบกพร่อง

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

แอปควรเปิดตัวบนโปรแกรมจําลอง Android หรือ iOS Simulator UI มีความตรงไปตรงมา จะมีช่องข้อความที่อนุญาตให้ผู้ใช้พิมพ์ข้อความดังกล่าว ผู้ใช้สามารถเลือกว่าจะส่งข้อมูลไปยังแบ็กเอนด์ด้วย REST หรือ gRPC หรือไม่ แบ็กเอนด์ใช้โมเดล TensorFlow ในการแยกประเภทข้อความกับอินพุตที่มีการประมวลผลล่วงหน้าและแสดงผลผลการค้นหาการแยกประเภทในแอปไคลเอ็นต์ซึ่งจะอัปเดต UI

b298f605d64dc132 d3ef3ccd3c338108.png

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

6. ทําให้โมเดลการจัดประเภทข้อความใช้งานได้ด้วยการแสดงผล TensorFlow

การแยกประเภทข้อความเป็นงานแมชชีนเลิร์นนิงที่ใช้กันทั่วไปและแยกประเภทข้อความออกเป็นหมวดหมู่ที่กําหนดไว้ล่วงหน้า ใน Codelab นี้ คุณจะต้องทําให้โมเดลที่ฝึกล่วงหน้าแล้วใช้งานได้จากฝึกโมเดลการตรวจจับสแปมด้วย TensorFlow Lite Model Makerlab ด้วย TensorFlow Serving และเรียกแบ็กเอนด์จากฟรอนท์เอนด์ของฟรอนท์เอนด์ให้แยกประเภทข้อความอินพุตเป็นสแปมหรือไม่ใช่สแปม

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

  • ในเทอร์มินัล ให้เริ่มแสดงโฆษณา TensorFlow ด้วย Docker แต่แทนที่ตัวยึดตําแหน่ง PATH/TO/SAVEDMODEL ด้วยเส้นทางสัมบูรณ์ของโฟลเดอร์ mm_spam_savedmodel ในคอมพิวเตอร์
docker pull tensorflow/serving

docker run -it --rm -p 8500:8500 -p 8501:8501 -v "PATH/TO/SAVEDMODEL:/models/spam-detection" -e MODEL_NAME=spam-detection 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: spam-detection 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. ทําให้ข้อความที่ป้อนเป็นโทเค็น

แบ็กเอนด์พร้อมให้บริการแล้ว ตอนนี้คุณเกือบพร้อมที่จะส่งคําขอไคลเอ็นต์ไปยังการแสดงผล TensorFlow แล้ว แต่ก่อนอื่นคุณต้องเข้ารหัสประโยคอินพุตก่อน หากตรวจสอบ Tensor อินพุตของโมเดล คุณจะพบว่าโมเดลต้องการรายการจํานวนเต็ม 20 ตัวแทนที่จะเป็นสตริงดิบ การทําให้เป็นโทเค็นคือเมื่อคุณจับคู่คําที่พิมพ์ในแอปกับรายการจํานวนเต็มตามพจนานุกรม ก่อนที่จะส่งคําเหล่านั้นไปยังแบ็กเอนด์เพื่อจัดประเภท เช่น หากพิมพ์ buy book online to learn more กระบวนการสร้างโทเค็นจะแมปกับ [32, 79, 183, 10, 224, 631, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] ตัวเลขเฉพาะอาจแตกต่างกันไปตามพจนานุกรมคําศัพท์

  1. ในไฟล์ lib/main.dart ให้เพิ่มโค้ดนี้ลงในเมธอด predict() เพื่อสร้างพจนานุกรมคําศัพท์ _vocabMap
// Build _vocabMap if empty.
if (_vocabMap.isEmpty) {
  final vocabFileString = await rootBundle.loadString(vocabFile);
  final lines = vocabFileString.split('\n');
  for (final l in lines) {
    if (l != "") {
      var wordAndIndex = l.split(' ');
      (_vocabMap)[wordAndIndex[0]] = int.parse(wordAndIndex[1]);
    }
  }
} 
  1. หลังข้อมูลโค้ดก่อนหน้า ให้เพิ่มโค้ดนี้เพื่อนําโทเค็นไปใช้:
// Tokenize the input sentence.
final inputWords = _inputSentenceController.text
    .toLowerCase()
    .replaceAll(RegExp('[^a-z ]'), '')
    .split(' ');
// Initialize with padding token.
_tokenIndices = List.filled(maxSentenceLength, 0);
var i = 0;
for (final w in inputWords) {
  if ((_vocabMap).containsKey(w)) {
    _tokenIndices[i] = (_vocabMap)[w]!;
    i++;
  }

  // Truncate the string if longer than maxSentenceLength.
  if (i >= maxSentenceLength - 1) {
    break;
  }
}

โค้ดนี้เป็นตัวพิมพ์เล็กเป็นสตริงประโยค นําอักขระที่ไม่ใช่ตัวอักษรออก แล้วแมปคํากับดัชนีจํานวนเต็ม 20 รายการตามตารางคําศัพท์

8. เชื่อมต่อแอป Flใช้งานด้วย TensorFlow Serving ผ่าน REST

คุณส่งคําขอไปยังการแสดง TensorFlow ได้ 2 วิธีดังนี้

  • REST
  • gRPC

ส่งคําขอและรับการตอบกลับผ่าน REST

มี 3 ขั้นตอนง่ายๆ ในการส่งคําขอและรับการตอบกลับผ่าน REST

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

คุณทําตามขั้นตอนเหล่านี้ในไฟล์ main.dart ได้

สร้างและส่งคําขอ REST ไปยังการแสดงโฆษณา TensorFlow

  1. ขณะนี้ฟังก์ชัน predict() ไม่ได้ส่งคําขอ REST ไปยังการแสดงผล TensorFlow คุณต้องใช้สาขา REST เพื่อสร้างคําขอ REST โดยทําดังนี้
if (_connectionMode == ConnectionModeType.rest) {
  // TODO: Create and send the REST request.

}
  1. เพิ่มโค้ดนี้ลงในสาขา REST
//Create the REST request.
final response = await http.post(
  Uri.parse('http://' +
      _server +
      ':' +
      restPort.toString() +
      '/v1/models/' +
      modelName +
      ':predict'),
  body: jsonEncode(<String, List<List<int>>>{
    'instances': [_tokenIndices],
  }),
);

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

  • เพิ่มโค้ดนี้หลังข้อมูลโค้ดก่อนหน้าเพื่อจัดการการตอบกลับ REST
// Process the REST response.
if (response.statusCode == 200) {
  Map<String, dynamic> result = jsonDecode(response.body);
  if (result['predictions']![0][1] >= classificationThreshold) {
    return 'This sentence is spam. Spam score is ' +
        result['predictions']![0][1].toString();
  }
  return 'This sentence is not spam. Spam score is ' +
      result['predictions']![0][1].toString();
} else {
  throw Exception('Error response');
}

โค้ดหลังการประมวลผลจะแยกความน่าจะเป็นที่ประโยคอินพุตเป็นข้อความสแปมออกจากการตอบกลับ และแสดงผลลัพธ์การแยกประเภทใน UI

เรียกใช้

  1. คลิกa19a0c68bc4046e6.pngเริ่มแก้ไขข้อบกพร่อง แล้วรอให้แอปโหลด
  2. ป้อนข้อความแล้วเลือก REST > จัดประเภท

ไฟล์ 8e21d795af36d07a.png e79a0367a03c2169.png

9. เชื่อมต่อแอป Flใช้งานด้วย TensorFlow Serving ผ่าน gRPC

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

b6f4449c2c850b0e.png

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

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

มี 4 ขั้นตอนง่ายๆ ในการส่งคําขอและรับการตอบกลับด้วย gRPC

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

คุณทําตามขั้นตอนเหล่านี้ในไฟล์ main.dart ได้

ไม่บังคับ: สร้างโค้ดไคลเอ็นต์ 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

google/protobuf/any.proto
google/protobuf/wrappers.proto
  • จากนั้นไปที่โฟลเดอร์ starter/lib/proto/ แล้วสร้างเทอร์มินัลไปที่เทอร์มินัล
bash generate_grpc_stub_dart.sh

สร้างคําขอ gRPC

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

if (_connectionMode == ConnectionModeType.rest) {

} else {
  // TODO: Create and send the gRPC request.

}
  • เพิ่มโค้ดนี้เพื่อสร้างคําขอ gRPC ดังนี้
//Create the gRPC request.
final channel = ClientChannel(_server,
    port: grpcPort,
    options:
        const ChannelOptions(credentials: ChannelCredentials.insecure()));
_stub = PredictionServiceClient(channel,
    options: CallOptions(timeout: const Duration(seconds: 10)));

ModelSpec modelSpec = ModelSpec(
  name: 'spam-detection',
  signatureName: 'serving_default',
);

TensorShapeProto_Dim batchDim = TensorShapeProto_Dim(size: Int64(1));
TensorShapeProto_Dim inputDim =
    TensorShapeProto_Dim(size: Int64(maxSentenceLength));
TensorShapeProto inputTensorShape =
    TensorShapeProto(dim: [batchDim, inputDim]);
TensorProto inputTensor = TensorProto(
    dtype: DataType.DT_INT32,
    tensorShape: inputTensorShape,
    intVal: _tokenIndices);

// If you train your own model, update the input and output tensor names.
const inputTensorName = 'input_3';
const outputTensorName = 'dense_5';
PredictRequest request = PredictRequest(
    modelSpec: modelSpec, inputs: {inputTensorName: inputTensor});

หมายเหตุ: ชื่อ Tensor อินพุตและเอาต์พุตอาจแตกต่างกันไปในแต่ละรุ่น แม้ว่าสถาปัตยกรรมของโมเดลจะเหมือนกันก็ตาม อย่าลืมอัปเดตโมเดลหากคุณฝึกโมเดลของคุณเอง

ส่งคําขอ gRPC ไปยังการแสดงผล TensorFlow

  • เพิ่มโค้ดนี้หลังจากข้อมูลโค้ดก่อนหน้าเพื่อส่งคําขอ gRPC ไปที่บริการ TensorFlow
// Send the gRPC request.
PredictResponse response = await _stub.predict(request);

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

  • เพิ่มโค้ดนี้หลังข้อมูลโค้ดก่อนหน้าเพื่อใช้ฟังก์ชันเรียกกลับเพื่อจัดการการตอบกลับ
// Process the response.
if (response.outputs.containsKey(outputTensorName)) {
  if (response.outputs[outputTensorName]!.floatVal[1] >
      classificationThreshold) {
    return 'This sentence is spam. Spam score is ' +
        response.outputs[outputTensorName]!.floatVal[1].toString();
  } else {
    return 'This sentence is not spam. Spam score is ' +
        response.outputs[outputTensorName]!.floatVal[1].toString();
  }
} else {
  throw Exception('Error response');
}

ตอนนี้โค้ดหลังการประมวลผลจะแยกผลลัพธ์ที่ได้จากการตอบกลับ และแสดงใน UI

เรียกใช้

  1. คลิกa19a0c68bc4046e6.pngเริ่มแก้ไขข้อบกพร่อง แล้วรอให้แอปโหลด
  2. ป้อนข้อความแล้วเลือก gRPC > จัดประเภท

e44e6e9a5bde2188.png 92644d723f61968c.png

10. ยินดีด้วย

คุณใช้ TensorFlow Serving เพื่อเพิ่มความสามารถในการจัดประเภทข้อความในแอปแล้ว

ใน Codelab ถัดไป คุณจะปรับปรุงโมเดลเพื่อให้ตรวจจับข้อความสแปมแบบเจาะจงที่แอปปัจจุบันตรวจไม่พบ

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