1. قبل البدء
في هذا الدرس التطبيقي، ستتعرّف على كيفية تنفيذ استنتاج لتصنيف النصوص من تطبيق Flutter باستخدام TensorFlow Serving من خلال REST وgRPC.
المتطلبات الأساسية
- معرفة أساسية بتطوير تطبيقات Flutter باستخدام Dart
- معرفة أساسية بتعلُّم الآلة باستخدام TensorFlow، مثل التدريب مقابل النشر
- معرفة أساسية بالمحطات الطرفية وDocker
- تدريب نموذج لرصد التعليقات غير المرغوب فيها باستخدام الدرس التطبيقي حول الترميز في TensorFlow Lite Model Maker
أهداف الدورة التعليمية
- كيفية إنشاء تطبيق بسيط باستخدام Flutter وتصنيف النصوص من خلال TensorFlow Serving (REST وgRPC)
- كيفية عرض النتائج في واجهة المستخدم
المتطلبات
- حزمة تطوير البرامج (SDK) في Flutter
- إعداد Android أو iOS لـ Flutter
- إعداد Visual Studio Code (VS Code) لاستخدام Flutter وDart
- Docker
- Bash
- برنامج تجميع بروتوكول المخزن المؤقت ومكوّن gRPC Dart الإضافي لبرنامج تجميع البروتوكول (مطلوب فقط إذا أردت إعادة إنشاء رمز gRPC بنفسك)
2. إعداد بيئة تطوير Flutter
لتطوير تطبيقات Flutter، تحتاج إلى برنامجَين لإكمال هذا التمرين العملي، وهما حزمة تطوير البرامج (SDK) الخاصة بـ Flutter ومحرِّر.
يمكنك تشغيل الدرس العملي باستخدام أيّ من الأجهزة التالية:
- محاكي iOS (يتطلّب تثبيت أدوات Xcode)
- محاكي Android (يتطلّب الإعداد في "استوديو Android")
- متصفّح (يجب استخدام Chrome لتصحيح الأخطاء).
- كتطبيق سطح مكتب على Windows أو Linux أو macOS يجب أن يتم التطوير على النظام الأساسي الذي تخطّط للنشر عليه. لذا، إذا أردت تطوير تطبيق Windows لسطح المكتب، يجب أن يتم التطوير على Windows للوصول إلى سلسلة الإنشاء المناسبة. هناك متطلبات خاصة بنظام التشغيل يتم تناولها بالتفصيل على الرابط docs.flutter.dev/desktop.
3- طريقة الإعداد
لتنزيل الرمز البرمجي لهذا الدرس التطبيقي حول الترميز، اتّبِع الخطوات التالية:
- انتقِل إلى مستودع GitHub الخاص بهذا الدرس العملي.
- انقر على الرمز > تنزيل ملف zip لتنزيل كل الرمز البرمجي لهذا الدرس التطبيقي حول الترميز.
- فك ضغط ملف ZIP الذي تم تنزيله لفتح مجلد جذر
codelabs-main
يحتوي على جميع الموارد التي تحتاج إليها.
في هذا الدرس العملي، تحتاج فقط إلى الملفات في الدليل الفرعي tfserving-flutter/codelab2
في المستودع، والذي يحتوي على مجلدَين:
- يحتوي المجلد
starter
على الرمز الأولي الذي ستستند إليه في هذا الدرس العملي. - يحتوي المجلد
finished
على الرمز البرمجي المكتمل لتطبيق العيّنة النهائي.
4. تنزيل التبعيات للمشروع
- في VS Code، انقر على ملف > فتح مجلد، ثم اختَر المجلد
starter
من رمز المصدر الذي نزّلته سابقًا. - إذا ظهر لك مربّع حوار يطلب منك تنزيل الحِزم المطلوبة لتطبيق المبتدئين، انقر على الحصول على الحِزم.
- إذا لم يظهر لك مربع الحوار هذا، افتح الوحدة الطرفية ثم نفِّذ الأمر
flutter pub get
في المجلدstarter
.
5- تشغيل التطبيق النموذجي
- في VS Code، تأكَّد من إعداد "محاكي Android" أو "محاكي iOS" بشكل صحيح وظهورهما في شريط الحالة.
على سبيل المثال، إليك ما يظهر عند استخدام هاتف Pixel 5 مع "محاكي Android":
في ما يلي ما يظهر عند استخدام iPhone 13 مع iOS Simulator:
- انقر على
بدء تصحيح الأخطاء.
تشغيل التطبيق واستكشافه
يجب تشغيل التطبيق على "محاكي Android" أو "محاكي iOS". واجهة المستخدم بسيطة جدًا. يتوفّر حقل نص يتيح للمستخدم كتابة النص. يمكن للمستخدم اختيار ما إذا كان يريد إرسال البيانات إلى الخلفية باستخدام REST أو gRPC. يستخدم الخلفية نموذج TensorFlow لتنفيذ تصنيف النصوص على الإدخال الذي تمت معالجته مسبقًا، ويعرض نتيجة التصنيف لتطبيق العميل الذي يعدّل واجهة المستخدم بدوره.
إذا نقرت على تصنيف، لن يحدث أي شيء لأنّه لا يمكنه التواصل مع الخلفية بعد.
6. نشر نموذج لتصنيف النصوص باستخدام TensorFlow Serving
تصنيف النصوص هو مهمة شائعة جدًا في مجال تعلُّم الآلة، وتصنّف النصوص إلى فئات محدَّدة مسبقًا. في هذا الدرس التطبيقي حول الترميز، ستنشر النموذج المدرَّب مسبقًا من الدرس التطبيقي حول الترميز الخاص بتدريب نموذج رصد التعليقات غير المرغوب فيها باستخدام أداة Model Maker في TensorFlow Lite باستخدام TensorFlow Serving، وستطلب من الخلفية من واجهة Flutter الأمامية تصنيف النص المُدخَل على أنّه غير مرغوب فيه أو مرغوب فيه.
بدء TensorFlow Serving
- في نافذة الأوامر، ابدأ TensorFlow Serving باستخدام 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 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: 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 Serving، ولكن عليك أولاً تحويل الجملة المدخلة إلى رموز مميزة. إذا فحصت موتر الإدخال الخاص بالنموذج، يمكنك ملاحظة أنّه يتوقّع قائمة تضم 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]
في عملية تحويل النص إلى رموز مميزة. يمكن أن تختلف الأرقام المحددة استنادًا إلى قاموس المفردات.
- في الملف
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]);
}
}
}
- بعد مقتطف الرمز السابق مباشرةً، أضِف الرمز التالي لتنفيذ عملية الترميز:
// 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. ربط تطبيق Flutter بمنصة TensorFlow للعرض من خلال REST
هناك طريقتان لإرسال الطلبات إلى TensorFlow Serving:
- REST
- gRPC
إرسال الطلبات وتلقّي الردود من خلال REST
هناك ثلاث خطوات بسيطة لإرسال الطلبات وتلقّي الردود من خلال REST:
- أنشئ طلب REST.
- أرسِل طلب REST إلى TensorFlow Serving.
- استخرِج النتيجة المتوقّعة من ردّ REST واعرض واجهة المستخدم.
يمكنك إكمال هذه الخطوات في الملف main.dart
.
إنشاء طلب REST وإرساله إلى TensorFlow Serving
- في الوقت الحالي، لا ترسل الدالة
predict()
طلب REST إلى TensorFlow Serving. عليك تنفيذ فرع REST لإنشاء طلب REST:
if (_connectionMode == ConnectionModeType.rest) {
// TODO: Create and send the REST request.
}
- أضِف الرمز التالي إلى فرع 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');
}
يستخرج رمز المعالجة اللاحقة احتمال أن تكون الجملة المُدخَلة رسالة غير مرغوب فيها من الرد ويعرض نتيجة التصنيف في واجهة المستخدم.
تشغيلها
- انقر على
بدء تصحيح الأخطاء ثم انتظِر إلى أن يتم تحميل التطبيق.
- أدخِل بعض النص ثم انقر على REST > تصنيف.
9- ربط تطبيق Flutter بخدمة TensorFlow Serving من خلال gRPC
بالإضافة إلى REST، يتيح TensorFlow Serving أيضًا استخدام gRPC.
gRPC هو إطار عمل حديث ومفتوح المصدر وعالي الأداء لاستدعاء الإجراءات عن بُعد (RPC) يمكن تشغيله في أي بيئة. يمكنه ربط الخدمات بكفاءة داخل مراكز البيانات وفي ما بينها مع إمكانية إضافة ميزات موازنة التحميل والتتبُّع والتحقّق من الصحة والمصادقة. وقد تبيّن أنّ gRPC يتفوّق على REST من حيث الأداء في الواقع العملي.
إرسال الطلبات وتلقّي الردود باستخدام gRPC
في ما يلي أربع خطوات بسيطة لإرسال الطلبات وتلقّي الردود باستخدام gRPC:
- اختياري: إنشاء رمز بديل لعميل gRPC
- أنشِئ طلب gRPC.
- أرسِل طلب gRPC إلى TensorFlow Serving.
- استخرِج النتيجة المتوقّعة من استجابة gRPC واعرض واجهة المستخدم.
يمكنك إكمال هذه الخطوات في الملف main.dart
.
اختياري: إنشاء رمز بديل لعميل gRPC
لاستخدام gRPC مع TensorFlow Serving، عليك اتّباع سير عمل gRPC. لمزيد من المعلومات عن التفاصيل، يُرجى الاطّلاع على مستندات gRPC.
تحدّد منصة TensorFlow للعرض وTensorFlow ملفات .proto
نيابةً عنك. اعتبارًا من الإصدار 2.8 من TensorFlow وTensorFlow Serving، هذه هي ملفات .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
على غرار طلب REST، يمكنك إنشاء طلب gRPC في فرع gRPC.
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});
ملاحظة: قد تختلف أسماء موترات الإدخال والإخراج من نموذج إلى آخر، حتى إذا كانت بنية النماذج هي نفسها. احرص على تعديلها إذا درّبت نموذجك الخاص.
إرسال طلب gRPC إلى TensorFlow Serving
- أضِف هذا الرمز بعد مقتطف الرمز السابق لإرسال طلب gRPC إلى TensorFlow Serving:
// 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');
}
الآن، يستخرج رمز ما بعد المعالجة نتيجة التصنيف من الرد ويعرضها في واجهة المستخدم.
تشغيلها
- انقر على
بدء تصحيح الأخطاء ثم انتظِر إلى أن يتم تحميل التطبيق.
- أدخِل بعض النص ثم انقر على gRPC > تصنيف.
10. تهانينا
لقد استخدمت TensorFlow Serving لإضافة إمكانات تصنيف النصوص إلى تطبيقك.
في تجربة البرمجة التالية، ستعمل على تحسين النموذج حتى تتمكّن من رصد رسائل غير مرغوب فيها معيّنة لا يمكن للتطبيق الحالي رصدها.