شبکههای عصبی و توانایی یادگیری فوقالعادشون... همون چیزهایی که احتمالاً ما ازشون میترسیم و به ادعای بسیاری از افراد قراره ما رو نابود کنند! ولی امروز اینجا اومدیم تا با هم یکی خوبش رو بسازیم، و با شعار «پیشرفت و علم ترسمرس حالیش نمیشه» به کار خودمون ادامه بدیم!
باید و حتماً بخشبهبخش پیش بریم و برای هرکدوم یه توضیحِ ریزی میدم تا بهتر درک کنیم قضیه رو... اگه هم حال و حوصله خوندن ادامه مطلب رو ندارید یا فکر میکنید که وارد مرحلهای شُدید که این چیزها سوسولبازی هستش، میتونید به اینجا برید و فقط کد رو نگاه بندازید!
به زبان بسیــــــــار ساده اگه توضیح بدم، شبکههای عصبی در واقع مجموعهای از لایهها هستن که هِی توشون یه سری مقادیر که بهشون میگیم وزن تغییر میکنه و انتظار داریم بعد از تمرین به درجهای برسه که نیازهامون رو برطرف کنه...
واستون سوال پیش نیومد که چرا در عنوان این مقاله نوشته شده صفر تا نَود؟ اگه با دقت به تصویر ابتدای صفحه نگاه بندازید، میبینید که چند نورون گوگولی داریم و هر یک از اونها یک یا چندتا شاخه رفته توشون (غیر از لایه اول که ورودی هست) و به تمام نورونهای جلوی خودشون وصلن (سَوا از لایه خروجی). ما انواع و اقسام حالتهای شبکه داریم در دنیای یادگیری عمیق، و به این شکل از شبکه عصبی میگن پِرسِپترون. داستان پشت هر یک از این شبکهها ممکنه فرق کنه و فلسفه خودشون رو داشته باشند، اما در نهایت چند لایه هستند که با وزنها بازی میکنند.
برای شروع میخوام خود ماهیت لایه رو توضیح بدم تا تَرسِتون بریزه... یک لایه در شبکه عصبی میتونه حتی چند صَد به اصطلاح نورون (گِره هم بهشون میگن) رو توی خودش نگهداری کنه و به قولی بُلوکهای سازه ما هستند. این نکته رو بدونید که این لایهها از خودشون مغز ندارند و باید توسط یه بالاسری فراخوانی بشند تا شروع به کار کنند. احتمالاً به این نتیجه رسیدید که خروجی هر لایه میشه ورودی لایه بعدی، نه؟! اشکال نداره، حداقل الان میدونید...
حالا وزنها چی هستند؟! تقریباً همهچیزی که داریم و قدرت یه شبکه عصبی به اونها وابسطه هست... وزن نشاندهنده قدرت اتصال بین واحدها هستش. بهفرض اگه وزن گِره شماره ۱ از گِره شماره ۲ مقدار بیشتری داشته باشه، به این معنی هست که نورون اولی تأثیر بیشتری نسبت به نورون دوم ما داره. اینطوریعه که مثلاً وقتی وزن نورون هفتم (از بین دهتا) در لایه خروجی سنگینتر از بقیه هست متوجه میشیم که شبکه عصبی ما که کارش تشخیص ارقام دستنوشته هست به ما میفهمونه کدوم رو حدس زده... (هفتمی در واقع میشه عدد ۶، چون از صفر شروع کردیم :)) دو یو آندراِستَند؟!
سوالی نیست از بخش قبل؟! مثلاً چه چیزی تصمیم میگیره که یه نورون فعالتر باشه و یه نورون ساکتتر باشه؟! خب، این کار بر عهده توابع فعالساز هست کَسَگَم! به بیان دیگه اگه توضیح بدم این توابع که طبق معمول دههاتا ازشون موجوده و حق انتخاب داریم توشون، تصمیمگیرنده این هستن که یه نورون از شبکه طَرد بشه یا نه. باز هم اینجا نکتهای وجود داره که باید بدونی! این طَرد شدن یه دفعه انجام نمیشه و در فرآیند تمرین دادن صورت میگیره... شما شدت ضربه رو با این حال میتونید تغییر بدید و کاری کنید که اولین خطا، آخرین خطای اون نورون بشه! (ولی نباید اِنقدر بیرحم بود، چون در مقابل شبکه وقتی میبینه با بچههاش اینطوری رفتار میکنید بازخورد خوبی به شما نمیده) راستی، در زبان تخصصی به این شدت ضربه، نرخ یادگیری یا Learning Rate میگن.
خود این بخش رو میشه به دو بخش کوچیکتر تقسیم کرد: انتشارِ رو به جلو، و پَسْ انتشار یا انتشارِ رو به عقب. در ادامه به صورت مجزا در موردشون توضیح مختصری میدم، و انتظار میره به این دو بخش خوب توجه کنید که موقع پیادهسازی خیلی باهاشون درگیر میشیم.
انتشارِ رو به جلو (Forward Propagation):
در اینجا باید دادههای خودمون رو از چپترین لایه یعنی لایه ورودی (با فرض اینکه موقع مصورسازی از چپ به راست میکِشیمِشون) به راستترین لایه که لایه خروجی محسوب میشه بفرستیم تا ببینیم شبکه عصبی ما براساس وضعیت کُنونیاش چه حدسی میزنه... به همین سادگی، کار دیگهای هم لازم نیست کنیم! برای پیشبینی کلاً باید این عمل رو انجام بدیم، پس محاسباتی انجام نمیده تا وضعیت خودش رو آپدیت کنه.
پَسْ انتشارِ (Back Propagation):
پَس انتشار، انتشارِ رو به عقب، که بهش انتشار معکوس خطاها هم میگن در واقع میآد و براساس میزان خطای بدست اومده از مرحله انتشار رو به جلو، وزنها رو در هر لایه بالا و پایین (تنظیم) میکنه. این کار برای این انجام میشه که نِرخ زیان (همون خطا) رو هِی کمتر و کمتر کنه.
موضوع پَس انتشار پُتانسیِل این رو داره که به یه مقاله مجزا تبدیل بشه... ماهیت سادهای داره، اما ریزنُکاتی داره (مخصوصاً در موقع پیادهسازی) که اون رو دشوار میکنه برای توضیح... به هر حال... من برای پیادهسازی از فِریموُرکِ کِراس (Keras) الهام گرفتم، اما قطعاً نباید انتظار اون کیفیت رو داشته باشید ازش... سه کِلاسِ اصلی رو تنها اینجا قرار میدم تا از شلوغی غیرضَروری جُلوگیری بشه... به این سه نقطهها گاهی دقت کنید، مُمْکنه حرفهای زیادی پُشتِشون نِشَسْته باشه...
توابع فعالسازی:
لایه یا بلوک شبکه عصبی:
خودِ خودِ خودِ شبکه عصبی:
حالا برای تست میخوایم باهاش بریم سراغ گِیتِ XOR که خیلــــی رایجه... حالا این گِیت چطور کار میکنه؟ سادهست، هر وقت ورودیها شبیه به هم باشند خروجی میشه صفر، وگرنه میشه یک.
میدونم، انتظار داشتید وقتی دارید نتایج رو میبینید بگه صفر یا یک، اما با یه ماتریس ۲ در ۱ مواجه شُدید! ما میخواستیم که شبکه عصبی روی دادههای تست به ترتیب این نتایج رو بدن: صفر، یک، یک و در آخر باز صفر. حالا باز به خروجی نگاه بندازید، و اون اعدادی که بیشتر از آستانه مد نظر شما برای قبولی هستند (مثلا ۰.۵ خوبه) رو با عدد ۱ جایگزین کنید و بقیه رو با ۰. تامام تامام!
یه نکته برای کسایی که کُد کِلاسِ شبکه عصبی رو نخوندن (یا هنوز بلد نیستن که بخونن)، وزنی که در طِی تمرین نوشته شده، تنها برای به لایه اول هست، شما میتونید همه رو چاپ کنید اصلاً، ولی برای شبکههای گُنده خوب نیست و نمیشه چیزی فهمید. در اینجا هم بیشتر جنبه زیبایی داره تا کاربرد، اما مقدار Loss یا نِرخِ زیان مفیده دیدنش. (اگه میبینید نِرخ زیان داره زیاد میشه تعجب نکنید، معیار سنجش دقت رو R2 گذاشتم که به ما یه عددی بین ۱- تا ۱ میده و هرچقدر به عدد یک نزدیکتر باشیم یعنی کارمون داره درستتر انجام میشه)
این فایلها رو چند جا آپلود کردم و لینک نوتبوکِش رو هم در ابتدای مقاله قرار دادم، اما باز برای اینکه همه رو با هم داشته باشید اینجا میذارم:
واقعاً نباید انتظار داشته باشید وقتی در سطح مبتدی هستید تمامی مطالب بالا رو درک کنید، پلهپله پیش برید و لقمه بزرگتر از دهنتون رو یهو برندارید... وبسایت کَگِل یه دوره خوب (و نسبتاً جامع) در زمینه یادگیری عمیق داره که بهنظرم نیازهای یه مبتدی رو پوشش میده، اگه دوست داشتید یه سری بهش بزنید از این لینک.
بدون شَک این پروژه من باگ و ایرادهایی داره، که کمکم گَندِش در میآد در طول زمان، با این حال اگه شما میخواید امتحانش کنید، برید یه گِیتِ دیگه رو باهاش بِسنجید و اگه هم قَصدِ تغییر دادنش رو دارید، مستقیم برید سراغ تابِعِ train و علیالخُصوصْ قسمت پَس انتشار و آپدِیتِ وزنهای شبکه.
منابع: