قبل عام تقريبًا ، أصدرت
Intel Movidius جهازًا للاستدلال الفعال على الشبكات العصبية التلافيفية - Movidius Neural Compute Stick (NCS). يسمح هذا الجهاز باستخدام الشبكات العصبية للتعرف على الأشياء أو اكتشافها في ظروف استهلاك الطاقة المحدود ، بما في ذلك في مهام الروبوتات. يحتوي NCS على واجهة USB ولا يستهلك أكثر من 1 وات. في هذه المقالة ، سأتحدث عن تجربة استخدام NCS مع Raspberry Pi لمهمة الكشف عن الوجوه في الفيديو ، بما في ذلك تدريب كاشف Mobilenet-SSD وتشغيله على Raspberry.
يمكن العثور على جميع التعليمات البرمجية في مستودعي:
تدريب الكاشف وعروض
كشف الوجه .
في مقالتي الأولى ، كتبت بالفعل عن اكتشاف الوجه باستخدام NCS: ثم كنا نتحدث عن كاشف
YOLOv2 ، الذي حولته من تنسيق
Darknet إلى تنسيق
Caffe ، ثم أطلقته على NCS. اتضح أن عملية التحويل غير تافهة: نظرًا لأن هذين التنسيقين يحددان الطبقة الأخيرة من الكاشف بطرق مختلفة ، يجب تحليل ناتج الشبكة العصبية بشكل منفصل ، على وحدة المعالجة المركزية ، باستخدام جزء من التعليمات البرمجية من Darknet. بالإضافة إلى ذلك ، لم يرضي هذا الكاشف في السرعة (حتى 5.1 FPS على الكمبيوتر المحمول الخاص بي) وبدقة - في وقت لاحق كنت مقتنعًا أنه بسبب حساسيته لجودة الصورة ، كان من الصعب الحصول على نتيجة جيدة على Raspberry Pi.
في النهاية ، قررت فقط تدريب كاشفاتي. وقع الاختيار على كاشف
SSD مع تشفير
Mobilenet : يتيح لك الجمع الخفيف لـ Mobilenet تحقيق سرعة عالية دون أي خسارة في الجودة ، ولا يعد كاشف SSD أدنى من YOLO ويعمل على NCS خارج الصندوق.
كيف يعمل كاشف Mobilenet-SSDلنبدأ مع Mobilenet. في هذه العمارة كاملة
3 ض ر ب 3 يتم استبدال الالتفاف (على جميع القنوات) بلفتي خفيفة: الأول
3 ض ر ب 3 بشكل منفصل لكل قناة ثم أكمل
1 ض ر ب 1 الالتواء. بعد كل الالتفاف ، يتم استخدام
BatchNorm وغير الخطية (ReLU). عادةً ما يتم ترك الالتفاف الأول للشبكة الذي يتلقى صورة كمدخلات كاملة. يمكن لهذه البنية أن تقلل إلى حد كبير من تعقيد الحسابات بسبب انخفاض طفيف في جودة التنبؤات. هناك
خيار أكثر تقدمًا ، لكنني لم أجربه بعد.
يعمل SSD (Single Shot Detector) على هذا النحو: يتم تعليق ناتجين على مخرجات العديد من مشفرات الالتفاف
1 ض ر ب 1 الطبقة التلافيفية: تتنبأ إحتمالات الطبقات ، والأخرى - إحداثيات الصندوق المحيط. هناك طبقة ثالثة تعطي إحداثيات ومواضع الإطارات الافتراضية على المستوى الحالي. المعنى: ناتج أي طبقة ينقسم بشكل طبيعي إلى خلايا. أقرب إلى نهاية الشبكة العصبية ، تصبح أصغر (في هذه الحالة ، بسبب الالتفاف
stride=2
) ، ويزداد مجال رؤية كل خلية. لكل خلية في كل طبقة من الطبقات المحددة المحددة ، قمنا بتعيين العديد من الإطارات الافتراضية بأحجام مختلفة وبنسب عرض إلى ارتفاع مختلفة ، ونستخدم طبقات تلافيفية إضافية لتصحيح الإحداثيات والتنبؤ باحتمالات الفئة لكل إطار. لذلك ، فإن كاشف SSD (مثل YOLO) يراعي دائمًا نفس عدد الإطارات. يمكن الكشف عن نفس الشيء على طبقات مختلفة: أثناء التدريب ، يتم إرسال الإشارة إلى جميع الإطارات التي تتقاطع مع الكائن بقوة كبيرة ، وأثناء التطبيق ، يتم دمج عمليات الكشف باستخدام الحد الأقصى من الكبت (NMS). تجمع الطبقة النهائية عمليات الكشف من جميع الطبقات ، وتأخذ في الاعتبار إحداثياتها الكاملة ، وتقطع عتبة الاحتمال وتنتج NMS.
تدريب الكاشف
العمارة
يوجد رمز تدريب الكاشف
هنا .
قررت استخدام
كاشف Mobilenet-SSD الجاهز المدرب على
PASCAL VOC0712 وتدريبه على اكتشاف الوجوه. أولاً ، إنه يساعد على تدريب الشبكة بشكل أسرع ، وثانيًا ، ليس عليك إعادة اختراع العجلة.
تضمن المشروع الأصلي البرنامج النصي
gen.py
، الذي جمع حرفيا ملف نموذج
.prototxt
، واستبدال معلمات الإدخال. نقلته إلى مشروعي ، مما أدى إلى توسيع الوظائف قليلاً. يسمح لك هذا البرنامج النصي بإنشاء أربعة أنواع من ملفات التكوين:
- القطار : عند المدخل - قاعدة LMDB للتدريب ، عند الإخراج - طبقة مع حساب وظيفة الخسارة وتدرجاتها ، هناك BatchNorm
- اختبار : عند الإدخال - قاعدة اختبار LMDB ، عند الإخراج - الطبقة مع حساب الجودة (متوسط الدقة) ، هناك BatchNorm
- نشر : عند الإدخال - الصورة ، عند الإخراج - الطبقة مع التنبؤات ، BatchNorm مفقود
- loy_bn : عند الإدخال - الصورة ، عند الإخراج - الطبقة مع التنبؤات ، هناك BatchNorm
أضفت الخيار الأخير في وقت لاحق ، بحيث في البرامج النصية يمكنك تحميل وتحويل الشبكة من BatchNorm دون لمس قاعدة بيانات LMDB - وإلا ، في حالة عدم وجود قاعدة البيانات ، لن يعمل أي شيء. (بشكل عام ، يبدو غريبًا بالنسبة لي أنه في Caffe يتم تعيين مصدر البيانات في بنية الشبكة - وهذا على الأقل ليس عمليًا جدًا).
كيف تبدو بنية الشبكة (قصيرة)- تسجيل الدخول:

