توابع فعال سازی
در این فصل، به چند مورد از توابع فعال سازی می پردازیم و نقش آنها را مورد بحث قرار می دهیم. ما از توابع فعال سازی متفاوتی برای موارد مختلف استفاده می کنیم و درک نحوه عملکرد آنها می تواند به شما کمک کند تا کدام یک از آنها را به درستی برای کارتان انتخاب کنید. عملکرد فعال سازی به خروجی یک نورون (یا لایه ای از نورون ها) اعمال می شود که خروجی ها را تغییر می دهد. ما از توابع فعال سازی استفاده می کنیم زیرا اگر تابع فعال سازی خود غیرخطی باشد، به شبکه های عصبی با دو یا چند لایه پنهان اجازه می دهد تا توابع غیرخطی را نقشه برداری کنند. ما در این فصل نحوه عملکرد آن را نشان خواهیم داد.
به طور کلی، شبکه عصبی شما دو نوع عملکرد فعال سازی خواهد داشت. اولی تابع فعال سازی مورد استفاده در لایه های پنهان و دومی در لایه خروجی استفاده می شود. معمولا عملکرد فعال سازی مورد استفاده برای نورون های پنهان برای همه آنها یکسان خواهد بود، اما لازم نیست.
عملکرد فعال سازی مرحله ای
به یاد بیاورید که هدفی که این تابع فعال سازی انجام می دهد، تقلید از یک نورون “شلیک” یا “شلیک نکردن” بر اساس اطلاعات ورودی است. ساده ترین نسخه این تابع مرحله ای است. در یک نورون واحد، اگر weights · inputs + bias نتیجه آن مقداری بیشتر از 0 است، نورون شلیک می کند و 1 را تولید می کند. در غیر این صورت، 0 خروجی می دهد.
شکل 4.01: نمودار عملکرد مرحله.
این تابع فعال سازی در طول تاریخ در لایه های پنهان مورد استفاده قرار گرفته است، اما امروزه به ندرت یک انتخاب است.
تابع فعال سازی خطی
یک تابع خطی به سادگی معادله یک خط است. هنگام نمودار به صورت یک خط مستقیم ظاهر می شود، جایی که y=x و مقدار خروجی برابر با ورودی است.
شکل 4.02: نمودار تابع خطی.
این تابع فعال سازی معمولا در مورد یک مدل رگرسیون برای خروجی آخرین لایه اعمال می شود – مدلی که به جای طبقه بندی، یک مقدار اسکالر خروجی می دهد. ما رگرسیون را در فصل 17 و به زودی در مثالی در این فصل پوشش خواهیم داد.
تابع فعال سازی Sigmoid
مشکل تابع step این است که چندان آموزنده نیست. وقتی به آموزش و بهینه سازهای شبکه می رسیم، خواهید دید که نحوه کار یک بهینه ساز با ارزیابی تأثیرات فردی وزن ها و سوگیری ها بر خروجی شبکه است. مشکل تابع step این است که برای بهینه ساز کمتر مشخص است که این تأثیرات چیست زیرا اطلاعات بسیار کمی از این تابع جمع آوری شده است. یا در (1) یا خاموش (0) است. سخت است که بگوییم این تابع مرحله چقدر به فعال سازی یا غیرفعال کردن “نزدیک” بوده است. شاید خیلی نزدیک بود، یا شاید خیلی دور بود. از نظر مقدار خروجی نهایی از شبکه، فرقی نمی کند که به خروجی چیز دیگری نزدیک باشد. بنابراین، وقتی زمان بهینه سازی وزن ها و تعصبات فرا می رسد، اگر توابع فعال سازی دقیق تر و آموزنده تری داشته باشیم، برای بهینه ساز آسان تر است.
تابع فعال سازی اصلی و دانه ای تر مورد استفاده برای شبکه های عصبی بود Sigmoid عملکرد فعال سازی، که به نظر می رسد:
شکل 4.03: نمودار تابع سیگموئید.
این تابع مقداری را در محدوده 0 برای بی نهایت منفی، از طریق 0.5 برای ورودی 0 و به 1 برای بی نهایت مثبت برمی گرداند. در فصل 16 بیشتر در مورد این تابع صحبت خواهیم کرد.
همانطور که قبلا ذکر شد، با “نورون های مرده”، معمولا بهتر است رویکرد دانه ای تری برای عملکردهای فعال سازی نورون پنهان داشته باشید. در این مورد، ما مقداری دریافت می کنیم که می تواند به مقدار اصلی خود معکوس شود. مقدار برگشتی شامل تمام اطلاعات ورودی است، برخلاف تابعی مانند تابع step، که در آن ورودی 3 همان مقدار ورودی 300000 را خروجی می دهد. خروجی تابع سیگموئید که در محدوده 0 تا 1 قرار دارد، با شبکه های عصبی نیز بهتر کار می کند – به ویژه در مقایسه با محدوده بی نهایت منفی تا مثبت – و غیرخطی بودن را اضافه می کند. اهمیت غیرخطی بودن به زودی در این فصل روشن تر خواهد شد. تابع سیگموئید، که از نظر تاریخی در لایه های پنهان استفاده می شد، در نهایت با تابع فعال سازی واحدهای خطی اصلاح شده (یا ReLU) جایگزین شد. با این حال، ما از سیگموئید استفاده خواهیم کرد، به عنوان تابع فعال سازی لایه خروجی در فصل 16.
تابع فعال سازی خطی اصلاح شده
شکل 4.04: نمودار عملکرد فعال سازی ReLU.
تابع فعال سازی خطی اصلاح شده ساده تر از سیگموئید است. این کاملا به معنای واقعی کلمه y=x است که از سمت منفی در 0 بریده شده است. اگر x کمتر یا مساوی 0 باشد، y 0 است – در غیر این صورت، y برابر با x است.
این عملکرد فعال سازی ساده و در عین حال قدرتمند، به دلایل مختلف – عمدتا سرعت و کارایی، پرکاربردترین عملکرد فعال سازی در زمان نگارش این مقاله است. در حالی که عملکرد فعال سازی سیگموئید پیچیده ترین نیست، اما محاسبه آن بسیار چالش برانگیزتر از عملکرد فعال سازی ReLU است. تابع فعال سازی ReLU بسیار نزدیک به یک تابع فعال سازی خطی است در حالی که غیر خطی باقی می ماند، به دلیل آن خم شدن پس از 0. با این حال، این ویژگی ساده بسیار موثر است.
چرا از توابع فعال سازی استفاده کنیم؟
اکنون که فهمیدیم توابع فعال سازی چه چیزی را نشان می دهند، برخی از آنها چگونه به نظر می رسند و چه چیزی را برمی گردانند، بیایید بحث کنیم که چرا در وهله اول از توابع فعال سازی استفاده می کنیم. در بیشتر موارد، برای اینکه یک شبکه عصبی با یک تابع غیرخطی مطابقت داشته باشد، باید شامل دو یا چند لایه پنهان باشد و برای استفاده از یک تابع فعال سازی غیرخطی به آن لایه های پنهان نیاز داریم.
اول از همه، تابع غیرخطی چیست؟ یک تابع غیرخطی را نمی توان به خوبی با یک خط مستقیم نشان داد، مانند یک تابع سینوسی:
شکل 4.05: نمودار y=sin(x)
در حالی که مطمئنا مشکلاتی در زندگی وجود دارد که ماهیت خطی دارند، به عنوان مثال، تلاش برای فهمیدن هزینه تعدادی پیراهن، و ما هزینه یک پیراهن فردی را می دانیم، و اینکه هیچ تخفیف عمده ای وجود ندارد، پس معادله محاسبه قیمت هر تعداد از آن محصولات یک معادله خطی است. سایر مشکلات زندگی مانند قیمت خانه چندان ساده نیستند. تعداد عواملی که نقش دارند، مانند اندازه، مکان، زمان سال برای فروش، تعداد اتاق ها، حیاط، محله و غیره، قیمت گذاری خانه را به یک معادله غیرخطی تبدیل می کند. بسیاری از مشکلات جالب و سخت زمان ما غیرخطی هستند. جذابیت اصلی شبکه های عصبی مربوط به توانایی آنها در حل مسائل غیرخطی است. ابتدا ، بیایید وضعیتی را در نظر بگیریم که در آن نورون ها عملکرد فعال سازی ندارند ، که همان عملکرد فعال سازی y = x است. با استفاده از این تابع فعال سازی خطی در یک شبکه عصبی با 2 لایه پنهان هر کدام از 8 نورون، نتیجه آموزش این مدل به صورت زیر خواهد بود:
شکل 4.06: شبکه عصبی با توابع فعال سازی خطی در لایه های پنهان که سعی در برازش دارند
y=sin(x)
هنگام استفاده از همان 2 لایه پنهان از 8 نورون که هر کدام با عملکرد فعال سازی خطی اصلاح شده هستند، نتیجه زیر را پس از آموزش مشاهده می کنیم:
شکل 4.07: توابع فعال سازی ReLU در لایه های مخفی که سعی می کنند y=sin(x) را بجا کنند
فعال سازی خطی در لایه های پنهان
اکنون که می بینید که اینطور است، هنوز باید در نظر بگیریم که چرا اینطور است. برای شروع، بیایید تابع فعال سازی خطی y=x را دوباره بررسی کنیم و بیایید این را در سطح نورون منفرد در نظر بگیریم. با توجه به مقادیر وزن ها و بایاس ها، خروجی یک نورون با تابع فعال سازی y=x چقدر خواهد بود؟ بیایید به چند مثال نگاه کنیم – ابتدا، بیایید سعی کنیم وزن اول را با یک مقدار مثبت به روز کنیم:
شکل 4.08: نمونه ای از خروجی با یک نورون با استفاده از یک تابع فعال سازی خطی.
همانطور که به تغییر وزن ها ادامه می دهیم، این بار با یک عدد منفی به روز می کنیم:
شکل 4.09: نمونه ای از خروجی با یک نورون با استفاده از یک تابع فعال سازی خطی، وزن به روز شده.
و به روز رسانی وزن ها و علاوه بر این یک bias :
شکل 4.10: نمونه ای از خروجی با یک نورون با استفاده از یک تابع فعال سازی خطی، به روز شده دیگری وزن
مهم نیست که با وزن ها و تعصبات این نورون چه کاری انجام دهیم ، خروجی این نورون کاملا خطی به y = x تابع فعال سازی خواهد بود. این ماهیت خطی در کل شبکه ادامه خواهد داشت:
شکل 4.11: یک شبکه عصبی با تمام توابع فعال سازی خطی.
مهم نیست که چه کاری انجام می دهیم، هر چقدر هم که لایه هایی داشته باشیم، این شبکه تنها در صورتی می تواند روابط خطی را به تصویر بکشد که از توابع فعال سازی خطی استفاده کنیم. باید کاملا واضح باشد که این مورد خواهد بود زیرا هر نورون در هر لایه به صورت خطی عمل می کند، بنابراین کل شبکه نیز یک تابع خطی است.
فعال سازی ReLU در یک جفت نورون
ما معتقدیم که کمتر واضح است که چگونه با یک تابع فعال سازی به سختی غیرخطی، مانند تابع فعال سازی خطی اصلاح شده، می توانیم به طور ناگهانی روابط و توابع غیرخطی را ترسیم کنیم، بنابراین حالا بیایید آن را پوشش دهیم. بیایید دوباره با یک نورون شروع کنیم. ما هم با وزن 0 و هم با بایاس 0 شروع می کنیم:
شکل 4.12: تک نورون با تک ورودی (وزن صفر) و عملکرد فعال سازی ReLU.
در این حالت ، مهم نیست که چه ورودی را منتقل کنیم ، خروجی این نورون همیشه 0 خواهد بود ، زیرا وزن 0 است و هیچ سوگیری وجود ندارد. بیایید وزن را 1 تنظیم کنیم:
شکل 4.13: تک نورون با تک ورودی و عملکرد فعال سازی ReLU، وزن روی 1.0 تنظیم شده است.
اکنون دقیقا مانند تابع خطی اصلاح شده اولیه به نظر می رسد، هنوز جای تعجب نیست! حالا بیایید دستور bias به 0.50:
شکل 4.14[3] : تک نورون با تک ورودی و عملکرد فعال سازی ReLU ، بایاس اعمال می شود.
می توانیم ببینیم که ، در این حالت ، با یک نورون واحد ، بایاس نقطه فعال سازی عملکرد کلی را به صورت افقی جبران می کند. با افزایش سوگیری، ما این نورون را زودتر فعال می کنیم. وقتی وزن را تا -1.0 نفی می کنیم چه اتفاقی می افتد؟
شکل 4.15: تک نورون با تک ورودی و عملکرد فعال سازی ReLU، وزن منفی.
با وزن منفی و این نورون منفرد ، عملکرد به این سوال تبدیل شده است که چه زمانی این نورون غیرفعال می شود. تا این نقطه، شما دیده اید که چگونه می توانیم از بایاس برای افست کردن تابع به صورت افقی و وزن برای تأثیرگذاری بر شیب فعال سازی استفاده کنیم. علاوه بر این، ما همچنین می توانیم کنترل کنیم که آیا این عملکرد برای تعیین محل فعال یا غیرفعال شدن نورون است یا خیر.
چه اتفاقی می افتد وقتی به جای یک نورون ، یک جفت نورون داشته باشیم؟ به عنوان مثال، بیایید وانمود کنیم که هر کدام 2 لایه پنهان از 1 نورون داریم. با فکر کردن به تابع فعال سازی y=x، جای تعجب نیست که یک تابع فعال سازی خطی بدون توجه به زنجیره ای از نورون ها نتایج خطی ایجاد می کند. بیایید ببینیم چه اتفاقی برای تابع خطی اصلاح شده برای فعال سازی می افتد. ما با آخرین مقادیر برای نورون اول و وزن 1 با بایاس 0 برای نورون دوم شروع می کنیم:
شکل 4.16: جفت نورون با ورودی های منفرد و توابع فعال سازی ReLU.
همانطور که تاکنون می بینیم، هیچ تغییری وجود ندارد. این به این دلیل است که سوگیری نورون دوم هیچ جبرانی انجام نمی دهد و وزن نورون دوم فقط خروجی را در 1 ضرب می کند، بنابراین هیچ تغییری وجود ندارد. بیایید سعی کنیم تعصب نورون دوم را اکنون تنظیم کنیم:
شکل 4.17: جفت نورون با ورودی های منفرد و توابع فعال سازی ReLU ، سایر سوگیری ها اعمال می شود.
اکنون ما رفتارهای نسبتا جالبی را می بینیم. سوگیری نورون دوم در واقع عملکرد کلی را تغییر داد، اما به جای تغییر آن به صورت افقی، عملکرد را به صورت عمودی تغییر داد. پس چه اتفاقی ممکن است بیفتد اگر وزن آن نورون دوم را به جای 1 -2 کنیم؟
شکل 4.18: جفت نورون با ورودی های منفرد و توابع فعال سازی ReLU، وزن منفی دیگر.
اتفاق هیجان انگیزی رخ داده است! آنچه ما در اینجا داریم یک نورون است که هم دارای یک نقطه فعال سازی و هم یک نقطه غیرفعال[5] [6] سازی است. هنگامی که هر دو نورون فعال می شوند ، هنگامی که “منطقه اثر” آنها وارد عمل می شود ، مقادیری را در محدوده دانه بندی ، متغیر و خروجی تولید می کنند. اگر هر نورون در جفت غیرفعال باشد ، این جفت خروجی غیر متغیر تولید می کند:
شکل 4.19: جفت نورون با ورودی های منفرد و توابع فعال سازی ReLU، منطقه اثر.
فعال سازی ReLU در لایه های پنهان
بیایید اکنون این مفهوم را در نظر بگیریم و از آن استفاده کنیم تا با استفاده از 2 لایه پنهان از 8 نورون هر کدام با تابع موج سینوسی مطابقت داشته باشد و می توانیم مقادیر را به صورت دستی تنظیم کنیم تا متناسب با منحنی باشد. ما این کار را با کار با 1 جفت نورون در یک زمان انجام خواهیم داد، به این معنی که 1 نورون از هر لایه به صورت جداگانه است. برای سادگی، ما همچنین فرض می کنیم که لایه ها به طور متراکم به هم متصل نیستند و هر نورون از اولین لایه پنهان تنها به یک نورون از لایه پنهان دوم متصل می شود. این معمولا در مورد مدل های واقعی صدق نمی کند، اما ما این ساده سازی را برای هدف این نسخه ی نمایشی می خواهیم. علاوه بر این، این مدل نمونه یک مقدار واحد را به عنوان ورودی، ورودی به تابع سینوسی می گیرد و یک مقدار واحد مانند تابع سینوسی را خروجی می دهد. لایه خروجی از تابع فعال سازی خطی استفاده می کند و لایه های پنهان از تابع فعال سازی خطی اصلاح شده استفاده می کنند.
برای شروع، همه وزن ها را روی 0 تنظیم می کنیم و با اولین جفت نورون کار می کنیم:
شکل 4.20: تنظیم دستی یک شبکه عصبی که با اولین جفت نورون شروع می شود.
در مرحله بعد، می توانیم وزن نورون های لایه پنهان و نورون خروجی را روی 1 تنظیم کنیم و می توانیم ببینیم که چگونه این بر خروجی تأثیر می گذارد:
شکل 4.21: تنظیم وزن برای جفت اول / بالا از نورون ها همه به 1.
در این حالت، می توانیم ببینیم که شیب تابع کلی تحت تأثیر قرار گرفته است. ما می توانیم این شیب را با تنظیم وزن اولین نورون لایه اول به 6.0 افزایش دهیم:
شکل 4.22: تنظیم وزن برای اولین نورون پنهان به 6.
برای مثال، اکنون می توانیم ببینیم که شیب اولیه این تابع همان چیزی است که ما می خواهیم، اما ما یک مشکل داریم. در حال حاضر، این عملکرد هرگز پایان نمی یابد زیرا این جفت نورون هرگز غیرفعال نمی شود. ما می توانیم به صورت بصری ببینیم که کجا می خواهیم غیرفعال سازی اتفاق بیفتد. اینجاست که خط اتصالات قرمز (خروجی شبکه عصبی فعلی ما) در ابتدا از موج سینوسی سبز جدا می شود. بنابراین اکنون، در حالی که شیب صحیح را داریم، باید این نقطه را به عنوان نقطه غیرفعال سازی خود تنظیم کنیم. برای انجام این کار، ما با افزایش بایاس برای نورون دوم جفت لایه پنهان به 0.70 شروع می کنیم. به یاد بیاورید که این عملکرد کلی را به صورت عمودی جبران می کند:
شکل 4.23: استفاده از بایاس برای دومین نورون پنهان در جفت بالا برای جبران عملکرد به صورت عمودی.
اکنون می توانیم وزن نورون 2 را روی -1 تنظیم کنیم و باعث ایجاد یک نقطه غیرفعال شدن ، حداقل به صورت افقی ، در جایی که می خواهیم رخ دهد:
شکل 4.24: تنظیم وزن نورون دوم در جفت بالا به -1.
حالا می خواهیم این شیب را برگردانیم. چگونه می توانیم خروجی این دو نورون را برگردانیم؟ به نظر می رسد که ما می توانیم وزن اتصال به نورون خروجی را که در حال حاضر 1.0 است بگیریم و فقط آن را به -1 برگردانیم و این تابع را برگرداند:
شکل 4.25: تنظیم وزن نورون خروجی روی -1.
ما مطمئنا در حال نزدیک شدن به این هستیم که این بخش اول را آنطور که می خواهیم متناسب کنیم. حالا، تنها کاری که باید انجام دهیم این است که این را کمی جبران کنیم. برای این مثال بهینه سازی شده با دست، ما می خواهیم از 7 جفت اول نورون در لایه های پنهان برای ایجاد شکل موج سینوسی استفاده کنیم، سپس از جفت پایین برای جبران همه چیز به صورت عمودی استفاده کنیم. اگر بایاس نورون دوم را در جفت پایین روی 1.0 و وزن نورون خروجی را 0.7 تنظیم کنیم ، می توانیم خط را به صورت عمودی به این صورت تغییر دهیم:
شکل 4.26: استفاده از جفت پایین نورون ها برای جبران کل عملکرد شبکه عصبی.
در این مرحله، بخش اول را با “ناحیه اثر” که اولین بخش صعودی موج سینوسی است، به پایان رسانده ایم. می توانیم از بخش بعدی که می خواهیم انجام دهیم شروع کنیم. ما می توانیم با تنظیم تمام وزن ها برای این جفت دوم نورون ها روی 1، از جمله نورون خروجی شروع کنیم:
شکل 4.27: شروع به تنظیم جفت دوم نورون ها (از بالا) برای بخش بعدی
عملکرد کلی.
در این مرحله، فعال شدن این جفت دوم نورون ها خیلی زود شروع می شود، که بر “ناحیه اثر” جفت بالایی که قبلا تراز کرده ایم تأثیر می گذارد. برای رفع این مشکل، ما می خواهیم این جفت دوم شروع به تأثیرگذاری بر خروجی کند، جایی که جفت اول غیرفعال می شود، بنابراین می خواهیم تابع را به صورت افقی تنظیم کنیم. [8] همانطور که از قبل به یاد می آورید، ما سوگیری اولین نورون را در این جفت نورون تنظیم می کنیم تا به این هدف برسیم. همچنین، برای تغییر شیب، وزنی را که به آن نورون اول می آید برای جفت دوم تنظیم می کنیم و آن را روی 3.5 تنظیم می کنیم. این همان روشی است که ما برای تنظیم شیب بخش اول استفاده کردیم که توسط جفت بالای نورون ها در لایه پنهان کنترل می شود. پس از این تنظیمات:
شکل 4.28: تنظیم وزن و بایاس به اولین نورون جفت دوم.
اکنون از همان متدولوژی استفاده خواهیم کرد که با جفت اول برای تنظیم نقطه غیرفعال سازی استفاده کردیم. وزن نورون دوم را در جفت لایه پنهان روی -1 و بایاس را روی 0.27 تنظیم کردیم.
شکل 4.29: تنظیم بایاس نورون 2 در جفت دوم.
سپس می توانیم عملکرد این بخش را برگردانیم، دوباره به همان روشی که با بخش اول انجام دادیم، با تنظیم وزن به نورون خروجی از 1.0 به -1.0:
شکل 4.30: چرخاندن بخش تابع جفت دوم، چرخاندن وزن به نورون خروجی.
و دوباره، درست مانند جفت اول، از جفت پایین برای رفع افست عمودی استفاده خواهیم کرد:
شکل 4.31: استفاده از جفت پایین نورون ها برای تنظیم عملکرد کلی شبکه.
سپس فقط به این روش ادامه می دهیم. ما آن را برای بخش بالا صاف می گذاریم، به این معنی که ما فقط زمانی فعال سازی را برای جفت سوم نورون لایه پنهان شروع می کنیم که بخواهیم شیب شروع به پایین آمدن کند:
شکل 4.32: تنظیم جفت سوم نورون ها برای بخش بعدی.
این فرآیند به سادگی برای هر بخش تکرار می شود و نتیجه نهایی را به ما می دهد:
شکل 4.33: فرآیند تکمیل شده (برای همه مقادیر به انیمیشن مراجعه کنید).
سپس می توانیم شروع به انتقال داده ها کنیم تا ببینیم چگونه مناطق تأثیر این نورون ها وارد عمل می شوند – تنها زمانی که هر دو نورون بر اساس ورودی فعال شوند:
شکل 4.34: نمونه ای از داده های عبوری از این مدل دست ساز.
در این حالت ، با توجه به ورودی 0.08 ، می توانیم ببینیم که تنها جفت های فعال شده ، جفت های برتر هستند ، زیرا این منطقه تأثیر آنها است. در ادامه با یک مثال دیگر:
شکل 4.35: نمونه ای از داده های عبوری از این مدل دست ساز.
در این حالت ، فقط جفت چهارم نورون فعال می شود.[9] همانطور که می بینید، حتی بدون هیچ یک از وزن های دیگر، ما از برخی ویژگی های خام یک جفت نورون با توابع فعال سازی خطی اصلاح شده استفاده کرده ایم تا به خوبی با این موج سینوسی مطابقت داشته باشیم. اگر اکنون همه وزن ها را فعال کنیم و به یک بهینه ساز ریاضی اجازه دهیم آموزش ببیند، می توانیم تناسب بهتری را ببینیم:
شکل 4.36: نمونه ای از اتصالات پس از اتصال کامل نورون ها و استفاده از بهینه ساز.
انیمیشن برای کل مفهوم اتصالات ReLU:
Anim 4.12-4.36: https://nnfs.io/mvp
اکنون باید برای شما منطقی تر شود که چگونه نورون های بیشتری می توانند مناطق منحصر به فرد تری از تأثیر را فعال کنند، چرا ما به دو یا چند لایه پنهان نیاز داریم، و چرا برای نقشه برداری از مسائل غیرخطی به توابع فعال سازی غیرخطی نیاز داریم. به عنوان مثال بیشتر، می توانیم مثال بالا را با 2 لایه پنهان از 8 نورون در نظر بگیریم و در عوض از 64 نورون در هر لایه پنهان استفاده کنیم و شاهد پیشرفت مداوم تر باشیم:
شکل 4.37: اتصالات با 2 لایه پنهان از 64 نورون هر کدام ، کاملا متصل ، با بهینه ساز.
Anim 4.37: https://nnfs.io/moo
کد عملکرد فعال سازی ReLU
با وجود نام فانتزی ، تابع فعال سازی خطی اصلاح شده برای کدگذاری ساده است. نزدیک ترین به تعریف آن:
inputs = [0, 2, -1, 3.3, -2.7, 1.1, 2.2, -100]
output = []
for i in inputs:
if i > 0:
output.append(i)
else:
output.append(0)
print(output)
>>>
[0, 2, 0, 3.3, 0, 1.1, 2.2, 0]
ما لیستی از ارزش ها را برای شروع تهیه کردیم. ReLU در این کد حلقه ای است که در آن بررسی می کنیم که آیا مقدار فعلی بزرگتر از 0 است یا خیر. اگر اینطور باشد، ما آن را به لیست خروجی اضافه می کنیم، و اگر اینطور نیست، 0 را اضافه می کنیم. این را می توان ساده تر نوشت، زیرا ما فقط باید بزرگترین مقدار را در نظر بگیریم: 0 یا مقدار نورون. مثلا:
inputs = [0, 2, -1, 3.3, -2.7, 1.1, 2.2, -100]
output = []
for i in inputs:
output.append(max(0, i))
print(output)
>>>
[0, 2, 0, 3.3, 0, 1.1, 2.2, 0]
NumPy contains an equivalent — np.maximum():
import numpy as np
inputs = [0, 2, -1, 3.3, -2.7, 1.1, 2.2, -100]
output = np.maximum(0, inputs)
print(output)
>>>
[0. 2. 0. 3.3 0. 1.1 2.2 0. ]
این روش هر عنصر از لیست ورودی (یا یک آرایه) را مقایسه می کند و یک شی با همان شکل پر از مقادیر جدید را برمی گرداند. ما از آن در کلاس فعال سازی خطی اصلاح شده جدید خود استفاده خواهیم کرد:
# فعال سازی ReLU
class Activation_ReLU:
# Forward pass
def forward(self, inputs):
# Calculate output values from input
self.output = np.maximum(0, inputs)
بیایید این تابع فعال سازی را روی خروجی های لایه متراکم در کد خود اعمال کنیم:
# مجموعه داده ایجاد کنید
X, y = spiral_data(samples=100, classes=3)
# ایجاد لایه متراکم با 2 ویژگی ورودی و 3 مقدار خروجی
dense1 = Layer_Dense(2, 3)
# ایجاد فعال سازی ReLU (برای استفاده با لایه متراکم):
activation1 = Activation_ReLU()
# از طریق این لایه داده های آموزشی خود را به جلو منتقل کنید
dense1.forward(X)
# عبور رو به جلو از طریق عملکرد فعال سازی.
# خروجی لایه قبلی را دریافت می کند
activation1.forward(dense1.output)
# بیایید خروجی چند نمونه اول را ببینیم:
print(activation1.output[:5])
>>>
[[0. 0. 0. ]
[0. 0.00011395 0. ]
[0. 0.00031729 0. ]
[0. 0.00052666 0. ]
[0. 0.00071401 0. ]]
همانطور که می بینید، مقادیر منفی بریده شده اند (به صفر اصلاح شده اند). این تمام چیزی است که در تابع فعال سازی خطی اصلاح شده مورد استفاده در لایه پنهان وجود دارد. بیایید در مورد تابع فعال سازی که قرار است در خروجی آخرین لایه استفاده کنیم صحبت کنیم.
عملکرد فعال سازی Softmax
در مورد ما، ما به دنبال این هستیم که این مدل به عنوان یک طبقه بندی کننده باشد، بنابراین ما یک تابع فعال سازی برای طبقه بندی می خواهیم. یکی از این موارد عملکرد فعال سازی Softmax است. اول، چرا با یک تابع فعال سازی دیگر زحمت می کشیم؟ این فقط بستگی به اهداف کلی ما دارد. در این حالت ، واحد خطی اصلاح شده نامحدود است ، با واحدهای دیگر نرمال نشده و انحصاری است. “نرمال نشده” به این معنی است که مقادیر می توانند هر چیزی باشند، خروجی [12، 99، 318] بدون زمینه است، و “انحصاری” به این معنی است که هر خروجی مستقل از خروجی های دیگر است. برای رفع این فقدان زمینه، فعال سازی softmax در داده های خروجی می تواند ورودی های نرمال نشده یا کالیبره نشده را بگیرد و توزیع نرمال احتمالات را برای کلاس های ما ایجاد کند. در مورد طبقه بندی، آنچه می خواهیم ببینیم پیش بینی این است که شبکه “فکر می کند” ورودی نشان دهنده کدام کلاس است. این توزیع بازگردانده شده توسط تابع فعال سازی softmax نشان دهنده نمرات اطمینان برای هر کلاس است و تا 1 جمع می شود. کلاس پیش بینی شده با نورون خروجی مرتبط است که بیشترین امتیاز اطمینان را بازگردانده است. با این حال، ما همچنین می توانیم به سایر امتیازات اطمینان در الگوریتم/برنامه فراگیر خود که از این شبکه استفاده می کند، توجه کنیم. به عنوان مثال، اگر شبکه ما توزیع اطمینان برای دو کلاس داشته باشد: [0.45، 0.55]، پیش بینی کلاس 2 است، اما اطمینان به این پیش بینی خیلی زیاد نیست. شاید برنامه ما در این مورد عمل نمی کند زیرا اعتماد به نفس زیادی ندارد.
در اینجا عملکرد Softmax آمده است:
ممکن است دلهره آور به نظر برسد، اما می توانیم آن را به قطعات ساده تقسیم کنیم و آن را در کد پایتون بیان کنیم، که ممکن است متوجه شوید که قابل دسترس تر از فرمول بالا است. برای شروع، در اینجا نمونه ای از خروجی های یک لایه شبکه عصبی آورده شده است:
layer_outputs = [4.8, 1.21, 2.385]
اولین قدم برای ما این است که خروجی ها را “نمایی” کنیم. ما این کار را با عدد اویلر انجام می دهیم، e، که تقریبا 2.71828182846 است و به عنوان عدد “رشد نمایی” شناخته می شود. نمایی کردن این ثابت را به توان پارامتر داده شده می رساند:
هم عدد و هم مخرج تابع Softmax حاوی e است که به توان z افزایش یافته است، جایی که z، شاخص های داده شده، به معنای یک مقدار خروجی مفرد است – شاخص i به معنای نمونه فعلی و شاخص j به معنای خروجی فعلی در این نمونه است. صورت مقدار خروجی فعلی را نمایی می کند و مخرج مجموع تمام خروجی های نمایی را برای یک نمونه معین می گیرد. سپس باید این نمایی ها را محاسبه کنیم تا ادامه دهیم:
# مقادیر خروجی قبلی که توضیح دادیم
# شبکه عصبی چیست
layer_outputs = [4.8, 1.21, 2.385]
# e – ثابت ریاضی، ما از E در اینجا برای مطابقت با یک کدگذاری مشترک استفاده می کنیم
# سبکی که در آن ثابت ها بزرگ هستند
E = 2.71828182846 # you can also use math.e
# برای هر مقدار در یک بردار، مقدار نمایی را محاسبه کنید
exp_values = []
for output in layer_outputs:
exp_values.append(E ** output) # ** – عملگر قدرت در پایتون
print(‘exponentiated values:’)
print(exp_values)
>>>
exponentiated values:
[121.51041751893969, 3.3534846525504487, 10.85906266492961]
توان اهداف متعددی را دنبال می کند. برای محاسبه احتمالات به مقادیر غیر منفی نیاز داریم. خروجی را به صورت [4.8، 1.21، -2.385] تصور کنید – حتی پس از نرمال سازی، آخرین مقدار همچنان منفی خواهد بود زیرا ما فقط همه آنها را بر مجموع آنها تقسیم می کنیم. احتمال منفی (یا اطمینان) چندان منطقی نیست. مقدار نمایی هر عددی همیشه غیر منفی است – 0 را برای بی نهایت منفی، 1 برای ورودی 0 و برای مقادیر مثبت افزایش می یابد:
شکل 4.38: نمودار یک تابع نمایی.
تابع نمایی یک تابع یکنواخت است. این بدان معناست که با مقادیر ورودی بالاتر، خروجی ها نیز بالاتر هستند، بنابراین کلاس پیش بینی شده را پس از اعمال آن تغییر نمی دهیم و در عین حال مطمئن می شویم که مقادیر غیر منفی دریافت می کنیم. همچنین ثبات را به نتیجه اضافه می کند زیرا توان نرمال شده بیشتر در مورد تفاوت بین اعداد است تا اندازه آنها. هنگامی که نما را انجام دادیم، می خواهیم این اعداد را به یک توزیع احتمال تبدیل کنیم (تبدیل مقادیر به بردار اطمینان، یکی برای هر کلاس، که برای همه چیز در بردار تا 1 جمع می شود). این بدان معناست که ما در حال انجام یک نرمال سازی هستیم که در آن یک مقدار معین را می گیریم و آن را بر مجموع همه مقادیر تقسیم می کنیم. برای خروجی های ما، که در این مرحله نمایی می شوند، این همان چیزی است که معادله تابع Softmax در مرحله بعدی توضیح می دهد – گرفتن یک مقدار نمایی معین و تقسیم آن بر مجموع تمام مقادیر نمایی شده. از آنجایی که هر مقدار خروجی به کسری از مجموع نرمال می شود، همه مقادیر اکنون در محدوده 0 تا 1 قرار دارند و تا 1 جمع می شوند – آنها احتمال 1 را بین خود به اشتراک می گذارند. بیایید مجموع و نرمال سازی را به کد اضافه کنیم:
# اکنون مقادیر را نرمال کنید
norm_base = sum(exp_values) # ما همه مقادیر را جمع می کنیم
norm_values = []
for value in exp_values:
norm_values.append(value / norm_base)
print(‘Normalized exponentiated values:’)
print(norm_values)
print(‘Sum of normalized values:’, sum(norm_values))
>>>
مقادیر نمایی نرمال شده:
[0.8952826639573506, 0.024708306782070668, 0.08000902926057876]
مجموع مقادیر نرمال شده: 1.0
ما می توانیم همان مجموعه عملیات را با استفاده از NumPy به روش زیر انجام دهیم:
import numpy as np
# مقادیر قبلی قبلی که توضیح دادیم
# شبکه عصبی چیست
layer_outputs = [4.8, 1.21, 2.385]
# برای هر مقدار در یک بردار، مقدار نمایی را محاسبه کنید
exp_values = np.exp(layer_outputs)
print(‘exponentiated values:’)
print(exp_values)
# اکنون مقادیر را نرمال کنید
norm_values = exp_values / np.sum(exp_values)
print(‘normalized exponentiated values:’)
print(norm_values)
print(‘sum of normalized values:’, np.sum(norm_values))
>>>
مقادیر نمایی:
[121.51041752 3.35348465 10.85906266]
مقادیر نمایی نرمال شده:
[0.89528266 0.02470831 0.08000903]
:مجموع مقادیر نرمال شده0.9999999999999999
توجه داشته باشید که نتایج مشابه هستند، اما محاسبه آن سریعتر است و خواندن کد با NumPy آسان تر است. ما می توانیم همه مقادیر را با یک فراخوانی np.exp() نمایی کنیم، سپس بلافاصله آنها را با مجموع نرمال کنیم. برای آموزش دسته ای، باید این قابلیت را تبدیل کنیم تا خروجی های لایه را به صورت دسته ای بپذیریم. انجام این کار به آسانی است:
# Get unnormalized probabilities
exp_values = np.exp(inputs)
# Normalize them for each sample
=احتمالات exp_values / np.sum(exp_values, axis=1, keepdims=True)
ما عملکردهای جدیدی داریم. به طور خاص، np.exp() بخش E**output را انجام می دهد . همچنین باید به معنای محور و keepdims در موارد بالا بپردازیم. بیایید ابتدا در مورد محور بحث کنیم. نشان دادن محور آسان تر از گفتن است، اما در یک آرایه/ماتریس دو بعدی، محور 0 به ردیف ها و محور 1 به ستون ها اشاره دارد. بیایید چند نمونه از چگونگی تأثیر محور بر مجموع با استفاده از NumPy را ببینیم. ابتدا، ما فقط پیش فرض را نشان می دهیم که None است
import numpy as np
layer_outputs = np.array([[4.8, 1.21, 2.385],
[8.9, -1.81, 0.2],
[1.41, 1.051, 0.026]])
print(‘Sum without axis’)
print(np.sum(layer_outputs))
print(‘This will be identical to the above since default is None:’)
print(np.sum(layer_outputs, axis=None))
>>>
جمع بدون محور
18.172
این با موارد بالا یکسان خواهد بود زیرا پیش فرض None است:
18.172
بدون هیچ محوری مشخص شده، ما فقط همه مقادیر را جمع می کنیم، حتی اگر در ابعاد مختلف باشند. بعد، axis=0. این به معنای جمع کردن ستون ها[10] row-wise, along axis 0 است. به عبارت دیگر ، خروجی همان اندازه این محور است ، زیرا در هر یک از موقعیت های این خروجی ، مقادیر تمام ابعاد دیگر در این موقعیت جمع می شوند تا آن را تشکیل دهند. در مورد آرایه دو بعدی ما، جایی که ما فقط یک بعد دیگر داریم، ستون ها، بردار خروجی این ستون ها را جمع می کند. این بدان معناست که ما 4.8+8.9+1.41 و غیره را انجام خواهیم داد.
print(‘Another way to think of it w/ a matrix == axis 0: columns:’)
print(np.sum(layer_outputs, axis=0))
>>>
راه دیگر برای فکر کردن به آن با ماتریس == محور 0: ستون ها:
[15.11 0.451 2.611]
با این حال، این چیزی نیست که ما می خواهیم. ما مجموع ردیف ها را می خواهیم. احتمالا می توانید حدس بزنید که چگونه این کار را با NumPy انجام دهید، اما ما همچنان نسخه “از ابتدا” را نشان خواهیم داد:
print(‘But we want to sum the rows instead, like this w/ raw py:’)
for i in layer_outputs:
print(sum(i))
>>>
اما ما می خواهیم ردیف ها را در عوض جمع کنیم، مانند این w/ py خام:
8.395
7.29
2.4869999999999997
با موارد فوق، می توانیم اینها را به هر شکلی که می خواهیم به فهرستی اضافه کنیم. با این حال، ما می خواهیم از NumPy استفاده کنیم. همانطور که احتمالا حدس زده اید، ما می خواهیم محور 1 را جمع بندی کنیم:
print(‘So we can sum axis 1, but note the current shape:’)
print(np.sum(layer_outputs, axis=1))
>>>
بنابراین می توانیم محور 1 را جمع کنیم، اما به شکل فعلی توجه کنید:
[8.395 7.29 2.487]
همانطور که با “توجه به شکل فعلی” اشاره شد، ما مبالغی را که انتظار داشتیم به دست آوردیم، اما در واقع، می خواهیم خروجی ها را به یک مقدار واحد در هر نمونه ساده کنیم. ما در تلاش هستیم تا تمام خروجی های یک لایه را برای هر نمونه در یک دسته جمع کنیم. تبدیل آرایه خروجی لایه با طول ردیف برابر با تعداد نورون های لایه، فقط به یک مقدار ما به یک بردار ستونی با این مقادیر نیاز داریم زیرا به ما امکان می دهد کل دسته نمونه ها را از نظر نمونه با یک محاسبه عادی کنیم.
print(‘Sum axis 1, but keep the same dimensions as input:’)
print(np.sum(layer_outputs, axis=1, keepdims=True))
>>>
محور 1 را جمع کنید، اما همان ابعاد ورودی را حفظ کنید:
[[8.395]
[7.29 ]
[2.487]]
با این کار ، ما همان ابعاد ورودی را حفظ می کنیم. حال، اگر آرایه حاوی دسته ای از خروجی ها را با این آرایه تقسیم کنیم، NumPy این کار را به صورت نمونه انجام می دهد. این بدان معناست که تمام مقادیر هر ردیف خروجی را بر ردیف مربوطه از آرایه مجموع تقسیم می کند. از آنجایی که این مجموع در هر ردیف یک مقدار واحد است، برای تقسیم با هر مقدار از ردیف خروجی مربوطه استفاده می شود). ما می توانیم همه اینها را در یک کلاس softmax ترکیب کنیم، مانند:
# فعال سازی Softmax
class Activation_Softmax:
# Forward pass
def forward(self, inputs):
# احتمالات غیرعادی را دریافت کنید
exp_values = np.exp(inputs – np.max(inputs, axis=1,
keepdims=True))
# آنها را برای هر نمونه عادی کنید
probabilities = exp_values / np.sum(exp_values, axis=1,
keepdims=True)
self.output = probabilities
در نهایت، ما همچنین تفریق بزرگترین ورودی ها را قبل از انجام توان قرار دادیم. دو چالش فراگیر اصلی در شبکه های عصبی وجود دارد: “نورون های مرده” و اعداد بسیار بزرگ (که به عنوان مقادیر “انفجاری” نامیده می شوند). نورون های “مرده” و تعداد زیادی می توانند در طول زمان ویرانی ایجاد کنند و شبکه را در طول زمان بی فایده کنند. تابع نمایی مورد استفاده در فعال سازی softmax یکی از منابع انفجار مقادیر است. بیایید چند نمونه از چگونگی و چرایی این اتفاق به راحتی را ببینیم:
import numpy as np
print(np.exp(1))
>>>
2.718281828459045
print(np.exp(10))
>>>
22026.465794806718
print(np.exp(100))
>>>
2.6881171418161356e+43
print(np.exp(1000))
>>>
__main__:1: RuntimeWarning: overflow encountered in exp
inf
برای ایجاد خطای سرریز به عدد بسیار زیادی نیاز نیست، در این مورد، فقط 1000 عدد لازم است. ما می دانیم که تابع نمایی به سمت 0 گرایش دارد زیرا مقدار ورودی آن به بی نهایت منفی نزدیک می شود و زمانی که ورودی 0 باشد خروجی 1 است (همانطور که در نمودار قبلی نشان داده شده است(
import numpy as np
print(np.exp(-np.inf), np.exp(0))
>>>
0.0 1.0
ما می توانیم از این ویژگی برای جلوگیری از سرریز شدن تابع نمایی استفاده کنیم. فرض کنید حداکثر مقدار را از لیستی از مقادیر ورودی کم کنیم. سپس مقادیر خروجی را تغییر می دهیم تا همیشه در محدوده ای از مقدار منفی تا 0 باشند، زیرا بزرگترین عدد کم شده توسط خود 0 را برمی گرداند و هر عدد کوچکتری که توسط آن کم شود منجر به یک عدد منفی می شود – دقیقا محدوده ای که در بالا مورد بحث قرار گرفت. با Softmax، به لطف نرمال سازی، می توانیم هر مقداری را از همه ورودی ها کم کنیم و خروجی را تغییر نمی دهد:
softmax = Activation_Softmax()
softmax.forward([[1, 2, 3]])
print(softmax.output)
>>>
[[0.09003057 0.24472847 0.66524096]]
softmax.forward([[-2, -1, 0]][11] ) # subtracted 3 – max from the list
print(softmax.output)
>>>
[[0.09003057 0.24472847 0.66524096]]
این یکی دیگر از ویژگی های مفید تابع نمایی و نرمال شده است. علاوه بر این محاسبات یک نکته دیگر نیز وجود دارد که باید به آن اشاره کرد. چه اتفاقی می افتد اگر داده های خروجی لایه، [1، 2، 3]، به عنوان مثال، بر 2 تقسیم کنیم؟
softmax.forward([[0.5, 1, 1.5]])
print(softmax.output)
>>>
[[0.18632372 0.30719589 0.50648039]]
اطمینان خروجی به دلیل ماهیت غیرخطی توان تغییر کرده است. این یک مثال از این است که چرا ما باید تمام داده های ورودی را به یک شبکه عصبی به یک روش مقیاس بندی کنیم، که در فصل 22 با جزئیات بیشتری توضیح خواهیم داد.
اکنون، می توانیم یک لایه متراکم دیگر را به عنوان لایه خروجی اضافه کنیم و آن را طوری تنظیم کنیم که به اندازه لایه قبلی دارای خروجی ها و به تعداد خروجی هایی باشد که داده های ما شامل کلاس ها هستند. سپس می توانیم فعال سازی softmax را روی خروجی این لایه جدید اعمال کنیم:
# مجموعه داده ایجاد کنید
X, y = spiral_data(samples=100, classes=3)
# ایجاد لایه متراکم با 2 ویژگی ورودی و 3 مقدار خروجی
dense1 = Layer_Dense(2, 3)
# ایجاد فعال سازی ReLU (برای استفاده با لایه متراکم):
activation1 = Activation_ReLU()
# ایجاد لایه دوم Dense با 3 ویژگی ورودی (همانطور که خروجی می گیریم)
# لایه قبلی در اینجا) و 3 مقدار خروجی
dense2 = Layer_Dense(3, 3)
# ایجاد فعال سازی Softmax (برای استفاده با لایه Dense):
activation2 = Activation_Softmax()
# از طریق این لایه داده های آموزشی خود را به جلو منتقل کنید
dense1.forward(X)
# یک پاس رو به جلو از طریق عملکرد فعال سازی انجام دهید
# خروجی اولین لایه متراکم را در اینجا می گیرد
activation1.forward(dense1.output)
# یک عبور رو به جلو از لایه دوم Dense انجام دهید
# خروجی های تابع فعال سازی لایه اول را به عنوان ورودی می گیرد
dense2.forward(activation1.output)
# یک پاس رو به جلو از طریق عملکرد فعال سازی انجام دهید
# خروجی لایه متراکم دوم را در اینجا می گیرد
activation2.forward(dense2.output)
# بیایید خروجی چند نمونه اول را ببینیم:
print(activation2.output[:5])
>>>
[[0.33333334 0.33333334 0.33333334]
[0.33333316 0.3333332 0.33333364]
[0.33333287 0.3333329 0.33333418]
[0.3333326 0.33333263 0.33333477]
[0.33333233 0.3333324 0.33333528]]
همانطور که می بینید، توزیع پیش بینی ها تقریبا برابر است، زیرا هر یک از نمونه ها ~33٪ (0.33) پیش بینی برای هر کلاس دارند. این نتیجه از مقداردهی اولیه تصادفی وزن ها (قرعه کشی از توزیع نرمال ، زیرا هر مقداردهی اولیه تصادفی منجر به این امر نمی شود) و سوگیری های صفر. این خروجی ها همچنین “نمرات اطمینان” ما هستند. برای تعیین اینکه مدل کدام طبقه بندی را به عنوان پیش بینی انتخاب کرده است، یک argmax را روی این خروجی ها انجام می دهیم که بررسی می کند کدام یک از کلاس ها در توزیع خروجی بالاترین اطمینان را دارند و شاخص آن را برمی گرداند – شاخص کلاس پیش بینی شده. با این حال، نمره اطمینان می تواند به اندازه خود پیش بینی کلاس مهم باشد. به عنوان مثال، argmax [0.22، 0.6، 0.18] همان argmax برای
[0.32، 0.36، 0.32]. در هر دوی اینها، تابع argmax مقدار شاخص 1 را برمی گرداند (عنصر دوم در پارادایم نمایه صفر پایتون)، اما بدیهی است که اطمینان 60 درصد بسیار بهتر از اطمینان 36 درصد است.
کد کامل تا این مرحله:
import numpy as np
import nnfs
from nnfs.datasets import spiral_data
nnfs.init()
# لایه متراکم
class Layer_Dense:
# Layer initialization
def __init__(self, n_inputs, n_neurons):
# وزن ها و سوگیری ها را مقداردهی اولیه کنید
self.weights = 0.01 * np.random.randn(n_inputs, n_neurons)
self.biases = np.zeros((1, n_neurons))
# Forward pass
def forward(self, inputs):
# مقادیر خروجی را از ورودی ها، وزن ها و بایاس ها محاسبه کنید
self.output = np.dot(inputs, self.weights) + self.biases
# فعال سازی ReLU
class Activation_ReLU:
# Forward pass
def forward(self, inputs):
# مقادیر خروجی را از ورودی ها محاسبه کنید
self.output = np.maximum(0, inputs)
# فعال سازی Softmax
class Activation_Softmax:
# Forward pass
def forward(self, inputs):
# احتمالات غیرعادی را دریافت کنید
exp_values = np.exp(inputs – np.max(inputs, axis=1,
keepdims=True))
# آنها را برای هر نمونه عادی کنید
probabilities = exp_values / np.sum(exp_values, axis=1,
keepdims=True)
self.output = probabilities
# مجموعه داده ایجاد کنید
X, y = spiral_data(samples=100, classes=3)
# ایجاد لایه متراکم با 2 ویژگی ورودی و 3 مقدار خروجی
dense1 = Layer_Dense(2, 3)
# ایجاد فعال سازی ReLU (برای استفاده با لایه متراکم):
activation1 = Activation_ReLU()
# ایجاد لایه دوم Dense با 3 ویژگی ورودی (همانطور که خروجی می گیریم)
# لایه قبلی در اینجا) و 3 مقدار خروجی (مقادیر خروجی)
dense2 = Layer_Dense(3, 3)
# ایجاد فعال سازی Softmax (برای استفاده با لایه Dense):
activation2 = Activation_Softmax()
# از طریق این لایه داده های آموزشی خود را به جلو منتقل کنید
dense1.forward(X)
# یک پاس رو به جلو از طریق عملکرد فعال سازی انجام دهید
# خروجی اولین لایه متراکم را در اینجا می گیرد
activation1.forward(dense1.output)
# یک عبور رو به جلو از لایه دوم Dense انجام دهید
# خروجی های تابع فعال سازی لایه اول را به عنوان ورودی می گیرد
dense2.forward(activation1.output)
# یک پاس رو به جلو از طریق عملکرد فعال سازی انجام دهید
# خروجی لایه متراکم دوم را در اینجا می گیرد
activation2.forward(dense2.output)
# بیایید خروجی چند نمونه اول را ببینیم:
print(activation2.output[:5])
>>>
[[0.33333334 0.33333334 0.33333334]
[0.33333316 0.3333332 0.33333364]
[0.33333287 0.3333329 0.33333418]
[0.3333326 0.33333263 0.33333477]
[0.33333233 0.3333324 0.33333528]]
ما آنچه را که برای انتقال داده های رو به جلو از طریق مدل خود نیاز داریم تکمیل کرده ایم. ما از تابع فعال سازی خطی اصلاح شده (ReLU) در لایه پنهان استفاده کردیم که بر اساس هر نورون کار می کند. ما علاوه بر این از تابع فعال سازی Softmax برای لایه خروجی استفاده کردیم زیرا مقادیر نرمال نشده را به عنوان ورودی می پذیرد و یک توزیع احتمال را خروجی می دهد، که ما از آن به عنوان نمرات اطمینان برای هر کلاس استفاده می کنیم. به یاد بیاورید که اگرچه نورون ها به هم مرتبط هستند، اما هر کدام وزن و تعصبات مربوطه خود را دارند و با یکدیگر “نرمال” نمی شوند.
همانطور که می بینید، مدل نمونه ما در حال حاضر تصادفی است. برای رفع این مشکل، به راهی نیاز داریم که محاسبه کنیم شبکه عصبی در پیش بینی های فعلی چقدر اشتباه است و شروع به تنظیم وزن ها و سوگیری ها برای کاهش خطا در طول زمان می کنیم. بنابراین، گام بعدی ما تعیین کمیت اشتباه مدل از طریق آنچه به عنوان تابع ضرر تعریف می شود، است.
مواد تکمیلی: https://nnfs.io/ch4
کد فصل، منابع بیشتر و اشتباهات این فصل.
I am a bit lost here on what the activation functions do exactly. How does a linear activation function differ from not having an activation function at all?
Keep reading 🙂 … but for a linear function, it doesn’t. This gets addressed again later. We don’t typically use linear activation functions unless regression
@harrison@pythonprogramming.net
لطفا توضیح دهید که چگونه نمودار تعصب و وزن را ترسیم کردید؟
_Assigned به هریسون Kinsley_
How do you calculate and come up with the graph? When I calculate, I don’t get the same graph. I used 1 as an input value and multiplied with -1.00, and added 0.50. I continued the same for the second neuron by using the previous neuron’s output as the second neuron’s input, but the output doesn’t fit according to the graph.
چگونه نقطه غیرفعال سازی را تعریف کنیم؟ بر اساس وزن نورون ها؟
یک صفر در وزنه قرار دهید؟ نقطه غیرفعال سازی زمانی است که یک نورون صفر آن را خروجی می دهد، بنابراین لایه بعدی نمی تواند آن را ادامه دهد.
CMIIW
In figures 4.14-4.18, we were able to get a similar solution with just 2 neurons. Why move the negative from the first neuron to the third? Can this same system be done with just 1 hidden layer, and thus 2 neurons per sub function in the piece wise function that this represents?
چگونه می توانید اطمینان حاصل کنید که تغییرات در جفت دوم نتیجه از جفت اول تاثیر نمی گذارد.
لایه خروجی از تابع فعال سازی خطی به جای تابع قطعه ای استفاده می کند، از این رو من گیج شده ام که چرا تغییرات در جفت دوم بر شکل بجا شده به دست آمده از جفت اول تأثیر نمی گذارد.
به نظر می رسد شما تابع قطعه ای را ایجاد می کنید که تابع sin را به 7 قسمت جدا می کند. هر ورودی فقط یک جفت نورون را برای تولید خروجی فعال می کند. اما، سوال این است که چگونه می توان مقادیر وزن و بیس را برای رسیدن به این هدف تعیین کرد. از توضیحات بالا، وزن ها و بیس های شما فقط بر اساس تطبیق شکل بصری تعیین می شوند و تقسیم ورودی x را در نظر نگرفته اند. این باعث می شود که خواندن این بخش بسیار گیج شوم
در صفحه بعد شما 1 = مجموع ردیف ها را توصیف می کنید در حالی که در اینجا 0 را به صورت ردیف و در امتداد محور 0 توصیف می کنید. برای ساده کردن این موضوع من 0 = مجموع ستون ها را توصیف می کنم.
Why do we need two sets of brackets here again?