ResNet50. التنفيذ الخاص

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

تحت الخفض ، مقارنة مع التنفيذ الأصلي ل TF ، تطبيق تجريبي للتعرف على الصور ، حسنا ... الاستنتاجات. من يهتم ، من فضلك.

يمكنك معرفة كيفية عمل ResNet ، على سبيل المثال ، هنا .

فيما يلي بنية الشبكة بالأرقام:



تبين أن الكود ليس أكثر بساطة ولا أكثر تعقيدًا من الثعبان.

رمز C ++ لإنشاء شبكة:
auto net = sn::Net(); net.addNode("In", sn::Input(), "conv1") .addNode("conv1", sn::Convolution(64, 7, 3, 2, sn::batchNormType::beforeActive, sn::active::none, mode), "pool1_pad") .addNode("pool1_pad", sn::Pooling(3, 2, sn::poolType::max, mode), "res2a_branch1 res2a_branch2a"); convBlock(net, vector<uint32_t>{ 64, 64, 256 }, 3, 1, "res2a_branch", "res2b_branch2a res2b_branchSum", mode); idntBlock(net, vector<uint32_t>{ 64, 64, 256 }, 3, "res2b_branch", "res2c_branch2a res2c_branchSum", mode); idntBlock(net, vector<uint32_t>{ 64, 64, 256}, 3, "res2c_branch", "res3a_branch1 res3a_branch2a", mode); convBlock(net, vector<uint32_t>{ 128, 128, 512 }, 3, 2, "res3a_branch", "res3b_branch2a res3b_branchSum", mode); idntBlock(net, vector<uint32_t>{ 128, 128, 512 }, 3, "res3b_branch", "res3c_branch2a res3c_branchSum", mode); idntBlock(net, vector<uint32_t>{ 128, 128, 512 }, 3, "res3c_branch", "res3d_branch2a res3d_branchSum", mode); idntBlock(net, vector<uint32_t>{ 128, 128, 512 }, 3, "res3d_branch", "res4a_branch1 res4a_branch2a", mode); convBlock(net, vector<uint32_t>{ 256, 256, 1024 }, 3, 2, "res4a_branch", "res4b_branch2a res4b_branchSum", mode); idntBlock(net, vector<uint32_t>{ 256, 256, 1024 }, 3, "res4b_branch", "res4c_branch2a res4c_branchSum", mode); idntBlock(net, vector<uint32_t>{ 256, 256, 1024 }, 3, "res4c_branch", "res4d_branch2a res4d_branchSum", mode); idntBlock(net, vector<uint32_t>{ 256, 256, 1024 }, 3, "res4d_branch", "res4e_branch2a res4e_branchSum", mode); idntBlock(net, vector<uint32_t>{ 256, 256, 1024 }, 3, "res4e_branch", "res4f_branch2a res4f_branchSum", mode); idntBlock(net, vector<uint32_t>{ 256, 256, 1024 }, 3, "res4f_branch", "res5a_branch1 res5a_branch2a", mode); convBlock(net, vector<uint32_t>{ 512, 512, 2048 }, 3, 2, "res5a_branch", "res5b_branch2a res5b_branchSum", mode); idntBlock(net, vector<uint32_t>{ 512, 512, 2048 }, 3, "res5b_branch", "res5c_branch2a res5c_branchSum", mode); idntBlock(net, vector<uint32_t>{ 512, 512, 2048 }, 3, "res5c_branch", "avg_pool", mode); net.addNode("avg_pool", sn::Pooling(7, 7, sn::poolType::avg, mode), "fc1000") .addNode("fc1000", sn::FullyConnected(1000, sn::active::none, mode), "LS") .addNode("LS", sn::LossFunction(sn::lossType::softMaxToCrossEntropy), "Output"); 


→ الرمز الكامل متاح هنا

يمكنك القيام بذلك بشكل أسهل ، قم بتحميل بنية الشبكة والأوزان من الملفات ،

مثل هذا:
  string archPath = "c:/cpp/other/skyNet/example/resnet50/resNet50Struct.json", weightPath = "c:/cpp/other/skyNet/example/resnet50/resNet50Weights.dat"; std::ifstream ifs; ifs.open(archPath, std::ifstream::in); if (!ifs.good()){ cout << "error open file : " + archPath << endl; system("pause"); return false; } ifs.seekg(0, ifs.end); size_t length = ifs.tellg(); ifs.seekg(0, ifs.beg); string jnArch; jnArch.resize(length); ifs.read((char*)jnArch.data(), length); // Create net sn::Net snet(jnArch, weightPath); 


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

تم إنشاء التطبيق فقط للمقالة ، ولن يتم دعمه ، وبالتالي لم يتم تضمينه في مستودع المشروع.



الآن ، ما حدث مقارنة TF.

مؤشرات بعد المدى من 100 صورة ، في المتوسط. الجهاز: i5-2400 ، GF1050 ، Win7 ، MSVC12.

تتطابق قيم نتائج التعرف مع الحرف الثالث.

رمز الاختبار
وحدة المعالجة المركزية: الوقت / img ، ملليوحدة معالجة الرسومات: الوقت / img ، مللي ثانيةوحدة المعالجة المركزية: RAM ، ميغابايتGPU: RAM ، ميغابايت
سكاي نت4101206001200
Tensorflow250254001400


في الواقع ، كل شيء محزن بالطبع.

بالنسبة لوحدة المعالجة المركزية (CPU) ، قررت عدم استخدام MKL-DNN ، فكرت في الانتهاء منه: إعادة توزيع الذاكرة للقراءة التسلسلية ، وتحميل سجلات المتجهات إلى الحد الأقصى. ربما كان من الضروري أن تؤدي إلى ضرب المصفوفة ، و / أو بعض الخارقة الأخرى. استراح هنا ، في البداية كان الأمر أسوأ ، سيكون من الأصح استخدام MKL بنفس الطريقة.

على وحدة معالجة الرسومات ، يتم قضاء الوقت في نسخ الذاكرة من / إلى ذاكرة بطاقة الفيديو ، ولا يتم تنفيذ جميع العمليات على وحدة معالجة الرسومات.

ما هي الاستنتاجات التي يمكن استخلاصها من كل هذه الضجة:

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

- لا تحاول استخدام واجهة C الأصلية لأطر ML. واستخدمها باللغة التي ركز عليها المطورون ، أي بيثون.

طريقة سهلة لاستخدام وظيفة ML من لغتك هي إجراء عملية خدمة على python وإرسال الصور إليها على المقبس ، وتحصل على تقسيم المسؤولية وعدم وجود كود ثقيل.

ربما كل شيء. كانت المقالة قصيرة ، لكن الاستنتاجات ، كما أعتقد ، لها قيمة ، ولا تنطبق على ML فقط.

شكرا لك

PS:
إذا كان لدى أي شخص الرغبة والقوة في محاولة اللحاق TF ، فمرحبا !)

PS2:
خفضت يديه في وقت مبكر. أخذ استراحة الدخان ، وأخذها مرة أخرى ، وعمل كل شيء.
بالنسبة لوحدة المعالجة المركزية ، ساعد الضرب على مصفوفة الضرب ، كما اعتقدت.
بالنسبة إلى وحدة معالجة الرسومات (GPU) ، قمت باختيار جميع العمليات في ليب منفصل ، بحيث دون النسخ إلى وحدة المعالجة المركزية والعكس بالعكس ، كان الطرح الوحيد لهذا النهج هو أنني اضطررت إلى إعادة كتابة (تكرار) جميع المشغلين ، على الرغم من أن بعض الأشياء تزامنت ، لكنني لم أقم بتوصيلها.
بشكل عام ، إليك كيف الآن:
وحدة المعالجة المركزية: الوقت / img ، ملليوحدة معالجة الرسومات: الوقت / img ، مللي ثانيةوحدة المعالجة المركزية: RAM ، ميغابايتGPU: RAM ، ميغابايت
سكاي نت19515600800
Tensorflow250254001400

وهذا هو ، على الأقل تحولت الاستنتاج حتى أسرع من TF.
لم يتغير رمز الاختبار .

Source: https://habr.com/ru/post/ar449864/


All Articles