در این پست قصد داریم به بررسی و پیاده‌سازی مقاله GhostNet بپردازیم.

در سال ۲۰۱۹ محققانی از شرکت هواوی مقاله‌ای تحت عنوان GhostNet: More Features from Cheap Operations ارائه دادند که در آن به ایده‌ای جالب جهت کاهش اندازه شبکه‌های عصبی عمیق پرداخته شده بود. آن‌ها به این نکته اشاره می‌کنند که افزونگی در نقشه‌های ویژگی شبکه‌های کانولوشنی، یکی از ویژگی های مهم CNN های موفق است. اما آن‌ها به جای استفاده از نورون‌ها و لایه‌های بیشتر، از عملیات‌های کم هزینه (مجموعه‌ای از تبدیلات خطی) برای استخراج نقشه‌های ویژگی بیشتر استفاده می‌کنند. به ماژول‌های مورد استفاده برای این کار ماژول‌های روح (Ghost) گفته می‌شود.

در شکل ۱ برخی نقشه‌های ویژگی تولید شده توسط اولین گروه residual از شبکه ResNet-50 را می‌بینید. سه مجموعه نقشه ویژگی که با رنگ‌های قرمز، آبی و سبز مشخص شده‌اند، یک جفت تقریبا مشابه در نقشه‌های ویژگی دارند که می‌توان آن‌ها را با مجموعه‌ای از تبدیلات خطی کم هزینه به یکدیگر تبدیل کرد. این موضوع با آچار در شکل نشان داده شده است.

Redundancy in feature maps
شکل ۱: تعدادی از نقشه‌های ویژگی تولید شده توسط اولین گروه residual از شبکه ResNet-50. نقشه‌های ویژگی با کادر همرنگ را می‌توان با کمک تبدیلات خطی کم هزینه به یکدیگر تبدیل کرد.

در واقع ایده اصلی GhostNet همین مورد است که می‌توان از روی یک نقشه ویژگی، نقشه ویژگی دیگری را با استفاده از تبدیلات خطی ساده بدست آورد بدون اینکه نیاز به پیچیدگی بیشتری در شبکه باشد. بنابراین می‌توان در ابتدا تعداد بسیار کمتری نقشه ویژگی تولید کرد و سپس باقی نقشه‌های ویژگی را به کمک تبدیلات خطی کم هزینه بدست آورد.

ماژول روح (Ghost Module)

همانطور که در شکل ۱ مشاهده میشود، نقشه‌های ویژگی خروجی لایه‌های کانولوشنی اغلب حاوی افزونگی زیادی هستند و برخی از آن‌ها می‌توانند مشابه یکدیگر باشند. تولید این نقشه‌های ویژگی اضافی به صورت یکی یکی با تعداد زیادی FLOP و پارامتر، غیر ضروری است. فرض کنید که نقشه‌های ویژگی خروجی «ارواح» تعداد انگشت شماری از نقشه‌های ویژگی اصلی با برخی تبدیلات کم هزینه هستند. این نقشه‌های ویژگی اصلی اغلب تعداد کمتری دارند و توسط فیلترهای کانولوشنی معمولی تولید می‌شوند. این نقشه‌های ویژگی اصلی در شکل ۲ بخش b با طیف رنگ نارنجی نشان داده شده است.

Ghost module
شکل ۲: (a) تصویری از لایه کانولوشنی معمول و (b) ماژول روح برای خروجی همان تعداد نقشه ویژگی. Φ نشان دهنده عملیات کم هزینه است.

همان‌طور که در شکل ۲ بخش b پیداست در ماژول روح ابتدا نقشه‌های ویژگی اصلی توسط فیلترهای کانولوشنی معمول استخراج می‌شوند و سپس در مرحله بعد به تعداد k نقشه ویژگی افزونه از روی این نقشه‌های ویژگی اصلی با کمک تبدیلات خطی (Φ1 تا Φk) بدست می‌آید. همچنین نقشه‌های ویژگی اصلی نیز توسط یک تابع همانی به خروجی انتقال می‌یابند. بنابراین با m نقشه ویژگی اصلی می‌توان به m + k نقشه ویژگی نهایی رسید.

گلوگاه روح (Ghost Bottleneck)

