پست وبلاگ

معماری MobileNet

معماری MobileNet

بسم الله الرحمن الرحیم

در این پست قصد داریم در مورد معماری موبایل نت صحبت کنیم و ببینیم ایده اصلی این مقاله در چه چیزی هست. در کارگاه مطالبی مطرح شد اما بعلت ذیق وقت فرصت نشد کامل در این رابطه صحبت بشه . در این پست قصد داریم یکبار دیگه مطالب مطرح شده حول ایده اصلی رو مرور کنیم و به درک بهتری برسیم. بحث اصلی حول ایده Depth-wise separable convolution میگرده که در ادامه به اون میپردازیم.

داستان چیه؟

بحث اصلی در اینجا کاهش سربار پردازشی هست. برای کاهش سربار پردازشی روشهای مختلفی رو دیدیم که از استفاده از باتل نک ها و یا انواع مختلف فاکتوریزیشن ها (متقارن و نامتقارن) میشه نام برد.

علاوه بر اینها دوتکنیک هم بوده که باید درموردشون صحبت کنیم . یکی از اون ها ایده group convolution هست که در الکس نت مشاهده کردیم (اونجا بیشتر بحث پیاده سازی دخیل بوده) و تکنیک دیگر هم Depth-wise separable convolution هست که در مقاله موبایل نت بهش پرداخته شد.

حالا اجازه بدید این دوتا بحث رو باز کنیم . ما با بحث Depth-wise separable convolution شروع میکنیم و توضیح میدیم که چی هست و چطور باعث کاهش سربار میشه. در اخر هم ارتباطش رو با Group Convolution بیان میکنیم.

قبل از اینکه به بحث Depth-wise separable convolution بپردازیم اول اجازه بدید با هم دیگه چک کنیم یک عملیات کانولوشن معمولی چه خصوصیاتی داره. چقدر سربار پردازشی به شبکه اعمال میکنه تا اینطور بتونیم با تکنیک های بالا قیاس کنیم و بطور عینی ببینیم چه اتفاقی دقیقا رخ میده و میزان کاهش سربار چقدر هست.

مثل مثالی که در کارگاه زدیم اینجا هم سعی میکنیم همون رو باز کنیم و توضیح بدیم.

فرض میکنیم که یک توده ورودی داریم با اندازه 28x28x192  و همینطور ۳۲ کرنل ۵×۵ که در نهایت به ما ۳۲ فیچرمپ با اندازه ۲۸در۲۸ قراره ارائه کنن. بصورت زیر :

در این حالت میزان پارامترها و عملیات اعشاری بصورت زیر خواهد بود :

یعنی همین یک عملیات ۱۵۳ هزار پارامتر و بیش از ۱۲۰ میلیون عملیات ضرب اعشاری رو در برخواهد داشت. در اینجا میبینیم که تعداد فیچرمپ های ورودی ،اندازه کرنل و همینطور تعداد فیچرمپ های خروجی تاثیر مستقیم بر روی نتایج حاصل دارن و اگر این اعداد بزرگ باشن سربار تا حد زیادی افزایش پیدا میکنه.

حالا در ایده depth-wise separable convolution این ایده مطرح میشه که بجای اینکه در یک عملیات کانولوشن معمولی که هر فیلتر روی تمامی فیچرمپهای ورودی اعمال میشه و بعد نتایج همه با هم ترکیب میشه و نهایتا یک خروجی ارائه میشه، ما بیاییم این یک مرحله رو به دو مرحله تبدیل کنیم. یعنی بعد از اینکه یک فیلتر روی فیچرمپی اعمال شد دیگه بلافاصله ترکیبش نکنیم. بیاییم در گام دوم توسط یک لایه کانولوشن ۱در۱ عمل ترکیب فیچرمپها با هم رو انجام بدیم .

یعنی من بیام یک فیلتر رو روی تمامی فیچرمپهای ورودی اعمال کنم اما نتایج رو ترکیب نکنم ؟ برای ترکیب بیام از یک فیلتر ۱در۱ استفاده کنم؟

جواب بله هست. اما یک نکته ای اینجا وجود داره. اگه همینطوری بخواییم پیش بریم، یعنی یک فیلتر روی تک تک فیچرمپهای ورودی اعمال بشه و بعد دوباره یک فیلتر ۱در۱ برای ترکیب استفاده بشه یک مشکلی بوجود میاد. چه مشکلی؟ بزارید بااهم حساب کنیم تا مشکل رو با هم ببینیم :

درحالت عادی هر فیلتر ما در تمامی فیچرمپهای ورودی ضرب میشد یعنی 5x5x192  و حالا وقتی n تا فیلتر داشته باشیم این عمل n بار تکرار میشه یعنی 5x5x192xn . حالا اگر n = 32 باشه میشه 5x5x192x32 . تا اینجا شده مثل حالت قبل. با این تفاوت که ما الان 32x192  فیجرمپ داریم که باهم ترکیب نکردیم. حالا اگر بخواییم از یک فیلتر ۱در۱ برای ترکیب حاصل عملیات کانولوشن مرحله قبل استفاده کنیم میشه 1x1x32x192xn  که n تعداد فیلترهای ۱در۱ ما هست (همون تعداد فیچرمپ خروجی ما که باید مساوی ۳۲ باشه تا با حالت دیفالت قیاس کنیم) که میشه 1x1x32x192x32  برابر میشه با 196,608  ! که حالا اگر این تعداد پارامتر رو با پارامترهای عملیات قبل جمع کنیم میبینیم به عدد 350,208  میرسیم! یعنی تعداد پارامتر در این حالت نسبت به حالت پیشفرض افزایش پیدا کرد؟!!!

خب این اصلا خوب نیست.این که برعکس بدتر شد!؟ قرار ما بر این بود که سربار رو کاهش بدیم اما depth-wise separable  که افزایش داد!

ما یک بحث دیگه ای در depth-wise separable convolution داریم که ازش بنام depth multiplier یا همون ضریب  عمق یاد میکنیم. یعنی چی؟ ضریب عمق یعنی هر فیلتر روی چند فیچرمپ ورودی خودش میتونه اعمال بشه. در حالت دیفالت در عملیات کانولوشن معمولی این مقدار برابر با تعداد فیچرمپ ورودی هست ولی چون ترکیب میشه دیگه خبری از عملیات دوم نیست که باعث افزایش تعداد پارامتر ها بشه و در نتیجه ما با همون 153,600  پارامتر مواجه میشیم. حالا اگه ما کمی به این ضریب عمق توجه کنیم میبینیم براحتی میتونیم با استفاده از اون به یک توازنی بین سربار پردازشی و خصوصیات مثبت حالت دیفالت برسیم. اجازه بدید اینو بازش کنیم. اگه ما ضریب عمق رو برابر با ۱ قرار بدیم چه اتفاقی می افته؟ یعنی هر فیلتر تنها روی یک فیچرمپ ورودی باید اعمال بشه. در اینجا اگه تعداد فیچرمپها برابر با k باشه تعداد k فیلتر هم نیاز خواهد بود. بزارید حساب کنیم باهم. هر فیلتر ۵در۵ در یک فیچرمپ اعمال بشه. یعنی ۱۹۲ تا فیلتر خواهیم داشت . پس تعداد پارامترها بصورت زیر در میاد:

حالا ما ۱۹۲ تا فیچرمپ جدید داریم که فیلتر روشون اعمال شده. حالا بیایید با ۳۲ تا فیلتر ۱در۱ این ۱۹۲ تا فیچرمپ رو با هم ترکیب کنیم. که میشه

که باز یعنی یک فیلتر ۱در۱ در تمامی ۱۹۲ فیچرمپ ورودی اعمال میشه و چون ۳۲ تا فیلتر داریم ۳۲ بار این کار تکرار میشه. حالا اگه این دو عدد رو جمع کنیم میبینیم

یعنی تنها تقریبا ۱۱ هزار پارامتر داریم و این حالت رو با حالت دیفالت مقایسه بکنید! میبینید از 153,600 پارامتر رسیدیم به ۱۰ ۱۱ هزار پارامتر! و این دقیقا حالتی هست که در موبایل نت مورد استفاده قرار گرفت. یعنی در موبایل نت ما فرض ما استفاده از depth multiplier = 1 هست.

اما یک سوال آیا اینجا واقعا داره یک کار صورت میگیره ؟ یعنی اینها معادل هم هستن ؟ آیا این کاری که کردیم معادل واقعا اون چیزی هست که قبلا اتفاق می افتاد؟ بزارید مثال بزنیم
آیا (5x5x192)32x  (یعنی هر فیلتر ۵در۵ در تمام ۱۹۲ فیچرمپ اعمال بشه ، و چون ۳۲ تا از این فیلترها داریم ۳۲ بار این کار تکرار بشه )
واقعا مساوی این چیزی هست که ما اینجا صحبتش رو کردیم ؟ یعنی ما اگه بیاییم بنویسیم 5x5x192 یعنی ۱۹۲ تا فیلتر ۵در۵ (یعنی هر فیلتر دریک کانال اعمال شده)
و بعد بنویسیم (1x1x192)32x یعنی فیلتر ۱در۱ درتمام ورودی ضرب بشه و چون ۳۲ تا از این فیلترها داریم این کار ۳۲ بار تکرار بشه .
آیا واقعا اینا یک کارو دارن انجام میدن ؟

