كيفية نشر نماذج TensorFlow للإنتاج باستخدام TF Serving

المقدمة

أصبح وضع نماذج تعلم الآلة (ML) في الإنتاج موضوعًا شائعًا ومتكررًا. تقدم العديد من الشركات والأطر حلولًا مختلفة تهدف إلى معالجة هذه المشكلة.

لمواجهة هذا القلق ، أصدرت Google خدمة TensorFlow (TF) على أمل حل مشكلة نشر نماذج ML في الإنتاج.

تقدم هذه المقالة برنامجًا تعليميًا عمليًا حول خدمة شبكة الفصل التلافيفي المدربة مسبقًا. بحلول نهاية هذه المقالة ، ستتمكن من استخدام TF Serving لنشر وتقديم طلبات إلى Deep CNN المدربة على TF. أيضًا ، سأقدم نظرة عامة على الكتل الرئيسية لـ TF Serving ، وسأناقش واجهات برمجة التطبيقات الخاصة به وكيف يعمل كل شيء.

شيء واحد ستلاحظه على الفور هو أنه يتطلب كودًا صغيرًا جدًا لتقديم نموذج TF. إذا كنت ترغب في متابعة البرنامج التعليمي وتشغيل المثال على جهازك ، فاتبعه كما هو. ولكن ، إذا كنت تريد فقط معرفة TensorFlow Serving ، فيمكنك التركيز على القسمين الأولين.

تؤكد هذه المقالة على بعض العمل الذي نقوم به هنا في مجموعة دايتان.

مكتبات خدمة TensorFlow - نظرة عامة

لنأخذ بعض الوقت لفهم كيفية تعامل TF Serving مع دورة الحياة الكاملة لنماذج ML. هنا ، سنتجاوز (على مستوى عالٍ) كل عنصر أساسي من عناصر TF Serving. الهدف من هذا القسم هو تقديم مقدمة سهلة لواجهة برمجة تطبيقات TF Serving. للحصول على نظرة عامة متعمقة ، يرجى التوجه إلى صفحة وثائق TF Serving.

يتكون TensorFlow Serving من بعض التجريدات. هذه التجريدات تطبيق واجهات برمجة التطبيقات لمهام مختلفة. أهمها Servable و Loader و Source و Manager. دعنا نذهب كيف نتفاعل.

باختصار ، تبدأ دورة حياة التقديم عندما تحدد TF Serving نموذجًا على القرص. مكون المصدر يعتني بذلك. وهي مسؤولة عن تحديد الموديلات الجديدة التي ينبغي تحميلها. في الممارسة العملية ، فإنه يراقب نظام الملفات لتحديد متى يصل إصدار طراز جديد إلى القرص. عندما يرى إصدارًا جديدًا ، فإنه يستمر من خلال إنشاء محمل لهذا الإصدار المحدد من النموذج.

باختصار ، يعرف Loader كل شيء تقريبًا عن النموذج. يتضمن كيفية تحميله وكيفية تقدير الموارد المطلوبة للنموذج ، مثل ذاكرة الوصول العشوائي (RAM) المطلوبة وذاكرة GPU. يحتوي Loader على مؤشر إلى النموذج الموجود على القرص جنبًا إلى جنب مع جميع البيانات الوصفية اللازمة لتحميله. ولكن هناك نقطة جذب هنا: لا يُسمح لل Loader بتحميل النموذج حتى الآن.

بعد إنشاء اللودر ، يقوم المصدر بإرساله إلى المدير كنسخة طموحة.

عند استلام نسخة Aspired من الطراز ، يتابع المدير عملية التقديم. هنا ، هناك احتمالان. واحد هو أن النسخة النموذج الأول هو الضغط للنشر. في هذه الحالة ، سيتأكد المدير من توفر الموارد المطلوبة. بمجرد أن يتم ذلك ، يمنح المدير الإذن Loader لتحميل النموذج.

والثاني هو أننا ندفع نسخة جديدة من نموذج موجود. في هذه الحالة ، يتعين على المدير الرجوع إلى ملحق "سياسة الإصدار" قبل المضي قدمًا. تحدد سياسة الإصدار كيفية إجراء عملية تحميل إصدار نموذج جديد.