ساختار Ghost Bottleneck در شکل ۳ نشان داده شده است. همانطور که مشاهده می‌شود ساختار Ghost Bottleneck شبیه ساختار پایه بلوک‌ها در ResNet می‌باشد که از لایه‌های کانولوشنی و اتصالات میانبر (shortcuts) تشکل شده است.

Ghost bottleneck
شکل ۳: ساختار Ghost Bottleneck

ساختار Ghost Bottleneck ارائه شده شامل دو ماژول روح پشت سر هم می‌باشد. ماژول روح اول به عنوان یک لایه انبساط (expansion) عمل می‌کند که تعداد کانال‌ها را افزایش می‌دهد. به نسبط بین تعداد کانال‌های خروجی به تعداد کانال‌های ورودی نسبط انبساط (expansion ratio) گفته می‌شود. ماژول روح دوم تعداد کانال‌ها را کاهش می‌دهد تا با مسیر میانبر همخوانی داشته باشد. اتصال میانبر بین ورودی ماژول روح اول و خروجی ماژول روح دوم برقرار می‌شود. بعد از ماژول اول batch normalization و ReLU استفاده می‌شود اما پس از ماژول دوم تنها از batch normalization استفاده می‌شود.

ساختار توضیح داده شده برای موقعی بود که گام (stride) برابر با ۱ می‌باشد. برای مواقعی که stride=2 است مسیر میانبر با یک لایه downsampe پیاده‌سازی می‌شود و یک لایه کانولوشن عمقی (depthwise) با گام ۲ بین دو ماژول روح قرار می‌گیرد.

معماری GhostNet

با توجه به اینکه MobileNetV3 (در زمان ارائه GhostNet) یکی از بهترین معماری‌های سبک وزن موجود بوده است، نویسندگان GhostNet معماری شبکه خود را بر اساس آن طراحی کرده و Ghost bottleneck را جایگزین bottleneck های معمول موجود در MobileNetV3 کرده‌اند. معماری بدست آمده در جدول ۱ به نمایش در آمده است.

GhostNet architecture
جدول ۱: معماری GhostNet. نماد #exp نشان‌دهنده میزان انبساط (expansion) است. #out نشان‌دهنده تعداد کانال‌های خروجی است. SE نشان ‌می‌دهد آیا از ماژول SE استفاده شده است یا خیر. G-bneck به معنای Ghost bottleneck می‌باشد.

معماری GhostNet عمدتاً از لایه‌های Ghost bottleneck با ماژول‌های روح به عنوان بلوک سازنده تشکیل شده است. با توجه به جدول ۱، لایه اول یک لایه کانولوشنی استاندارد با ۱۶ فیلتر است، سپس یک سری گلوگاه‌های روح با کانال‌های به تدریج افزایش یافته قرار می‌گیرند. این گلوگاه‌های روح با توجه به اندازه نقشه‌های ویژگی ورودی آن‌ها به مراحل (stages) مختلف گروه‌بندی می‌شوند. گام برای تمامی گلوگاه‌های روح برابر ۱ در نظر گرفته می‌شود به جز آخرین مورد در هر مرحله (stage) که برای آن stride=2 است. در نهایت از یک average pooling سراسری و یک لایه کانولوشنی برای تبدیل نقشه‌های ویژگی به یک بردار ویژگی ۱۲۸۰ بعدی برای دسته‌بندی نهایی استفاده می‌شود. ماژول فشار و برانگیختگی (SE) نیز طبق جدول ۱ بر روی اتصالات میانبر در بعضی گلوگاه‌های روح اعمال می‌شود.

پیاده‌سازی

پیاده‌سازی آماده GhostNet به همراه وزن‌های از پیش آموزش داده شده در کتابخانه timm موجود است. برای استفاده از این مدل آماده و از پیش آموزش داده شده تنها نیاز است تا از دستورات زیر استفاده نمایید.

به کمک دستورات فوق مدل GhostNet لود شده و بر روی یک تصویر دلخواه دانلود شده تست می‌شود. البته برای ورودی دادن تصویر به مدل نیاز به پیش‌پردازش تصویر می‌باشد که این کار توسط تابع preprocess انجام می‌شود. در نهایت برچسب پیش‌بینی شده توسط مدل برای تصویر ورودی، چاپ می‌شود.

پیاده‌سازی از پایه

در این قسمت قصد داریم مدل GhostNet را با استفاده از پای‌تورچ از پایه پیاده‌سازی کرده و آن را بر روی دیتاست Oxford-IIIT Pet آموزش دهیم. ما برای این کار از کدهای رسمی مدل که توسط نویسندگان مقاله در این ریپازیتوری گیت‌هاب ارائه شده است استفاده می‌کنیم. ابتدا به کل کدهای ارائه شده برای ساخت مدل توجه کنید.

کدهای فوق از تعدادی تابع و کلاس برای ساخت مدل GhostNet تشکیل شده است. تابع _make_divisible برای اطمینان از تقسیم‌پذیر بودن تعداد کانال‌های هر لایه بر ۸ به کار می‌رود. تابع hard_sigmoid برای پیاده‌سازی تابع فعال‌ساز Hard sigmoid استفاده می‌شود. کلاس SqueezeExcite برای پیاده‌سازی ماژول فشار و برانگیختگی (SE) استفاده می‌شود. کلاس ConvBnAct شامل یک لایه کانولوشن، یک لایه batch normalization و یک لایه فعال‌ساز (به طور پیشفرض ReLU) می‌باشد. از این کلاس تنها برای ساخت لایه آخر stage ششم مدل (ردیف ۱۸ ام در جدول ۱) استفاده شده است.

کلاس GhostModule به پیاده‌سازی ماژول روح می‌پردازد. پیاده‌سازی این کلاس طبق توضیحات داده شده برای ماژول روح در بخش بالا می‌باشد. البته باید به این نکته مهم اشاره کرد که cheap_operation یا عملیات کم هزینه در اینجا توسط کانولوشن عمقی (Depthwise convolution) انجام می‌شود. این یک تصمیم طراحی توسط نویسندگان مقاله بوده که در مقاله به طور صریح به آن اشاره نکرده‌اند و صرفا از لفظ تبدیلات خطی کم هزینه استفاده کرده‌اند، اما در کد پیاده‌سازی شده این کار توسط فیلترهای کانولوشنی عمقی انجام شده است.

پیاده‌سازی کلاس GhostBottleneck نیز طبق شکل ۳ انجام شده است. در نهایت نیز پیاده‌سازی مدل GhostNet توسط کلاس GhostNet و طبق جدول ۱ صورت می‌گیرد. مقداردهی پارامترهای لایه‌های مدل نیز توسط تابع ghostnet انجام می‌گیرد.

اکنون که با کدهای ساخت مدل آشنا شدیم به آموزش مدل با کمک کدهای زیر می‌پردازیم.

کدهای فوق شامل بخش‌های مختلف برای دانلود و پیش‌پردازش داده، تعریف تابع خطا و بهینه‌ساز و در نهایت آموزش مدل می‌باشد. همانطور که پیش‌تر نیز گفته شد، دیتاست مورد استفاده ما Oxford-IIIT Pet است که شامل ۳۷ کلاس کلی از انواع سگ و گربه می‌باشد. همچنین ما برای آموزش مدل از ۵۰ epoch به همراه Early stopping استفاده کردیم، که آموزش مدل ما پس از ۲۲ epoch متوقف شد و به دقت ۳۶ درضد روی داده آموزشی رسید. واضح است که این دقت کافی نیست و برای دستیابی به دقت بیشتر می‌توان از تکنیک‌های مختلفی مانند افزایش داده (Data augmentation)، تغییر پارامترها و غیره استفاده کرد که فرصت پرداختن به آن‌ها در اینجا نمی‌باشد.

در نهایت توسط کدهای زیر می‌توان پیش‌بینی مدل را بر روی یک تصویر دلخواه بررسی کرد.

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

شکل ۴: خروجی مدل آموزش داده شده بر رویک تصویر نمونه

در بالا قطعه کدهای لازم برای پیاده‌سازی مدل GhostNet از پایه و همچنین آموزش آن بر روی دیتاستی دلخواه آورده شد. شما می‌توانید کدهای لازم برای پیاده‌سازی، آموزش و ارزیابی GhostNet را به همراه وزن‌های از پیش آموزش داده شده از گیت‌هاب دانلود کنید. همچنین می‌توانید تمامی کدهای فوق به همراه نتیجه اجرای آن‌ها را در این نوت‌بوک کولب مشاهده نمایید.