وقتی به فرمت ضربها نگاه میکنیم . میگیم خب بنظر اینطور میاد بجای اینکه ضرب رو در اندازه فیلتر بزرگتر کنیم رفتیم روی اندازه فیلتر ۱در۱ کردیم و البته اونجا هم همه کانالهای ورودی رو لحاظ کردیم . پس فرقی نباید داشته باشن. اما واقعا اینطور نیست. واقعا اینها یک کار رو انجام نمیدن . در مورد اول هر فیلتر بطور جداگانه روی تمام کانالها اعمال میشه و اون بحث همسایگی لحاظ میشه و اگر ارتباطی وجود داشته باشه شکل میگیره . اما در حالت دوم اینطور نیست. اینجا ما ترکیب انواع مختلف فیلترها رو داریم .در حالت اول ما رپرزنتشن حاصلمون ترکیب اعمال فیلتر روی تمامی کانالها بود یعنی دید ما روی تمام توده ورودی از منظر فیلتر ما بود. اما اینجا در حالت دوم ما رپرزنتشن حاصلمون از ترکیب دیدهای مختلف روی بخشهای مختلف توده ورودی هست.

خب چیکار کنیم؟ اینجا اهمیت اون depth multiplier مشخص میشه و ما میتونیم با تنظیم اون اصطلاحا به یک بالانس و یا توازنی در این بین برسیم یعنی سعی کنیم تا حدی خصوصیات و نکات مثبت در عملیات کانولوشن معمولی رو داشته باشیم و از طرفی بتونیم سربار رو کاهش بدیم. بعنوان مثال در همین مثال بالا با قرار دادن depth multiplier برابر عددی مثل ۴ ما به  تعداد پارامتری برابر با ۴۳,۷۷۶ میرسیم که هرچند نسبت به تعداد ۱۰,۹۴۴ ۴ برابر افزایش پیدا کرده اما هنوز نسبت به تعداد ۱۵۳,۶۰۰ حالت دیفالت ما خیلی کمتره و در این حالت هر فیلتر دیدی نسبت به چند فیچرمپ ورودی داره برخلاف حالت ساده depth multiplier = 1 . به همین شکل میشه توازنی رو از این طریق اعمال کرد.

بحث Group Convolution هم به همین صورته با این تفاوت که در گروپ کانولوشن ایده اینه که هر کرنل تنها به تعداد از فیچرمپهای ورودی نگاه کنه (depth multiplier = k) و اینکه نتایج مثل حالت معمولی با هم ترکیب میشن دیگه اینطور نیست که مثل depth-wise separable convolution توسط یک لایه جداگانه ترکیب بشن.

پس بطور خلاصه حالا اگه بخواییم اصطلاحا این گپ بین دو حالت (استاندارد و depth-wise ) رو کاهش بدیم چیکار میتونیم بکنیم ؟
میشه بجای اینکه هر فیلتر به یک کانال نگاه بکنه به مجموعه ای از این کانالها نگاه کنه و بعد ادامه رو بریم اینجا هم باز دو راه پیش میاد
۱٫ تعمیم این روش به روشی که موبایل نت استفاده کرد یا بعبارت دیگه استفاده از depth multiplier یی بزرگتر از ۱
۲٫عدم استفاده از کانولوشن ۱در۱ در انتها و صرفا استفاده از بحث group convolution (یعنی هر کرنل رو محدود به چند کانال ورودی کنیم نه همه )

گروپ کانولوشن در الکس نت مطرح شد سال ۲۰۱۲ اولین بار و چون بحث اونجا مربوط به پیاده سازی بود، بعد از اینکه امکان پیاده سازی معماری الکس نت بصورت یکپارچه و در یک کارت گرافیک مهیا شد دیگه کسی بدنبال چک کردن ویژگی های مربتط با جداسازی عملیات کانولوشن به صورتی که صحبتش رفت تا مدتها نرفت. تا اینکه بعد از چند سال چندتا ازمحققا شروع به تست در حوزه فاکتوریزیشن کردن و این هم یکی از مباحث بود که نتیجه اش رو الان شما مشاهده میکنید.

ناگفته نماند که کماکان پیاده سازی این مساله در کفی بشدت کند و غیربهینه است . در سایر فریم ورکها هم چندان بهینه سازی نشده اما از کفی بنظر میاد بهتر باشن.

اینها بحثهایی بود که باید مطرح میشد ولی متاسفانه مطرح نشد. امیدوارم با توضیحات بالا عزیزانی که مشکل داشتن مشکلشون کامل برطرف شده باشه. انشاءالله سایر مطالب هم در ادمه سعی میشه توضیحات تکمیلی (اگر نیاز باشه) در قالب پستهای جداگانه ارائه بشه. در مطلب بعدی به بحث فاکتوریزیشن ها میپردازیم که در مقاله سوم گوگل نت بحثش مطرح شد.

اگر مشکل یا مساله ای هست لطفا در کامنت بفرمایید

 

سید حسین حسن پور متی کلایی

درباره ی سید حسین حسن پور متی کلایی

موسس و مدیر سایت. اطلاعات در مورد فعالیت های کاری و تحصیلی : linkedIn . برای ارتباط از بخش تماس با ما یا در باره من استفاده کنید.

مقالات مرتبط

6 دیدگاه در “معماری MobileNet

    1. سلام
      البته اینجا من معماری موبایل نت رو کامل توضیح ندادم یه بحث اصلیش رو که تو ارائه ناقص عنوان شد و خوب بهش نپرداختیم(سریع رد شدیم) رو اینجا باز کردم و توضیح کامل سعی کردم بدم
      تو موبایل نت دو بحث ضریب عرض و ضریب رزولوشن هم داریم که برای کاستمایز کردن شبکه ارائه شده و خوندنش در مقاله اصلا سخت نیست برای همین من دیگه اونو عنوان نکردم.
      دنزنت هم چیز خاصی نداره ولی باشه سعی میکنم در اسرع وقت یه مطلب در مورد اون هم بگم ولی قبلش باید بحث فاکتوریزیشن رو باز کنم که این هم خیلی نکات مختلف داره .پس اول سعی من اینه فرصتی پیش اومد بحث فاکتوریزیشن و نکات اصلی در مقاله اینسپشن ۳ به بعد رو باز کنم بعدش بریم سراغ مابقی موارد.

  1. ممنون
    مهندس در بعضی معماری ها مثله squezeNet میبینی دو تا لایه کانالوشن گذاشته بصورت موازی البته نه تمامی لایه ها اینجوری باشه ها مثله الکس نت ، فقط بعضی لایه ها بصورت موازی هستند آیا این دلیل خاصی داره ؟ نمیشه اون دو تا لایه موازی رو ادغام کرد بشه یک لایه البته با فیلتر های جمع فیلترهای اون دوتا

    1. سلام اون دوتا لایه موازی که صحبت میکنید تو مقاله بهش میگن firemodule و در هر بلاک فایر ماجول داره دوتا ترنسفورمیشن اتفاق می افته
      یعنی بنوعی اینا سعی کردن همون چیزی که در گوگل نت۱ انیسپشن بلاک شما دیدی اینجا بصورت ساده ترش لحاظ کنن. با اینکار لایه بعدی دوتا دید نسبت به داده قبل از خودش داره و ایده اینه که شبکه مثلا هرکدوم از این دیدها براش بهتر باشه میتونه حالا ازش بهره ببره.
      در الکس نت ایده و بحث کلا یک چیز دیگه بوده اونجا بیشتر بحث پیاده سازی بوده و باز اونجا از بحث گروپ کانولوشن سعی کردن استفاده کنن. اینجا داستان اون چیزی هست که بالا گفتیم. ارائه ترنسفورمیشن های مختلف به یک لایه بالایی. قبلش هم برای کاهش سربار پردازشی از یک فیلتر ۱در۱ استفاده کردن که خوب دقت کنید میبینید ایده همون ایده سابق هست حالا محدودتر شده اینجا.
      اگه اون لایه های موازی ادغام بشن (یعنی پشت سر هم باشن؟ یا اینکه نه موازی باشن ولی بجای کانکت شدن جمع بشن نتایج؟ مورد اول که بالا توضیح دادیم ایده اشون چی بوده. اگه پشت سر هم بزارن دیگه میشه یه مسیر خطی و لایه بالایی فقط یک دید رو خواهد داشت از لایه زیرین . اگه مورد دوم باشه برای تجمیع فیچرها بهتره از جمع استفاده نکنید چون باعث خراب کردن اونها میشه و نسبت به زمانی که فیچرها رو کانکت کنید بدتره.

پاسخ دهید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *