1. قبل البدء
في هذا الدرس العملي، ستتعرّف على كيفية تنفيذ استنتاج لتصنيف الصور من موقع إلكتروني باستخدام TensorFlow Serving مع REST وgRPC.
المتطلبات الأساسية
- معرفة أساسية بتطوير الويب، مثل HTML وJavaScript
- معرفة أساسية بتعلُّم الآلة باستخدام TensorFlow، مثل التدريب والنشر
- معرفة أساسية بالمحطات الطرفية وDocker
أهداف الدورة التعليمية
- كيفية العثور على نماذج تصنيف الصور المدرَّبة مسبقًا على TensorFlow Hub
- كيفية إنشاء موقع إلكتروني بسيط وتقديم توقّعات باستخدام نموذج تصنيف الصور الذي تم تنزيله من خلال TensorFlow Serving (REST وgRPC)
- كيفية عرض نتيجة الرصد في واجهة المستخدم
المتطلبات
- Docker
- Google Chrome
- Web Server for Chrome
- Node.js وNPM
- Bash
- برنامج تجميع المخزن المؤقت للبروتوكول (مطلوب فقط إذا أردت إعادة إنشاء رمز gRPC بنفسك)
- المكوّن الإضافي لإنشاء رموز gRPC-web (لا تحتاج إليه إلا إذا أردت إعادة إنشاء رمز gRPC بنفسك)
2. طريقة الإعداد
لتنزيل الرمز البرمجي لهذا الدرس التطبيقي حول الترميز، اتّبِع الخطوات التالية:
- انتقِل إلى مستودع GitHub هذا.
- انقر على الرمز > تنزيل ملف zip لتنزيل كل الرمز البرمجي لهذا الدرس التطبيقي حول الترميز.
- فكّ ضغط ملف zip الذي تم تنزيله لفتح مجلد جذر
codelabs
يحتوي على جميع الموارد التي تحتاج إليها.
في هذا الدرس العملي، تحتاج فقط إلى الملفات في الدليل الفرعي TFServing/ImageClassificationWeb
في المستودع، والذي يحتوي على مجلدَين:
- يحتوي المجلد
starter
على الرمز الأولي الذي ستستند إليه في هذا الدرس العملي. - يحتوي المجلد
finished
على الرمز البرمجي المكتمل لتطبيق العيّنة النهائي.
3- تثبيت التبعيات
لتثبيت التبعيات، اتّبِع الخطوات التالية:
- في نافذة الأوامر، انتقِل إلى المجلد
starter
ثم ثبِّت حِزم NPM المطلوبة:
npm install
4. تشغيل الموقع الإلكتروني التجريبي
استخدِم إضافة Web Server for Chrome لتحميل ملف TFServing/ImageClassificationWeb/starter/dist/index.html
:
- أدخِل
Chrome://apps/
في شريط العناوين في Chrome، ثم ابحث عن Web Server for Chrome في قائمة التطبيقات. - شغِّل تطبيق Web Server for Chrome، ثم اختَر المجلد
TFServing/ImageClassificationWeb/starter/dist/
. - انقر على زر التبديل خادم الويب لتفعيله، ثم انتقِل إلى http://localhost:8887/ في المتصفّح.
تشغيل الموقع الإلكتروني واستكشافه
من المفترض أن يظهر لك الموقع الإلكتروني الآن. واجهة المستخدم بسيطة جدًا: هناك صورة قطة تريد تصنيفها ويمكن للمستخدم إرسال البيانات إلى الخلفية باستخدام REST أو gRPC. تنفّذ الخلفية عملية تصنيف الصور على الصورة وتعرض نتيجة التصنيف على الموقع الإلكتروني.
إذا نقرت على تصنيف، لن يحدث أي شيء لأنّه لا يمكنه التواصل مع الخلفية بعد.
5- نشر نموذج لتصنيف الصور باستخدام TensorFlow Serving
تصنيف الصور هو مهمة شائعة جدًا في تعلُّم الآلة، وتصنّف الصور إلى فئات محدّدة مسبقًا استنادًا إلى المحتوى الأساسي للصورة. في ما يلي مثال على تصنيف الزهور:
يتوفّر عدد من نماذج تصنيف الصور المدرَّبة مسبقًا على TensorFlow Hub. ستستخدم نموذج Inception v3 الشائع لهذا الدرس التطبيقي حول الترميز.
لنشر نموذج تصنيف الصور باستخدام TensorFlow Serving، اتّبِع الخطوات التالية:
- نزِّل ملف نموذج Inception v3.
- فك ضغط ملف
.tar.gz
الذي تم تنزيله باستخدام أداة فك ضغط، مثل 7-Zip. - أنشئ مجلدًا باسم
inception_v3
، ثم أنشئ مجلدًا فرعيًا باسم123
داخله. - ضَع مجلد
variables
وملفsaved_model.pb
اللذين تم استخراجهما في المجلد الفرعي123
.
يمكنك الرجوع إلى المجلد inception_v3
باسم المجلد SavedModel
. 123
هو مثال على رقم الإصدار. يمكنك اختيار رقم آخر إذا أردت.
يجب أن تبدو بنية المجلد كما في الصورة التالية:
بدء TensorFlow Serving
- في نافذة الأوامر، ابدأ TensorFlow Serving باستخدام Docker، ولكن استبدِل
PATH/TO/SAVEDMODEL
بالمسار المطلق للمجلدinception_v3
على جهاز الكمبيوتر.
docker pull tensorflow/serving docker run -it --rm -p 8500:8500 -p 8501:8501 -v "PATH/TO/SAVEDMODEL:/models/inception" -e MODEL_NAME=inception 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/inception/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/inception/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: inception 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 ...
6. إعداد خادم Envoy الوكيل
في الوقت الحالي، لا يضبط TensorFlow Serving عنوان Access-Control-Allow-Origin
، لذا يحظر المتصفّح الطلب من JavaScript في الواجهة الأمامية إلى TensorFlow Serving لأسباب تتعلّق بالأمان. لحلّ هذه المشكلة، عليك استخدام خادم وكيل، مثل Envoy، لكي يعمل كخادم وكيل للطلب من JavaScript إلى الخلفية في TensorFlow Serving.
Start Envoy
- في نافذة الأوامر، نزِّل صورة Envoy وابدأ تشغيل Envoy باستخدام Docker، ولكن استبدِل العنصر النائب
PATH/TO/ENVOY-CUSTOM.YAML
بالمسار المطلق للملفenvoy-custom.yaml
في المجلدstarter
.
docker pull envoyproxy/envoy-dev:fd3e8370ddb7a96634c192d1461516e6de1d1797 docker run --add-host host.docker.internal:host-gateway --rm -it -p 9901:9901 -p 8000:8000 -p 8080:8080 -v PATH/TO/ENVOY-CUSTOM.YAML:/envoy-custom.yaml envoyproxy/envoy-dev:fd3e8370ddb7a96634c192d1461516e6de1d1797 -c /envoy-custom.yaml
ينزّل Docker صورة Envoy تلقائيًا أولاً. بعد ذلك، من المفترض أن يبدأ Envoy. يجب أن يبدو السجلّ على النحو التالي:
[2022-03-02 07:51:48.563][1][info][main] [source/server/server.cc:436] response trailer map: 152 bytes: grpc-message,grpc-status [2022-03-02 07:51:48.681][1][info][main] [source/server/server.cc:772] runtime: {} [2022-03-02 07:51:48.682][1][info][admin] [source/server/admin/admin.cc:134] admin address: 0.0.0.0:9901 [2022-03-02 07:51:48.683][1][info][config] [source/server/configuration_impl.cc:127] loading tracing configuration [2022-03-02 07:51:48.683][1][info][config] [source/server/configuration_impl.cc:87] loading 0 static secret(s) [2022-03-02 07:51:48.683][1][info][config] [source/server/configuration_impl.cc:93] loading 2 cluster(s) [2022-03-02 07:51:48.687][1][info][config] [source/server/configuration_impl.cc:97] loading 2 listener(s) [2022-03-02 07:51:48.694][1][info][config] [source/server/configuration_impl.cc:109] loading stats configuration [2022-03-02 07:51:48.696][1][info][main] [source/server/server.cc:868] starting main dispatch loop [2022-03-02 07:51:48.881][1][info][runtime] [source/common/runtime/runtime_impl.cc:446] RTDS has finished initialization [2022-03-02 07:51:48.881][1][info][upstream] [source/common/upstream/cluster_manager_impl.cc:207] cm init: all clusters initialized [2022-03-02 07:51:48.881][1][info][main] [source/server/server.cc:849] all clusters initialized. initializing init manager [2022-03-02 07:51:48.881][1][info][config] [source/server/listener_manager_impl.cc:784] all dependencies initialized. starting workers [2022-03-02 07:51:48.902][1][warning][main] [source/server/server.cc:747] there is no configured limit to the number of allowed active connections. Set a limit via the runtime key overload.global_downstream_max_connections
7. ربط الموقع الإلكتروني بمنصة TensorFlow من خلال REST
أصبح الخلفية جاهزة الآن، لذا يمكنك إرسال طلبات العميل إلى TensorFlow Serving لتصنيف الصور. هناك طريقتان لإرسال الطلبات إلى TensorFlow Serving:
- REST
- gRPC
إرسال الطلبات وتلقّي الردود من خلال REST
هناك ثلاث خطوات بسيطة لإرسال الطلبات وتلقّيها من خلال REST:
- أنشئ طلب REST.
- أرسِل طلب REST إلى TensorFlow Serving.
- استخرِج النتيجة المتوقّعة من ردّ REST واعرض النتيجة.
يمكنك تنفيذ هذه الخطوات في ملف src/index.js
.
إنشاء طلب REST
في الوقت الحالي، لا ترسل الدالة classify_img()
طلب REST إلى TensorFlow Serving. عليك تنفيذ فرع REST هذا لإنشاء طلب REST أولاً:
if (radioButtons[0].checked) {
console.log('Using REST');
// TODO: Add code to send a REST request to TensorFlow Serving.
}
يتوقّع TensorFlow Serving تلقّي طلب POST يحتوي على موتر الصورة لنموذج Inception v3 الذي تستخدمه، لذا عليك استخراج قيم RGB من كل بكسل في الصورة إلى مصفوفة ثم تضمين المصفوفة في JSON، وهو حمولة الطلب.
- أضِف الرمز التالي إلى فرع REST:
//Create the REST request.
let imgTensor = new Array();
let pixelArray = new Array();
context.drawImage(img, 0, 0);
for(let i=0; i<inputImgHeight; i++) {
pixelArray[i] = new Array();
for (let j=0; j<inputImgWidth; j++) {
pixelArray[i][j] = new Array();
pixelArray[i][j].push(context.getImageData(i, j, 1, 1).data[0]/255);
pixelArray[i][j].push(context.getImageData(i, j, 1, 1).data[1]/255);
pixelArray[i][j].push(context.getImageData(i, j, 1, 1).data[2]/255);
}
}
imgTensor.push(pixelArray);
const RESTURL = 'http://localhost:8000/v1/models/inception:predict';
let xhr = new XMLHttpRequest();
xhr.open('POST', RESTURL);
xhr.setRequestHeader('Content-Type', 'application/json;charset=utf-8;');
let data = JSON.stringify({
instances: imgTensor
});
xhr.onload = () => {
}
xhr.onerror = () => {
console.log('REST request error');
}
إرسال طلب REST إلى TensorFlow Serving
يمكنك الآن إرسال الطلب.
- أضِف هذا الرمز بعد الرمز أعلاه مباشرةً في فرع REST:
// Send the REST request.
xhr.send(data);
معالجة استجابة REST من TensorFlow Serving
يعرض نموذج Inception v3 مصفوفة من الاحتمالات التي تشير إلى أنّ الصورة تنتمي إلى فئات محدّدة مسبقًا. عند نجاح التوقّع، يجب عرض الفئة الأكثر احتمالاً في واجهة المستخدم.
عليك تنفيذ أداة معالجة الحدث onload()
للتعامل مع الردّ.
xhr.onload = () => {
}
- أضِف هذا الرمز إلى أداة معالجة الأحداث
onload()
:
// Process the REST response.
const response = JSON.parse(xhr.responseText);
const maxIndex = argmax(response['predictions'][0])
document.getElementById('category').textContent = 'Predicted category: ' + maxIndex;
الآن، يستخرج المستمع الاحتمالات المتوقّعة من الرد، ويحدّد الفئة الأكثر احتمالاً للكائن، ويعرض النتيجة في واجهة المستخدم.
تشغيلها
- في نافذة الوحدة الطرفية، انتقِل إلى المجلد
starter
واستخدِم webpack لتجميع جميع ملفات JavaScript في ملف واحد يمكنك تضمينه في الملفdist/index.html
:
npm install -g npx npm install --save-dev webpack npx webpack
- أعِد تحميل http://localhost:8887/ في المتصفّح، ثم انقر على REST > تصنيف.
يعرض الموقع الإلكتروني 286
كفئة متوقّعة، وهي تتطابق مع التصنيف Egyptian Cat
في مجموعة بيانات ImageNet.
8. ربط الموقع الإلكتروني بمنصة TensorFlow للعرض من خلال gRPC
بالإضافة إلى REST، يتيح TensorFlow Serving أيضًا استخدام gRPC.
gRPC هو إطار عمل حديث ومفتوح المصدر وعالي الأداء لاستدعاء الإجراءات عن بُعد (RPC) يمكن تشغيله في أي بيئة. يمكنه ربط الخدمات بكفاءة داخل مراكز البيانات وفي ما بينها مع إمكانية إضافة ميزات موازنة التحميل والتتبُّع والتحقّق من الصحة والمصادقة. وقد تبيّن أنّ gRPC يتفوّق على REST من حيث الأداء في الواقع العملي.
إرسال الطلبات وتلقّي الردود باستخدام gRPC
في ما يلي أربع خطوات بسيطة:
- اختياري: إنشاء رمز بديل لعميل gRPC
- أنشِئ طلب gRPC.
- أرسِل طلب gRPC إلى TensorFlow Serving.
- استخرِج النتيجة المتوقّعة من ردّ gRPC واعرضها في واجهة المستخدم.
يمكنك إكمال هذه الخطوات في الملف src/index.js
.
اختياري: إنشاء رمز بديل لعميل 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
- في الوحدة الطرفية، انتقِل إلى المجلد
starter/src/proto/
وأنشئ رمزًا زائفًا:
bash generate_grpc_stub_js.sh
إنشاء طلب gRPC
على غرار طلب REST، يمكنك إنشاء طلب gRPC في فرع gRPC.
if (connectionMode[picker.selectedRow(inComponent: 0)] == "REST") {
}
else {
print("Using gRPC")
// TODO: Add code to send a gRPC request to TensorFlow Serving.
}
- أضِف الرمز التالي إلى فرع gRPC:
// Create the gRPC request.
const PredictModule = require('./proto/generated/tensorflow_serving/apis/predict_pb.js');
const PredictionServiceClientModule = require('./proto/generated/tensorflow_serving/apis/prediction_service_grpc_web_pb.js');
const ModelModule = require('./proto/generated/tensorflow_serving/apis/model_pb.js');
const TensorModule = require('./proto/generated/tensorflow/core/framework/tensor_pb.js');
const GPRCURL = 'http://localhost:8080';
const stub = new PredictionServiceClientModule.PredictionServiceClient(GPRCURL);
const modelSpec = new ModelModule.ModelSpec();
modelSpec.setName('inception');
const tensorProto = new TensorModule.TensorProto();
const tensorShapeProto = new TensorModule.TensorShapeProto();
const batchDim = (new TensorModule.TensorShapeProto.Dim()).setSize(1);
const heightDim = (new TensorModule.TensorShapeProto.Dim()).setSize(inputImgHeight);
const widthDim = (new TensorModule.TensorShapeProto.Dim()).setSize(inputImgWidth);
const channelDim = (new TensorModule.TensorShapeProto.Dim()).setSize(3);
tensorShapeProto.setDimList([batchDim, heightDim, widthDim, channelDim]);
tensorProto.setDtype(proto.tensorflow.DataType.DT_FLOAT);
tensorProto.setTensorShape(tensorShapeProto);
context.drawImage(img, 0, 0);
for(let i=0; i<inputImgHeight; i++) {
for (let j=0; j<inputImgWidth; j++) {
tensorProto.addFloatVal(context.getImageData(i, j, 1, 1).data[0]/255);
tensorProto.addFloatVal(context.getImageData(i, j, 1, 1).data[1]/255);
tensorProto.addFloatVal(context.getImageData(i, j, 1, 1).data[2]/255);
}
}
const predictionServiceRequest = new PredictModule.PredictRequest();
predictionServiceRequest.setModelSpec(modelSpec);
predictionServiceRequest.getInputsMap().set('inputs', tensorProto);
إرسال طلب gRPC إلى TensorFlow Serving
يمكنك الآن إرسال الطلب.
- أضِف هذا الرمز مباشرةً بعد الرمز في فرع gRPC في مقتطف الرمز السابق:
// Send the gRPC request.
stub.predict(predictionServiceRequest, {}, function(err, response) {
// TODO: Add code to process the response.
});
معالجة استجابة gRPC من TensorFlow Serving
أخيرًا، عليك تنفيذ دالة رد الاتصال المذكورة أعلاه للتعامل مع الردّ.
- أضِف الرمز التالي إلى نص الدالة في مقتطف الرمز السابق:
// Process the gRPC response.
if (err) {
console.log(err.code);
console.log(err.message);
}
else {
const maxIndex = argmax(response.getOutputsMap().get('logits').getFloatValList());
document.getElementById('category').textContent = 'Predicted category: ' + maxIndex;
}
الآن، يستخرج المستمع الاحتمالات المتوقّعة من الرد، ويحدّد الفئة الأكثر احتمالاً للكائن، ويعرض النتيجة في واجهة المستخدم.
تشغيلها
- في نافذة الأوامر، استخدِم Webpack لتجميع جميع ملفات JavaScript في ملف واحد يمكنك تضمينه في ملف
index.html
:
npx webpack
- أعِد تحميل http://localhost:8887/ في المتصفّح.
- انقر على gRPC > تصنيف.
يعرض الموقع الإلكتروني الفئة المتوقّعة 286
، والتي تتطابق مع التصنيف Egyptian Cat
في مجموعة بيانات ImageNet.
9- تهانينا
لقد استخدمت TensorFlow Serving لإضافة إمكانات تصنيف الصور إلى موقعك الإلكتروني.