- تحويلات الإحالات الناجحة الكاملة: 32 قناة ،
stride=2
- تحويل Mobilenet conv1 - conv11 : 64 ، 128 ، 128 ، 256 ، 256 ، 512 ... 512 قنوات ، بعضها
stride=2
- طبقة الكشف:

- convil Mobilenet conv12 ، conv13 : 1024 قناة ، conv12 لها
stride=2
- طبقة الكشف:

- التفاف كامل conv14_1 ، conv14_2 : 256 ، 512 قناة ،
kernel_size=1
الأولى kernel_size=1
، stride=2
الثانية stride=2
- طبقة الكشف:

- الالتفاف الكامل conv15_1 ، conv15_2 : 128 ، 256 قناة ،
kernel_size=1
الأولى kernel_size=1
، stride=2
الثانية stride=2
- طبقة الكشف: 3 م ر ا ت 3 م ر ا ت 256
- إتمام conv16_1 ، conv16_2 التواءات : 128 ، 256 قناة ،
kernel_size=1
الأولى kernel_size=1
، stride=2
الثانية stride=2
- طبقة الكشف: 2 ض ر ب 2 ض ر ب 256
- الالتواء الكامل conv17_1 ، conv17_2 : 64 ، 128 قناة ، أول
kernel_size=1
، stride=2
الثانية stride=2
- طبقة الكشف: 1 ض ر ب 1 ض ر ب 128
- إخراج كشف الطبقة النهائية
لقد قمت بتصحيح بنية الشبكة قليلاً. قائمة التغييرات:
- من الواضح أن عدد الفصول تغير إلى 1 (بدون احتساب الخلفية).
- القيود على النسبة الباعية للرقع المقطوعة أثناء التدريب: تغيرت من [0.5،2.0] على [0.7،1.4] (قررت تبسيط المهمة قليلاً وعدم التعلم من الصور الممتدة للغاية).
- من الإطارات الافتراضية ، بقي فقط إطارات مربعة ، اثنان لكل خلية. لقد قمت بتقليل أحجامها بشكل كبير ، لأن الوجوه أصغر بكثير من الأشياء الموجودة في مشكلة الكشف عن الأشياء الكلاسيكية.
يحسب Caffe أحجام الإطار الافتراضية على النحو التالي: الحصول على الحد الأدنى لحجم الإطار
s والحد الأقصى
L ، يخلق إطارًا صغيرًا وكبيرًا بأبعاد
s و
sqrtsL . نظرًا لأنني كنت أرغب في اكتشاف الوجوه الصغيرة قدر الإمكان ، فقد حسبت
stride
الكاملة لكل طبقة كشف وقمت بموازنة الحد الأدنى لحجم الإطار لها. باستخدام هذه المعلمات ، سيتم وضع الإطارات الافتراضية الصغيرة بالقرب من بعضها البعض ولن تتقاطع. لذلك لدينا على الأقل ضمان بأن التقاطع مع الكائن سيكون موجودًا لنوع من الإطار. لقد قمت بتعيين الحجم الأقصى مرتين. بالنسبة للطبقات
conv16_2 ، conv17_2 ، قمت بتعيين الأبعاد على العين لتكون هي نفسها. بهذه الطريقة
s،L لجميع الطبقات كانت:

كيف تبدو بعض الإطارات الافتراضية (ضوضاء الوضوح) البيانات
استخدمت مجموعتي
بيانات :
WIDER Face و
FDDB . يحتوي WIDER على العديد من الصور ذات الوجوه الصغيرة والباهتة للغاية ، ويميل FDDB بشكل أكبر إلى الصور الكبيرة للوجوه (وترتيب بحجم أصغر من WIDER). يختلف تنسيق التعليق التوضيحي قليلاً فيه ، ولكن هذه بالفعل تفاصيل.
لم أستخدم جميع البيانات للتدريب: لقد قمت برمي وجوه صغيرة جدًا (أقل من ستة بكسل أو أقل من 2٪ من عرض الصورة) ، ورميت جميع الصور بنسبة عرض أقل من 0.5 أو أكثر من 2 ، ورميت جميع الصور التي تم وضع علامة عليها بأنها "ضبابية" في مجموعة بيانات WIDER ، بما أنها تتوافق في معظمها مع أشخاص صغار جدًا ، وكان علي على الأقل أن أواءم نسبة الوجوه الصغيرة والكبيرة. بعد ذلك ، قمت بعمل جميع الإطارات مربعة ، وتوسيع الجانب الأصغر: قررت أنني لست مهتمًا بنسب الوجه ، وتم تبسيط مهمة الشبكة العصبية قليلاً. قمت أيضًا برمي جميع الصور بالأبيض والأسود ، والتي كان عددها قليلًا ، والتي تعطلت عليها قاعدة البيانات.
لاستخدامها للتدريب والاختبار ، تحتاج إلى تجميع قاعدة LMDB منها. كيف تفعل ذلك:
- لكل صورة ، يتم إنشاء الترميز بتنسيق
.xml
. - يتم
train.txt
ملف train.txt
مع أسطر النموذج "path/to/image.png path/to/labels.xml"
، ويتم إنشاء نفس الشيء للاختبار. - يتم
test_name_size.txt
ملف test_name_size.txt
مع أسطر النموذج "test_image_name height width"
- ينشئ ملف
labelmap.prototxt
مع تطابقات labelmap.prototxt
العددية
تم
ssd-caffe/scripts/create_annoset.py
(مثال من ملف Makefile):
python3 /opt/movidius/ssd-caffe/scripts/create_annoset.py --anno-type=detection \ --label-map-file=$(wider_dir)/labelmap.prototxt --min-dim=0 --max-dim=0 \ --resize-width=0 --resize-height=0 --check-label --encode-type=jpg --encoded \ --redo $(wider_dir) \ $(wider_dir)/trainval.txt $(wider_dir)/WIDER_train/lmdb/wider_train_lmdb ./data
labelmap.prototxt item { name: "none_of_the_above" label: 0 display_name: "background" } item { name: "face" label: 1 display_name: "face" }
مثال ترميز .xml <?xml version="1.0" ?> <annotation> <size> <width>348</width> <height>450</height> <depth>3</depth> </size> <object> <name>face</name> <bndbox> <xmin>161</xmin> <ymin>43</ymin> <xmax>241</xmax> <ymax>123</ymax> </bndbox> </object> </annotation>
إن استخدام مجموعتي بيانات في نفس الوقت يعني فقط أنك بحاجة إلى دمج الملفات المقابلة في أزواج بعناية ، وعدم نسيان تسجيل المسارات بشكل صحيح ، وكذلك تبديل الملف للتدريب.
بعد ذلك ، يمكنك البدء في التدريب.
تدريب
يمكن العثور على رمز للتدريب النموذجي في
دفتر ملاحظات Colab الخاص بي.
لقد قمت بالتدريب في Google Colaboratory ، لأن الكمبيوتر المحمول الخاص بي بالكاد قام باختبار الشبكة ، وأغلق المكالمة بشكل عام في التدريب. سمح لي تعاونية بتدريب الشبكة بسرعة كافية ومجانا. الصيد الوحيد هو أنني اضطررت إلى كتابة نص برمجي لتجميع SSD-Caffe لـ Colaboratory (بما في ذلك أشياء غريبة مثل إعادة تجميع وتحرير المصدر) ، والذي يستغرق حوالي 40 دقيقة. يمكن العثور على مزيد من التفاصيل
في منشوري السابق .
يحتوي Colaboratory على ميزة أخرى: بعد 12 ساعة ، تموت السيارة ، مما يؤدي إلى محو جميع البيانات بشكل دائم. أفضل طريقة لتجنب فقدان البيانات هي تحميل قرص Google الخاص بك في النظام وحفظ أوزان الشبكة هناك كل 500-1000 تكرارات تدريب.
بالنسبة للكاشف الخاص بي ، تمكن في جلسة واحدة في Colaboratory من معرفة 4500 تكرار ، وتم تدريبه بالكامل في جلستين.
كانت جودة التنبؤات (متوسط الدقة) في مجموعة بيانات الاختبار التي أبرزتها (دمج WIDER و FDDB مع القيود المذكورة أعلاه) حوالي 0.87 لأفضل نموذج. لقياس mAP على المقاييس المحفوظة أثناء التدريب ، هناك
scripts/plot_map.py
.
يعمل الكاشف على مثال (غريب جدًا) من مجموعة بيانات:
إطلاق على NCS
عرض كشف الوجه
هنا .
لتجميع شبكة عصبية لـ Neural Compute Stick ، تحتاج إلى
Movidius NCSDK : فهو يحتوي على أدوات لتجميع الشبكات العصبية وتوصيفها ، بالإضافة إلى C ++ و Python APIs. تجدر الإشارة إلى أن الإصدار الثاني تم إصداره مؤخرًا ، وهو غير متوافق مع الإصدار الأول: تمت إعادة تسمية جميع وظائف واجهة برمجة التطبيقات لسبب ما ، وتغيير التنسيق الداخلي للشبكات العصبية ، وإضافة FIFO للتفاعل مع NCS ، (وأخيرًا) التحويل التلقائي من تعويم 32 بت إلى تعويم 16 بت ، والتي كانت تفتقر إلى C ++. لقد قمت بتحديث جميع مشاريعي إلى الإصدار الثاني ، لكني تركت عكازين للتوافق مع الإصدار الأول.
بعد تدريب الكاشف ، يجدر دمج طبقات BatchNorm مع الالتواءات المجاورة لتسريع الشبكة العصبية. يقوم البرنامج النصي
merge_bn.py
بذلك من هنا ، والذي استعرته أيضًا من مشروع Mobilenet-SSD.
ثم تحتاج إلى استدعاء الأداة المساعدة
mvNCCompile
، على سبيل المثال:
mvNCCompile -s 12 -o graph_ssd -w ssd-face.caffemodel ssd-face.prototxt
يوجد هدف
graph_ssd
لهذا في ملف Makefile للمشروع. إن ملف
graph_ssd
الناتج هو وصف للشبكة العصبية بتنسيق يفهمه NCS.
الآن حول كيفية التفاعل مع الجهاز نفسه. العملية ليست معقدة للغاية ، ولكنها تتطلب كمية كبيرة إلى حد ما من التعليمات البرمجية. تسلسل الإجراءات هو تقريبًا ما يلي:
- احصل على واصف الجهاز بالرقم التسلسلي
- افتح الجهاز
- قراءة ملف الشبكة العصبية المترجمة إلى المخزن المؤقت (كملف ثنائي)
- إنشاء رسم بياني حسابي فارغ لـ NCS
- ضع الرسم البياني على الجهاز باستخدام البيانات من الملف وحدد FIFO له على الإدخال / الإخراج ؛ يمكن الآن تحرير المخزن المؤقت مع محتويات الملف
- بدء الكاشف:
- احصل على الصورة من الكاميرا (أو من أي مصدر آخر)
- قم بمعالجته: قم بالتدرج إلى الحجم المطلوب ، قم بالتحويل إلى float32 وصب في النطاق [-1،1]
- تحميل صورة على الجهاز وطلب الاستدلال
- طلب نتيجة (سيتم حظر البرنامج حتى يتم استلام النتيجة)
- تحليل النتيجة ، حدد إطارات الكائنات (حول التنسيق - المزيد)
- عرض التوقعات
- تحرير جميع الموارد: إزالة FIFO والرسم البياني للحساب ، وإغلاق الجهاز وإزالة مقبضه
تقريبا كل إجراء مع NCS له وظيفة منفصلة خاصة به ، وفي C ++ يبدو مرهقًا للغاية ، وعليك مراقبة إصدار جميع الموارد بعناية. لكي لا أفرط في تحميل الكود ، قمت بإنشاء
فئة مجمعة للعمل مع NCS . في ذلك ، يتم إخفاء جميع أعمال التهيئة في المنشئ
load_file
، وعند تحرير الموارد - في المدمر ، يتم تقليل العمل مع NCS إلى استدعاء 2-3 أساليب من الفئة. بالإضافة إلى ذلك ، هناك وظيفة ملائمة لشرح الأخطاء التي حدثت.
قم بإنشاء غلاف بتمرير حجم الإدخال وحجم الإخراج (عدد العناصر) إلى المنشئ:
NCSWrapper NCS(NETWORK_INPUT_SIZE*NETWORK_INPUT_SIZE*3, NETWORK_OUTPUT_SIZE);
نقوم بتحميل الملف المتجمع مع الشبكة العصبية ، ونهيئ كل ما نحتاجه في نفس الوقت:
if (!NCS.load_file("./models/face/graph_ssd")) { NCS.print_error_code(); return 0; }
نقوم بتحويل الصورة إلى float32 (
image
هي
cv::Mat
بتنسيق
CV_32FC3
) وتنزيلها على الجهاز:
if(!NCS.load_tensor_nowait((float*)image.data)) { NCS.print_error_code(); break; }
نحصل على النتيجة (
result
هي مؤشر
float
مجاني ، يتم دعم المخزن المؤقت للنتيجة بواسطة الغلاف) ؛ حتى نهاية الحسابات ، يتم حظر البرنامج:
if(!NCS.get_result(result)) { NCS.print_error_code(); break; }
في الواقع ، يحتوي الغلاف أيضًا على طريقة تسمح لك بتحميل البيانات والحصول على النتيجة في نفس الوقت:
load_tensor((float*)image.data, result)
. لقد رفضت استخدامه لسبب: باستخدام طرق منفصلة ، يمكنك تسريع تنفيذ التعليمات البرمجية قليلاً. بعد تحميل الصورة ، ستقف وحدة المعالجة المركزية في وضع الخمول حتى تأتي نتيجة التنفيذ باستخدام NCS (في هذه الحالة تبلغ حوالي 100 مللي ثانية) ، وفي هذا الوقت يمكنك القيام بعمل مفيد: اقرأ إطارًا جديدًا وقم بتحويله ، بالإضافة إلى عرض الاكتشافات السابقة . هذه هي بالضبط الطريقة التي يتم بها تنفيذ البرنامج التجريبي ، في حالتي فإنه يزيد قليلاً من FPS. يمكنك الذهاب إلى أبعد من ذلك وبدء معالجة الصور وكاشف الوجه بشكل غير متزامن في دفقين مختلفين - هذا يعمل حقًا ويسمح لك بتسريع أكثر قليلاً ، ولكن لم يتم تنفيذه في البرنامج التجريبي.
ونتيجة لذلك ، يعرض الكاشف مصفوفة عائمة بحجم
7*(keep_top_k+1)
. هنا ،
keep_top_k
هي المعلمة المحددة في ملف
.prototxt
للنموذج وتظهر عدد عمليات الكشف (بترتيب تقليل الثقة) التي يجب إرجاعها. يمكن تكوين هذه المعلمة ، بالإضافة إلى المعلمة المسؤولة عن عمليات تصفية التصفية حسب الحد الأدنى لقيمة الثقة ، ومعلمات منع الحد الأقصى في ملف
.prototxt
في الطبقة الأخيرة. تجدر الإشارة إلى أنه إذا أعاد Caffe العديد من الاكتشافات التي تم العثور عليها في الصورة ، فإن NCS تُرجع دائمًا
keep_top_k
الاكتشافات بحيث يكون حجم الصفيف ثابتًا.
صفيف النتائج نفسه
keep_top_k+1
النحو التالي: إذا
keep_top_k+1
مصفوفة تحتوي على صفوف
keep_top_k+1
و 7 أعمدة ، ففي الصف الأول ، سيكون هناك عدد من الاكتشافات في العنصر الأول ، وابتداءً من الصف الثاني سيكون هناك
"garbage, class_index, class_probability, x_min, y_min, x_max, y_max"
بتنسيق
"garbage, class_index, class_probability, x_min, y_min, x_max, y_max"
. يتم تحديد الإحداثيات في النطاق [0،1] ، لذا ستحتاج إلى ضربها في ارتفاع / عرض الصورة. ستكون العناصر المتبقية من الصفيف القمامة. في هذه الحالة ، يتم تنفيذ الحد الأقصى من القمع تلقائيًا ، حتى قبل الحصول على النتيجة (يبدو ، مباشرة على NCS).
تحليل الكاشف void get_detection_boxes(float* predictions, int w, int h, float thresh, std::vector<float>& probs, std::vector<cv::Rect>& boxes) { int num = predictions[0]; float score = 0; float cls = 0; for (int i=1; i<num+1; i++) { score = predictions[i*7+2]; cls = predictions[i*7+1]; if (score>thresh && cls<=1) { probs.push_back(score); boxes.push_back(Rect(predictions[i*7+3]*w, predictions[i*7+4]*h, (predictions[i*7+5]-predictions[i*7+3])*w, (predictions[i*7+6]-predictions[i*7+4])*h)); } } }
ميزات إطلاق التوت بي
يمكن تشغيل البرنامج التجريبي نفسه على جهاز كمبيوتر عادي أو كمبيوتر محمول مع Ubuntu ، أو على Raspberry Pi مع Raspbian Stretch. أنا أستخدم Raspberry Pi 2 model B ، ولكن يجب أن يعمل العرض التوضيحي على نماذج أخرى أيضًا. يحتوي ملف makefile للمشروع على هدفين
make switch_rpi
التبديل:
make switch_desk
للكمبيوتر / الكمبيوتر المحمول وإنشاء
make switch_rpi
لـ Raspberry Pi. الفرق الأساسي في رمز البرنامج هو أنه في الحالة الأولى ، يتم استخدام OpenCV لقراءة البيانات من الكاميرا ، وفي الحالة الثانية ، مكتبة
RaspiCam . لتشغيل العرض التوضيحي على Raspberry ، يجب عليك تجميعه وتثبيته.
الآن نقطة مهمة للغاية: تثبيت NCSDK. إذا اتبعت تعليمات التثبيت القياسية على Raspberry Pi ، فلن ينتهي أي شيء جيد: سيحاول المثبت سحب وتجميع SSD-Caffe و Tensorflow. بدلاً من ذلك ، يحتاج NCSDK إلى الترجمة
في وضع API فقط . في هذا الوضع ، ستتوفر فقط واجهات برمجة التطبيقات C ++ و Python (أي ، لن يكون من الممكن تجميع الرسوم البيانية للشبكة العصبية وتوصيفها). هذا يعني أنه يجب أولاً تجميع الرسم البياني للشبكة العصبية على جهاز كمبيوتر عادي ، ثم نسخه إلى Raspberry. للراحة ، أضفت ملفين مجمعين إلى المستودع ، لـ YOLO و SSD.
نقطة أخرى مثيرة للاهتمام هي الاتصال المادي البحت لـ NCS بتوت العليق. يبدو أنه ليس من الصعب توصيله بموصل USB ، ولكن عليك أن تتذكر أن قضيته ستحظر الموصلات الثلاثة الأخرى (إنها صحية تمامًا ، لأنها تعمل كمبرد). أسهل طريقة للخروج هي توصيله عبر كابل USB.
من الجدير بالذكر أيضًا أن سرعة التنفيذ ستختلف باختلاف إصدارات USB (لهذه الشبكة العصبية المحددة: 102 مللي ثانية لـ USB 3.0 ، 92 مللي ثانية لـ USB 2.0).
الآن عن قوة NCS. وفقًا للوثائق ، تستهلك ما يصل إلى 1 واط (عند 5 فولت على موصل USB ، ستصل إلى 200 مللي أمبير ؛ للمقارنة: تستهلك كاميرا Raspberry حتى 250 مللي أمبير). عندما يتم تشغيله بواسطة شاحن عادي بجهد 5 فولت ، 2 أمبير ، كل شيء يعمل بشكل رائع. ومع ذلك ، فإن محاولة توصيل اثنين أو أكثر من NCSs بتوت العليق يمكن أن يسبب مشاكل. في هذه الحالة ، يوصى باستخدام مقسم USB مع إمكانية الطاقة الخارجية.
على Raspberry ، الإصدار التجريبي أبطأ من جهاز الكمبيوتر / الكمبيوتر المحمول: 7.2 FPS مقابل 10.4 FPS. ويرجع ذلك إلى عدة عوامل: أولاً ، من المستحيل التخلص من الحسابات على وحدة المعالجة المركزية ، ولكن يتم تنفيذها بشكل أبطأ بكثير ؛ ثانيًا ، تؤثر سرعة نقل البيانات (على USB 2.0).
للمقارنة ، حاولت تشغيل كاشف للوجه على Raspberry YOLOv2 من مقالتي الأولى ، لكنه عمل بشكل سيء للغاية: بسرعة 3.6 FPS ، يفتقد الكثير من الوجوه حتى على إطارات بسيطة. من الواضح أنها حساسة جدًا لمعلمات الصورة المدخلة ، التي تكون جودتها في حالة كاميرا Raspberry بعيدة عن المثالية. يعمل SSD بشكل أكثر استقرارًا ، على الرغم من أنني اضطررت إلى تعديل إعدادات الفيديو في إعدادات RapiCam قليلاً. كما أنه يفتقد أحيانًا الوجوه في الإطار ، ولكنه نادرًا ما يفعل ذلك. لزيادة الثبات في التطبيقات الحقيقية ، يمكنك إضافة
متعقب بسيط
للنقرطة .
بالمناسبة: نفس الشيء يمكن استنساخه في Python ، هناك برنامج
تعليمي حول PyImageSearch (Mobilenet-SSD يستخدم لمهمة الكشف عن الكائنات).
أفكار أخرى
لقد اختبرت أيضًا فكرتين لتسريع الشبكة العصبية نفسها:
الفكرة الأولى: يمكنك فقط ترك الكشف عن الطبقات
conv11
و
conv13
، وإزالة جميع الطبقات الإضافية. ستحصل على كاشف يكتشف الوجوه الصغيرة فقط ويعمل بشكل أسرع قليلاً. الكل في الكل ، لا يستحق كل هذا العناء.
كانت الفكرة الثانية مثيرة للاهتمام ، لكنها لم تنجح: حاولت رمي التواءات من الشبكة العصبية بأوزان قريبة من الصفر ، على أمل أن تصبح أسرع. ومع ذلك ، كان هناك عدد قليل من مثل هذه الالتواءات ، وأزال إزالتها فقط قليلاً الشبكة العصبية (الحدبة الوحيدة: هذا يرجع إلى حقيقة أن عدد القنوات قد توقف عن قوة قناتين).
الخلاصة
فكرت في الكشف عن الوجوه على Raspberry لفترة طويلة ، كمهمة فرعية لمشروعي الروبوتي. لم يعجبني الكاشفات الكلاسيكية من حيث السرعة والجودة ، وقررت تجربة طرق الشبكة العصبية ، وفي نفس الوقت اختبار عصا الحساب العصبي ، ونتيجة لذلك كان هناك مشروعان على GitHub وثلاث مقالات عن حبري (بما في ذلك الحالي). بشكل عام ، النتيجة تناسبني - على الأرجح ، سأستخدم هذا الكاشف في الروبوت الخاص بي (ربما سيكون هناك مقال آخر حوله). من الجدير بالذكر أن الحل الذي قدمته قد لا يكون الحل الأمثل - ومع ذلك ، فهذا مشروع تدريبي ، تم إجراؤه جزئيًا بدافع الفضول لـ NCS. ومع ذلك ، آمل أن تكون هذه المقالة مفيدة لشخص ما.