رگرسیون با پایتون
این آموزش یکی از پستهای زیرمجموعه آموزشهای مرتبط با پایتون میباشد، که به آموزش پیاده سازی رگرسیون با پایتون میپردازد. در صورتی که تمایل دارید کل آموزشهای مرتبط با پایتون را مشاهده کنید، از این لینک استفاده کنید. در ضمن اگه حوصله خوندن متن ندارید، میتونید از ویدیو آخر این پست استفاده کنید و از متن صرف نظر کنید. همچنین اگه با خوندن این مقاله سوالی برای شما پیش اومد، خوشحال میشم که اون را از طریق راههای ارتباطی که در همین وبسایت موجوده، باهام مطرح کنید.
پیشنیاز این آموزش تسلط نسبی بر پایتون است.
در ضمن نسخه زبان انگلیسی این آموزش را هم از این لینک میتونید ببینید.
رگرسیون چیه؟
رگرسیون یا Regression یکی از الگوریتمهای یادگیری ماشین هست که سعی میکنه رابطه بین چندتا متغیر را بفهمه و اون رابطه را به صورت فرمول ریاضی دربیاره. مثلا فرض کنید شما قصد دارید بدونید بین متراژ یک خونه، سال ساخت، منطقهای که اون خونه قرار داره، جنوبی یا شمالی بودن خونه و قیمت اون خونه چه رابطه ریاضی وجود داره! یعنی فرمول ریاضیای داشته باشید که متراژ، سال ساخت و بقیه متغیرهای تأثیر گذار در قیمت خونه را داخلش قرار بدید و اون قیمت خونه را بهتون بده. این دقیقا جایی هست که رگرسیون به کمکتون میاد. اجازه بدید با یک مثال خیلی سادهتر جلو بریم، فرض کنید تعدادی نقطه در محور مختصات به شکل زیر داریم:
قصد داریم بدونیم رابطه ریاضی بین x و y چیه! وقتی نقاط را به الگوریتم رگرسیون میدیم و این الگوریتم را اجرا میکنیم، به ما خروجی زیر را میده:
طبق نظر رگرسیون، بهترین فرمول ریاضی که میتونه x و y را بهم مرتبط کنه، همین فرمولی هست که در تصویر میبینید. منحنی قرمزی هم که در تصویر میبینید، منحنی مرتبط با همین فرمول ریاضی است که من بهش منحنی رگرسیون میگم. حالا این فرمول به چه درد میخوره؟ این فرمول برای زمانی خوبه که شما یک مقدار x جدید دارید و قصد دارید y را پیشبینی کنید. برای تخمین y جدید کافیه x جدید را داخل فرمول قرار بدید و y جدید را بدست بیارید.
وقتی گفتم طبق نظر رگرسیون این بهترین فرمول هست، منظور از بهترین فرمول، فرمولی هست که کمترین خطا را داشته باشه و منظور از خطا، اختلاف بین مقادیر پیشبینی شده و واقعی است. این یعنی رگرسیون فرمولی به ما میده که مجموع فاصله نقاط از محنی رگرسیون در کمترین حالت ممکن هست.
اینکه رگرسیون چطور این ارتباط را بدست میاره، خیلی قضایای ریاضی پشتش هست که در این مقاله حرفی ازش نمیزنم چون ممکنه از موضوع اصلی مقاله دور بشیم ولی توصیه میکنم حتما در رابطه با فرمولهای ریاضی رگرسیون سرچ کنید تا با درک بهتری بتونید از این الگوریتم استفاده کنید. اینم بگم که این فرمولها خیلی ساده هستند و انتظار چیزای پیچیده نداشته باشید.
در این مثال، رگرسیون ما درجه 2 بود، چون فرمولی که y و x را به هم مرتبط میکنه، توان 2 هست ولی رگرسیون در حالت کلی هر درجهای میتونه داشته باشه، همچنین به رگرسیون درجه 1، خطی میگیم. غیر از درجه، رگرسیون هر تعداد متغیر مستقل میتونه داشته باشه، در این مثال فقط یک متغیر مستقل داشتیم که اونم x بود ولی اگه دوباره مثال قیمت خونه را در نظر بگیرید، اونجا تعداد متغیرهای مستقل بیشتر از یکی بود، مثلا متراژ و سال ساخت دو تا متغیر مستقل در اون مثال بودند و متغیر قیمت خونه هم متغیر وابسته ما بود (متغیر وابسته، متغیری هست که مقدارش از متغیرهای مستقل بدست میاد).
مشخصههای مهم در رگرسیون
در رگرسیون دو تا عامل خیلی مهمه: یکی از اونا را قبلا گفتم و اونم اینه که منحنی رگرسیون باید کمترین خطا را نسبت به دیتا ما داشته باشه، خطا در رگرسیون به صورتهای مختلفی میتونه محاسبه بشه ولی استانداردترین حالت R² یا ضریب تشخیص (Coefficient of Determination) هست. R² یک عدد بین صفر و یک هست که به ما میگه چقدر تغییرات متغیر وابسته به متغیرهای گسسته ربط داره یا به زبون ساده به ما میگه که منحنی رگرسیون چقدر خوب روی دیتای ما فیت شده یا اصطلاحا قالب دیتای ما شده. هر چقدر R² به عدد یک نزدیکتر باشه، این یعنی خطای رگرسیون ما کمتره. این مشخصه هم فرمول ریاضی مخصوص به خودش را داره، پیشنهاد میکنم دربارش سرچ کنید و بهش نگاهی بندازید.
عامل دیگه قدرت پیشبینی منحنی رگرسیون هست، این یعنی مدل رگرسیون ما بتونه، دیتاهای جدید را خوب پیشبینی کنه. همیشه باید بین R² و قدرت پیشبینی در هر مدل رگرسیون تعادلی وجود داشته باشه. اینکه قدرت پیشبینی را چطور بدست میاریم، جلوتر در کدهای پایتون بهتون میگم ولی الان مثال پایین را ببینید:
در منحنی بالا سمت چپ تصویر بالا، یک مدل رگرسیون با درجه 1 برای نقاط سبز رنگ ایجاد شده، R² این منحنی رگرسیون برابر 0.09 شده که اصلا خوب نیست! از تصویر هم مشخصه که رگرسیون خطی برای این دیتا مناسب نیست. حالا منحنی بالا سمت راست را در نظر بگیرید، یک منحنی رگرسیون درجه دو برای نقاط در نظر گرفته شده که R² برابر 0.77 داره، خب الان همونطور که میبینید نمودار خیلی بهتر چفت دیتای ما شده. بریم منحنی پایین سمت چپ، درجه رگرسیون 3 شده و R² هم 0.91 که خیلی به یک نزدیکتره ولی آیا این منحنی بهتر از منحنی قبلی هست؟ خب همونطور که گفتم عامل دیگهای به نام قدرت پیشبینی هم در رگرسیون تعیین کننده هست. همین منحنی حدودا از xهای 55 به بعد پیشبینی افت در yها کرده که اگه دوباره به نقاط سبز نگاه کنید هیچ نشونی از افت نمیبینیم! این مورد در منحنی پایین سمت راست مشهودتره، جایی که R² برابر یک شده و منحنی رگرسیون از تک تک نقاط دیتا (نقاط سبز) گذشته (یعنی عملا خطا برابر صفر شده) ولی طبق منحنی در x = 60 باید انتظار داشته باشیم که y برابر صفر بشه! دو منحنی اخیر R² بالایی داشتند ولی قدرت پیشبینی اونها پایینه. برای همین گفتم که همیشه باید بین این دو تا عامل، تعادل برقرار کرد.
رگرسیون خطی ساده
به رگرسیون درجه 1 با یک متغیر مستقل، رگرسیون خطی ساده میگیم. فرمول کلی این نوع رگرسیون به شکل زیر هست:
اگه یادتون باشه در ریاضی به b0، عرض از مبدا (Intercept) میگفتیم و به b1، شیب (Slope). کل هدف ما از انجام رگرسیون پیدا کردن همین ضرایب b0 و b1 هست. بریم سراغ پیادهسازی رگرسیون خطی ساده با پایتون:
رگرسیون خطی ساده با پایتون
من از VS Code به عنوان محیط برنامه نویسی استفاده میکنم، البته روی VS Code خودم پلاگین Jupyter را نصب کردم تا محیطی شبیه Jupyter Notebook بدست بیارم ولی شما میتونید از خود Jupyter Notebook یا هر محیط دیگهای استفاده کنید. دوباره یادآوری میکنم که اگه حوصله خوندن متن ندارید، میتونید از ویدیو آخر این مقاله استفاده کنید و متن را صرف نظر کنید.
با استفاده از کد پایین، کتابخونههای مورد نیازم را ایمپورت میکنم:
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error
from math import sqrt
from sklearn.preprocessing import PolynomialFeatures
کتابخانه sklearn در حقیقت مهمترین کتابخانه این کد هست، چون با استفاده از این کتابخانه رگرسیون با پایتون را پیاده خواهم کرد.
البته این کتابخونهها فقط برای رگرسیون خطی ساده نیست و تمامی کتابخونههای مورد نیاز من در این آموزشه. در قسمت بعدی کد، متغیرهای مستقل و وابسته (x و y) را با مقادیرشون تعریف میکنم. این متغیرها باید آرایه Numpy باشند:
x = np.array([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30]
).reshape((-1, 1))
y = np.array([2,5,9,13,15,17,21,25,27,28,31,36,42,43,44,49,50,53,60,61,63,65,71,75,76,78,83,84,85,90])
چیزی که شاید براتون سوال شده باشه اینه که چرا از تابع reshape استفاده کردم؟ جواب اینه که برای ایجاد یک مدل رگرسیون با Sklearn باید متغیر مستقل حداقل یک ستون داشته باشه، برای همین با استفاده از این تابع، آرایه سطری خودم را به ستونی تبدیل کردم.
در کد پایین، با استفاده از تابع train_test_split از sklearn متغیرهای x و y را به 4 بخش تقسیم کردم. در ایجاد مدلهای هوش مصنوعی ما معمولا یک قسمت از دیتا را جدا میکنیم و با اون مدل را ایجاد میکنیم (اصطلاحا مدل را Train میکنیم) و با قسمت باقیمانده از دیتا، مدلی که ایجاد کردیم را تست میکنیم و دقتش را اندازه میگیریم. اینجا هم متغیرهای xTrain و yTrain برای ایجاد مدل قراره بکار بره و متغیرهای xTest و yTest برای ارزیابی قدرت پیشبینی مدل بکار میره. همچنین پارامتر test_size مشخص میکنه که چقدر از دیتا را برای تست لازم داریم، من این پارامتر را روی 0.2 قرار دادم، یعنی 20 درصد دیتا را برای تست جدا کن. پارامتر random_state را برابر یک عدد دلخواه قرار میدم که هر بار که کد را اجرا میکنم، مقادیر متغیرهای xTest، xTrain و بقیه متغیرهای حاصل از این تابع، عوض نشه:
xTrain, xTest, yTrain, yTest = train_test_split(x, y, test_size=0.2, random_state=100)
با کد زیر مدل رگرسیون خودم را ایجاد میکنم:
model = LinearRegression()
model.fit(xTrain , yTrain)
در کد بالا با استفاده از کلاس LinearRegression یک متغیر به نام model ایجاد کردم. این متغیر در حقیقت شی اولیه مدل رگرسیون من هست. در خط بعدی کد هم مدل را با استفاده از متد fit و دیتاهایی که برای آموزش مدل کنار گذاشته بودم، train کردم.
حالا که مدل هوش مصنوعی من آموزش دید، با استفاده از کدهای پایین به ترتیب میتونم R²، عرض از مبدأ و شیب خط را خروجی بگیرم:
print(f"coefficient of determination: {model.score(xTrain,yTrain)}")
print(f"intercept: {model.intercept_}")
print(f"coefficients: {model.coef_}")
خروجی کد بالا به شکل زیر هست:
coefficient of determination: 0.9980332483807082
intercept: -0.424086916319915
coefficients: [3.04147018]
ضریب تشخیص (R²) مدل من برابر 0.99 هست که خیلی خوبه، این یعنی خط رگرسیون کاملا روی دیتا سوار شده ولی قدرت پیشبینی مدل آیا خوبه یا بده؟ به این سوال در ادامه جواب میدم. بریم سراغ بقیه قسمتهای خروجی، طبق خط دوم و سوم خروجی، معادله خط من به شکل زیر هست:
در کد پایین، رفتم سراغ پیشبینی یک مقدار جدید. یعنی قصد دارم بدونم به ازای x برابر با 31، مقدار y چقدره؟ تابع predict شی model اینکار را برای من انجام میده. البته شما میتونید خودتون معادله خط بالا را در یک تابع پایتون بنویسید و بدون استفاده از تابع predict هم پیشبینی را انجام بدید:
newX = np.array([31]).reshape(-1 , 1)
newY = model.predict(newX)
print(f'New Value: {newY}')
خروحی کد بالا به شکل زیر هست:
New Value: [93.86148867]
یعنی به ازای x برابر با 31 مقدار y برابر 93.86 هست.
خب من تونستم y را به ازای یک مقدار جدید پیشبینی کنم ولی خطای این پیشبینی چقدره؟ در حقیقت همین خطاست که قدرت پیشبینی مدل را اندازه میگیره. هر چی خطای پیشبینی مدل کمتر باشه، قدرت پیشبینی بالاتره (دقت کنید که دارم از خطای پیشبینی صحبت میکنم نه خطای منحنی رگرسیون! خطای پیشبینی با استفاده از دیتای تست که جدا کردیم، بدست میاد ولی خطای منحنی در حقیقت در دل پارامتر R² لحاظ شده). خطای پیشبینی مدل را از طریق متد میانگین مربعات خطا (MSE) محاسبه میکنم، فرمول MSE به شکل زیر هست:
به طور خلاصه این فرمول، مقادیر واقعی دیتا را از مقادیر پیشبینی شده کم میکنه، به توان دو میرسونه و بر تعداد نقاط تقسیم میکنه. به زبون ساده میانگین توان دوم خطاها را بدست میاره. در Sklearn برای محاسبه MSE از تابع mean_squared_error استفاده میکنم:
yPred = model.predict(xTest)
mse = mean_squared_error(yTest, yPred)
print(f'RMSE: {sqrt(mse)}')
در کد بالا، با استفاده از تابع predict، یک سری y برای xTestهایی که برای تست مدل کنار گذاشتیم محاسبه کردهام، بعد مقادیر پیشبینی شده را به همراه مقادیر واقعی y (منظورم از مقادیر واقعی yTest هست) به mean_squared_error دادهام تا MSE را برام حساب کنه، در پایان هم از MSE جذر گرفتم و چاپش کردم. جذر MSE پارامتری به شما میده به نام RMSE (مخفف Root Mean Squared Error)، با RMSE شما میتونید یک بازه برای مقدار واقعی پیشبینیتون در نظر بگیرید که جلوتر دربارش توضیح میدم. خروجی کد بالا به شکل پایین هست:
RMSE: 1.9621026711889837
این عدد یعنی هر چی که مدل پیشبینی کرده میتونه تقریبا 2 واحد خطا داشته باشه یا به عبارت دیگه اگه برای یک x، مدل پیشبینی عدد 100 را کرده باشه، مقدار واقعی را میتوان را در بازه 98 تا 102 در نظر گرفت.
با کد پایین میتونید رگرسیون خودتون را در قالب نمودار یا چارت نمایش بدید:
yhat = model.predict(x)
plt.plot(x , y, 'bo')
plt.plot(x , yhat, 'r')
plt.show()
خروجی کد بالا به شکل زیر هست:
رگرسیون درجه 2 با یک متغیر مستقل
با در نظرگیری درجه و تعداد متغیر مستقل، ما بینهایت نوع رگرسیون داریم ولی رگرسیون خطی مهمترین نوع رگرسیون هست. به طور مثال معادله رگرسیون درجه 2 زیر را در نظر بگیرید:
اگه من یک متغیر جدید به نام x1 در نظر بگیریم و مقدارش را برابر توان دوم x قرار بدم:
در این صورت معادله رگرسیون به شکل زیر میشه:
همونطور که دیدید، با یک تغییر ساده معادله درجه 2 به صورت معادله خطی دراومد، برای همین گفتم مهمترین نوع رگرسیون همون رگرسیون خطی هست. ما هر نوع رگرسیونی را با هر تعداد متغیر مستقل و هر درجهای را میتونیم به رگرسیون خطی تبدیل کنیم.
رگرسیون درجه 2 با یک متغیر مستقل با پایتون
مثل رگرسیون خطی اول باید متغیرهای مستقل و وابسته را تعریف و مقداردهی کنم:
x = np.array([5, 15, 25, 35, 45, 55]).reshape((-1, 1))
y = np.array([15, 11, 2, 8, 25, 32])
همونطور که توضیح دادم، رگرسیون درجه 2 یا بیشتر را باید به شکل رگرسیون خطی درآوریم، یعنی در اینجا متغیر x را باید به دو متغیر x و x به توان دو تبدیل بشه. اینجا جایی هست که کلاس PolynomialFeatures به کمکمون میاد. این کلاس متغیرهای مستقل و درجه رگرسیون را از ما میگیره و هر تعداد متغیری که برای تبدیل معادله رگرسیون به معادله خطی نیاز باشه را به ما خروجی میده. کد پایین نحوه استفاده از این کلاس را نشون میده:
transformer = PolynomialFeatures(degree=2, include_bias=False)
transformer.fit(x)
x_ = transformer.transform(x)
print(x_)
خروجی این کد به شکل زیر هست:
[[ 5. 25.]
[ 15. 225.]
[ 25. 625.]
[ 35. 1225.]
[ 45. 2025.]
[ 55. 3025.]]
بقیه مراحل رگرسیون هم دقیقا مانند رگرسیون خطی هست، فقط به جای متغیر x از _x استفاده میکنم:
xTrain, xTest, yTrain, yTest = train_test_split(x_, y, test_size=0.2, random_state=100)
model = LinearRegression()
model.fit(xTrain , yTrain)
print(f"coefficient of determination: {model.score(x_,y)}")
print(f"intercept: {model.intercept_}")
print(f"coefficients: {model.coef_}")
حواستون باشه که خروجیای که _model.coef به ما در اینجا میده، یک لیست هست که عنصر اولش ضریب x را داره و عنصر دومش هم ضریب x به توان به دو. intercept هم که مثل قبل مقدار b0 را در خودش داره. خروجی کد بالا به شکل زیر هست:
coefficient of determination: 0.8814087929983071
intercept: 19.613259668508313
coefficients: [-1.11270718 0.02497238]
طبق خروجی بالا، معادله رگرسیون من به شکل زیر هست:
برای پیشبینی یک مقدار جدید هم از کد پایین استفاده میکنم. بقیه مراحل رگرسیون مثل سنجش خطا و… هم مثل همون رگرسیون خطی انجام میشه:
newX = np.array([7]).reshape(-1 , 1)
transformer = PolynomialFeatures(degree=2, include_bias=False)
transformer.fit(newX)
newX_ = transformer.transform(newX)
newY = model.predict(newX_)
print(f'New Value: {newY}')
رگرسیون درجه 2 با دو متغیر مستقل
معادله کلی این نوع رگرسیون به شکل زیر هست:
کدهای ایجاد این نوع مدل رگرسیون هم به شکل زیر هست. البته من در کدهای پایین دیگه از بخش بندی کردن دیتا به صورت train و test و همچنین سنجش خطا صرف نظر کردم چون کدهای مواردی که گفتم، شکل همون رگرسیون خطی هستند:
x = np.array([[0, 1], [5, 1], [15, 2], [25, 5], [35, 11], [45, 15], [55, 34], [60, 35]])
y = np.array([4, 5, 20, 14, 32, 22, 38, 43])
transformer = PolynomialFeatures(degree=2, include_bias=False)
transformer.fit(x)
x_ = transformer.transform(x)
model = LinearRegression()
model.fit(x_ , y)
print(f"coefficient of determination: {model.score(x_,y)}")
print(f"intercept: {model.intercept_}")
print(f"coefficients: {model.coef_}")
مقالههای مرتبط
در صورتی که به این موضوع و کلا یادگیری الگوریتمهای هوش مصنوعی علاقه دارید، میتونید از آموزشهای پیادهسازی درخت تصمیم با پایتون، جنگل تصادفی و KNN من هم استفاده کنید. قطعا این مقالات و دانش مرتبط با هوش مصنوعی خیلی در عصر حاضر واجبه!
ویدیو آموزش پیادهسازی رگرسیون با پایتون
قبل اینکه ویدیو را ببینید، بگم که من ویدیوها را در چنل یوتوب خودم هم آپلود میکنم، خیلی خوشحال میشم که اونجا هم من را دنبال کنید.