نقش لایههای Fully Connected در شبکههای عصبی کانولوشنی (CNN)
لایههای Fully Connected (FC) یا Dense Layers یکی از مهمترین بخشهای شبکههای عصبی کانولوشنی (CNN) هستند. این لایهها معمولاً در انتهای شبکه قرار میگیرند و وظیفه نهایی آنها تصمیمگیری برای طبقهبندی یا تخمین خروجی (regression) است. اما نقش لایههای Fully Connected بسیار فراتر از یک تصمیمگیرنده ساده است. لایههای کانولوشن (Convolutional Layers) در مراحل ابتدایی شبکه، ویژگیهای محلی مانند لبهها، بافتها و الگوهای ساده را استخراج میکنند. با افزایش عمق شبکه، این ویژگیها پیچیدهتر میشوند، اما همچنان ماهیتی محلی دارند. اینجاست که لایههای Fully Connected وارد عمل میشوند. وظیفه اصلی این لایهها، ترکیب ویژگیهای محلی و ساخت یک نمایش سراسری (Global Representation) از داده است. به عبارت دیگر، لایههای Dense اطلاعات استخراجشده توسط فیلترهای مختلف را با یکدیگر ترکیب میکنند و ویژگیهای غنیتر و تفکیکپذیرتری میسازند. هرچه داده از چندین لایه Fully Connected عبور میکند، نمایش ویژگیها برای انجام تصمیمگیری نهایی مناسبتر میشود. به همین دلیل، آخرین لایه Fully Connected معمولاً وظیفه تولید خروجی نهایی شبکه را بر عهده دارد؛ اما تمام لایههای FC قبل از آن، در حال یادگیری و بهبود نمایش ویژگیها هستند. نکته مهم اینجاست که حذف لایههای Fully Connected معمولاً باعث کاهش قابل توجه ظرفیت یادگیری مدل میشود. در نتیجه، شبکه عصبی برای یادگیری روابط پیچیده به تعداد بیشتری لایه کانولوشن یا ساختارهای جایگزین نیاز خواهد داشت. به همین دلیل، در بسیاری از معماریهای کلاسیک مانند LeNet-5، AlexNet و VGG، لایههای Fully Connected نقش بسیار مهمی در عملکرد نهایی شبکه ایفا میکنند. در این آموزش ابتدا نقش Fully Connected را به صورت دقیق بررسی میکنیم و سپس اولین شبکه عصبی کانولوشنی مشهور دنیا، یعنی LeNet-5 را با استفاده از PyTorch از صفر پیادهسازی کرده و روی یک مجموعه داده واقعی آموزش خواهیم داد.

از آنجا که شبکه های کانولوشن در مباحث تحقیقاتی و پروژه ای بسیار کاربردی هستند، قصد دارم در چندین پست آماده کنم که در آن با شبکه های کانولوشنالی، از اولین شبکه ی ارائه شده تا شبکه های مدرن کانولوشنالی به صورت مرحله به مرحله آشنا شویم. در هر بخش چالشهای موجود و نحوه حل آن با ساختارهای جدید را بررسی خواهیم کرد. کدهای هر کدام از شبکه های عصبی که توضیح داده میشود، در GitHub هم قرار خواهم داد تا به صورت منظم به هر کدام از این شبکه ها نیاز داشتید دسترسی داشته باشید. امیدوارم بتونم مباحث رو به خوبی برای شما منتقل کنم.
مطالعه پست قبلی: لایه pooling در شبکه های کانولوشنالی
برای درک بهتر ساختار و نقش لایه های fully-connected بهتر است اول شبکه ی عصبی پرسپترون چندلایه آشنا شویم. چرا که این لایه ها همان شبکه عصبی MLP هستند که به انتهای شبکه کانولوشنالی وصل شده اند.
ساختار یک نورن در یک شبکه عصبی پرسترون چندلایه