على وجه التحديد ، عند تحميل نسخة جديدة من نموذج ما ، يمكننا الاختيار بين الحفاظ على (1) توافر أو (2) موارد. في الحالة الأولى ، نحن مهتمون بالتأكد من أن نظامنا متاح دائمًا لطلبات العملاء الواردة. نحن نعلم أن المدير يسمح للودر بإنشاء الرسم البياني الجديد باستخدام الأوزان الجديدة.

في هذه المرحلة ، لدينا إصداران من النماذج تم تحميلهما في نفس الوقت. ولكن يقوم المدير بإلغاء تحميل الإصدار الأقدم فقط بعد اكتمال التحميل ، ويكون من الآمن التبديل بين الطرز.

من ناحية أخرى ، إذا كنا نريد توفير الموارد عن طريق عدم وجود مخزن مؤقت إضافي (للإصدار الجديد) ، فيمكننا اختيار الحفاظ على الموارد. قد يكون من المفيد للموديلات الثقيلة جدًا وجود فجوة صغيرة في التوافر ، مقابل توفير الذاكرة.

في النهاية ، عندما يطلب العميل مقبضًا للنموذج ، يقوم المدير بإرجاع مؤشر إلى Servable.

مع هذه النظرة العامة ، تم تعييننا على الغوص في تطبيق حقيقي. في الأقسام التالية ، وصفنا كيفية خدمة شبكة عصبية تلافيفية (CNN) باستخدام TF Serving.

تصدير نموذج للخدمة

الخطوة الأولى لخدمة نموذج ML المدمج في TensorFlow هي التأكد من أنه في التنسيق الصحيح. للقيام بذلك ، يوفر TensorFlow فئة SavedModel.

SavedModel هو تنسيق التسلسل العالمي لنماذج TensorFlow. إذا كنت معتادًا على TF ، فربما تكون قد استخدمت TensorFlow Saver لاستمرار متغيرات النموذج الخاص بك.

يوفر TensorFlow Saver وظائف لحفظ / استعادة ملفات نقاط التفتيش في النموذج من / إلى القرص. في الواقع ، يلتف SavedModel مع TensorFlow Saver ويُقصد به أن يكون الطريقة القياسية لتصدير نماذج TF للعرض.

يحتوي كائن SavedModel على بعض الميزات الرائعة.

أولاً ، يتيح لك حفظ أكثر من رسم بياني واحد إلى كائن SavedModel واحد. بمعنى آخر ، يسمح لنا أن يكون لدينا رسومات بيانية مختلفة للمهام المختلفة.

على سبيل المثال ، افترض أنك انتهيت للتو من تدريب النموذج الخاص بك. في معظم الحالات ، لتحقيق الاستدلال ، لا يحتاج الرسم البياني الخاص بك إلى بعض العمليات الخاصة بالتدريب. قد تتضمن عمليات التشغيل هذه متغيرات المُحسِّن ، وتوترات جدولة معدل التعلم ، وعمليات التجهيز الإضافية الإضافية للمعالجة ، وما إلى ذلك.

علاوة على ذلك ، قد ترغب في تقديم نسخة كمية من الرسم البياني لنشر المحمول.

في هذا السياق ، يتيح لك SavedModel حفظ الرسوم البيانية بتكوينات مختلفة. في مثالنا ، سيكون لدينا ثلاثة رسوم بيانية مختلفة مع علامات مقابلة مثل "التدريب" ، "الاستدلال" ، و "المحمول". أيضًا ، ستشارك هذه الرسوم البيانية الثلاثة في نفس مجموعة المتغيرات - التي تؤكد على كفاءة الذاكرة.

منذ وقت ليس ببعيد ، عندما أردنا نشر نماذج TF على الأجهزة المحمولة ، كنا بحاجة إلى معرفة أسماء مداخل الإدخال والإخراج للتغذية والحصول على البيانات من / إلى النموذج. هذه الحاجة اضطر المبرمجين للبحث عن الموتر التي يحتاجون إليها بين جميع التنسورات من الرسم البياني. إذا لم يتم تسمية التنسورات بشكل صحيح ، فقد تكون المهمة شاقة للغاية.

لتسهيل الأمور ، يوفر SavedModel الدعم لـ SignatureDefs. باختصار ، يحدد SignatureDefs توقيع حساب مدعوم من TensorFlow. إنه يحدد التنسورات المدخلات والمخرجات المناسبة للرسم البياني الحسابي. ببساطة ، مع هذه التوقيعات ، يمكنك تحديد العقد الدقيقة لاستخدامها في الإدخال والإخراج.

لاستخدام واجهات برمجة التطبيقات الخاصة بالخدمة المدمجة ، تتطلب TF Serving أن تشتمل النماذج على واحد أو أكثر من SignatureDefs.

لإنشاء مثل هذه التوقيعات ، نحتاج إلى تقديم تعريفات للمدخلات والمخرجات واسم الطريقة المطلوبة. تمثل المدخلات والمخرجات تعيينًا من السلسلة إلى كائنات TensorInfo (المزيد حول هذا الأخير). هنا ، نحدد التنسورات الافتراضية لتغذية واستقبال البيانات من وإلى الرسم البياني. تستهدف المعلمة method_name أحد واجهات برمجة التطبيقات التي تعمل على مستوى عالٍ من TF.

حاليًا ، هناك ثلاثة واجهات برمجة التطبيقات (APIs) تقدم: التصنيف والتنبؤ والانحدار. يطابق كل تعريف توقيع API RPC محددة. يتم استخدام SegnatureDef للتصنيف لـ Classify RPC API. يستخدم Predict SegnatureDef لواجهة برمجة تطبيقات Predict RPC ، وعلى.

بالنسبة لتوقيع التصنيف ، يجب أن يكون هناك موتر للمدخلات (لتلقي البيانات) وواحد على الأقل من مسببين محتملين للمخرجات: الفئات و / أو الدرجات. يتطلب الانحدار SignatureDef موترًا واحدًا بالضبط للإدخال والآخر للإخراج. أخيرًا ، يتيح توقيع Predict عددًا ديناميكيًا من توترات الإدخال والإخراج.

بالإضافة إلى ذلك ، يدعم SavedModel تخزين الأصول للحالات التي يعتمد فيها التهيئة ops على الملفات الخارجية. أيضا ، لديها آليات لمسح الأجهزة قبل إنشاء SavedModel.

الآن ، دعونا نرى كيف يمكننا القيام بذلك في الممارسة العملية.

إعداد البيئة

قبل أن نبدأ ، استنساخ هذا التطبيق TensorFlow DeepLab-v3 من جيثب.

DeepLab هو أفضل تقسيم Google الدلالي ConvNet. بشكل أساسي ، تأخذ الشبكة صورة كمدخلات وتقوم بإخراج صورة تشبه القناع تفصل بعض الكائنات عن الخلفية.

تم تدريب هذا الإصدار على مجموعة بيانات Pascal VOC. وبالتالي ، يمكنه تقسيم ما يصل إلى 20 فصلًا والتعرف عليها. إذا كنت تريد معرفة المزيد عن تقسيم الدلالي و DeepLab-v3 ، ألقِ نظرة على الغوص في شبكات تقسيم الدلالي التلافيفي العميق و Deeplab_V3.

جميع الملفات المتعلقة بالخدمة موجودة في: ./deeplab_v3/serving/. ستجد هناك ملفين مهمين: deeplab_saved_model.py و deeplab_client.ipynb

قبل الذهاب إلى أبعد من ذلك ، تأكد من تنزيل نموذج Deeplab-v3 الذي تم تدريبه مسبقًا. توجه إلى مستودع GitHub أعلاه ، وانقر على رابط نقاط التفتيش ، وقم بتنزيل المجلد المسمى 16645 /.

في النهاية ، يجب أن يكون لديك مجلد باسم tboard_logs / مع وضع المجلد 16645 / داخله.

الآن ، نحن بحاجة إلى إنشاء بيئات بيثون الافتراضية. واحد لبيثون 3 والآخر لبيثون 2. لكل env ، تأكد من تثبيت التبعيات اللازمة. يمكنك العثور عليها في ملف serve_requirements.txt و client_requirements.txt.

نحتاج إلى اثنين من envs من Python لأن نموذجنا ، DeepLab-v3 ، تم تطويره تحت Python 3. ومع ذلك ، فإن TensorFlow Serving Python API يتم نشره فقط من أجل Python 2. لذلك ، لتصدير النموذج وتشغيل TF ، فإننا نستخدم Python 3 env . لتشغيل شفرة العميل باستخدام واجهة برمجة تطبيقات TF Serving python ، نستخدم حزمة PIP (متاحة فقط لـ Python 2).

