إنشاء موقع إلكتروني بسيط يصنّف الصور

1. قبل البدء

في هذا الدرس العملي، ستتعرّف على كيفية تنفيذ استنتاج لتصنيف الصور من موقع إلكتروني باستخدام TensorFlow Serving مع REST وgRPC.

المتطلبات الأساسية

  • معرفة أساسية بتطوير الويب، مثل HTML وJavaScript
  • معرفة أساسية بتعلُّم الآلة باستخدام TensorFlow، مثل التدريب والنشر
  • معرفة أساسية بالمحطات الطرفية وDocker

أهداف الدورة التعليمية

  • كيفية العثور على نماذج تصنيف الصور المدرَّبة مسبقًا على TensorFlow Hub
  • كيفية إنشاء موقع إلكتروني بسيط وتقديم توقّعات باستخدام نموذج تصنيف الصور الذي تم تنزيله من خلال TensorFlow Serving (REST وgRPC)
  • كيفية عرض نتيجة الرصد في واجهة المستخدم

المتطلبات

2. طريقة الإعداد

لتنزيل الرمز البرمجي لهذا الدرس التطبيقي حول الترميز، اتّبِع الخطوات التالية:

  1. انتقِل إلى مستودع GitHub هذا.
  2. انقر على الرمز > تنزيل ملف zip لتنزيل كل الرمز البرمجي لهذا الدرس التطبيقي حول الترميز.

a72f2bb4caa9a96.png

  1. فكّ ضغط ملف zip الذي تم تنزيله لفتح مجلد جذر codelabs يحتوي على جميع الموارد التي تحتاج إليها.

في هذا الدرس العملي، تحتاج فقط إلى الملفات في الدليل الفرعي TFServing/ImageClassificationWeb في المستودع، والذي يحتوي على مجلدَين:

  • يحتوي المجلد starter على الرمز الأولي الذي ستستند إليه في هذا الدرس العملي.
  • يحتوي المجلد finished على الرمز البرمجي المكتمل لتطبيق العيّنة النهائي.

3- تثبيت التبعيات

لتثبيت التبعيات، اتّبِع الخطوات التالية:

  • في نافذة الأوامر، انتقِل إلى المجلد starter ثم ثبِّت حِزم NPM المطلوبة:
npm install

4. تشغيل الموقع الإلكتروني التجريبي

استخدِم إضافة Web Server for Chrome لتحميل ملف TFServing/ImageClassificationWeb/starter/dist/index.html:

  1. أدخِل Chrome://apps/ في شريط العناوين في Chrome، ثم ابحث عن Web Server for Chrome في قائمة التطبيقات.
  2. شغِّل تطبيق Web Server for Chrome، ثم اختَر المجلد TFServing/ImageClassificationWeb/starter/dist/.
  3. انقر على زر التبديل خادم الويب لتفعيله، ثم انتقِل إلى http://localhost:8887/ في المتصفّح.

f7b43cd44ebf1f1b.png

تشغيل الموقع الإلكتروني واستكشافه

من المفترض أن يظهر لك الموقع الإلكتروني الآن. واجهة المستخدم بسيطة جدًا: هناك صورة قطة تريد تصنيفها ويمكن للمستخدم إرسال البيانات إلى الخلفية باستخدام REST أو gRPC. تنفّذ الخلفية عملية تصنيف الصور على الصورة وتعرض نتيجة التصنيف على الموقع الإلكتروني.

837d97a27c59a0b3.png

إذا نقرت على تصنيف، لن يحدث أي شيء لأنّه لا يمكنه التواصل مع الخلفية بعد.

5- نشر نموذج لتصنيف الصور باستخدام TensorFlow Serving

تصنيف الصور هو مهمة شائعة جدًا في تعلُّم الآلة، وتصنّف الصور إلى فئات محدّدة مسبقًا استنادًا إلى المحتوى الأساسي للصورة. في ما يلي مثال على تصنيف الزهور:

a6da16b4a7665db0.png

يتوفّر عدد من نماذج تصنيف الصور المدرَّبة مسبقًا على TensorFlow Hub. ستستخدم نموذج Inception v3 الشائع لهذا الدرس التطبيقي حول الترميز.

لنشر نموذج تصنيف الصور باستخدام TensorFlow Serving، اتّبِع الخطوات التالية:

  1. نزِّل ملف نموذج Inception v3.
  2. فك ضغط ملف .tar.gz الذي تم تنزيله باستخدام أداة فك ضغط، مثل 7-Zip.
  3. أنشئ مجلدًا باسم inception_v3، ثم أنشئ مجلدًا فرعيًا باسم 123 داخله.
  4. ضَع مجلد variables وملف saved_model.pb اللذين تم استخراجهما في المجلد الفرعي 123.

يمكنك الرجوع إلى المجلد inception_v3 باسم المجلد SavedModel. ‫123 هو مثال على رقم الإصدار. يمكنك اختيار رقم آخر إذا أردت.

يجب أن تبدو بنية المجلد كما في الصورة التالية:

21a8675ac8d31907.png

بدء 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:

  1. أنشئ طلب REST.
  2. أرسِل طلب REST إلى TensorFlow Serving.
  3. استخرِج النتيجة المتوقّعة من ردّ 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;

الآن، يستخرج المستمع الاحتمالات المتوقّعة من الرد، ويحدّد الفئة الأكثر احتمالاً للكائن، ويعرض النتيجة في واجهة المستخدم.

تشغيلها

  1. في نافذة الوحدة الطرفية، انتقِل إلى المجلد starter واستخدِم webpack لتجميع جميع ملفات JavaScript في ملف واحد يمكنك تضمينه في الملف dist/index.html:
npm install -g npx
npm install --save-dev webpack
npx webpack
  1. أعِد تحميل http://localhost:8887/ في المتصفّح، ثم انقر على REST > تصنيف.

يعرض الموقع الإلكتروني 286 كفئة متوقّعة، وهي تتطابق مع التصنيف Egyptian Cat في مجموعة بيانات ImageNet.

c865a93b9b58335d.png

8. ربط الموقع الإلكتروني بمنصة TensorFlow للعرض من خلال gRPC

بالإضافة إلى REST، يتيح TensorFlow Serving أيضًا استخدام gRPC.

b6f4449c2c850b0e.png

‫gRPC هو إطار عمل حديث ومفتوح المصدر وعالي الأداء لاستدعاء الإجراءات عن بُعد (RPC) يمكن تشغيله في أي بيئة. يمكنه ربط الخدمات بكفاءة داخل مراكز البيانات وفي ما بينها مع إمكانية إضافة ميزات موازنة التحميل والتتبُّع والتحقّق من الصحة والمصادقة. وقد تبيّن أنّ gRPC يتفوّق على REST من حيث الأداء في الواقع العملي.

إرسال الطلبات وتلقّي الردود باستخدام gRPC

في ما يلي أربع خطوات بسيطة:

  1. اختياري: إنشاء رمز بديل لعميل gRPC
  2. أنشِئ طلب gRPC.
  3. أرسِل طلب gRPC إلى TensorFlow Serving.
  4. استخرِج النتيجة المتوقّعة من ردّ gRPC واعرضها في واجهة المستخدم.

يمكنك إكمال هذه الخطوات في الملف src/index.js.

اختياري: إنشاء رمز بديل لعميل gRPC

لاستخدام gRPC مع TensorFlow Serving، عليك اتّباع سير عمل gRPC. لمزيد من المعلومات عن التفاصيل، يُرجى الاطّلاع على مستندات gRPC.

a9d0e5cb543467b4.png

تحدّد منصة 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;
}

الآن، يستخرج المستمع الاحتمالات المتوقّعة من الرد، ويحدّد الفئة الأكثر احتمالاً للكائن، ويعرض النتيجة في واجهة المستخدم.

تشغيلها

  1. في نافذة الأوامر، استخدِم Webpack لتجميع جميع ملفات JavaScript في ملف واحد يمكنك تضمينه في ملف index.html:
npx webpack
  1. أعِد تحميل http://localhost:8887/ في المتصفّح.
  2. انقر على gRPC > تصنيف.

يعرض الموقع الإلكتروني الفئة المتوقّعة 286، والتي تتطابق مع التصنيف Egyptian Cat في مجموعة بيانات ImageNet.

9- تهانينا

لقد استخدمت TensorFlow Serving لإضافة إمكانات تصنيف الصور إلى موقعك الإلكتروني.

مزيد من المعلومات