همانطور که در شکل زیر مشخص هست، یک نورون در MLP به تمام عناصر بردار ویژگی ورودی خود به صورت همزمان دسترسی دارد و آنها را به صورت وزندار باهم ترکیب کرده و با بایاس جمع میکند. در ادامه یک تابع فعال روی مقدار بدست آمده اعمال میشود تا خاصیت nonlinearity اضافه کند. به عبارتی هر نورون در ساختار MLP تمام ویژگی ها را به صورت غیرخطی باهم ترکیب کرده و یک ویژگی جدید میسازد. از کنار هم قرار گرفتن این ویژگی ها (هر نورون یک ویژگی جدید میسازد)، ویژگی های بهتری ساخته شده و فضای ویژگی ساده تر میشود. در نتیجه تصمیم گیری تسهیل تر و دقیق تر میشود.
قضیه کاور

قضیه کاور اثبات میکند که میتوان با ترکیب غیرخطی ویژگی ها، ویژگی های بهتری ساخت و در نتیجه آن فضای ویژگی ساده تر و تصمیم گیری راحتتر میشود.
به عبارتی قضیه کارو میگه که اگر یک داده (تشکیل شده از تعدادی ویژگی) در فضای غیرخطی باشد، میتوان با یک ترکیب غیرخطی ویژگی ها، ویژگی های بهتری استخراج کرد، و داده را همانند شکل زیر از فضای غیرخطی به فضای خطی منتقل کرد.
این دقیقا کاری هست که یک نورون در ساختار MLP به همراه تابع فعال غیرخطی انجام میدهد. البته یک نورون تنها یک ویژگی میتواند از ترکیب ویژگیهای اصلی بسازد. برای همین، برای اینکه بتوان فضای بهتری ساخت، لازم هست تعداد بیشتری از نورونها استفاده شوند، تا یک بردار ویژگی جدید ساخته شود. در شکل بالا فرض کنید که در فضای اصلی دو تا ویژگی داشتیم و دو تا نورون استفاده کردیم و هر کدام با ترکیب ویژگی های ورودی یک ویژگی ساخته اند که در مجموع دو ویژگی z1,z2 ساخته شده است. البته تعداد نورونها ربطی به تعداد ویژگی های ورودی ندارد و میتواند خیلی بیشتر یا کمتر یا حتی مساوی آنها باشد. اینجا صرفا برای اینکه بتوانیم بصری سازی بکنیم، فضای ویژگی ورودی و خروجی را دوبعدی در نظر گرفتیم.
میدان دید (receptive field) یک نورون

از آنجا که هر نورون در ساختار MLP به تمامی ویژگی ها در بردار ویژگی ورودی دسترسی دارد (برای همین هست که این نورون ها را fully-connected مینامند)، میتوان گفت که میدان دید وسیعی دارند که در نتیجه آن اشراف کاملی به ورودی خود دارند.
لایه های fully-connected در ساختار MLP

از آنجا که در عمل فقط با داشتن یک نوررون نمیتوان فضا را ساده تر کرد، به جای یک نورون چندین نورون کنار هم قرار گرفته و یک لایه ای از نورونها تشکیل میدهند. در این لایه هر نورون به تمام ویژگی های ووردی همزمان دسترسی دارد و با ترکیب غیرخطی آنها یک ویژگی جدید میسازد. از کنار هم قرار گرفتن این ویژگی ها، بردار ویژگی جدید (در خروجی لایه) ساخته می شود. اما از آنجا که ممکنه مسئله بسیار پیچیده باشد، برای همین، به جای یک لایه چندین لایه پشت سر هم قرار میدهند تا فضای بسیار پیچیده به فضای ساده تبدیل شود.
به طور کلی در ساختار MLP دو نوع لایه وجود دارد، لایه پنهان (یک یا چندتا) و لایه خروجی (یک لایه). دلیل نامگذاری این لایه ها هم این هست که خروجی مطلوب لایه آخر ساختار یا همان لایه خروجی را داریم، برای همین این لایه ها میتوانند مستقیما با مقایسه خروجی مطلوب و خروجی تخمین زده خودشان، وزنهای سیناپسی خود را تنظیم کنند.
اما لایه های قبل تر از لایه آخر، به خروجی مطلوب خودشان دسترسی ندارند و به همین دلیل این لایه ها را لایه پنهان نام گذاری کرده اند. این لایه ها با کمک پس انتشار خطا، از روی خروجی مطلوب لایه آخر کمک گرفته و وزنهای سیناپسی خود را در جهت کاهش هزینه تصمیم گیری تنظیم میکنند.
نقش لایه های پنهان در ساختار MLP
لایه های پنهان، نقش نگاشت ویژگی را دارند. داده را از یک فضای پیچیده به فضای ساده تری منتقل میکنند تا کار تصمیم گیری در آخرین لایه (یا همان لایه خروجی) راحتتر و دقیق تر باشد.
نقش لایه خروجی در ساختار MLP
تصمیم گیری نهایی در این لایه انجام میشود. این لایه بردار ویژگی نهایی ساخته شده توسط لایه های پنهان را دریافت کرده و از روی آنها خروجی را تخمین میزنند. به عبارتی اگر مسئله طبقه بندی باشد، مشخص میکند که داده ورودی به چه کلاسی تعلق دارد.
در ویدیوی زیر نقش لایه ها در شبکه عصبی پرسپترون چندلایه به خوبی نمایش داده شده است.
ایراد شبکه عصبی پرسپترون چندلایه
شبکه عصبی MLP نیاز دارد که یک بردار ویژگی به آن داده شود. همانطور که در پست مربوط به لایه کانولوشن صحبت کردیم، این لایه ها نمیتوانند کار استخراج ویژگی از روی داده خام (سیگنال یا تصویر) انجام دهند. شبکه عصبی MLP فقط روی برداری از ویژگی ها میتواند خوب عمل کند. همانطور که صحبت کردیم، لایه های مختلف در ساختار MLP که معمولا با عنوان لایه های پنهان میشناسیم، این ویژگی ها دریافت کرده و ترکیب غیرخطی از همه ویژگی ها انجام میدهند و رفته رفته فضای پیچیده را به فضای بسیار ساده ای تبدیل میکنند که لایه آخر یا همان لایه خروجی بتواند از روی آنها تصمیم دقیق و درستی بگیرید.
خوب بردار ویژگی را چطوری برای یک داده خام (تصویر یا سیگنال) بسیازیم؟ این کار را لایه های convolution و pooling انجام میدهند.
بررسی ساختار شبکه عصبی کانولوشنالی Lenet-05

همانطور که در شکل بالا مشخص هست شبکه عصبی کانولوشنالی LeNet-05 به طور کلی از سه نوع لایه FC, convolution و pooling دارد.
فرایند استخراج ویژگی در شبکه های کانولوشن
همانطور که صحبت کردیم، بخش استخراج ویژگی از روی داده خام (اینجا تصویر) توسط لایه های کانولوشن انجام میشود. لایه های کانولوشن ابتدائی به خاطر میدان دید کوچک ویژگی های سطح پایین (low level features) مثل لبه، انحنا استخراج میکنند. رفته رفته که تعداد لایه های کانولوشن بیشتر میشود، به همان میزان میدان دید (receptive field) کرنلها افزایش میابد و ویژگی های سطح بالا استخراج میشود (مثل شکل هندسه، اشیاء خاص و …).
همانطور که در ابتدا صحبت کردیم، کرنلهای کانولوشن به صورت محلی ویژگی استخراج میکنند. در زیر میدان دید یک کرنل کانولوشن (یک نورون) نشان داده شده است.

این در حالی هست که در یک تصویر هم اطلاعات محلی و هم اطلاعات کلی مهم هست. باید این را در نظر داشته باشیم که با کنار هم قرار داده شدن این اطلاعات محلی، ما میتوانیم محتوای داخل یک تصویر را درک کنیم. برای مثال برای تشخیص چهره یک فرد، ما به گوشها، چشمها، لب، بینی، ابرو و اطلاعات محلی شبیه به اینها نیاز داریم که با کنار هم قرار دادن همه این اطلاعات تشخیص چهره را انجام بدهیم. برای مثال اگر تنها گوش یک فرد (اطلاعات محلی بخشی از تصویر هست) را به ما بدهند، شناسایی هویت فرد بسیار سخت خواهد بود. ولی اگه همه این اطلاعات را همزمان به ما بدهند راحتتر میتوانیم شناسایی هویت را انجام بدهیم.
چیزی که دارم تلاش میکنم بگم اینه که فرض کنید هر کرنل کانولوشن نهایتا یک اجزای خاص از چهره را مدل کرده و براش ویژگی استخراج کرده است. برای مثال یک کرنل اطلاعات گوش، یکی چشم وووو را استخراج کرده است. حال باید همه این اطلاعات محلی استخراج شده توسط کرنلهای کانولوشن به صورت سراسری (global) ترکیب شوند تا اطلاعات کافی برای شناسایی فراهم شود.
این کار را لایه های fully-connected میتوانند انجام دهند. بعد از تکمیل استخراج ویژگی توسط لایه های کانولوشن و پولینگ، تصویر چندکاناله نهایی به یک بردار تبدیل می شود، و به لایه های fc ارائه می شود. هر نورون در این لایه ها، به تمام ویژگی ها همزمان دسترسی داشته و در نتیجه آنها را باهم ترکیب میکند. اینطوری ویژگی ها به صورت سراسری باهم ادغام شدند. اما ممکن است فضای ویژگی همچنان پیچیده باشد و کار تصمیم گیری سخت باشد. برای همین چندین لایه fc پشت سرهم قرار میدهند تا ویژگی های تفکیک پذیرتر و با معناتری ساخته شوند و در نهایت آخرین لایه fc کار تصمیم گیری را انجام میدهد. در شکل زیر نشان داده شده است که یک نورون در لایه های Fully-connected به تمام ویژگی های استخراج شده به صورت همزمان دسترسی دارند. (به عبارتی به همه عناصر تصاویر ساخته شده چه از لحاظ عمقی و چه از لحاظ مکانی یک نورون fc دسترسی دارد و همه آنها را باهم ترکیب کرده و یک ویژگی بسیار غنی میسازد).

به طور کلی متوان گفت که یک نورون در Fc به تمام بخشهای تصویر دسترسی دارد و میدان دید کاملی دارد.
Representational power در شبکه های عصبی
به توانایی یک شبکه عصبی در حل مسئله را representational power گفته میشود. و ثابت شده است که هرچقدر شبکه عمق بیشتری داشته باشد، این توان افزایش میابد. البته همینطوری هم نمیشه لایه اضافه کرد. هم احتمال overfitting میتونه بره بالا و هم پیچیدگی محاسباتی و هم افزایش حافظه. اگه برگردیم به سالهای 2014 به قبل، سیستمهای سخت افزاری خیلی خوب نبود و محدودیت حافظه و پردازش وجود داشت و همچین روشهای بهینه سازی و یک سری ساختارهای جدید (که بعدا صحبت خواهیم کرد) وجود نداشت و عملا اگه لایه ها از یک حدی بالاتر میرفت آموزش شبکه عصبی ممکن نمیشد.
بگذریم. برگردیم به بحث خودمون. لایه های کانولوشن بخشی از توان representation شبکه را تشکیل میدهند و نقش خیلی مهمی در این معماری های کانولوشنالی دارند. اما بیشترین توان representation را لایه های fully-connected به شبکه عصبی میدهند و بدون داشتن این لایه ها یا با تعداد کم این لایه ها، توان representation مدل بسیار پایین خواهد بود و عملکرد مدل بسیار پایین خواهد آمد.
نقش لایه های کانولوشن (convolutional layers) در CNNs

لایه های کانولوشن کار استخراج ویژگی را انجام میدهند. این لایه ها پیکسلهای تصویر را به صورت محلی بررسی کرده و ویژگی استخراج میکنند. بدون این لایه ها، عملا شبکه های کانولوشن معنایی ندارند. این لایه های در مقایسه با fc تعداد پارامتر بسیاااار کمتری دارند.
نقش لایه های pooling در CNNs
لایه های پولینگ دو کار اساسی در ساختار CNNs انجام میدهند. یکی ابعاد مکانی را کاهش میدهند. و دوم اینکه مدل را در مقابل تغییرات کوچک مقاوم میکنند. این لایه ها هیچ پارامتر قابل یادگیری ندارند.
نقش لایه های fully-connected در CNNs
لایه های fc اول ویژگی های استخراج شده توسط لایه های pooling و convolution را به صورت سراسری ترکیب میکنند و ویژگی های بسیار غنی تری میسازند. سپس در لایه آخر (لایه خروجی) کار تصمیم گیری را انجام میشود. یعنی خروجی مربوط به داده ورودی را تخمین میزند. برای مثال در مسائل طبقه بندی مشخص میکند که داده ورودی به چه کلاسی تعلق دارد. این لایه ها به خاطر fully-connected بودنشان، پارامترهای خیلی زیادی را شامل میشوند. به عبارتی بیشترین تعداد پارامترها را این لایه ها تشکیل میدهند.


معماری شبکه های کانولشنالی
معمولا چندین لایه کانولوشن و پولینگ پشت سرهم قرار میدهند تا استخراج ویژگی انجام شود. سپس در انتها یک یا چندین لایه fc جهت محاسبه خروجی نهایی (تصمیم گیری) استفاده میکنند.
conv→φ(.)→Pooling→conv→φ(.)→Pooling→…Flatten()→FC→φ(.)→FC→φ(.)…→FC→φ(.)
پیاده سازی شبکه عصبی LeNet-05 در پایتورچ
لازم به ذکر هست که در ساختار LeNet-05 که اولین بار توسط یان لیکان و همکاران مطرح شد، هنوز روشهای بهینه سازی خیلی کارامد نبودند، یا لایه MaxPooling و batch normalization وجود نداشت. اما اخیرا به لطف مطالعات و اکتشافات وسیع در حوزه یادگیری عمیق، ابزارها و الگوریتمهای خیلی بهتری مطرح شده اند. ما معماریهای جدید را در پستهای آینده بررسی خواهیم کرد. فقط برای اینکه هم کد کمی بهینه باشد، من به دوشکل شبکه عصبی lenet را پیاده سازی خواهم کرد: یکی براساس همان مقاله اصلی، یکی هم با کمک ابزارهای مدرن.
در این معماری از average pooling و همچنین توابع فعال Sigmoid استفاده شده است. در حالی که بعدها ثابت شد که توابع فعال دیگه ای مثل ReLU در مقایسه با Sigmoid خیلی بهتر هستند و همچنین maxpooling عملکرد بهتری در مقایسه با average pooling است.
مشکل دیگه شبکه های عصبی، آموزش آنها بود که بعدها الگوریتم بهینه سازی Adam مطرح شد که در مقایسه با SGD بسیار بهتر عمل میکند و همچنین لایه های BatchNormalization که به همگرایی شبکه عصبی بسیار کمک میکند. من در ورژن اصلی، طبق مقاله الگوریتم را پیاده سازی کرده ام و در ورژن جدید با در نظر گرفتن برخی از ابزارهای مدرن چه در معماری و چه در آموزش.
برای دسترسی به بخش کامل کدها میتوانید به GitHub من مراجعه کنید. لینک دسترسی کدها در زیرآورده شده است.
- کد GitHub شبکه عصبی LeNet-05 جهت دسته بندی داده های MNIST (ورژن اصلی مقاله)
- کد شبکه عصبی LeNet-05 جهت دسته بندی داده های MNIST (ورژن جدید بهبود یافته)
دوستان عزیزم، مباحث گفته شده بخشی کوتاه از دوره جامع و تخصصی یادگیری عمیق هست.
ما در دوره یادگیری عمیق، ابتدا شبکه های عصبی عمیق را کاملا به صورت دستی پیاده سازی میکنیم و بعدش میریم سراغ ابزارهای آماده در پایتورچ. و برای پیاده سازی دستی هم، اول ریاضیات و اثبات روابط، بعدش نمایش ماتریسی عملیات، را کامل یاد میگیریم. در آخر هم بعد از معرفی ابزار پایتورچ پروژه های عملی مختلف در زمینه های پردازش تصویر، پردازش سیگنال و پردازش متن انجام میدهیم تا با چالشهای واقعی انجام پروژه به صورت عملی آشنا شویم.
دوستانی که قصد یادگیری تخصصی و عمیق شبکه های عمیق هستند، پیشنهاد میکنم دوره جامع یادگیری عمیق مارا نگاه کنند. در این دوره که بیش از یک سال طول کشید تا مباحث آماده شود، سعی کردیم مباحث مهم و کاربردی را همه از لحاظ تئوری و هم از لحاظ عملی آموزش دهم و تا درک تخصصی از این ابزار بدست بیاریم و بتونیم از آنها در انجام پرژه ها به خوبی استفاده کنیم و از همه مهمتر بتونیم این شبکه ها را توسعه دهیم. چه از لحاظ ریاضیات و چه از لحاظ ساختار در کاربردهای مختلف.
دیدگاه ها