لاحظ أنه يمكنك التخلي عن Python 2 env باستخدام واجهات برمجة التطبيقات Serving من bazel. ارجع إلى TF Serving Instalation للحصول على مزيد من التفاصيل.

مع استكمال هذه الخطوة ، لنبدأ بما يهم حقًا.

كيف افعلها

لاستخدام SavedModel ، يوفر TensorFlow وسيلة فائدة سهلة الاستخدام عالية المستوى تدعى SavedModelBuilder. توفر فئة SavedModelBuilder وظائف لحفظ الرسوم البيانية الوصفية المتعددة والمتغيرات المرتبطة بها والأصول.

دعنا نستعرض مثالاً جارياً عن كيفية تصدير نموذج سي بي ديب سيشنشن سي إن للعرض.

كما ذكر أعلاه ، لتصدير النموذج ، نستخدم فئة SavedModelBuilder. سيقوم بإنشاء ملف مخزن مؤقت لبروتوكول SavedModel مع متغيرات النموذج وأصوله (إذا لزم الأمر).

دعونا تشريح الرمز.

يتلقى SavedModelBuilder (كمدخلات) الدليل الذي يمكن من خلاله حفظ بيانات النموذج. هنا ، متغير export_path هو تسلسل export_path_base و model_version. نتيجة لذلك ، سيتم حفظ إصدارات طراز مختلفة في دلائل منفصلة داخل مجلد export_path_base.

دعنا نقول أن لدينا نسخة أساسية من نموذجنا في الإنتاج ، لكننا نريد نشر نسخة جديدة منه. لقد قمنا بتحسين دقة نموذجنا ونريد تقديم هذا الإصدار الجديد لعملائنا.

لتصدير نسخة مختلفة من نفس الرسم البياني ، يمكننا فقط تعيين FLAGS.model_version على قيمة عدد صحيح أعلى. بعد ذلك ، سيتم إنشاء مجلد مختلف (يحمل الإصدار الجديد من نموذجنا) داخل مجلد export_path_base.

الآن ، نحن بحاجة إلى تحديد المدخلات والمخرجات Tensors من نموذجنا. للقيام بذلك ، نستخدم SignatureDefs. تحدد التواقيع نوع النموذج الذي نريد تصديره. يوفر تعيين من السلاسل (أسماء Tensor المنطقية) إلى كائنات TensorInfo. تتمثل الفكرة في أنه بدلاً من الإشارة إلى أسماء الموتر الفعلية للإدخال / الإخراج ، يمكن للعملاء الرجوع إلى الأسماء المنطقية المحددة بواسطة التوقيعات.

من أجل تقديم خدمة الدلالة Segmentation CNN ، سنقوم بإنشاء توقيع متوقع. لاحظ أن وظيفة build_signature_def () تأخذ التعيين لتوترات الإدخال والإخراج وكذلك واجهة برمجة التطبيقات المطلوبة.

يتطلب SignatureDef تحديد: المدخلات والمخرجات واسم الطريقة. لاحظ أننا نتوقع ثلاث قيم للمدخلات - صورة ، واثنين من التنسورات الأخرى التي تحدد أبعادها (الارتفاع والعرض). بالنسبة للمخرجات ، حددنا نتيجة واحدة فقط - قناع إخراج التقسيم.

لاحظ أن السلاسل "الصورة" و "الارتفاع" و "العرض" و "segmentation_map" ليست متوترة. بدلاً من ذلك ، فهي أسماء منطقية تشير إلى الموتر الفعلي لإدخالات المدخلات و image_height_tensor و image_width_tensor. وبالتالي ، يمكن أن تكون أي سلسلة فريدة تريد.

أيضاً ، تتعلق التعيينات في SignatureDefs بكائنات TensorInfo protobuf ، وليس التنسورات الفعلية. لإنشاء كائنات TensorInfo ، نستخدم وظيفة الأداة المساعدة: tf.saved_model.utils.build_tensor_info (tensor).

هذا هو. الآن ندعو الوظيفة add_meta_graph_and_variables () لبناء كائن المخزن المؤقت لبروتوكول SavedModel. ثم نقوم بتشغيل طريقة الحفظ () وستستمر في إلقاء نظرة سريعة على نموذجنا على القرص الذي يحتوي على متغيرات وأصول النموذج.

يمكننا الآن تشغيل deeplab_saved_model.py لتصدير نموذجنا.

إذا سارت الأمور على ما يرام ، فسترى المجلد. / الخدمة / الإصدارات 1 /. لاحظ أن الرقم "1" يمثل الإصدار الحالي للنموذج. داخل كل دليل فرعي ، ستشاهد الملفات التالية:

  • save_model.pb أو save_model.pbtxt. هذا هو ملف SavedModel التسلسلي. يتضمن تعريفًا أو أكثر من الرسوم البيانية للنموذج ، بالإضافة إلى تعريفات التوقيع.
  • المتغيرات. يحتوي هذا المجلد على المتغيرات المتسلسلة للرسومات.

الآن ، نحن مستعدون لإطلاق خادم النموذج الخاص بنا. للقيام بذلك ، قم بتشغيل:

tensorflow_model_server $ --port = 9000 --model_name = deeplab --model_base_path = <كامل / المسار / إلى / تخدم / الإصدارات />

يشير model_base_path إلى مكان حفظ النموذج الذي تم تصديره. أيضًا ، لا نحدد مجلد الإصدار في المسار. يتم التحكم في إصدار الإصدار بواسطة TF Serving.

توليد طلبات العميل

رمز العميل واضح للغاية. ألقِ نظرة عليه في: deeplab_client.ipynb.

أولاً ، نقرأ الصورة التي نريد إرسالها إلى الخادم وتحويلها إلى التنسيق الصحيح.

بعد ذلك ، نقوم بإنشاء كعب gRPC. يتيح لنا كعب الروتين الاتصال بأساليب الخادم البعيد. للقيام بذلك ، نقوم بتفعيل فئة beta_create_PredictionService_stub في الوحدة النمطية prediction_service_pb2. في هذه المرحلة ، يحمل كعب الروتين المنطق اللازم لاستدعاء الإجراءات عن بُعد (من الخادم) كما لو كانت محلية.

الآن ، نحن بحاجة إلى إنشاء وتعيين كائن الطلب. نظرًا لأن الخادم الخاص بنا يقوم بتنفيذ TensorFlow Predict API ، فنحن بحاجة إلى تحليل طلب التنبؤ. لإصدار طلب تنبؤ ، أولاً ، نقوم بإنشاء مثيل لفئة PredictRequest من الوحدة النمطية Forecast_pb2. نحتاج أيضًا إلى تحديد معلمات model_spec.name و model_spec.signature_name. الاسم param هو وسيطة 'model_name' التي حددناها عندما أطلقنا الخادم. ويشير signature_name إلى الاسم المنطقي المعين للمعلمة signature_def_map () للروتين add_meta_graph ().

بعد ذلك ، يجب علينا توفير بيانات الإدخال كما هو محدد في توقيع الخادم. تذكر أنه في الخادم ، حددنا واجهة برمجة تطبيقات Predict لتتوقع صورة بالإضافة إلى عددين (ارتفاع الصورة وعرضها). لتغذية بيانات الإدخال في كائن الطلب ، يوفر TensorFlow الأداة المساعدة tf.make_tensor_proto (). تقوم هذه الطريقة بإنشاء كائن TensorProto من كائن numpy / Python. يمكننا استخدامه لتغذية الصورة وأبعادها إلى كائن الطلب.

يبدو أننا مستعدون للاتصال بالخادم. للقيام بذلك ، ندعو الأسلوب Predict () (باستخدام كعب الروتين) وتمرير كائن الطلب كوسيطة.

بالنسبة للطلبات التي تُرجع استجابة واحدة ، يدعم gRPC كلاً من: المكالمات المتزامنة وغير المتزامنة. وبالتالي ، إذا كنت تريد القيام ببعض الأعمال أثناء معالجة الطلب ، فيمكننا استدعاء Predict.future () بدلاً من Predict ().

الآن يمكننا جلب والاستمتاع بالنتائج.

آمل أن تكونوا قد أحببت هذا المقال. شكرا للقراءة!

إذا كنت تريد المزيد ، تحقق من: