机器学习----多项式回归
多項(xiàng)式回歸簡介
考慮下面的數(shù)據(jù),雖然我們可以使用線性回歸來擬合這些數(shù)據(jù),但是這些數(shù)據(jù)更像是一條二次曲線,相應(yīng)的方程是y=ax2+bx+c,這是式子雖然可以理解為二次方程,但是我們呢可以從另外一個(gè)角度來理解這個(gè)式子:
如果將x2理解為一個(gè)特征,將x理解為另外一個(gè)特征,換句話說,本來我們的樣本只有一個(gè)特征x,現(xiàn)在我們把他看成有兩個(gè)特征的一個(gè)數(shù)據(jù)集。多了一個(gè)特征x2,那么從這個(gè)角度來看,這個(gè)式子依舊是一個(gè)線性回歸的式子,但是從x的角度來看,他就是一個(gè)二次的方程
以上這樣的方式,就是所謂的多項(xiàng)式回歸
相當(dāng)于我們?yōu)闃颖径嗵砑恿艘恍┨卣?#xff0c;這些特征是原來樣本的多項(xiàng)式項(xiàng),增加了這些特征之后,我們們可以使用線性回歸的思路更好的我們的數(shù)據(jù)
什么是多項(xiàng)式回歸
import numpy as np import matplotlib.pyplot as plt x = np.random.uniform(-3, 3, size=100) X = x.reshape(-1, 1) # 一元二次方程 y = 0.5 * x**2 + x + 2 + np.random.normal(0, 1, 100) plt.scatter(x, y) plt.show()線性回歸?
from sklearn.linear_model import LinearRegressionlin_reg = LinearRegression() lin_reg.fit(X, y) y_predict = lin_reg.predict(X) plt.scatter(x, y) plt.plot(x, y_predict, color='r') plt.show()很明顯,我們用一跟直線來擬合一根有弧度的曲線,效果是不好的
解決方案, 添加一個(gè)特征
原來所有的數(shù)據(jù)都在X中,現(xiàn)在對X中每一個(gè)數(shù)據(jù)都進(jìn)行平方,
再將得到的數(shù)據(jù)集與原數(shù)據(jù)集進(jìn)行拼接,
在用新的數(shù)據(jù)集進(jìn)行線性回歸
從上圖可以看出,當(dāng)我們添加了一個(gè)特征(原來特征的平方)之后,再從x的維度來看,就形成了一條曲線,顯然這個(gè)曲線對原來數(shù)據(jù)集的擬合程度是更好的
# 第一個(gè)系數(shù)是x前面的系數(shù),第二個(gè)系數(shù)是x平方前面的系數(shù) lin_reg2.coef_ array([ 0.99870163, 0.54939125]) lin_reg2.intercept_ 1.88552367865160013.總結(jié)
多線性回歸在機(jī)器學(xué)習(xí)算法上并沒有新的地方,完全是使用線性回歸的思路
他的關(guān)鍵在于為原來的樣本,添加新的特征。而我們得到新的特征的方式是原有特征的多項(xiàng)式的組合。
采用這樣的方式,我們就可以解決一些非線性的問題
與此同時(shí)需要主要,我們在上一章所講的PCA是對我們的數(shù)據(jù)進(jìn)行降維處理,而我們這一章所講的多項(xiàng)式回歸顯然在做一件相反的事情,他讓我們的數(shù)據(jù)升維,在升維之后使得我們的算法可以更好的擬合高緯度的數(shù)據(jù)
scikit-learn中的多項(xiàng)式回歸和Pipeline
import numpy as np import matplotlib.pyplot as plt x = np.random.uniform(-3, 3, size=100) X = x.reshape(-1, 1) y = 0.5 * x**2 + x + 2 + np.random.normal(0, 1, 100) # sklearn中對數(shù)據(jù)進(jìn)行預(yù)處理的函數(shù)都封裝在preprocessing模塊下,包括之前學(xué)的歸一化StandardScaler from sklearn.preprocessing import PolynomialFeatures poly = PolynomialFeatures(degree=2) # 表示為數(shù)據(jù)的特征最多添加2次冪 poly.fit(X) X2 = poly.transform(X) X2.shape (100, 3) X[:5,:] array([[ 0.14960154],[ 0.49319423],[-0.87176575],[-1.33024477],[ 0.47383199]]) # 第一列是sklearn為我們添加的X的零次方的特征 # 第二列和原來的特征一樣是X的一次方的特征 # 第三列是添加的X的二次方的特征 X2[:5,:] array([[ 1. , 0.14960154, 0.02238062],[ 1. , 0.49319423, 0.24324055],[ 1. , -0.87176575, 0.75997552],[ 1. , -1.33024477, 1.76955114],[ 1. , 0.47383199, 0.22451675]]) from sklearn.linear_model import LinearRegressionlin_reg2 = LinearRegression() lin_reg2.fit(X2, y) y_predict2 = lin_reg2.predict(X2) plt.scatter(x, y) plt.plot(np.sort(x), y_predict2[np.argsort(x)], color='r') plt.show() lin_reg2.coef_ array([ 0. , 0.9460157 , 0.50420543]) lin_reg2.intercept_ 2.1536054095953823關(guān)于PolynomialFeatures
X = np.arange(1, 11).reshape(-1, 2) X array([[ 1, 2],[ 3, 4],[ 5, 6],[ 7, 8],[ 9, 10]]) poly = PolynomialFeatures(degree=2) poly.fit(X) X2 = poly.transform(X) X2.shape (5, 6) X2 array([[ 1., 1., 2., 1., 2., 4.],[ 1., 3., 4., 9., 12., 16.],[ 1., 5., 6., 25., 30., 36.],[ 1., 7., 8., 49., 56., 64.],[ 1., 9., 10., 81., 90., 100.]])將5行2列的矩陣進(jìn)行多項(xiàng)式轉(zhuǎn)換后變成了5行6列
第一列是1 對應(yīng)的是0次冪
第二列和第三列對應(yīng)的是原來的x矩陣,此時(shí)他有兩列一次冪的項(xiàng)
第四列是原來數(shù)據(jù)的第一列平方的結(jié)果
第六列是原來數(shù)據(jù)的第二列平方的結(jié)果
第五列是原來數(shù)據(jù)的兩列相乘的結(jié)果
可以想象如果將degree設(shè)置為3,那么將產(chǎn)生一下10個(gè)元素
也就是說PolynomialFeatures會(huì)窮舉出所有的多項(xiàng)式組合
Pipeline
pipline的英文名字是管道,那么 我們?nèi)绾问褂霉艿滥?#xff0c;先考慮我們多項(xiàng)式回歸的過程
1.使用PolynomialFeatures生成多項(xiàng)式特征的數(shù)據(jù)集
2.如果生成數(shù)據(jù)冪特別的大,那么特征直接的差距就會(huì)很大,導(dǎo)致我們的搜索非常慢,這時(shí)候可以進(jìn)行數(shù)據(jù)歸一化
3.進(jìn)行線性回歸
pipline 的作用就是把上面的三個(gè)步驟合并,使得我們不用一直重復(fù)這三步
過擬合和欠擬合
import numpy as np import matplotlib.pyplot as plt np.random.seed(666) x = np.random.uniform(-3.0, 3.0, size=100) X = x.reshape(-1, 1) y = 0.5 * x**2 + x + 2 + np.random.normal(0, 1, size=100) plt.scatter(x, y) plt.show()使用線性回歸
from sklearn.linear_model import LinearRegressionlin_reg = LinearRegression() lin_reg.fit(X, y) lin_reg.score(X, y) 0.49537078118650091 y_predict = lin_reg.predict(X) plt.scatter(x, y) plt.plot(np.sort(x), y_predict[np.argsort(x)], color='r') plt.show()使用均方誤差來看擬合的結(jié)果,這是因?yàn)槲覀兺瑯佣际菍σ唤M數(shù)據(jù)進(jìn)行擬合,所以使用不同的方法對數(shù)據(jù)進(jìn)行擬合
得到的均方誤差的指標(biāo)是具有可比性的。
使用多項(xiàng)式回歸
from sklearn.pipeline import Pipeline from sklearn.preprocessing import PolynomialFeatures from sklearn.preprocessing import StandardScalerdef PolynomialRegression(degree):return Pipeline([("poly", PolynomialFeatures(degree=degree)),("std_scaler", StandardScaler()),("lin_reg", LinearRegression())]) poly2_reg = PolynomialRegression(degree=2) poly2_reg.fit(X, y) Pipeline(steps=[('poly', PolynomialFeatures(degree=2, include_bias=True, interaction_only=False)), ('std_scaler', StandardScaler(copy=True, with_mean=True, with_std=True)), ('lin_reg', LinearRegression(copy_X=True, fit_intercept=True, n_jobs=1, normalize=False))]) y2_predict = poly2_reg.predict(X) # 顯然使用多項(xiàng)式回歸得到的結(jié)果是更好的 mean_squared_error(y, y2_predict) 1.0987392142417856 plt.scatter(x, y) plt.plot(np.sort(x), y2_predict[np.argsort(x)], color='r') plt.show() poly10_reg = PolynomialRegression(degree=10) poly10_reg.fit(X, y)y10_predict = poly10_reg.predict(X) mean_squared_error(y, y10_predict) 1.0508466763764164 plt.scatter(x, y) plt.plot(np.sort(x), y10_predict[np.argsort(x)], color='r') plt.show() poly100_reg = PolynomialRegression(degree=100) poly100_reg.fit(X, y)y100_predict = poly100_reg.predict(X) mean_squared_error(y, y100_predict) 0.68743577834336944 plt.scatter(x, y) plt.plot(np.sort(x), y100_predict[np.argsort(x)], color='r') plt.show()這條曲線只是原來隨機(jī)生成的點(diǎn)(分布不均勻)對應(yīng)的y的預(yù)測值連接起來的曲線,不過有x軸很多地方可能沒有數(shù)據(jù)點(diǎn),所以連接的結(jié)果和原來的曲線不一樣(不是真實(shí)的y曲線)。
下面嘗試真正還原原來的曲線(構(gòu)造均勻分布的原數(shù)據(jù)集)
總有一條曲線,他能擬合所有的樣本點(diǎn),使得均方誤差的值為0
degree從2到10到100的過程中,雖然均方誤差是越來越小的,從均方誤差的角度來看是更加小的
但是他真的能更好的預(yù)測我們數(shù)據(jù)的走勢嗎,例如我們選擇2.5到3的一個(gè)x,使用上圖預(yù)測出來的y的大小(0或者-1之間)顯然不符合我們的數(shù)據(jù)
換句話說,我們使用了一個(gè)非常高維的數(shù)據(jù),雖然使得我們的樣本點(diǎn)獲得了更小的誤差,但是這根曲線完全不是我們想要的樣子
他為了擬合我們所有的樣本點(diǎn),變的太過復(fù)雜了,這種情況就是過擬合【over-fitting】
相反,在最開始,我們直接使用一根直線來擬合我們的數(shù)據(jù),也沒有很好的擬合我們的樣本特征,當(dāng)然他犯的錯(cuò)誤不是太過復(fù)雜了,而是太過簡單了
這種情況,我們成為欠擬合-【under-fitting】
對于現(xiàn)在的數(shù)據(jù)(基于二次方程構(gòu)造),我們使用低于2項(xiàng)的擬合結(jié)果,就是欠擬合;高于2項(xiàng)的擬合結(jié)果,就是過擬合
為什么要使用訓(xùn)練數(shù)據(jù)集和測試數(shù)據(jù)集
模型的泛化能力
使用上節(jié)的過擬合結(jié)果,我們可以得知,雖然我們訓(xùn)練出的曲線將原來的樣本點(diǎn)擬合的非常好,總體的誤差非常的小, 但是一旦來了新的樣本點(diǎn),他就不能很好的預(yù)測了,在這種情況下,我們就稱我們得到的這條彎彎曲曲的曲線,他的**泛化能力(由此及彼的能力)**非常弱
image.png
訓(xùn)練數(shù)據(jù)集和測試數(shù)據(jù)集的意義
我們訓(xùn)練的模型目的是為了使得預(yù)測的數(shù)據(jù)能夠盡肯能的準(zhǔn)確,在這種情況下,我們觀察訓(xùn)練數(shù)據(jù)集的擬合程度是沒有意義的 我們真正需要的是,我們得到的模型的泛化能力更高,解決這個(gè)問題的方法也就是使用訓(xùn)練數(shù)據(jù)集,測試數(shù)據(jù)集的分離
測試數(shù)據(jù)對于我們的模型是全新的數(shù)據(jù),如果使用訓(xùn)練數(shù)據(jù)獲得的模型面對測試數(shù)據(jù)也能獲得很好的結(jié)果,那么我們就說我們的模型泛化能力是很強(qiáng)的。 如果我們的模型面對測試數(shù)據(jù)結(jié)果很差的話,那么他的泛化能力就很弱。事實(shí)上,這是訓(xùn)練數(shù)據(jù)集更大的意義
train test split的意義
from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=666) lin_reg = LinearRegression() lin_reg.fit(X_train, y_train) y_predict = lin_reg.predict(X_test) mean_squared_error(y_test, y_predict) 2.2199965269396573 poly2_reg = PolynomialRegression(degree=2) poly2_reg.fit(X_train, y_train) y2_predict = poly2_reg.predict(X_test) mean_squared_error(y_test, y2_predict) 0.80356410562978997 poly10_reg = PolynomialRegression(degree=10) poly10_reg.fit(X_train, y_train) y10_predict = poly10_reg.predict(X_test) mean_squared_error(y_test, y10_predict) 0.92129307221507939 poly100_reg = PolynomialRegression(degree=100) poly100_reg.fit(X_train, y_train) y100_predict = poly100_reg.predict(X_test) mean_squared_error(y_test, y100_predict) 14075796419.234262剛剛我們進(jìn)行的實(shí)驗(yàn)實(shí)際上在實(shí)驗(yàn)?zāi)P偷膹?fù)雜度,對于多項(xiàng)式模型來說,我們回歸的階數(shù)越高,我們的模型會(huì)越復(fù)雜,在這種情況下對于我們的機(jī)器學(xué)習(xí)算法來說,通常是有下面一張圖的。橫軸是模型復(fù)雜度(對于不同的算法來說,代表的是不同的意思,比如對于多項(xiàng)式回歸來說,是階數(shù)越高,越復(fù)雜;對于KNN來說,是K越小,模型越復(fù)雜,k越大,模型最簡單,當(dāng)k=n的時(shí)候,模型就簡化成了看整個(gè)樣本里,哪種樣本最多,當(dāng)k=1來說,對于每一個(gè)點(diǎn),都要找到離他最近的那個(gè)點(diǎn)),另一個(gè)維度是模型準(zhǔn)確率(也就是他能夠多好的預(yù)測我們的曲線)
通常對于這樣一個(gè)圖,會(huì)有兩根曲線:
- 一個(gè)是對于訓(xùn)練數(shù)據(jù)集來說的,模型越復(fù)雜,模型準(zhǔn)確率越高,因?yàn)槟P驮綇?fù)雜,對訓(xùn)練數(shù)據(jù)集的擬合就越好,相應(yīng)的模型準(zhǔn)確率就越高
- 對于測試數(shù)據(jù)集來說,在模型很簡單的時(shí)候,模型的準(zhǔn)確率也比較低,隨著模型逐漸變復(fù)雜,對測試數(shù)據(jù)集的準(zhǔn)確率在逐漸的提升,提升到一定程度后,如果模型繼續(xù)變復(fù)雜,那么我們的模型準(zhǔn)確率將會(huì)進(jìn)行下降(欠擬合->正合適->過擬合)
欠擬合和過擬合的標(biāo)準(zhǔn)定義
欠擬合:算法所訓(xùn)練的模型不能完整表述數(shù)據(jù)關(guān)系 過擬合:算法所訓(xùn)練的模型過多的表達(dá)了數(shù)據(jù)間的噪音關(guān)系
學(xué)習(xí)曲線
隨著訓(xùn)練樣本的逐漸增多,算法訓(xùn)練出的模型的表現(xiàn)能力
import numpy as np import matplotlib.pyplot as plt np.random.seed(666) x = np.random.uniform(-3.0, 3.0, size=100) X = x.reshape(-1, 1) y = 0.5 * x**2 + x + 2 + np.random.normal(0, 1, size=100) plt.scatter(x, y) plt.show()學(xué)習(xí)曲線
實(shí)際編程實(shí)現(xiàn)學(xué)習(xí)曲線
from sklearn.model_selection import train_test_splitX_train, X_test, y_train, y_test = train_test_split(X, y, random_state=10) X_train.shape (75, 1)2.1觀察線性回歸的學(xué)習(xí)曲線:觀察線性回歸模型,隨著訓(xùn)練數(shù)據(jù)集增加,性能的變化
from sklearn.linear_model import LinearRegression from sklearn.metrics import mean_squared_errortrain_score = [] test_score = [] for i in range(1, 76):lin_reg = LinearRegression()lin_reg.fit(X_train[:i], y_train[:i])y_train_predict = lin_reg.predict(X_train[:i])train_score.append(mean_squared_error(y_train[:i], y_train_predict))y_test_predict = lin_reg.predict(X_test)test_score.append(mean_squared_error(y_test, y_test_predict)) plt.plot([i for i in range(1, 76)], np.sqrt(train_score), label="train") plt.plot([i for i in range(1, 76)], np.sqrt(test_score), label="test") plt.legend() plt.show()從趨勢上看:
在訓(xùn)練數(shù)據(jù)集上,誤差是逐漸升高的。這是因?yàn)槲覀兊挠?xùn)練數(shù)據(jù)越來越多,我們的數(shù)據(jù)點(diǎn)越難得到全部的累積,不過整體而言,在剛開始的時(shí)候誤差變化的比較快,后來就幾乎不變了
在測試數(shù)據(jù)集上,在使用非常少的樣本進(jìn)行訓(xùn)練的時(shí)候,剛開始我們的測試誤差非常的大,當(dāng)訓(xùn)練樣本大到一定程度以后,我們的測試誤差就會(huì)逐漸減小,減小到一定程度后,也不會(huì)小太多,達(dá)到一種相對穩(wěn)定的情況
在最終,測試誤差和訓(xùn)練誤差趨于相等,不過測試誤差還是高于訓(xùn)練誤差一些,這是因?yàn)?#xff0c;訓(xùn)練數(shù)據(jù)在數(shù)據(jù)非常多的情況下,可以將數(shù)據(jù)擬合的比較好,誤差小一些,但是泛化到測試數(shù)據(jù)集的時(shí)候,還是有可能多一些誤差
首先整體從趨勢上,和線性回歸的學(xué)習(xí)曲線是類似的
仔細(xì)觀察,和線性回歸曲線的不同在于,線性回歸的學(xué)習(xí)曲線1.5,1.8左右;2階多項(xiàng)式回歸穩(wěn)定在了1.0,0.9左右,2階多項(xiàng)式穩(wěn)定的誤差比較低,說明使用二階線性回歸的性能是比較好的
在使用20階多項(xiàng)式回歸訓(xùn)練模型的時(shí)候可以發(fā)現(xiàn),在數(shù)據(jù)量偏多的時(shí)候,我們的訓(xùn)練數(shù)據(jù)集擬合的是比較好的,但是測試數(shù)據(jù)集的誤差相對來說增大了很多,離訓(xùn)練數(shù)據(jù)集比較遠(yuǎn),通常這就是過擬合的結(jié)果,他的泛化能力是不夠的
總結(jié)
對于欠擬合比最佳的情況趨于穩(wěn)定的那個(gè)位置要高一些,說明無論對于訓(xùn)練數(shù)據(jù)集還是測試數(shù)據(jù)集來說,誤差都比較大。這是因?yàn)槲覀儽旧砟P瓦x的就不對,所以即使在訓(xùn)練數(shù)據(jù)集上,他的誤差也是大的,所以才會(huì)呈現(xiàn)出這樣的一種形態(tài)
對于過擬合的情況,在訓(xùn)練數(shù)據(jù)集上,他的誤差不大,和最佳的情況是差不多的,甚至在極端情況,如果degree取更高的話,那么訓(xùn)練數(shù)據(jù)集的誤差會(huì)更低,但是問題在于,測試數(shù)據(jù)集的誤差相對是比較大的,并且訓(xùn)練數(shù)據(jù)集的誤差和測試數(shù)據(jù)集的誤差相差比較大(表現(xiàn)在圖上相差比較遠(yuǎn)),這就說明了此時(shí)我們的模型的泛化能力不夠好,他的泛化能力是不夠的
驗(yàn)證數(shù)據(jù)集與交叉驗(yàn)證
使用分割訓(xùn)練數(shù)據(jù)集和測試數(shù)據(jù)集來判斷我們的機(jī)器學(xué)習(xí)性能的好壞,雖然是一個(gè)非常好的方案,但是會(huì)產(chǎn)生一個(gè)問題:針對特定測試數(shù)據(jù)集過擬合
我們每次使用測試數(shù)據(jù)來分析性能的好壞。一旦發(fā)現(xiàn)結(jié)果不好,我們就換一個(gè)參數(shù)(可能是degree也可能是其他超參數(shù))重新進(jìn)行訓(xùn)練。這種情況下,我們的模型在一定程度上圍繞著測試數(shù)據(jù)集打轉(zhuǎn)。也就是說我們在尋找一組參數(shù),使得這組參數(shù)訓(xùn)練出來的模型在測試結(jié)果集上表現(xiàn)的最好。但是由于這組測試數(shù)據(jù)集是已知的,我們相當(dāng)于在針對這組測試數(shù)據(jù)集進(jìn)行調(diào)參,那么他也有可能產(chǎn)生過擬合的情況,也就是我們得到的模型針對測試數(shù)據(jù)集過擬合了
那么怎么解決這個(gè)問題呢? 解決的方式其實(shí)就是:我們需要將我們的問題分為三部分,這三部分分別是訓(xùn)練數(shù)據(jù)集,驗(yàn)證數(shù)據(jù)集,測試數(shù)據(jù)集。 我們使用訓(xùn)練數(shù)據(jù)集訓(xùn)練好模型之后,將驗(yàn)證數(shù)據(jù)集送給這個(gè)模型,看看這個(gè)訓(xùn)練數(shù)據(jù)集訓(xùn)練的效果是怎么樣的,如果效果不好的話,我們重新?lián)Q參數(shù),重新訓(xùn)練模型。直到我們的模型針對驗(yàn)證數(shù)據(jù)來說已經(jīng)達(dá)到最優(yōu)了。 這樣我們的模型達(dá)到最優(yōu)以后,再講測試數(shù)據(jù)集送給模型,這樣才能作為衡量模型最終的性能。換句話說,我們的測試數(shù)據(jù)集是不參與模型的創(chuàng)建的,而其他兩個(gè)數(shù)據(jù)集都參與了訓(xùn)練。但是我們的測試數(shù)據(jù)集對于模型是完全不可知的,相當(dāng)于我們在模型這個(gè)模型完全不知道的數(shù)據(jù)
這種方法還會(huì)有一個(gè)問題。由于我們的模型可能會(huì)針對驗(yàn)證數(shù)據(jù)集過擬合,而我們只有一份驗(yàn)證數(shù)據(jù)集,一旦我們的數(shù)據(jù)集里有比較極端的情況,那么模型的性能就會(huì)下降很多,那么為了解決這個(gè)問題,就有了交叉驗(yàn)證。
交叉驗(yàn)證 Cross Validation
交叉驗(yàn)證相對來說是比較正規(guī)的、比較標(biāo)準(zhǔn)的在我們調(diào)整我們的模型參數(shù)的時(shí)候看我們的性能的方式
交叉驗(yàn)證:在訓(xùn)練模型的時(shí)候,通常把數(shù)據(jù)分成k份,例如分成3份(ABC)(分成k分,k屬于超參數(shù)),這三份分別作為驗(yàn)證數(shù)據(jù)集和訓(xùn)練數(shù)據(jù)集。這樣組合后可以分別產(chǎn)生三個(gè)模型,這三個(gè)模型,每個(gè)模型在測試數(shù)據(jù)集上都會(huì)產(chǎn)生一個(gè)性能的指標(biāo),這三個(gè)指標(biāo)的平均值作為當(dāng)前這個(gè)算法訓(xùn)練處的模型衡量的標(biāo)準(zhǔn)是怎樣的。 由于我們有一個(gè)求平均的過程,所以不會(huì)由于一份驗(yàn)證數(shù)據(jù)集中有比較極端的數(shù)據(jù)而導(dǎo)致模型有過大的偏差,這比我們只分成訓(xùn)練、驗(yàn)證、測試數(shù)據(jù)集要更加準(zhǔn)確
Validation 和 Cross Validation
import numpy as np from sklearn import datasets digits = datasets.load_digits() X = digits.data y = digits.target測試train_test_split
from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.4, random_state=666) from sklearn.neighbors import KNeighborsClassifierbest_k, best_p, best_score = 0, 0, 0 # k為k近鄰中的尋找k個(gè)最近元素 for k in range(2, 11): # p為明科夫斯基距離的pfor p in range(1, 6):knn_clf = KNeighborsClassifier(weights="distance", n_neighbors=k, p=p)knn_clf.fit(X_train, y_train)score = knn_clf.score(X_test, y_test)if score > best_score:best_k, best_p, best_score = k, p, scoreprint("Best K =", best_k) print("Best P =", best_p) print("Best Score =", best_score) Best K = 3 Best P = 4 Best Score = 0.986091794159使用交叉驗(yàn)證
# 使用sklearn提供的交叉驗(yàn)證 from sklearn.model_selection import cross_val_scoreknn_clf = KNeighborsClassifier() # 返回的是一個(gè)數(shù)組,有三個(gè)元素,說明cross_val_score方法默認(rèn)將我們的數(shù)據(jù)集分成了三份 # 這三份數(shù)據(jù)集進(jìn)行交叉驗(yàn)證后產(chǎn)生了這三個(gè)結(jié)果# cv默認(rèn)為3,可以修改改參數(shù),修改修改不同分?jǐn)?shù)的數(shù)據(jù)集 cross_val_score(knn_clf,X_train,y_train,cv=3) array([ 0.98895028, 0.97777778, 0.96629213]) best_k, best_p, best_score = 0, 0, 0 for k in range(2, 11):for p in range(1, 6):knn_clf = KNeighborsClassifier(weights="distance", n_neighbors=k, p=p)scores = cross_val_score(knn_clf, X_train, y_train)score = np.mean(scores)if score > best_score:best_k, best_p, best_score = k, p, scoreprint("Best K =", best_k) print("Best P =", best_p) print("Best Score =", best_score) Best K = 2 Best P = 2 Best Score = 0.982359987401通過觀察兩組調(diào)參過程的結(jié)果可以發(fā)現(xiàn)
1.兩組調(diào)參得出的參數(shù)結(jié)果是不同的,通常這時(shí)候我們更愿意詳細(xì)使用交叉驗(yàn)證的方式得出的結(jié)果。
因?yàn)槭褂胻rain_test_split很有可能只是過擬合了測試數(shù)據(jù)集得出的結(jié)果
2.使用交叉驗(yàn)證得出的最好分?jǐn)?shù)0.982是小于使用分割訓(xùn)練測試數(shù)據(jù)集得出的0.986,因?yàn)樵诮徊骝?yàn)證的
過程中,通常不會(huì)過擬合某一組的測試數(shù)據(jù),所以平均來講這個(gè)分?jǐn)?shù)會(huì)稍微低一些
但是使用交叉驗(yàn)證得到的最好參數(shù)Best_score并不是真正的最好的結(jié)果,我們使用這種方式只是為了拿到
一組超參數(shù)而已,拿到這組超參數(shù)后我們就可以訓(xùn)練處我們的最佳模型
回顧網(wǎng)格搜索
我們上面的操作,實(shí)際上在網(wǎng)格搜索的過程中已經(jīng)進(jìn)行了,只不過這個(gè)過程是sklean的網(wǎng)格搜索自帶的一個(gè)過程
from sklearn.model_selection import GridSearchCVparam_grid = [{'weights': ['distance'],'n_neighbors': [i for i in range(2, 11)], 'p': [i for i in range(1, 6)]} ]grid_search = GridSearchCV(knn_clf, param_grid, verbose=1) grid_search.fit(X_train, y_train) Fitting 3 folds for each of 45 candidates, totalling 135 fits[Parallel(n_jobs=1)]: Done 135 out of 135 | elapsed: 1.9min finished的意思就是交叉驗(yàn)證中分割了三組數(shù)據(jù)集,而我們的參數(shù)組合為9*6=45中組合
3組數(shù)據(jù)集,45種組合,一共要進(jìn)行135次的訓(xùn)練.
cv參數(shù)
cross_val_score(knn_clf, X_train, y_train, cv=5) array([ 0.99543379, 0.96803653, 0.98148148, 0.96261682, 0.97619048]) # cv默認(rèn)為3,可以修改改參數(shù),修改修改不同分?jǐn)?shù)的數(shù)據(jù)集 grid_search = GridSearchCV(knn_clf, param_grid, verbose=1, cv=5)總結(jié)
雖然整體速度慢了,但是這個(gè)結(jié)果卻是可信賴的
模型正則化-Regularization
什么是模型正則化
下圖是我們之前使用多項(xiàng)式回歸過擬合一個(gè)樣本的例子,可以看到這條模型曲線非常的彎曲,而且非常的陡峭,可以想象這條曲線的一些θ系數(shù)會(huì)非常的大。 模型正則化需要做的事情就是限制這些系數(shù)的大小
模型正則化基本原理
一些需要注意的細(xì)節(jié):
嶺回歸 Ridge Regression
編程實(shí)現(xiàn)嶺回歸
嶺回歸 Ridge Regression
import numpy as np import matplotlib.pyplot as plt np.random.seed(42) x = np.random.uniform(-3.0, 3.0, size=100) X = x.reshape(-1, 1) y = 0.5 * x + 3 + np.random.normal(0, 1, size=100) plt.scatter(x, y) plt.show() from sklearn.pipeline import Pipeline from sklearn.preprocessing import PolynomialFeatures from sklearn.preprocessing import StandardScaler from sklearn.linear_model import LinearRegressiondef PolynomialRegression(degree):return Pipeline([("poly", PolynomialFeatures(degree=degree)),("std_scaler", StandardScaler()),("lin_reg", LinearRegression())]) from sklearn.model_selection import train_test_splitnp.random.seed(666) X_train, X_test, y_train, y_test = train_test_split(X, y) from sklearn.metrics import mean_squared_errorpoly_reg = PolynomialRegression(degree=20) poly_reg.fit(X_train, y_train)y_poly_predict = poly_reg.predict(X_test) mean_squared_error(y_test, y_poly_predict) 167.94010867293571 X_plot = np.linspace(-3, 3, 100).reshape(100, 1) y_plot = poly_reg.predict(X_plot)plt.scatter(x, y) plt.plot(X_plot[:,0], y_plot, color='r') plt.axis([-3, 3, 0, 6]) plt.show()將繪制封裝成函數(shù)
def plot_model(model):X_plot = np.linspace(-3, 3, 100).reshape(100, 1)y_plot = model.predict(X_plot)plt.scatter(x, y)plt.plot(X_plot[:,0], y_plot, color='r')plt.axis([-3, 3, 0, 6])plt.show()plot_model(poly_reg)使用嶺回歸
from sklearn.linear_model import Ridgedef RidgeRegression(degree, alpha):return Pipeline([("poly", PolynomialFeatures(degree=degree)),("std_scaler", StandardScaler()),("ridge_reg", Ridge(alpha=alpha))]) # 注意alpha后面的參數(shù)是所有theta的平方和,而對于多項(xiàng)式回歸來說,嶺回歸之前得到的θ都非常大 # 我們前面系數(shù)alpha可以先取的小一些(正則化程度輕一些) # 第一個(gè)參數(shù)是degree20, 0.0001是第二個(gè)參數(shù)alpha ridge1_reg = RidgeRegression(20, 0.0001) ridge1_reg.fit(X_train, y_train)y1_predict = ridge1_reg.predict(X_test) mean_squared_error(y_test, y1_predict) 1.3233492754051845 # 通過使用嶺回歸,使得我們的均方誤差小了非常多,曲線也緩和了非常多 plot_model(ridge1_reg) ridge2_reg = RidgeRegression(20, 1) ridge2_reg.fit(X_train, y_train)y2_predict = ridge2_reg.predict(X_test) mean_squared_error(y_test, y2_predict) 1.1888759304218448 # 讓ridge2_reg 的alpha值等于1,均差誤差更加的縮小,并且曲線越來越趨近于一根傾斜的直線 plot_model(ridge2_reg) ridge3_reg = RidgeRegression(20, 100) ridge3_reg.fit(X_train, y_train)y3_predict = ridge3_reg.predict(X_test) mean_squared_error(y_test, y3_predict) 1.3196456113086197 # 得到的誤差依然是比較小,但是比之前的1.18大了些,說明正則化做的有些過頭了 plot_model(ridge3_reg) ridge4_reg = RidgeRegression(20, 10000000) ridge4_reg.fit(X_train, y_train)y4_predict = ridge4_reg.predict(X_test) mean_squared_error(y_test, y4_predict) 1.8408455590998372 # 當(dāng)alpha非常大,我們的模型實(shí)際上相當(dāng)于就是在優(yōu)化θ的平方和這一項(xiàng),使得其最小(因?yàn)镸SE的部分相對非常小) # 而使得θ的平方和最小,就是使得每一個(gè)θ都趨近于0,這個(gè)時(shí)候曲線就趨近于一根直線了 plot_model(ridge4_reg)LASSO回歸
使用|θ|代替θ2來標(biāo)示θ的大小
Selection Operator – 選擇運(yùn)算符
LASSO回歸有一些選擇的功能
實(shí)際編程(準(zhǔn)備代碼參考上一節(jié)嶺回歸)
LASSO
import numpy as np import matplotlib.pyplot as plt np.random.seed(42) x = np.random.uniform(-3.0, 3.0, size=100) X = x.reshape(-1, 1) y = 0.5 * x + 3 + np.random.normal(0, 1, size=100) plt.scatter(x, y) plt.show() from sklearn.model_selection import train_test_splitnp.random.seed(666) X_train, X_test, y_train, y_test = train_test_split(X, y) from sklearn.pipeline import Pipeline from sklearn.preprocessing import PolynomialFeatures from sklearn.preprocessing import StandardScaler from sklearn.linear_model import LinearRegressiondef PolynomialRegression(degree):return Pipeline([("poly", PolynomialFeatures(degree=degree)),("std_scaler", StandardScaler()),("lin_reg", LinearRegression())]) from sklearn.metrics import mean_squared_errorpoly_reg = PolynomialRegression(degree=20) poly_reg.fit(X_train, y_train)y_predict = poly_reg.predict(X_test) mean_squared_error(y_test, y_predict) 167.94010867293571 def plot_model(model):X_plot = np.linspace(-3, 3, 100).reshape(100, 1)y_plot = model.predict(X_plot)plt.scatter(x, y)plt.plot(X_plot[:,0], y_plot, color='r')plt.axis([-3, 3, 0, 6])plt.show()plot_model(poly_reg) from sklearn.linear_model import Lassodef LassoRegression(degree, alpha):return Pipeline([("poly", PolynomialFeatures(degree=degree)),("std_scaler", StandardScaler()),("lasso_reg", Lasso(alpha=alpha))]) lasso1_reg = LassoRegression(20, 0.01) lasso1_reg.fit(X_train, y_train)y1_predict = lasso1_reg.predict(X_test) mean_squared_error(y_test, y1_predict) 1.1496080843259966 plot_model(lasso1_reg) lasso2_reg = LassoRegression(20, 0.1) lasso2_reg.fit(X_train, y_train)y2_predict = lasso2_reg.predict(X_test) mean_squared_error(y_test, y2_predict) 1.1213911351818648 plot_model(lasso2_reg) lasso3_reg = LassoRegression(20, 1) lasso3_reg.fit(X_train, y_train)y3_predict = lasso3_reg.predict(X_test) mean_squared_error(y_test, y3_predict) 1.8408939659515595 plot_model(lasso3_reg)總結(jié)Ridge和Lasso
α=100的時(shí)候,使用Ridge的得到的模型曲線依舊是一根曲線,事實(shí)上,使用Ridge很難得到一根傾斜的直線,他一直是彎曲的形狀。
但是使用LASSO的時(shí)候,當(dāng)α=0.1,雖然得到的依然是一根曲線,但是他顯然比Radge的程度更低,更像一根直線。
這是因?yàn)?strong>LASSO趨向于使得一部分theta值為0(而不是很小的值),所以可以作為特征選擇用,LASSO的最后兩個(gè)字母SO就是Selection Operator的首字母縮寫 使用LASSO的過程如果某一項(xiàng)θ等于0了,就說明LASSO Regression認(rèn)為這個(gè)θ對應(yīng)的特征是沒有用的,剩下的那些不等于0的θ就說明LASSO Regression認(rèn)為對應(yīng)的這些特征有用,所以他可以當(dāng)做特征選擇用。
L1 范數(shù)常常用于特征選擇
L2 范數(shù)常常用于防止模型過擬合
http://t.hengwei.me/post/%E6%B5%85%E8%B0%88l0l1l2%E8%8C%83%E6%95%B0%E5%8F%8A%E5%85%B6%E5%BA%94%E7%94%A8.html#1-l0-%E8%8C%83%E6%95%B0
https://zhuanlan.zhihu.com/p/29360425
總結(jié)
以上是生活随笔為你收集整理的机器学习----多项式回归的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: XiaoHu日志 6/29~7/30
- 下一篇: 【数字图像处理】图像感兴趣区域与图像放大