تا اینجا با تئوری های مربوط به linear regression آشنا شدیم. تو این پست قراره درباره ی پیاده سازی این الگوریتم صحبت کنم. اگر پست های قبلی درباره ی این الگوریتم رو نخونید حتما اونها رو بخونید تا با تئوری ها و منطق این الگوریتم آشنا بشید. خب بریم سراغ پیاده سازی ...
برای اینکه linear regression رو پیاده سازی کنیم از یک سری داده ی fake (یا همون داده هایی که خودمون تولید کردیم) استفاده میکنیم. خوبی این کار اینه که راحت تر میتونیم نتایج رو تحلیل و برسی کنیم و اگر نیاز شد شرایط رو تغییر بدیم کارمون راحت تره، مثلا میتونیم noise داده ها رو کم و زیاد کنیم یا تعداد داده هایی که داریم رو تغییر بدیم و ببینیم که الگوریتم چه جوری عمل میکنه.
برای پیاده سازی از کتابخونه numpy و matplotlib پایتون استفاده میکنیم. numpy برای محاسبات عددی (و به ویژه محاسبات ماتریس ها و تنسور ها) به کار میره و matplotlib برای رسم نمودار. برای خوندن این پست نیاز نیست که این کتابخونه ها رو بلد باشید و در طول مسیر هر چیزی که نیاز باشه رو توضیح. اگر خواستید این کتابخونه رو بیشتر یادبگیرید، تو یکی از ریپو های گیت هابم برای آموزش این کتابخونه ها (و چند تای دیگه) یه سری Notebook آماده کردم. (این نوتبوک ها به زبان انگلیسی هستن، بعدا تو ویرگول پست فارسی درباره ی این کتابخونه ها خواهم نوشت).
برای پیاده سازی از jupyter notebook استفاده میکنم که البته فعلا نیاز نیست کار کردن با اون رو بلد باشید. (بعدا درباره ی jupyter notebook و اینکه چرا ازش استفاده میشه توضیح میدم).
خب برگردیم به بحث خودمون، برای پیاده سازی این الگوریتم، میایم و فرض میکنیم قراره یک سری داده رو یاد بگریم که بر اساس خط 4x+1 تولید شدن. البته از اون جایی که داده های واقعی همیشه Noise دارن، باید به داده هایی که تولید میکنیم هم noise اضافه کنیم. (فرض میکنیم noise داده ها از یک توزیع نرمال تولید شده باشه).
برای اینکه از کتابخونه های numpy و matplotlib استفاده کنید، اول باید اون ها رو نصب کنید. اگر از pip استفاده میکنید، با دستور pip install numpy matplotlib و اگر از anaconda استفاده میکنید با دستور conda install numpy matplotlib این کتابخونه ها رو نصب کنید.
بعد از نصب اونها یک فایل جدید بسازید (یا یک فایل .py که با vs code بشه توش نوشت، یا یه پروژه ی جدید تو pyCharm و یا یه notebook جدید با ژوپیتر. در هر صورت برای ادامه ی کار تفاوتی نداره)
بعد از ساختن فایل، اولین کاری که باید انجام بدید اینه که کتابخونه ها رو Import کنید:
معمولا کتابخونه ی numpy رو با np و matplitlib رو با plt مخفف میکنن.
تو مرحله ی بعدی باید داده ها رو تولید کنیم. اول از همه یه متغییر به نام THETA_1 و یکی هم به نام THETA_0 تعریف میکنیم که مقادیر واقعی تتا ها رو تو اونها ذخیره کنیم:
حالا باید بیایم و مقادیر X و Y رو تعریف کنیم. برای مقدار X میایم و به صورت تصادفی 200 تا عدد بین 0 تا 10 انتخاب میکنیم. (میتونید هر تعداد دیگه ای عدد و در هر بازه که دوست داشتید انتخاب کنید، من اینجا برای انکه نمودار هایی که رسم میکنیم قشنگ تر بشن این اعداد رو انتخاب کردم. اگر تعداد نقاط زیاد باشه خیلی در هم میشن.)
برای مقدار Y باید بیایم و مقدار هر نقطه رو اول در THETA_1 ضرب کنیم و بعد با THETA_0 جمع کنیم. در نهایت هم یک عدد تصادفی به عدد حاصل اضافه کنیم (عدد تصادفی برای اینه که به داد ها noise اضافه بشه).
کد این قسمت به این صورت میشه:
اول از همه از تابع uniform تو ماژول random کتابخونه ی numpy استفاده کردم که یک سری عدد به صورت تصادفی بین 0 تا 10 انتخاب کنم و بعد اونا رو با مقادیر که گفتم ضرب و جمع کردم. (اینجا اعداد X با توزیع یکنواخت یا همون uniform تولید شدن، ولی میتونید از هر توزیع دیگه ای هم استفاده کنید.)
اگر از پست قبلی یادتون باشه، الگوریتم gradient descent میومد و تلاش میکرد که مقدار خطا رو کم کنه (مثل پایین اومدن از کوه). این الگوریتم تو هر مرحله در جهت خلاف شیب، قدم بر میداشت. الان میخوایم قدم برداشتن در خلاف جهت شیب رو پیاده سازی کنیم.
برای حرکت در خلاف جهت شیب اول باید بیایم و شیب رو حساب کنیم. (تو پست قبلی کامل منطق پشت این الگوریتم رو توضیح دادم). برای محاسبه ی شیب باید مشتق تابع J نسبت به تتا 0 و تتا 1 رو حساب کنیم. اگر یادتون باشه تابع J به این صورت تعریف میشد:
باید مشتق این تابع نسبت به تتا 0 و تتا 1 رو حساب کنیم:
برای تتا 0 داریم:
و برای تتا 1 داریم:
خب حالا باید بیایم و یک تابع بنویسم که این مقادیر رو حساب کنه. ورودی این تابع با X و Y و تتا 0 و تتا 1 باشه ولی فعلا برای ساده تر شدن کد فقط تتا 0 و تتا 1 رو به تابع پاس میدیم (X و Y رو به صورت global تعریف کردیم قبلا. مطمئنا این کد تمیز ترین کد ممکن نیست ولی فعلا هدفم اینه که بتونیم این الگوریتم رو پیاده سازی کنیم و در ادامه که به بحث vectorization برسیم به تمیز تر شدن کد فکر میکنیم)
پیاده سازی این توابع به این صورت میشه:
بازم میگم که اینجا اصلا هدف پیاده سازی یک کد تمیز نیست :)
حالا باید از این دو تا تابع استفاده کنیم و یک قدم از gradient descent رو پیاده سازی کنیم.
بازم برای یادآوری، هر قدم از gradient descent اینجوری میشه:
پیاده سازی این قسمت هم به این صورت میشه:
برای train کردن الگوریتم، باید اول بیایم و یک مقدار تصادفی به تتا 0 و تتا 1 بدیم (یا دستی مقدار بدیم). بعد با چند قدم gradient descent اون پارامتر ها رو بهینه تر کنیم.
کد این قسمت هم به این صورت میشه:
و به این ترتیب کل کدی که برای پیاده سازی این الگوریتم نیاز بود رو نوشتیم ???
اگر الان بیایم و تابع train رو صدا بزنیم، مقادیر تتا 0 و تتا 1 محاسبه میشن. برای مثال اگر این الگوریتم رو با 10 قدم و مقدار lr=.01 اجرا کنیم، در نهایت به مقدار 3.85 برای تتا 1 و .62 برای تتا 0 میرسیم.
معمولا برای برسی اینکه train درست هست یا نه میان و مقدار تابع J رو بر حسب شماره ی قدم با یه نمودار نشون میدن. اگر همه چیز درست باشه، این مقدار باید همش کم بشه. اگر ما هم این کار رو انجام بدیم، نمودار این شکلی میشه:
البته تو این نموار رادیکال مقدار خطا بر حسب زمان نشون داده شده.
این کلیت کدی بود که برای پیاده سازی این الگوریتم نیاز بود. چون بعدا خیلی از این کار ها رو با کتابخونه انجام میدیم خیلی وارد جزئیات نشدم و صرفا میخواستم بهتون یک دید کلی بدم.
کد مربوط به این پست رو میتونید تو این لینک ببینید.