همانطور که در مطلب مربوط به رگرسیون خطی ساده (Simple linear regression) گفته شد، چنانچه بین متغیرهای مستقل (dependent variable) و متغیرهای وابسته (independent variable) در یک مسئله، رابطه خطی وجود داشته باشد، میتوان از رگرسیون خطی استفاده کرد تا برای متغیرهای مستقل جدید، متغیرهای وابسته متناسب با آنها را پیشبینی کرد.
به عنوان مثال:
جدول زیر نشاندهنده مساحت چند خانه و قیمت آنها است.
در این جدول مقادیر متراژ خانهها به عنوان متغیرهای مستقل (X) درنظر گرفته میشوند. در مقابل قیمت هرخانه نیز به عنوان یک متغیر وابسته (Y) شناخته میشود.
اگر دادههای جدول بالا را در قالب یک نمودار نشان دهیم، به تصویر زیر می رسیم.
مشخص است که یک رابطه خطی بین متراژ خانه و قیمت آن وجود دارد، به این معنی که با افزایش متراژ خانه، قیمت آن نیز افزایش مییابد.
پس اگر ما بتوانیم معادله این رابطه خطی را پیدا کنیم، میتوانیم قیمت خانه را برای متراژهای دیگر نیز محاسبه کنیم. مثلا میخواهیم پیشبینی کنیم قیمت یک خانه 40 متری چقدر است.
معادله خط را به خاطر دارید؟
حالت کلی معادله یک خط به صورت زیر است:
y = ax+b
در این معادله به a شیب خط (slop) و به b عرض از مبدا (intercept) میگویند.
شیب خط مقدار زاویه خط با محور افقی و عرض از مبدا، فاصله نقطه شروع خط از محور y را مشخص میکند.
مثلا :
y = 2x + 1
برای رسم این خط کافی است برای مقادیر مختلف x مقدار y را محاسبه کنیم و سپس نقاط را به هم وصل کنیم.
Y = (2 * -2) + 1= -3
Y = (2 * -1) + 1= -1
Y = (2 * 0) + 1= 1
Y = (2 * 1) + 1= 3
Y = (2 * 2) + 1= 5
Y = (2 * 3) + 1= 7
نمایش داده های بالا بر روی محورهای مختصات به صورت زیر است:
مثال مربوط به قیمت خانهها را که فراموش نکردهاید!
همانطور که پیشتر گفتیم، که اگر دادههای جدول بالا را به صورت مختصات xو y در نظر بگیریم، شکل زیر به دست میآید.
import matplotlib.pyplot as plt from numpy.polynomial.polynomial import polyfit Area = [10,15,20,25,30] Price = [500,750,1000,1250,1500] plt.scatter(Area, Price, color='blue') plt.xlabel('Area') plt.ylabel('Price') input_size = len(Area) x = Area y = Price
آیا میتوانید معادله این خط را به دست آورید و خطی را رسم کنید که با تمامی نقاط کمترین فاصله را داشته باشد؟ با توجه به دادههای مسئله، حتی به صورت چشمی نیز میتوان خط صافی را رسم که دقیقا از روی تمامی نقاط عبور میکند.
(این حالت که خط رسم شده کمترین فاصله را از نقاط داشته باشد، بهترین حالت خط رگرسیون است، بعدا در مبحث ارزیابی خطای مدل، به این موضوع بیشتر میپردازیم.)
معادله این خط نیز حالتی از y = ax+b است اما چگونه a و b را پیدا کنیم که بتوانیم برای xهای جدید نیز y را محاسبه کنیم؟
مثلا y را برای x=40 محاسبه کنیم که با توجه به مثال مورد بحث، میشود قیمت خانهای با متراژ40 متر مربع.
برای حل این مثل از رگرسیون خطی ساده (Simple linear regression) استفاده میکنیم.
این که چرا از پسوند "ساده" در این عبارت استفاده کردهایم؟
به طور کلی اگر در محاسبه رگرسیون خطی، برای متغیر وابسته (y)، بیشتر از یک متغیر مستقل (x) را در نظر بگیریم، رگرسیون خطی ما از نوع چندگانه (Multiple Linear Regression) است.
در ادامه مطلب با این تعریف بیشتر آشنا می شوید.
فرمول کلی رگرسیون خطی ساده همان y = ax+b است که به طور معمول به جای a و b از θ0 (تتا صفر) و θ1 (تتا یک)استفاده میکنیم.
y = θ0 + θ1 x
برای محاسبه θ0 و θ1 نیز به صورت زیر عمل میکنیم:
یکبار دیگه دادهها رو ببینیم و محاسبات رو شروع کنیم.
هدفمون که یادتون نرفته، به دنبال پیدا کردن معادله خط موردنظر برای دادهها هستیم.
اولین قدم محاسبه میانگین هر یک از متغیرهاست:
avg_x = (10 + 15 + 20 + 25 + 30)/5 = 20
avg_y = (500 + 750 + 1000 + 1250 + 1500)/5 = 1000
Area = [10,15,20,25,30] Price = [1,3,3,2,5] input_size = len(Area) x = Area y = Price def avg(avg_input): my_temp = 0 for i in range (0, input_size): my_temp = my_temp + avg_input[i] return(my_temp/input_size) avg_x = avg(x) avg_y = avg(y) print("Average of x: ", avg_x) print("Average of y: ", avg_y)
پس با توجه به مقادیر بالا، معادله خط مورد نظر به صورت زیر به دست میآید:
y = θ0 + θ1 x
y = 0 + 50 x
یعنی تابع yما در این مثال هر x ورودی را در 50 ضرب میکند و به خروجی میدهد.
import matplotlib.pyplot as plt from numpy.polynomial.polynomial import polyfit Area = [10,15,20,25,30] Price = [500,750,1000,1250,1500] plt.scatter(Area, Price, color='blue') plt.xlabel('Area') plt.ylabel('Price') input_size = len(Area) x = Area y = Price def avg(avg_input): my_temp = 0 for i in range (0, input_size): my_temp = my_temp + avg_input[i] return(my_temp/input_size) avg_x = avg(x) avg_y = avg(y) print("Average of x: ", avg_x) print("Average of y: ", avg_y) def beta1(): beta1_numerator = 0 beta1_denumerator = 0 for i in range (0, input_size): beta1_numerator = beta1_numerator + ((x[i] - avg_x)*(y[i] - avg_y)) beta1_denumerator = beta1_denumerator + ((x[i] - avg_x)**2) return(beta1_numerator/beta1_denumerator) print("Beta1 = ", beta1()) beta0 = avg_y - (beta1() * avg_x) print("Beta0 = ", avg_y , "-" , "(" , beta1() , "*" , avg_x , ") =" , beta0)
حالا میتوانیم به راحتی قیمت یک خانه 40 متری نیز محاسبه کنیم.
X = 40
y = 0 + 50 x
y = 0 + (50* 40) = 2000
import matplotlib.pyplot as plt from numpy.polynomial.polynomial import polyfit Area = [10,15,20,25,30] Price = [500,750,1000,1250,1500] plt.scatter(Area, Price, color='blue') plt.xlabel('Area') plt.ylabel('Price') input_size = len(Area) x = Area y = Price def avg(avg_input): my_temp = 0 for i in range (0, input_size): my_temp = my_temp + avg_input[i] return(my_temp/input_size) avg_x = avg(x) avg_y = avg(y) print("Average of x: ", avg_x) print("Average of y: ", avg_y) def beta1(): beta1_numerator = 0 beta1_denumerator = 0 for i in range (0, input_size): beta1_numerator = beta1_numerator + ((x[i] - avg_x)*(y[i] - avg_y)) beta1_denumerator = beta1_denumerator + ((x[i] - avg_x)**2) return(beta1_numerator/beta1_denumerator) print("Beta1 = ", beta1()) beta0 = avg_y - (beta1() * avg_x) print("Beta0 = ", avg_y , "-" , "(" , beta1() , "*" , avg_x , ") =" , beta0) def capital_y(user_input): return(beta0 + (beta1() * user_input)) user_input = 40 print("Predicted value is: ", capital_y(user_input))
قیمت پیشبینی شده برای x=40، کاملا روی خط رگرسیونی است که آن را محاسبه کردهایم.
خب، در مثالی که مطرح شد نقاط کاملا روی یک خط مستقیم قرار میگیرند، اما آیا همیشه چیدمان نقاط به همین شکل است؟
جواب: خیییییییر :)
به مثال زیر دقت کنید:
در این جدول نیز مثل قبل، مقادیر متراژ خانهها به عنوان متغیرهای مستقل (X) و قیمت هرخانه نیز به عنوان یک متغیر وابسته (Y) شناخته میشود. اگر دادههای جدول بالا را در قالب یک نمودار نشان دهیم:
مشخص است که اینبار نیز یک رابطه خطی بین متراژ خانه و قیمت آن وجود دارد، یعنی به طور متوسط و در نگاه کلی با افزایش متراژ خانه، قیمت آن نیز افزایش مییابد. اما دیگر نمیتوان به سادگی یک خط صاف و مستقیم را به گونهای رسم کرد که از تمامی نقاط موجود در صفحه کمترین فاصله ممکن را داشته باشد.
در ادامه چند خط رگرسیون فرضی را رسم میکنیم. آیا میتوانید با اطمینان 100% بگویید که یکی از این خط ها بهترین خط ممکن است؟
راه حل سادست، همون مقادیر θ0 و θ1 را محاسبه کنید تا به جوابهای ممکن برسید.
تا اینجا مرور مباحث گذشته بود، اما برسیم به مبحث اصلی این مطلب یعنی:
آموزش رگرسیون خطی چندگانه(Multiple Linear Regression)، مرحله به مرحله و با مثال
مثال متراژ و قیمت خانهها رو که فراموش نکردید. حالا اگر به جای یک متغیر مستقل X بیشتر از یک متغیر مستقل داشته باشیم، چه میشود؟ مثلا قیمت خانهها برگرفته از متراژ و سال ساخت باشد. یا مثلا قیمت یک اتومبیل بر اساس قدرت آن و کیلومتر طی شده محاسبه شود.
متوجه تفاوت رگرسیون خطی ساده (Simple Linear Regression) و رگرسیون خطی چندگانه (Multiple Linear Regression) شدید؟
خب، بریم سراغ یک مثال از رگرسیون خطی چندگانه و حل این مثال به صورت مرحله به مرحله.
همچنین اعداد کوچکی نیز پایین سمت راست Xها نوشته میشوند که به معنی ایندکس ردیف مورد نظر از جدول کلی اطلاعات هستند، مثلا با توجه به اطلاعات مثال ارایه شده:
البته در این مطلب آموزشی ما ایندکس متغیر مستقل (اینکه متغیر چندم ما است) را پایین سمت راست مینویسم، و اعداد در جایگاه توان به معنی توان هستند.
معادله کلی خط در حالت رگرسیون خطی چندگانه با توجه به اینکه تعداد Xها بیشتر از یک است، به صورت زیر محاسبه میشود:
در صورتی که از معادله بالا مشتق جزئی نسبت به متغیرها بگیریم، به معادلات زیر میرسیم:
برای حل این معادلات به غیر از مقادیر تتا ها:
میتوانیم سایر مقادیر را با توجه به دادههای ارایه شده در صورت مسئله محاسبه کنیم.
منظور از m نیز، تعداد ردیفهای جدول اطلاعات است؛ در این مثل m=4
x1 = [1,2,3,4] x2 = [10,1,2,3] y = [12,18,24,30] input_size = len(x1) def paramcalc(x1,x2,y): x1y = [] x2y = [] x1x2 = [] x1power2 = [] x2power2 = [] sum_x1 = 0 sum_x2 = 0 sum_y = 0 sum_x1y = 0 sum_x2y = 0 sum_x1x2 = 0 sum_x1power2 = 0 sum_x2power2 = 0 for i in range(0, input_size): x1y.append(x1[i]*y[i]) x2y.append(x2[i]*y[i]) x1x2.append(x1[i]*x2[i]) x1power2.append(x1[i]**2) x2power2.append(x2[i]**2) for i in range(0, input_size): sum_x1 = sum_x1 + x1[i] sum_x2 = sum_x2 + x2[i] sum_y = sum_y + y[i] sum_x1y = sum_x1y + x1y[i] sum_x2y = sum_x2y + x2y[i] sum_x1x2 = sum_x1x2 + x1x2[i] sum_x1power2 = sum_x1power2 + x1power2[i] sum_x2power2 = sum_x2power2 + x2power2[i] print("x1y = ",x1y , "sum_x1y = ",sum_x1y) print("x2y = ",x2y , "sum_x2y = ",sum_x2y) print("x1x2 = ",x1x2 ,"sum_x1x2 = ",sum_x1x2) print("x1power2 = ",x1power2 , "sum_x1power2 = ",sum_x1power2) print("x2power2 = ",x2power2 , "sum_x2power2 = ",sum_x2power2) print("sum_x1 = ", sum_x1) print("sum_x2 = ", sum_x2) print("sum_y = ", sum_y) paramcalc(x1,x2,y)
مقادیر به دست آمده را در فرمولها جایگزین میکنیم:
مرتبتر که بنویسمشون
اینجا ما سه معادله داریم و سه مجهول، برای حل کردن این مسئله میتوانیم از روش حذف متغیر استفاده کنیم یا از روش کرامر (Cramer rule)، که به نظر من پیادهسازی روش کرامر، در یک برنامه کامپیوتری، راحتتر و قابلفهمتر هست (البته به نظر من).
طبق روش کرامر، ما میتوانیم سه متغیر مورد نظرمان را به صورت زیر محاسبه کنیم:
صورت و مخرج کسرها در تصویر بالا به معنی دترمینان آن ماتریس هستند.
برای مثالی که مشغول حل کردن آن هستیم، مقادیر به صورت زیر جایگذاری میشوند:
یادآوری دترمینان (Determinant):
برای محاسبه دترمینان یک ماتریس 3*3 به صورت زیر عمل میکنیم:
در محاسبات زیر، با هدف ساده کردن درک بخشهای تفکیک شده، شروع و پایان هر پرانتز با رنگی متفاوت مشخص شده است.
y = 6 + 6x1+ 0x2
که میشود
y = 6 + 6x1
def paramcalc(x1,x2,y,input_x1,input_x2): x1y = [] x2y = [] x1x2 = [] x1power2 = [] x2power2 = [] sum_x1 = 0 sum_x2 = 0 sum_y = 0 sum_x1y = 0 sum_x2y = 0 sum_x1x2 = 0 sum_x1power2 = 0 sum_x2power2 = 0 for i in range(0, input_size): x1y.append(x1[i]*y[i]) x2y.append(x2[i]*y[i]) x1x2.append(x1[i]*x2[i]) x1power2.append(x1[i]**2) x2power2.append(x2[i]**2) for i in range(0, input_size): sum_x1 = sum_x1 + x1[i] sum_x2 = sum_x2 + x2[i] sum_y = sum_y + y[i] sum_x1y = sum_x1y + x1y[i] sum_x2y = sum_x2y + x2y[i] sum_x1x2 = sum_x1x2 + x1x2[i] sum_x1power2 = sum_x1power2 + x1power2[i] sum_x2power2 = sum_x2power2 + x2power2[i] print("x1y = ",x1y , "sum_x1y = ",sum_x1y) print("x2y = ",x2y , "sum_x2y = ",sum_x2y) print("x1x2 = ",x1x2 ,"sum_x1x2 = ",sum_x1x2) print("x1power2 = ",x1power2 , "sum_x1power2 = ",sum_x1power2) print("x2power2 = ",x2power2 , "sum_x2power2 = ",sum_x2power2) print("sum_x1", sum_x1) print("sum_x2", sum_x2) print("sum_y", sum_y) orginal_m = [[input_size,sum_x1,sum_x2], [sum_x1,sum_x1power2,sum_x1x2], [sum_x2,sum_x1x2,sum_x2power2] ] teta0_numerator = [[sum_y,sum_x1,sum_x2], [sum_x1y,sum_x1power2,sum_x1x2], [sum_x2y,sum_x1x2,sum_x2power2] ] teta1_numerator = [[input_size,sum_y,sum_x2], [sum_x1,sum_x1y,sum_x1x2], [sum_x2,sum_x2y,sum_x2power2] ] teta2_numerator = [[input_size,sum_x1,sum_y], [sum_x1,sum_x1power2,sum_x1y], [sum_x2,sum_x1x2,sum_x2y] ] teta0 = det(teta0_numerator)/det(orginal_m) teta1 = det(teta1_numerator)/det(orginal_m) teta2 = det(teta2_numerator)/det(orginal_m) print("teta0 = " , teta0) print("teta1 = " , teta1) print("teta2 = " , teta2) predicted_value = teta0 + (teta1 * input_x1) + (teta2 * input_x2) print("predicted value = ", predicted_value) def det(m): det_result = (m[0][0] * ((m[1][1] * m[2][2]) - (m[1][2] * m[2][1]))) -(m[0][1] * ((m[1][0] * m[2][2]) - (m[1][2] * m[2][0]))) +(m[0][2] * ((m[1][0] * m[2][1]) - (m[1][1] * m[2][0]))) return (det_result) x1 = [1,2,3,4] x2 = [10,1,2,3] y = [12,18,24,30] input_size = len(x1) input_x1 = 0 input_x2 = 0 paramcalc(x1,x2,y,input_x1,input_x2)