從零開始學(xué)機(jī)器學(xué)習(xí)——線性和多項(xiàng)式回歸
在之前的學(xué)習(xí)中,我們已經(jīng)對(duì)數(shù)據(jù)的準(zhǔn)備工作以及數(shù)據(jù)可視化有了一定的了解。今天,我們將深入探討基本線性回歸和多項(xiàng)式回歸的概念與應(yīng)用。
如果在過(guò)程中涉及到一些數(shù)學(xué)知識(shí),大家也不必感到畏懼,我會(huì)逐步為大家進(jìn)行詳細(xì)的講解,以便大家能夠更好地理解這些內(nèi)容。希望通過(guò)今天的學(xué)習(xí),能夠幫助大家建立起對(duì)這兩種回歸方法的清晰認(rèn)識(shí),并掌握它們?cè)趯?shí)際問(wèn)題中的應(yīng)用。
線性和多項(xiàng)式回歸
通常情況下,回歸分析主要分為兩種類型:線性回歸和多項(xiàng)式回歸。線性回歸旨在通過(guò)一條直線來(lái)描述變量之間的關(guān)系,而多項(xiàng)式回歸則允許我們使用多項(xiàng)式函數(shù)來(lái)更靈活地捕捉數(shù)據(jù)的復(fù)雜趨勢(shì)。為了幫助大家直觀地理解這兩種回歸方法,我們可以通過(guò)圖片進(jìn)行展示。
圖片
其實(shí),線性回歸和多項(xiàng)式回歸之間的區(qū)別,可以簡(jiǎn)單地歸結(jié)為直線與曲線的差異。
基本線性回歸
線性回歸練習(xí)的目標(biāo)在于能夠繪制出一條理想的回歸線,那么什么才算是“完美的線”呢?簡(jiǎn)而言之,完美的線是指使所有分散的數(shù)據(jù)點(diǎn)到這條線的距離最短的情況。通常,我們使用最小二乘法來(lái)實(shí)現(xiàn)這一目標(biāo)。
理想情況下,這個(gè)總和應(yīng)盡可能小,以確保回歸線能夠最佳地代表數(shù)據(jù)的趨勢(shì)。接下來(lái),讓我們先看一下相關(guān)的數(shù)學(xué)公式,以便更深入地理解這一過(guò)程。
我們希望求出一組未知參數(shù),使得樣本點(diǎn)與擬合線之間的總誤差(即距離)最小化。最直觀的感受如下圖:
圖片
因?yàn)橄鄿p的結(jié)果可能會(huì)出現(xiàn)負(fù)數(shù)的情況,為了確保我們計(jì)算出的值始終為正,因此在這里引入了平方的概念。無(wú)論減法的結(jié)果是正還是負(fù),經(jīng)過(guò)平方處理后,所有的結(jié)果都將轉(zhuǎn)化為正數(shù)。
這條綠線被稱為最佳擬合線,可以用一個(gè)數(shù)學(xué)等式來(lái)表示:
Y = a + bX
X 是“解釋變量”。Y 是“因變量”。直線的斜率是 b,a 是 y 軸截距,指的是 X = 0 時(shí) Y 的值。
一個(gè)好的線性回歸模型將是一個(gè)用最小二乘回歸法與直線回歸得到的高(更接近于 1)相關(guān)系數(shù)的模型。相關(guān)系數(shù)(也稱為皮爾遜相關(guān)系數(shù))我來(lái)解釋一下:
圖片
我們可以發(fā)現(xiàn)相關(guān)系數(shù)反映的是變量之間的線性關(guān)系和相關(guān)性的方向(第一排),而不是相關(guān)性的斜率(中間),也不是各種非線性關(guān)系(第三排)。請(qǐng)注意:中間的圖中斜率為0,但相關(guān)系數(shù)是沒(méi)有意義的,因?yàn)榇藭r(shí)變量是0。
工具分析
當(dāng)然,相關(guān)性這種指標(biāo)的手動(dòng)計(jì)算并不實(shí)際,尤其是在面對(duì)大規(guī)模數(shù)據(jù)時(shí),手動(dòng)處理不僅效率低下,還容易出錯(cuò)。因此,利用現(xiàn)有的框架,如Scikit-learn,能夠更高效、準(zhǔn)確地進(jìn)行相關(guān)性分析,讓我們專注于更重要的任務(wù)。
?
pip install scikit-learn
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from datetime import datetime
from sklearn.preprocessing import LabelEncoder
pumpkins = pd.read_csv('../data/US-pumpkins.csv')
pumpkins.head()
在獲取樣本數(shù)據(jù)后,進(jìn)行分析時(shí),通常需要篩選出我們需要的字段,而那些不需要的則應(yīng)予以丟棄,以便進(jìn)一步深入分析。
pumpkins = pumpkins[pumpkins['Package'].str.contains('bushel', case=True, regex=True)]
columns_to_select = ['Package', 'Variety', 'City Name', 'Low Price', 'High Price', 'Date']
pumpkins = pumpkins.loc[:, columns_to_select]
price = (pumpkins['Low Price'] + pumpkins['High Price']) / 2
month = pd.DatetimeIndex(pumpkins['Date']).month
day_of_year = pd.to_datetime(pumpkins['Date']).apply(lambda dt: (dt-datetime(dt.year,1,1)).days)
new_pumpkins = pd.DataFrame(
{'Month': month,
'DayOfYear' : day_of_year,
'Variety': pumpkins['Variety'],
'City': pumpkins['City Name'],
'Package': pumpkins['Package'],
'Low Price': pumpkins['Low Price'],
'High Price': pumpkins['High Price'],
'Price': price})
new_pumpkins.loc[new_pumpkins['Package'].str.contains('1 1/9'), 'Price'] = price/1.1
new_pumpkins.loc[new_pumpkins['Package'].str.contains('1/2'), 'Price'] = price*2
new_pumpkins.iloc[:, 0:-1] = new_pumpkins.iloc[:, 0:-1].apply(LabelEncoder().fit_transform)
new_pumpkins.head()
盡管代碼可能會(huì)讓人感到困惑,其核心邏輯其實(shí)很簡(jiǎn)單:我們只需提取出所需的字段,包括年月、價(jià)格、城市和包裝信息。如果你對(duì)編程不太熟悉,也完全沒(méi)關(guān)系,現(xiàn)在有很多代碼助手可供使用,選用任何一個(gè)都能輕松寫出所需的代碼。關(guān)鍵在于保持清晰的邏輯思路,而不是被代碼的復(fù)雜性所干擾。
import matplotlib.pyplot as plt
plt.scatter('City','Price',data=new_pumpkins)
正常來(lái)說(shuō),城市字段應(yīng)該是文字也就是城市名字,但是因?yàn)橛?jì)算的原因,這里有一段代碼new_pumpkins.iloc[:, 0:-1] = new_pumpkins.iloc[:, 0:-1].apply(LabelEncoder().fit_transform)將數(shù)據(jù)集中的分類變量轉(zhuǎn)換為數(shù)值變量,以便于后續(xù)的機(jī)器學(xué)習(xí)建模。
圖片
如上所示的效果圖展示了我們的初步分析結(jié)果。當(dāng)然,你可以選擇任何一個(gè)字段來(lái)進(jìn)行價(jià)格的取點(diǎn)分析,但通過(guò)肉眼觀察,往往難以發(fā)現(xiàn)變量之間的相關(guān)性。不過(guò),這并不成問(wèn)題,因?yàn)楝F(xiàn)在有很多強(qiáng)大的工具可以幫助我們進(jìn)行深入的數(shù)據(jù)分析。
print(new_pumpkins['City'].corr(new_pumpkins['Price']))
# 0.32363971816089226
確實(shí),整個(gè)過(guò)程簡(jiǎn)單明了,一句話就可以概括。接下來(lái),只需將剩余的分析任務(wù)交給框架即可。在這里,我們觀察到相關(guān)性大約只有0.3,顯示出與1的距離相當(dāng)明顯,這意味著變量之間的關(guān)系并不強(qiáng)。
因此,我們可以嘗試更換另一個(gè)字段來(lái)對(duì)價(jià)格進(jìn)行分析,以進(jìn)一步探討不同變量之間的相關(guān)性。
print(new_pumpkins['Package'].corr(new_pumpkins['Price']))
# 0.6061712937226021
圖片
我們清楚,影響價(jià)格的因素有很多,因此每次都單獨(dú)嘗試更換字段并運(yùn)行代碼,以判斷哪個(gè)字段與價(jià)格的相關(guān)性最高,顯得十分繁瑣和低效。在這種情況下,我們可以采用一種更為高效的方法——構(gòu)造熱力圖。
corr = poly_pumpkins.corr()
corr.style.background_gradient(cmap='coolwarm')
代碼其實(shí)非常簡(jiǎn)單,僅需兩行就能實(shí)現(xiàn)我們的目標(biāo)。接下來(lái),讓我們一起觀察運(yùn)行結(jié)果,以便更好地理解這些代碼的效果和輸出。
圖片
根據(jù)這些相關(guān)性系數(shù),你可以直觀地看到 Package 和 Price 之間的良好相關(guān)性。
回歸模型
到目前為止,我們可以初步確認(rèn),包裝方式的價(jià)格與城市地區(qū)分布的價(jià)格相比,更具相關(guān)性。這一發(fā)現(xiàn)為我們的分析奠定了基礎(chǔ),因此接下來(lái),我們將以包裝方式為核心,創(chuàng)建一個(gè)回歸模型。
new_columns = ['Package', 'Price']
lin_pumpkins = new_pumpkins.drop([c for c in new_pumpkins.columns if c not in new_columns], axis='columns')
X = lin_pumpkins.values[:, :1]
y = lin_pumpkins.values[:, 1:2]
同樣的,我們只要包裝和價(jià)格字段即可。讓后取第一列為X軸數(shù)據(jù),第二列為Y軸數(shù)據(jù)。
接下來(lái),開始構(gòu)建回歸模型,和第一節(jié)差不多,仍然是從樣本總抽取測(cè)試集以及訓(xùn)練集,使用Python的scikit-learn庫(kù)來(lái)訓(xùn)練一個(gè)線性回歸模型,并對(duì)測(cè)試集進(jìn)行預(yù)測(cè),代碼再寫一次:
from sklearn.linear_model import LinearRegression
from sklearn.metrics import r2_score, mean_squared_error, mean_absolute_error
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)
lin_reg = LinearRegression()
lin_reg.fit(X_train,y_train)
pred = lin_reg.predict(X_test)
accuracy_score = lin_reg.score(X_train,y_train)
print('Model Accuracy: ', accuracy_score)
# Model Accuracy: 0.3315342327998987
我來(lái)簡(jiǎn)單的解釋一下:
- 將數(shù)據(jù)集分為訓(xùn)練集和測(cè)試集
- 創(chuàng)建線性回歸模型并訓(xùn)練它,是的,就兩行代碼完成了訓(xùn)練,都不用我們自己寫什么東西。
- 使用訓(xùn)練好的模型對(duì)測(cè)試集進(jìn)行預(yù)測(cè)
- 計(jì)算并打印模型的準(zhǔn)確度
最終的分?jǐn)?shù)確實(shí)不是很高,畢竟相關(guān)性也不是很好。
我們可以對(duì)已經(jīng)訓(xùn)練好的模型進(jìn)行可視化,以更直觀地展示其性能和預(yù)測(cè)結(jié)果。
plt.scatter(X_test, y_test, color='black')
plt.plot(X_test, pred, color='blue', linewidth=3)
plt.xlabel('Package')
plt.ylabel('Price')
plt.show()
圖片
這樣,我們基本上就成功實(shí)現(xiàn)了一個(gè)基礎(chǔ)的線性回歸模型。
多項(xiàng)式回歸
另一種重要的線性回歸類型是多項(xiàng)式回歸。盡管在許多情況下,我們觀察到變量之間存在直接的線性關(guān)系——例如,南瓜的體積越大,價(jià)格通常也會(huì)隨之上漲——但在某些情況下,這種關(guān)系可能無(wú)法簡(jiǎn)單地用平面或直線來(lái)表示。
正如我們?cè)谏厦娴膱D例中所見(jiàn),如果我們的模型采用曲線而非直線,這可能更有效地貼合數(shù)據(jù)點(diǎn)的分布,從而更準(zhǔn)確地捕捉變量之間的復(fù)雜關(guān)系。
new_columns = ['Variety', 'Package', 'City', 'Month', 'Price']
poly_pumpkins = new_pumpkins.drop([c for c in new_pumpkins.columns if c not in new_columns], axis='columns')
我們重新看下數(shù)據(jù),然后直接拿出Package和Price值。
X=poly_pumpkins.iloc[:,3:4].values
y=poly_pumpkins.iloc[:,4:5].values
接下來(lái),我們將直接進(jìn)行模型訓(xùn)練。在這個(gè)過(guò)程中,我們使用了另一個(gè)API,即scikit-learn庫(kù),來(lái)構(gòu)建一個(gè)包含多項(xiàng)式特征轉(zhuǎn)換和線性回歸模型的管道(pipeline)。這個(gè)管道的設(shè)計(jì)旨在簡(jiǎn)化數(shù)據(jù)處理流程,使得多項(xiàng)式特征的生成和模型的訓(xùn)練能夠高效地串聯(lián)在一起。
from sklearn.preprocessing import PolynomialFeatures
from sklearn.pipeline import make_pipeline
pipeline = make_pipeline(PolynomialFeatures(4), LinearRegression())
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)
pipeline.fit(np.array(X_train), y_train)
y_pred=pipeline.predict(X_test)
PolynomialFeatures
在這里,我想簡(jiǎn)要介紹一下PolynomialFeatures這一功能。PolynomialFeatures是scikit-learn庫(kù)中用于生成多項(xiàng)式特征的工具。
它的主要作用是將輸入的特征轉(zhuǎn)換為多項(xiàng)式形式,從而允許我們?cè)谀P椭锌紤]變量之間的非線性關(guān)系。例如,當(dāng)我們有一個(gè)單一特征時(shí),使用PolynomialFeatures可以創(chuàng)建該特征的平方、立方,甚至更高次的特征。
我們的代碼中參數(shù)設(shè)置為4,意味著我們希望對(duì)輸入特征X進(jìn)行四次多項(xiàng)式轉(zhuǎn)換。這種轉(zhuǎn)換將使得每個(gè)原始特征生成其四次冪的組合,從而豐富我們的特征集。
為了幫助大家更直觀地理解這一過(guò)程,我將提供一組示例數(shù)據(jù),展示經(jīng)過(guò)PolynomialFeatures處理后,原有的X特征數(shù)據(jù)被轉(zhuǎn)化成了什么樣的多項(xiàng)式特征。
import numpy as np
X = np.array([1, 2, 3, 4, 5])
X = X.reshape(-1, 1)
X
這是我們當(dāng)前的特征集X,包含了一組簡(jiǎn)單的數(shù)據(jù)點(diǎn)。
array([[1],
[2],
[3],
[4],
[5]])
pp = PolynomialFeatures(4)
X_poly = pp.fit_transform(X)
print(X_poly)
轉(zhuǎn)換后會(huì)生成如下:
[[ 1. 1. 1. 1. 1.]
[ 1. 2. 4. 8. 16.]
[ 1. 3. 9. 27. 81.]
[ 1. 4. 16. 64. 256.]
[ 1. 5. 25. 125. 625.]]
這樣做的意義就在于:線性回歸假設(shè)特征與目標(biāo)之間的關(guān)系是線性的。通過(guò)多項(xiàng)式特征轉(zhuǎn)換,可以捕捉更復(fù)雜的非線性關(guān)系。這有助于提高模型的擬合能力。
在訓(xùn)練過(guò)程中,模型實(shí)際上是在學(xué)習(xí)一個(gè)非線性函數(shù),舉個(gè)例子例如:二次多項(xiàng)式 表示一個(gè)拋物線。
那么這個(gè)模型訓(xùn)練后就會(huì)產(chǎn)生如下效果:
df = pd.DataFrame({'x': X_test[:,0], 'y': y_pred[:,0]})
df.sort_values(by='x',inplace = True)
points = pd.DataFrame(df).to_numpy()
plt.plot(points[:, 0], points[:, 1],color="blue", linewidth=3)
plt.xlabel('Package')
plt.ylabel('Price')
plt.scatter(X,y, color="black")
plt.show()
圖片
接下來(lái),我們可以基于這個(gè)非線性模型進(jìn)行預(yù)測(cè)即可。簡(jiǎn)單演示一下:
pipeline.predict( np.array([ [2.75] ]) )
# array([[46.34509342]])
總結(jié)
在探討線性回歸和多項(xiàng)式回歸的旅程中,我們不僅學(xué)習(xí)了如何構(gòu)建模型,還理解了背后的數(shù)學(xué)原理和應(yīng)用場(chǎng)景。通過(guò)逐步引導(dǎo),希望大家對(duì)數(shù)據(jù)分析的復(fù)雜性有了更深的認(rèn)識(shí)。未來(lái),在實(shí)際問(wèn)題中靈活應(yīng)用這些回歸技術(shù),將使我們?cè)跀?shù)據(jù)驅(qū)動(dòng)的決策中占據(jù)優(yōu)勢(shì)。
無(wú)論是選擇合適的回歸方法,還是運(yùn)用強(qiáng)大的工具,我們都能夠更有效地從數(shù)據(jù)中提煉出有價(jià)值的見(jiàn)解。接下來(lái),期待大家在實(shí)踐中不斷探索、深入挖掘,最終實(shí)現(xiàn)對(duì)數(shù)據(jù)的深刻理解與應(yīng)用。