محمد جواد نجادی
محمد جواد نجادی
خواندن ۳۰ دقیقه·۹ ماه پیش

عمیق‌تر و عمیق‌تر در عمق یادگیری عمیق و شبکه‌های عصبی مصنوعی

# پارت 2 : نحوه یادگیری در شبکه های عصبی عمیق ( انواع یادگیری و توابع فعالساز و پس انتشار( backpropagation)

باید به لایه های عمیق تری نفوذ کنیم!

تولید شده توسط DALEE-3
تولید شده توسط DALEE-3


این مقاله در سه پارت کامل می شود که به شرح زیر است :

پارت 1 : تاریخچه هوش مصنوعی و چگونگی عملکرد شبکه های عصبی ساده

پارت 2 : نحوه یادگیری در شبکه های عصبی عمیق ( انواع یادگیری و توابع فعالساز و پس انتشار( backpropagation)

پارت 3 : انواع مهم ترین معماری های شبکه های عصبی عمیق(RNN / LSTM / CNN)

که در این 3 پارت به این موضوعات خواهیم پرداخت که شبکه های عصبی مصنوعی چیه ؟ چطور کار میکند ؟ یادگیری عمیق چیه ؟ در شبکه های عمیق یادگیری چطور انجام می شه و انواع روش های یادگیری شبکه چیه ؟ انواع معماری های اصلی شبکه های عمیق چی هست ؟ آیا واقعا کارکرد شبکه های عصبی مصنوعی مانند نورون های شبکه عصبی انسان است یا فقط الهامی از آن هست ؟ کاربردهای شبکه عصبی چیه ؟ معایبی هم داره ؟


مرور مطالب پیشین

در پارت 1 تاریخچه ای کوتاه از فراز و نشیب های هوش مصنوعی و زمستان های اون صحبت کردیم و تلاش هایی که برای پیشبرد اون انجام شد. یک پرسپترون ساده را مورد بررسی قرار دادیم تا بفهمیم چه بخش هایی دارد در واقع یک ANN ساده چطور داده رو میگیره وزن دهی میشه لایه پنهان چطور کار میکنه. در ANN ساده در واقع عملیات خاصی روی داده ها انجام نمی شود. تایع فعالسازی در ANN یک تابع خطی هست که داده های ورودی را به خروجی می برد پس تفاوت چندانی با تسک های رگرسیون در مدل های سنتی ندارد.

اما اگر بخواهیم با داده های بزرگ تر کار کنیم و نتایجی مثل تشخیص چهره یا پردازش زبان طبیعی و ... را داشته باشیم. تابع فعالساز خطی به تنهایی کاربردی ندارد چرا که عملیات خاصی را انجام نمی دهد. از بین همه توابع فعالساز غیر خطی کدام بهترین عملکرد را دارد ؟ چه مشکلاتی در توابع فعالساز داریم و چطور میتونیم اونها رو کم و به حداقل برسونیم ؟ پردازش اطلاعات در نورون ها چطور انجام میشه ؟ پردازش ترتیبی بهتر هست یا موازی ؟ میتونیم اون ها رو ترکیب کنیم؟ نورون باید بتواند که یاد گیری داشته باشد . اما چه نوع یادگیری هایی داریم ؟ داده ها ممکن است لیبل دار یا بدون لیبل باشند همانطور که در مدل های قدیمی ماشین لرنینگ داشتیم . وقتی تعداد لایه ها در شبکه عصبی بیشتر می شود و به عمق می رویم این کار چطور انجام می شه ؟ از همه مهم تر پس انتشار چطور عمل میکنه ؟ چه مشکلاتی میتونه داشته باشه و چطور می تونیم اونها رو به حداقل برسونیم؟ قاعده زنجیره ای !؟ بیاید با هم در رو باز کنیم و وارد بشیم ...

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

شبکه های عصبی مصنوعی چطور یاد میگیرند ؟

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

در شبکه های عصبی مصنوعی با توجه به نوع مسئله ای که داریم می توانیم نوع معماری و یادگیری شبکه را تعیین کنیم . مثلا در پردازش زبان طبیعی (NLP) که داده ها دارای یک ترتیب خاص هستند از شبکه‌های عصبی بازگشتی (RNN) به طور گسترده استفاده می شود و پردازش اطلاعات در آن بصورت ترتیبی (Sequential Processing) است. هر داده ورودی به ترتیب خاصی به شبکه داده می‌شود. و وقتی به هر لایه از شبکه می ‌رسند پردازش ‌های مختلفی (درادامه این مقاله به آنها خواهیم پرداخت ) بر روی هر ورودی انجام می‌شود و سپس خروجی مرتبط با هر کلمه یا کاراکتر را تولید می‌کند. نوع دیگر پردازش موازی (Parallel Processing) است که اغلب در شبکه‌های عصبی عمیق (Deep Neural Networks) برای افزایش سرعت و کارایی استفاده می‌شود، زیرا می‌تواند زمان اجرای وظایف را به طور قابل ملاحظه‌ای کاهش دهد. به عنوان مثال، در شبکه‌های عصبی کانولوشنال (CNN) که معمولاً برای تصویربرداری و پردازش تصویر استفاده می‌شوند، عملیات‌های پیچیده ‌ای مانند تصاویر سه بعدی یا ماتریس‌های بزرگ مورد استفاده قرار می ‌گیرند. این شبکه ‌ها به طور فراوان از پردازش موازی برای انجام عملیات ماتریسی مانند ترکیب، تفکیک، و تصویربرداری استفاده می ‌کنند. اینجاست که به قدرت GPUها (Graphics Processing Units) پی می بریم چرا که پردازش در آنها به صورت موازی است . به این معنی که چندین عملیات محاسباتی همزمان و موازی اجرا می ‌شوند. این امکان به وجود می‌آید که میلیون ‌ها تسک محاسباتی را همزمان اجرا کند .

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

انواع مختلف یادگیری در شبکه‌های عصبی

در همه انواع یاد گیری در شبکه های عصبی مصنوعی هدف این است که شبکه بتواند الگوهای موجود در داده‌های ورودی را شناسایی کرده و پیش‌بینی‌های درستی برای داده‌های جدید ارائه کند. هر کدام از آنها می توانند برای تسک خاصی مورد استفاده قرار گیرند. یکی از این روش ها یاد گیری نظارت‌شده (Supervised Learning) برای مسائلی مانند تشخیص الگو، دسته‌بندی، رده‌بندی، و پیش‌بینی استفاده می‌شود. در این نوع یادگیری، داده های ورودی به شبکه در مرحله آموش لیبل دار هستند . مثلا در دیتا ست ImageNet ، برای پردازش تصویر داری تصاویر با لیبل است که موجب می شود در مرحله آموزش شبکه با لیبل هایی که دارد بتواند الگو ها را تشخیص دهد و بهترین نتیجه را در مرحله تست بگیریم .

روش بعدی یادگیری بدون نظارت (Unsupervised Learning) است. معمولاً برای پیدا کردن الگوهای پنهان، کاوش داده، خوشه‌بندی، و تقسیم ‌بندی داده‌ها مورد استفاده قرار می ‌گیرد. داده‌های ورودی بدون برچسب آموزش داده می‌شوند. بهترین مثال برای آن شبکه‌های خود نظارتی (Autoencoders) است که برای یاد گیری از داده‌های بدون لیبل طراحی شده‌اند. هدف اصلی این است که شبکه بتواند ویژگی‌های مهم و اساسی داده‌ها را بازتولیدکند. شبکه‌های خود نظارتی یکی از مثال‌های معروف از یادگیری بدون نظارت در شبکه‌های عصبی هستند که در حال حاضر نیز به وفور مورد استفاده قرار می‌گیرند.

روش دیگر یادگیری نیمه نظارتی (Semi-Supervised Learning) که می توان گفت ترکیب یادگیری با ناظر و بدون ناظراست. چرا که تعدادی محدود داده های لیبل شده هم دارد. این نوع یادگیری معمولاً در مواردی استفاده می‌شود که دسترسی داده‌های بدون لیبل فراوان اما داده‌های لیبل دار محدود است. هدف این نوع یادگیری، استفاده همزمان از داده‌های لیبل دار و بدون لیبل برای بهبود یادگیری و افزایش دقت مدل است. مثلا در تصویربرداری پزشکی، ممکن است داده‌های برچسب‌دار (مثلاً تصاویری که بیماری خاصی را نشان می‌دهند) محدود باشند. با استفاده از داده‌های بدون برچسب، مدل‌ها می‌توانند ساختارهای مهم در تصاویر را فراگیری کنند.

یادگیری تقویتی (Reinforcement Learning) در بازی‌های رایانه‌ای، رباتیک، و تصمیم ‌گیری‌های پیچیده مورد استفاده قرار می‌گیرد. در هر مرحله از زمان، عامل (agent) در یک وضعیت خاص قرار دارد و بر اساس وضعیت خود، یک عمل خاص را انتخاب می‌کند (پاداش یا مجازات). هدف عامل در یادگیری تقویتی، کم کردن مجازات و افزایش پاداش است. عامل با یاد گیری ارتباط بین عمل‌هایی که در وضعیت‌های مختلف انجام می‌دهد و پاداش یا مجازاتی که از محیط دریافت می ‌کند، تلاش می‌کند رفتار بهینه را انتخاب کند.

برای انجام یادگیری تقویتی، الگوریتم‌های مختلفی وجود دارند، از جمله الگوریتم‌های Q-Learning، Deep Q-Networks (DQN)، Policy Gradient و Actor-Critic. هر یک از این الگوریتم‌ها روی اصول مشابهی بنا شده‌ان. اما با استفاده از تکنیک‌ها و تغییرات مختلف، برای حل مسائل مختلفی به کار می‌روند.از معایب یادگیری تقویتی این که نیاز به تعداد زیادی آزمایش و خطا برای یاد گیری، پیچیدگی الگوریتم‌ها و حساسیت به تنظیم پارامترها می‌شود.

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

1 .توابع فعالساز(Activation Function)

در ANN از توابع فعالسازی برای انتقال اطلاعات در لایه ها (از ورودی به خروجی یا در مدل های بازگشتی از خروجی به ورودی) استفاده می کنیم. از توابع فعالسازی در لایه های مختلف شبکه و در حالت های مختلف از پردازش تا وزن دهی و کاهش ضرر و نتیجه نهایی استفاده می کنیم.این توابع در واقع باعث غیر خطی شدن عملکرد کلی نورون ها می شوند. یکی از اصلی‌ ترین چالش‌ ها در آموزش شبکه‌های عمیق مشکل گرادیان ناپایدار محو شونده (Gradient Vanishing) است. زمانی که اطلاعات از لایه ای به لایه بالاتر منتقل می‌شوند. وزن‌های بدست آمده در مرحله آموزش به سمت عمق شبکه حرکت می‌کنند، و گرادیان‌ها معمولاً به سمت صفر متمایل می‌شوند در نتیجه اطلاعات به درستی در شبکه منتقل نمی‌شود. این می‌تواند باعث کاهش یا ناپدید شدن گرادیان‌ ها در لایه‌های پایین‌ تر و عمیق‌تر شود. بنابراین استفاده از توابع فعال‌سازی مناسب می ‌تواند به کاهش مشکل گرادیان ناپایدار محو شونده و بهبود عملکرد شبکه‌های عمیق کمک کند.

البته با توجه به نوع مسئله و نتیجه ای که می خواهیم بگیریم از انواع مختلف توابع فعالساز استفاده می کنیم که نمونه هایی از انوع مختلف توابع خطی را بیان خواهیم کرد .

توابع فعالساز خطی Linear Activation Function

 این تابع فعالساز خطی با کد پایتون توسط من ایجاد شده است
این تابع فعالساز خطی با کد پایتون توسط من ایجاد شده است


توابع فعالساز خطی که به عنوان "بدون فعالسازی" یا "تابع هویت " نیز شناخته می شود. زیرا هیچ ویرایش ورودی را انجام نمی دهد . تابع هیچ کاری برای مجموع وزنی ورودی انجام نمی دهد، فقط مقدار داده شده را به خروجی منتقل می کند . معمولاً در موارد خاص و مسائل خاص استفاده می شود. در برخی از مسائل، مانند مسائل رگرسیون، ممکن است در لایه‌های خروجی برای محاسبه مقادیر پیش‌بینی استفاده شوند.

f(x) = x

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

در بیشتر مدل ها می توان گفت اکثرا از توابع غیر خطی استفاده می شود. زیرا برای حل مسائل پیچیده مناسب هستند.


توابع فعالساز غیر خطی Non-Linear Activation Functions

غیر خطی بودن تابع فعالسازی به ANN ها امکان مدلسازی روابط پیچیده را می دهند .در واقع خیلی از کاربرد ها مخصوصا در دنیای امروز مانند تشخیص چهره یا تشخیص دست خط و خیلی مسائل دیگر مرزهای تصمیم و الگوهای غیر خطی دارند. البته در شبکه عصبی ممکن است از توابع خطی هم استفاده کنیم. مخصوصا در لایه خروجی اما برای شبکه های پیچیده تعداد زیادی توابع فعاساز غیرخطی داریم که هر کدام برای بخش های خاصی از شبکه عصبی اعمال می شوند. می توان گفت پرکاربرد ترین ها تابع سیگموئید ((لجستیک) Sigmoid) , تابع تانژانت هیپربولیک ((تانه) tanh) , واحد خطی باز‌انگیزی شده(ReLU) ,که دارای محبوبیت بیشتری است و خودش شامل بخش های مختلفی می شود یا Softmax . بیایید به دنیای فعالسازها برویم خط مقدم شبکه های عصبی!


1.1.تابع فعالسازی سیگموید (Sigmoid Activation Function)

تابع سیگموید با کد پایتون توسط من ایجاد شده است
تابع سیگموید با کد پایتون توسط من ایجاد شده است


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

σ(x) = 1 / (1 + exp(-x))

در اینجا، exp عدد اویلر (پایه لگاریتم طبیعی) است و xورودی تابع می‌باشد.

یکی از نقاط ضعف تابع Sigmoid ، مشکل گرادیان محو شونده است. وقتی ورودی تابع Sigmoid بسیار بزرگ می‌شود، خروجی تابع به سمت 1 و وقتی ورودی بسیار کوچک است، خروجی به سمت صفر 0 گرایش پیدا می کند. این باعث می‌شود که مشتق تابع Sigmoid در این نقاط به سمت صفر برود، یعنی گرادیان‌ها بسیار کوچک شوند. و فرایند آموزش شبکه عمیق ممکن است کند یا حتی متوقف شود.


2.1.تابع تانژانت هیپربولیک ((تانه) tanh)

تابع تانژانت با کد پایتون توسط من ایجاد شده است
تابع تانژانت با کد پایتون توسط من ایجاد شده است


تابع تانژانت (tanh) یک تابع غیر خطی مانند Sigmoid به شکل S است. با این تفاوت که خروجی ای بین (1.-1) دارد. که باعث می شود مقدار میانی آن 0 باشد و مقادیر منفی را هم شامل شود.

فرمول ریاضی tanh(x) به صورت زیر است :

tanh(x) = (e^x - e^(-x)) / (e^x + e^(-x))

در این فرمولe نمایانگر عدد اویلر است و x ورودی تابع است.

هر دو تابع فعال‌سازی tanh و sigmoidمحدودیتی در مقادیر خروجی خود دارند، که این محدودیت می‌تواند به کنترل مشکل گسترش گرادیان کمک کند. زمانی که مقادیر خروجی شبکه عصبی به سمت مقادیر بسیار بزرگ یا بسیار کوچک حرکت می‌کنند، ممکن است گرادیان‌ها به سرعت بزرگ یا کوچک شوند، که مشکلاتی را در فرآیند آموزش به وجود می آورند.

در تابع tanh محدوده خروجی بین (1.-1)است. که باعث "مرکز‌شده کردن داده‌ها" می شود. یعنی میانگین آن‌ها به صورتی تنظیم شده است که در حدود صفر باشد. این می‌تواند به عملکرد بهتر شبکه‌های عصبی کمک کند، زیرا وزن‌ها به سمت هر دو طرفی از صفر متمایل نخواهند شد.

در این نمودار می توانید تفاوت بین Sigmoid vs. Tanh را بهتر درک کنید.

توابع Sigmoid vs. Tanh با کد پایتون توسط من ایجاد شده است
توابع Sigmoid vs. Tanh با کد پایتون توسط من ایجاد شده است


در تابع Sigmoid معمولاً به دلیل وجود محدودیت خروجی در بازه (0،1) ممکن است شبکه‌های عصبی دچار مشکل گرادیان ناپایداری شوند، به خصوص در شبکه‌های عمیق. tanh به دلیل ویژگی‌هایی که در کنترل گرادیان‌ها و میانگین ‌گیری داده‌ها ارائه می‌دهد به طور کلی معمولاً ترجیح داده می‌شود.

3.1. تابع فعالسازی Rectified Linear Unit (ReLU)

تابع Relu با کد پایتون توسط من ایجاد شده است
تابع Relu با کد پایتون توسط من ایجاد شده است


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

فرمول محاسباتی ReLU به صورت زیر است :

f(x) = max(x, 0)

این فرمول نشان می دهد که اگرمقدار ورودی مثبت باشد. ReLU ورودی را به عنوان خروجی ارسال می کند. درغیر این صورت مقدار خروجی صفر است و هر نورونی که خروجی ReLU آن صفر باشد، به معنای غیرفعال بودن آن نورون است. اصطلاحا آن نورون مرده است. به عبارت دیگر ReLU برای ورودی‌هایی که کمتر از صفر هستند، مقدار ثابت صفر را خروجی می‌دهد. که به آن “اسپارسیتی (sparsity) “می گویند.

این ویژگی با کاهش هم ‌پیوندی (co-adaptation) (هم ‌پیوندی زمانی اتفاق می‌افتد که دیتکتورهای ویژگی ، به جای اینکه ویژگی‌های مجزا را یاد بگیرند به طور فزاینده‌ای به یاد گیری یکدیگر وابسته می‌شوند) (دیتکتورهای ویژگی(feature detectors) (آشکارسازهای ویژگی، بخش‌هایی از شبکه‌های عصبی مصنوعی هستند که وظیفه‌ی شناسایی و استخراج الگوهای خاص از داده‌ها را بر عهده دارند) را بهبود بخشیده و overfitting را کاهش دهد.

تابع ReLU از نظر محاسباتی کارایی بالایی دارد و در مقایسه با توابع فعال‌سازی پیچیده‌ ترمانندsigmoid یا tanh که توان دار هستند ومخصوصا برای مقادیر بزرگ که گرادیان آنها به سرعت به صفر نزدیک می‌شود ، ReLU به خصوص از طریق الگوریتم backpropagation بهترین عملکرد را ارائه می‌دهد. گرادیان ReLU همواره 0 یا 1 است. که بهینه ‌سازی را در طول آموزش آسان ‌تر می‌کند و منجر به کاهش گرادیان می‌شوند .

اما مهم است توجه داشته باشیم که ReLU با مشکل "نیروی زنده ‌ی ReLU" مواجه است، این مسئله ممکن است به خصوص در آغاز آموزش شبکه بوجود بیاید. مشکل "نیروی زنده‌ی ReLU" یا "Dying ReLU" به موقعیتی اشاره دارد که نورون‌های در یک شبکه عصبی با استفاده از تابع فعال‌سازی ReLU به گونه‌ای تنظیم ‌شوند که گرادیان آن‌ها همیشه صفر باشد و در نتیجه هیچ گونه به ‌روزرسانی وزنی در آموزش انجام نمی‌شود. این موضوع می‌تواند به عدم آموزش یا آموزش ناکافی برای برخی از نورون‌ها منجر شود و کارایی شبکه را به شدت کاهش دهد. حتی باعث شود که بخشی از شبکه به طور کامل غیرفعال شود. برای کاهش مشکل "نیروی زنده‌ی ReLU"، می‌توان از نسخه‌هایی مانند Leaky ReLU، Parametric ReLU (PReLU) ، Exponential Linear Units (ELU) استفاده کرد. که مسئله Dying ReLU را به صورتی محدود شده رفع می‌کنند.


4.1. تابع فعالسازی Leaky ReLU

در این نمودار میتونیم تفاوت واضح ReLU Leaky ReLU Vs  را ببینیم. این نمودار توسط من با کد پایتون رسم شده است .
در این نمودار میتونیم تفاوت واضح ReLU Leaky ReLU Vs را ببینیم. این نمودار توسط من با کد پایتون رسم شده است .


یک نسخه اصلاح‌ شده از تابع فعال‌سازی ReLU است که به طور خاص برای مقابله با مشکل "نیروی زنده‌ی ReLU" طراحی شده است. در Leaky ReLU، برای مقادیر منفی، یک شیب بسیار کوچک (به عنوان مثال، 0.01) به جای صفر استفاده می‌شود. در نتیجه اگر مقدار غیر صفر باشد Dying ReLU اتفاق نمی افتد. این باعث می‌شود که شبکه عصبی قابلیت آموزش و عملکرد بهتری داشته باشد، به ویژه در مواقعی که ممکن است مواجه به داده‌های با الگوهای منفی باشد.

فرمول ریاضی آن به این صورت است که :

f(x) = { x, if x ≥ 0
ax, if x < 0


که در آن a معمولاً یک عدد کوچک مثبت است که معمولاً بین 0 و 0.01 یا 0.1 قرار دارد.

زمانی که شبکه‌های عمیق با گرادیان‌های ناپایدار روبرو می‌شود، استفاده از Leaky ReLU می‌تواند کمک کننده باشد زیرا حداقل گرادیانی برای ورودی‌های منفی دارد. Leaky ReLU به دلیل محاسبه شیب برای ورودی‌های منفی نسب به ReLU ممکن است به محاسبات بیشتری نیاز داشته باشد. به طور مثال برای بهبود عملکرد شبکه‌های عصبی کانولوشنی (CNN) برای شناسایی اشیاء در تصاویر تاریک یا کم نور که اطلاعات ضروری در این قسمت‌ها از دست رفته است یا شبکه‌های متخاصم مولد (GANs)، که ممکن است با گرادیان‌های پراکنده (sparse gradients) روبرو شویم، استفاده از Leaky ReLU می‌تواند بسیار مفید باشد.


5.1. رلو پارامتری (PReLU)

در این نمودار می تونیم تفاوت واضح  Leaky ReLU  Vs Parameterized ReLU (PReLU) را ببینیم. این نمودار توسط من با کد پایتون رسم شده است
در این نمودار می تونیم تفاوت واضح Leaky ReLU Vs Parameterized ReLU (PReLU) را ببینیم. این نمودار توسط من با کد پایتون رسم شده است


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

فرمولی همانند فرمول Leaky ReLU دارد به این تفاوت که مقدار a در طول فرآیند آموزش توسط شبکه عصبی تعیین می‌شود.


6.1. تابع فعال‌سازی Exponential Linear Unit (ELU)

تابع ELU با کد پایتون توسط من ایجاد شده است
تابع ELU با کد پایتون توسط من ایجاد شده است


واحد خطی نمایی (ELU) برای مقادیر منفی یک تابع غیرخطی با رفتار نرمتراز ReLU استفاده می‌کند، ELU نیاز به پارامتری برای تنظیم شیب ندارد و فقط یک پارامتر برای تنظیم شدت نرمی تابع دارد. این باعث می‌شود که ELU در نزدیکی مبدا خطی باشد و به طور میانگین سریع‌تر از صفر به سمت منفی می‌رود که می‌تواند باعث کاهش اثر شیفت بایاس شود. این به معنای این است که مدل با توجه به داده‌های ورودی و بایاس‌های آموزش داده شده به دقت بیشتری پاسخ می‌دهد و عملکرد بهتری دارد. ELU در بخش منفی خود، از یک تابع اکسپوننشیال (نمایی) استفاده می‌کند.

فرمول آن به صورت زیر است :

f(x) = { x , if x ≥ 0 a(e^(x^) - 1) ,if x < 0

در اینجا، a یک پارامتر مثبت است که به عنوان شیب ELU عمل می‌کند. این تابع به طور مشابه با ReLUبرای ورودی‌های مثبت عمل می‌کند، اما برای ورودی‌های منفی از یک تابع غیرخطی با رفتاری نرمتر استفاده می‌کند.

یکی از مزایای ELU نسبت به توابع دیگر، جلوگیری از مشکل Dying ReLU است. ELU یک تابع فعال‌سازی قدرتمند و انعطاف ‌پذیر است که می‌تواند در طیف وسیعی از وظایف یادگیری عمیق مورد استفاده قرار گیرد.


7.1.تابع فعالسازی Scaled Exponential Linear Unit (SELU):

تابع SELU با کد پایتون توسط من ایجاد شده است
تابع SELU با کد پایتون توسط من ایجاد شده است

ای ال اس یو ELUS (Scaled Exponential Linear Unit) نیز یک تابع فعال‌سازی است که از ELU مشتق گرفته شده است، یکی از مزایای بزرگ SELU این است که خروجی‌های آن از توزیعی با واریانس ثابت تولید می‌شوند. این ویژگی باعث می‌شود که شبکه‌های عصبی کوچک با استفاده از SELU بهتر بتوانند با مشکلاتی همچون گرادیان‌های ناپایداری که در شبکه‌های عمیق وجود دارند، مقابله کنند.

فرمول ریاضی این است :

f(x) = λ * x if x ≥ 0
f(x) = λ * α * (exp(x) - 1) if x < 0
λ ≈ 1.0507
α ≈ 1.6733

پارامترهای λ و α مقادیر ثابت هستند که برای بهینه سازی عملکرد SELU انتخاب شده اند.

اس ای ال یو SELU نسبت به توابع فعال‌سازی ساده ‌تر مانند ReLU یا Leaky ReLU پیچیده ‌تر است. این تابع شامل عملیات اکسپوننشیال است که ممکن است نیازمند محاسبات بیشتری باشد و از لحاظ محاسباتی هزینه‌ بر باشد. مقادیر پارامترهای SELU (مانند ضریب اکسپوننشیال) بسیار حساس هستند و نیاز به تنظیم دقیق دارند.

همانطور که در تصویر مشاهده می کنید SELU در محدوده‌ای نزدیک به صفر، رفتارش شبیه به یک تابع خطی است. این باعث می‌شود که شبکه‌های عصبی که با SELU آموزش می بینند با مشکل مرگ تابع (vanishing gradient problem) که ممکن است در توابع فعال‌سازی غیرخطی مشاهده شود، کمتر مواجه شوند .


8.1.تابع فعال سازی Softmax

تابع softmax با کد پایتون توسط من ایجاد شده است
تابع softmax با کد پایتون توسط من ایجاد شده است


تابع softmax که با نام softargmax  یا تابع نمایی نرمال شده نیز شناخته می‌شود، . خروجی‌های خام شبکه عصبی را به بردار احتمالات تبدیل می‌کند. Softmax به طور غیرمستقیم عمل نرمال‌سازی را انجام می‌دهد. هدف اصلی Softmax تبدیل مقادیر خروجی به توزیع احتمال است، اما این کار به طور ضمنی باعث نرمال‌سازی مقادیر نیز می‌شود.

یعنی قبل از اعمال softmax، برخی از مولفه های برداری ممکن است منفی یا بزرگتر از یک باشند. و ممکن است 1 نباشند. اما پس از اعمال softmax، هر جزء در بازه زمانی قرار می گیرد ( 0 ، 1 ) ، و اجزاء تا 1 جمع می شوند، به طوری که می توان آنها را به عنوان احتمال تفسیر کرد. علاوه بر این، اجزای ورودی بزرگتر با احتمالات بزرگتر مطابقت دارند.

فرمول استاندارد softmax به صورت زیر است :

σ(z)_j = e^(z_j) / (∑_(k=1)^K e^(z_k))

تابع استاندارد softmax اغلب در لایه نهایی یک طبقه‌ بندی کننده مبتنی بر شبکه عصبی استفاده می‌شود. در واقع، می‌توانید تابع softmax را به عنوان تعمیم برداری فعال‌سازی سیگموئید در نظر بگیرید. دو تابع softmax و سیگموئید هر دو مشتق پذیر هستند، این ویژگی بسیار مهم است زیرا در الگوریتم‌های بهینه‌سازی و آموزش شبکه‌های عصبی، معمولاً به محاسبه مشتقات توابع نیاز داریم. به عنوان مثال، در الگوریتم پس‌انتشار خطا (backpropagation) ، مشتقات تابع فعال‌سازی‌ برای محاسبه گرادیان‌ها استفاده می‌شوند تا وزن‌ها و سایر پارامترهای شبکه بروزرسانی شوند. بنابراین، ویژگی مشتق پذیری این توابع ضروری است .

تابع سیگموئید معمولاً برای مسائل دسته ‌بندی دو کلاسه استفاده می‌شود، در حالی که تابع softmax برای دسته‌بندی چند کلاسه استفاده می‌شود. با این حال، اگر تعداد کلاس ‌ها 2 باشد، تابع softmax به تابع سیگموئید تبدیل می‌شود.

پیاده سازی توابع فعالسازی

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

در PyTorch با"import torch.nn as nn" پیاده سازی می شوند مانند شبه کد های زیر:

import torch.nn as nn
# Define a layer with ReLU activation
relu_layer = nn.ReLU()
######################################
# Define a layer with Sigmoid activation
sigmoid_layer = nn.Sigmoid()
######################################
# Define a layer with Tanh activation
tanh_layer = nn.Tanh()

در TensorFlow با tf.keras.layers.Activation برای پیاده سازی توابع فعالسازی استفاده می شود مانند شبه کد های زیر :

import tensorflow as tf
# Example of using ReLU activation function
activation = tf.keras.layers.Activation('relu')
# Example of using Sigmoid activation function
activation = tf.keras.layers.Activation('sigmoid')
# Example of using Tanh activation function
activation = tf.keras.layers.Activation('tanh')
# Example of using Leaky ReLU activation function
activation = tf.keras.layers.LeakyReLU(alpha=0.01)
# Adjust the alpha value as needed


انواع توابع فعالساز دیگری هم وجود دارد که می توان از آن ها با توجه به مسئله ای که داریم می توانیم استفاده کنیم. اما مهم ترین و پرکاربرد ترین توابع در بالا ذکر شد. در قسمت بعد می خواهیم پس انتشار (backpropagation) را بررسی کنیم . آماده ای ؟؟؟؟؟ بزن بریم !


2 .پس انتشار (Backpropagation)

پس انتشار، مخفف "انتشار به عقب خطاها" اصطلاح "تصحیح خطای پس انتشار" در سال 1962 توسط فرانک روزنبلات معرفی شد. و همانطور که در تاریخچه مقاله ها در بالا اشاره شد توسط Paul Werbos در سال 1974 توسعه داده شد. اما تا سالها مورد توجه قرار نگرفت .انتشار پس ‌پشتی برای اولین بار در سال 1986 توصیف شد.

پس انتشار (backpropagation) یک الگوریتم اساسی در یادگیری عمیق است که " گرادیان تابع خطا(gradient of the loss function) " را نسبت به وزن‌ های شبکه به کمک مشتقات جزئی توابعی که در شبکه استفاده می‌شوند با استفاده از chian rule محاسبه می‌کند. روشی برای بروزرسانی وزن ‌ها (W) به طوریکه loss function کمترین مقدار شود. معمولاً با الگوریتم‌های بهینه‌سازی مانند کاهش تصادفی گرادیان(stochastic gradient descent (SGD)) یا نمونه های مشابه آن این کار را انجام می دهد.

ترم " backpropagation" شامل انتشار خطا به سمت عقب (propagating the error backward) از لایه خروجی به لایه ورودی است، در حالی که وزن‌ها به ‌روز رسانی می‌شوند. اما پس انتشار چیه و چرا این روش در یادگیری شبکه های عصبی خیلی مهم هست .


منبع این گیف مقاله ای در Medium است
منبع این گیف مقاله ای در Medium است


الگوریتم backpropagation دارای دو مرحله است forward pass و backward pass در اینجا ابتدا مرحله‌ی forward pass را شرح می‌دهم.

فاز فوروارد (forward pass)

مرحله‌ی forward pass در الگوریتم backpropagation ، مرحله ‌ای است که داده‌ها از لایه‌های ورودی به لایه‌های خروجی منتقل و خروجی محاسبه می‌شود. در مرحلهٔ forward pass ، توابع فعال‌سازی غیر خطی برای لایه ‌های مختلف شبکه عصبی عمل می‌کنند. برای مثال معمولا از تابع ReLU (Rectified Linear Unit) استفاده می‌شود.

این خاصیت باعث می‌شود که ReLU نورون‌هایی که فعالیت ضعیف دارند (یعنی خروجی منفی دارند) را غیرفعال کند، در نتیجه شبکه عصبی بیشتر به ویژگی‌های مهم و برجسته داده‌ها توجه کند. در مرحله forward pass ، مقادیر ورودی به هر لایه از توابع فعالسازی مورد نظر عبور می‌کنند و سپس به لایه بعدی منتقل می‌شوند. این عمل مثلا در PyTorch توسط ماژول‌های مانند `torch.relu` (برای ReLU) با شبه کد زیر می توانیم انجام دهیم که در آن مقادیر ورودی x را به تابع torch.relu می‌دهد و خروجی محاسبه شده را چاپ می‌کند.

import torch
x = torch.tensor([-1.0, 0.0, 1.0, 2.0])
output = torch.relu(x)
print(output)

یا با ماژول `torch.sigmoid` (برای Sigmoid) که به راحتی با کد زیر قابل پیاده سازی است. این کد مقادیر ورودی x را به تابع torch.sigmoid می‌دهد و خروجی محاسبه شده را چاپ می‌کند.

import torch
x = torch.tensor([-1.0, 0.0, 1.0, 2.0])
output = torch.sigmoid(x)
print(output)


محاسبه تلفات یا خطا (Loss Calculation):

تقریبا می توان گفت Loss Calculation در هنگام forward pass انجام می شوئد. داده ها به خروجی رفته اند و برای backward pass باید آماده شوند. پس باید مقدار خطا ها با Loss Calculation محاسبه شود. ما معمولاً از یک تابع هزینه (Loss Function) برای محاسبه این خطا استفاده می‌کنیم که با اختلاف مقادیر پیش‌بینی شده توسط مدل و مقادیر واقعی، خطای مدل را بدست بیاوریم. هدف ما این است که این خطا ها را کم کنیم تا مدل بهتری برای پیش‌بینی داده‌ها داشته باشیم.

در PyTorch، محاسبه تلفات به سادگی با یکی از توابع هزینه به عنوان مثال Cross Entropy Loss امکان‌ پذیر است. این توابع هزینه به عنوان یک لایه از شبکه عصبی تعریف می‌شوند و مقدار خطا را محاسبه می‌کنند و به عنوان ورودی به مرحله backward pass برای بروزرسانی وزن‌ها و بایاس‌ها وارد مرحله بعد یعنی backwar pass می شویم. توجه داشته باشید که در PyTorch، توابع هزینه به صورت خودکار محاسبه می‌شوند و شما نیازی به انجام محاسبات دستی ندارید. و به سادگی با کد زیر میتوان آن را محاسبه کرد .

# Cross Entropy Loss
criterion = nn.CrossEntropyLoss()

گذر به عقب (Backward Pass (Backpropagation)):

بعد از انجام forward pass وارد فاز جدید یعنی Backward Pass می شویم. در مرحله Backward Pass (Backpropagation)، خطا از لایه خروجی به لایه‌ های ورودی منتشر می‌شود. این انتشار خطا از انتهای شبکه به سمت ورودی ‌ها با قاعده زنجیره‌ای (Chain Rule) انجام می‌ شود. Chain Rule به ما اجازه می‌دهد که مشتق یک تابع ترکیبی را با استفاده از مشتقات تابع‌های تشکیل ‌دهنده آن محاسبه کنیم. این الگوریتم برای محاسبه گرادیان‌ها (مشتقات جزئی) نسبت به وزن ‌های شبکه در زمان آموزش استفاده می‌شود، که سپس با استفاده از روش‌های بهینه‌ سازی مانند روش گرادیان کاهشی، وزن‌ها را بروزرسانی می کند. به طور خلاصه، Backpropagation مراحل مختلفی دارد که به کمک قاعده زنجیره‌ای، خطا را از لایه‌های خروجی به لایه‌های ورودی منتشر می ‌کند و گرادیان‌ ها را محاسبه می‌کند تا وزن‌ها بروزرسانی شوند.

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

# Define the loss function
criterion = nn.MSELoss()

و سپس PyTorch به صورت خودکار مشتقات لازم را محاسبه و وزن‌ها را به ‌روز می‌کند. به عنوان مثال، می ‌توانید از کلاس‌ های torch.nn.Module و torch.optim برای تعریف شبکه عصبی و انتخاب روش بهینه ‌سازی استفاده کنید. سپس با تعریف تابع خطا و استفاده از توابع مشتق ‌گیری PyTorch، می ‌توانید شبکه را آموزش دهید.

در کلاس torch.nn.Module:

import torch.nn as nn
# Define a simple neural network model
class SimpleModel(nn.Module):
def __init__(self):
super(SimpleModel, self).__init__()
self.fc1 = nn.Linear(64, 32) # Define a linear layer with input size 64 and output size 32
self.fc2 = nn.Linear(32, 10) # Define another linear layer with input size 32 and output size 10
def forward(self, x):
x = nn.functional.relu(self.fc1(x)) # Apply ReLU activation function to the output of first layer
x = self.fc2(x) # Get the output of second layer
return x
# Create an instance of the SimpleModel
model = SimpleModel()

در کلاس torch.optim:

import torch.optim as optim
# Define the model
model = SimpleModel()
# Define the optimizer
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
# Define the loss function
criterion = nn.CrossEntropyLoss()
# Inside the training loop:
# optimizer.zero_grad() # Clear gradients
# outputs = model(inputs) # Forward pass
# loss = criterion(outputs, labels) # Calculate loss
# loss.backward() # Backward pass
# optimizer.step() # Update weights

به‌روزرسانی وزن‌ها (Update Weights):

بعد از طی کردن همه مراحل پس انتشار و محاسبه خطا و برگشت مقدار خطا از لایه خروجی به لایه ورودی و محاسبه گرادیان ها باید وزن ها بروزرسانی شوند. وزن (Weight) در مدل‌های یاد گیری عمیق یکی از پارامترهای مهم است که مقادیر آن در زمان آموزش مدل بروزرسانی می‌شود. بروزرسانی وزن به معنای تغییر مقادیر وزن‌ها است به نحوی که مدل بهترین عملکرد را داشته باشد. این بروزرسانی معمولاً با استفاده از الگوریتم‌های بهینه‌سازی مانند Gradient Descent و یا روش‌های بهینه‌سازی پیشرفته ‌تری مانند Adam انجام می‌شود. در برخی موارد، ممکن است بهینه‌ سازی انجام شده باشد و بروزرسانی وزن‌ها لازم نباشد.

در کتابخانه PyTorch ، وزن‌ها به ‌صورت شیء torch.nn.Parameter نمایش داده می‌شوند و می ‌توانند به ‌راحتی در کد تعریف و مدیریت شوند.

import torch
import torch.nn as nn
# Define a linear layer with two inputs and one output
class LinearLayer(nn.Module):
def __init__(self):
super(LinearLayer, self).__init__()
self.weight = nn.Parameter(torch.randn(2, 1)) # Define weights as a Parameter object
self.bias = nn.Parameter(torch.randn(1)) # Define bias as a Parameter object
def forward(self, x):
return torch.matmul(x, self.weight) + self.bias

در این کد، ما یک لایه خطی با دو ورودی و یک خروجی تعریف کرده‌ایم. وزن‌ها (weight) و انحراف (bias) به عنوان اشیاء torch.nn.Parameter درون تابع __init__ تعریف شده‌اند.

به طور خلاصه، backpropagation فرآیند محاسبه گرادیان‌ها و بروزرسانی وزن ‌های شبکه عصبی است که از خطاهای مشاهده شده در لایه‌های خروجی شروع می‌شود و به سمت لایه ‌های ورودی حرکت می ‌کند تا وزن‌های شبکه بروز شوند و عملکرد شبکه بهبود یابد.


نتیجه‌گیری پارت 2

در این پارت توانستیم راجع به توابع فعالساز مهم مطالب مهمی را بیاموزیم و بفهمیم که در منطق آنها چه می گذرد. در واقع ما هنگام استفاده از آنها فقط این توابع را صدا میزنیم و در پشت صحنه تمام محاسبات انجام می شود . استفاده از تابع فعالساز مناسب به گرفتن نتیجه مطلوب در شبکه کمک می کند . زمانیکه ما بدانیم که در منطق آنها چه می گذرد هنگام استفاده بهتر می توانیم تصمیم بگیریم که کدام یک را به کار ببریم. همچنین الگوریتم پس انتشار را بررسی کردیم و فاز های مختلف آن را آموختیم که با قاعده زنجیره ای chian rule که در واقع مشتق گیری از وزن هاست و اساس پس انتشار می باشد آشنا شدیم. در واقع پس انتشار ترکیبی از Feedforward و Feedback است که در دو فاز مهم در نحوه یادگیری شبکه هستند. فاز های پس انتشار در 4 مرحله پیوسته انجام می شود. می توانیم بگوییم که درهمه شبکه های عصبی مرحله Feedforward وجود دارد چرا که وزن های داده های ورودی به شبکه باید برای انجام محاسبات و رفتن به خروجی این مرحله را داشته باشند .

در پارت 3 به موضوع بسیار مهم معماری شبکه های عصبی خواهیم پرداخت. انواع معماری های مهم (RNN, LSTM, CNN) رو خواهیم آموخت. و کاربردهایی که در حال حاظر در دنیای واقعی دارند. من برای یادگیری اونها بسیار شگفت زده هستم. بسیاری از سوالاتی که ممکن است در ذهن شما باشد با فهمیدن این معماری ها پاسخ داده خواهد شد. به من بپیوندید . نظرات خودتون رو راجع به این مقاله 3 قسمتی با من در اشتراک بگذارید . ممنون


ممنون از شما که در این سفر به دنیای Deeps با من همراه بودید.

لینک مقاله به انگلیسی در Medium

لینک پارت 1 مقاله به انگلیسی در Medium

پارت 1 : تاریخچه هوش مصنوعی و چگونگی عملکرد شبکه های عصبی ساده

پارت 2 : نحوه یادگیری در شبکه های عصبی عمیق ( انواع یادگیری و توابع فعالساز و پس انتشار( backpropagation)

پارت 3 : انواع مهم ترین معماری های شبکه های عصبی عمیق (RNN / LSTM / CNN )

لطفا اگر مایل بودید مقاله دیگر من " آیا DeepFake فناوری آینده‌ای است یا یک تهدید؟ آیا خط میان واقعیت و ساختگی از بین خواهد رفت؟" رو در ویرگول ببینید .

صفر تا صد درباره PCA یا بررسی دقیق تحلیل مؤلفه‌های اصلی بدانید

امیدوارم از مطالب لذت برده باشید .


ماشین لرنینگدیپ لرنینگروش پس انتشاریادگیری‌ماشین
جونیور دیتاساینتیتست
شاید از این پست‌ها خوشتان بیاید