تا ایجای کار یه داده رو load کردیم و اون رو با نمودار نقطه های (scatter plot) نشون دادیم. همین طور درباره ی اینکه قراره دقیقا چه کاری انجام بدیم که بگیم الگوریتم ما یه چیزی یاد بگیره صحبت کردیم و گفتم باید یک تابع به نام h رو یاد بگیریم که این تابع این شکلی تعریف میشد:
بعد از این هم درباره ی اینکه یک تابع h خوب چه ویژگی ای داره حرف زدیم. تو این پست قراره درباره ی اینکه چه جوری باید این تابع رو پیدا کنیم، حرف بزنیم.
تو پست قبلی توضیح دادم که اگر برای یک خط میانگین فاصله ی اون از نقاط ما کم باشه، اون خط یک خط خوبه. پس ما اگر بتونیم این خط رو پیدا کنیم در واقع به جواب مسئله رسیدیم. برای همین اومدیم و یک تابع به نام J تعریف کردیم که ورودیش پارمتر های یک خط (تتا 0 و تتا 1) بودن و خروجی اون میانگین فاصله خط با نقاط (به طور دقیق تر نصف میانگین فاصله ی خط با نقاط).
پس در واقع میتونیم بگیم که به ازای هر پارامتری میشه بیایم و این میانگین رو حساب کنیم. از اینجا به بعد به این مقدار میگیم error با خطای اون خط یعنی:
h(تتا 0, تتا1) = میزان خطای خطی که پارامتر های اون تتا 0 و تتا 1 باشن
در واقع الان میتونیم هر مقداری رو به عنوان تتا 0 و تتا 1 به تابع J بدیم و ببینیم چه قدر خطا داره. پس میتونیم برای این تابع یک نمودار بکشیم که یکی از محور ها مقدار تتا 0، یکی دیگه مقدار تتا 1 و یکی دیگه هم مقدار J رو نشون بده. اگر این کار رو انجام بدیم، به نمودار پایین میرسیم:
محور x مربوط به تتا 0، محور y مربوط به تتا 1 و محور z مربوط به مقدار تابع J میشه. یعنی اگر شما بیاید و یک خط با شیب و عرض از مبدا بین -20 تا 20 (اعداد روز محور های x و y) انتخاب کنید و ببینید مقدار اون طبق نمودار چند میشه، مقدار تابع J برای یک خط با این پارامتر ها رو پیدا کردید. (درباره ی رسم این نمودار تو بخش کد نویسی توضیح میدم).
هدف ما این بود که میانگین اختلافات تابع h با y هایی که داریم حداقل بشه، پس توی نموداری که داریم، اگر یک خط رو پیدا کنیم که مقدار اون تو محور z ها پایین تر باشه (نقاط تیره تر نمودار)، خط بهتری رو انتخاب کردیم.
جمع بندی تمام این حرفا این میشه که ما میخوایم تتا 0 و تتا 1 ای رو پیدا کنیم که تابع J رو میبینمم میکنن.
ولی چه جوری باید این کار رو انجام بدیم؟
احتمالا خیلی از شما میدونید برای اینکه بخوایم مینیمم/ماکسیمم یک تابع رو پیدا کنیم، میتونیم نقاطی که مشتق توی اون ها صفر هست رو حساب کنیم و اون نقاط میشن جواب های ما. اما اینجا نمیشه از این روش استفاده کرد! (البته به طور خاص برای این الگوریتم میشه و تو پست های بعدی دربارش توضیح میدم ولی در حالت کلی برای خیلی از الگوریتم های ماشین لرنینگ نمیشه از این راه استفاده کرد)
چرا نمیشه از این راه استفاده کرد؟ چون نمیشه معادله ای که یک سمت اون مشتق J هست و سمت دیگه ی اون صفر رو حل کرد و به یک فرمول رسوند. (در اصلاح میگن closed-form solution نداره).
پس باید چی کار کنیم؟
برای حل این مشکل میشه از یک الگوریتم استفاده کرد به نام gradient descent (ترجمه ی اسم این الگوریتم میشه الگوریتم گرادیان نزولی *_* ولی به نظرم خود gradient descent بهتره).
در ساده ترین حالت میشه گفت این الگوریتم کاری که انجام میده اینه که از کوه پایین میاد! چه ربطی به پیدا کردن مینیمم تابع داره؟ ربطش به اینه که فرض کنید شما روی تابعی وایسادید که میخواید اون رو مینیمم کنید. اگر بخواید نقطه ی مینیمم رو پیدا کنید باید به قسمت های با ارتفاع کمتر برید. این الگوریتم هم دقیقا همین کار رو میکنه. اول میاد و شیب اون جایی که وایساده رو پیدا میکنه، بعد در خلاف جهت شیب یک قدم میره پایین تر، اینجوری به جایی میرسه که ارتفاع اون کمتره. حالا دوباره شیب رو حساب میکنه و بازم در جهت خلاف شیب میاد پایین تر. انقدر این کار رو انجام میده که یا به یه جایی برسه که از نظر ما (مایی که داریم از الگوریتم استفاده میکنیم) نقطه ی خوبی رو پیدا کرده باشه، یا دیگه هرچی قدم بر میداره به جای بهتری نرسه (یا دقیق ترش به جایی خیلی بهتری نرسه، مثلا ممکنه اگر قدم برداره به جایی برسه که مقدار J اونجا .0001 کمتر باشه ولی واقعا این قدر کاهش تفاوتی برای ما اینجاد نمیکنه)
چه جوری باید شیب تابع رو حساب کنیم؟ با گرادیان. (گرادیان یه برداره که هر مولفه ی اون مشتق تابع نسبت به یکی از متغییر های تابعه، مثلا برای تابع J که تعریف کردیم، میشه یک بردار 2 بعدی که مولفه ی اول اون مشتق J نسبت به تتا 0 و مولفه ی دوم اون مشتق نسبت به تتا 1 میشه.)
حالا باید خلاف جهت گرادیان (منفی گرادیان) حرکت کنیم.
فرض کنید تو نقطه ی 10 از محور x وایسادید و میخواید حرکت کنید و برید به نقطه ی 20. چه جوری باید این حرکت رو انجام بدید؟ باید موقعیت خودتون رو با 10 جمع کنید. پس با جمع و تفریق کرد یه عددی با مقدار فعلی متغییر ها میتونید روی محور ها حرکت کنید.
پس برای حرکت در جهت خلاف گرادیان باید مقدار فعلی پارامتر ها رو با منفی گرادیان جمع کنیم(=مقدار گرادیان رو از پارامتر ها کم کنیم). اگر این حرکت رو به صورت ریاضیاتی بنویسیم اینجوری میشه:
البته هنوز باید یک بخش دیگه به این معادله اضافه کنیم و اون هم برای اینه که نوشن بده چه قدر در جهت خلاف گرادیان باید حرکت کنیم؟ (طول قدم ها چه قدر باشن). برای این کار میایم و یک عدد به نام learning rate رو تو گرادیان ضرب میکنیم. پس معادله ی نهایی میشه:
اگر نعادله رو برای هر تتا به صورت جداگانه بنویسم، داریم:
در نهایت الگوریتم linear regression اینجوری میشه:
تو پست بعدی این الگوریتم رو پیاده سازی میکنیم و میبنیم که به چه صورت باید کد اون رو نوشت. اما قبل از اون یک مورد دیگه رو باید برسی کنیم و اون هم اینه که قدار learning rate رو چه جوری باید تعیین کنیم؟
اگر learning rate رو خیلی کوچیک اتخاب کنیم، خیلی طول میکشه که gradient descent به نقطه ی پایین برسه. (چون باید قدم های کوچیکی برداره، باید تعداد زیادی قدم برداره) و اگر اون رو خیلی بزرگ انتخاب کنیم ممکنه تو همون قدم اول انقدر قدمش بزرگ باشه که نقطه ی minimum رو رد کنه. (این قضیه خیلی مشکل سازه چون معمولا تو نقطه ی جدید مقدار گرادیان بیشتره، در تیجه قدم بعدی بزرگ تر میشه و باز میره به جایی که گرادیان بیشتره و دوباره تو قدم بعدی همین اتفاق میوفته، پس در نهایت هیچ وقت به نقطه ی مینیمم نمیرسه)
در عمل این مقدار یه عددی مثل 0.1، 0.05، 0.01 و حتی مقادیر خیلی کمتر مثل 0.0001 ست میشه. (در ادامه خیلی بیشتر در این مورد حرف میزنیم)
تو ماشین لرنینگ به این مدل پارامتر ها میگن hyper-parameter. در واقع اینا پارامتر هایی هستن که باید خودمون دستی انتخاب کنیم و مقدار اونها تو یادگیری اثر داره.
به این ترتیب بحث ما درباره ی الگوریتم linear regression با یک متغییر ورودی و یک متغییر خروجی تموم میشه. تو پست های بعدی درباره ی اینکه چه جوری ورودی با بیشتر از یک متغییر کار کنیم صحبت میکنم، بعدش میریم سراغ تغییر الگوریتم برای classification و به الگوریتم logistic regression میرسیم. این وسطا هم کد تمام این الگوریتم ها رو پیاده سازی میکنیم :)