線性回歸算法是使用線性方程對數(shù)據(jù)集擬合得算法,是一個非常常見的回歸算法驰凛。本章首先從最簡單的單變量線性回歸算法開始介紹胸懈,然后介紹了多變量線性回歸算法,其中成本函數(shù)以及梯度下降算法的推導過程會用到部分線性代數(shù)和偏導數(shù)恰响;接著重點介紹了梯度下降算法的求解步驟以及性能優(yōu)化方面的內容趣钱;最后通過一個房價預測模型,介紹了線性回歸算法性能優(yōu)化的一些常用步驟和方法胚宦。
1.單變量線性回歸算法
我們先考慮最簡單的單變量線性回歸算法首有,即只有一個輸入特征。
1.預測函數(shù)
針對數(shù)據(jù)集x和y间唉,預測函數(shù)會根據(jù)輸入特征x來計算輸出值h(x)绞灼。其輸入和輸出的函數(shù)關系如下:
這個方程表達的是一條直線。我們的任務是構造一個函數(shù)呈野,來映射數(shù)據(jù)集中的輸入特征x和輸出值y低矮,使得預測函數(shù)計算出來的值與真實值y的整體誤差最小。構造函數(shù)的關鍵就是找到合適的和的值被冒,和成為模型參數(shù)军掂。
假設我們有如下的數(shù)據(jù)集:
假設模型參數(shù)轮蜕,,則預測函數(shù)為蝗锥。針對數(shù)據(jù)集中的第一個樣本跃洛,輸入為1,根據(jù)模型函數(shù)預測出來的值是4终议,與輸出值y是吻合的汇竭。針對第二個樣本,輸入為2穴张,根據(jù)模型函數(shù)預測出來的值是7细燎,與實際輸出值y相差1。模型的求解過程就是找出一組最合適的模型參數(shù)和皂甘,以便能最好地擬合數(shù)據(jù)集玻驻。
怎樣來判斷最好地擬合了數(shù)據(jù)集呢?沒錯偿枕,就是使用成本函數(shù)(也叫損失函數(shù))璧瞬。當擬合成本最小時,即找到了最好的擬合參數(shù)渐夸。
2.成本函數(shù)
單變量線性回歸算法的成本函數(shù)是:
其中嗤锉,是預測值和真實值之間的誤差,故成本就是預測值和真實值之間誤差平方的平均值墓塌,之所以乘以1/2是為了方便計算档冬。這個函數(shù)也稱為均方差公式。有了成本函數(shù)桃纯,就可以精確地測量模型對訓練樣本擬合的好壞程度。
3.梯度下降算法
有了預測函數(shù)披坏,也可以精確地測量預測函數(shù)對訓練樣本的擬合情況态坦。但怎么求解模型參數(shù)乃沙,的值呢盗飒?這時梯度下降算法就排上了用場膳沽。
我們的任務是找到合適的芹血,埋哟,使得成本函數(shù)最小捡遍。為了便于理解牵祟,我們切換到三維空間來描述這個任務痪蝇。在一個三維空間里攻旦,以作為x軸喻旷,以作為y軸,以成本函數(shù)作為z軸牢屋,那么我們的任務就是要找出當z軸上的值最小的時候所對應的x軸上的值和y軸上的值且预。
梯度下降算法的原理是:先隨機選擇一組槽袄,,同時選擇一個參數(shù)作為移動的步長锋谐。然后遍尺,讓x軸上的和y軸上的分別向特定的方向移動一小步,這個步長的大小就由參數(shù)決定涮拗。經(jīng)過多次迭代之后乾戏,x軸和y軸上的值決定的點就慢慢靠近z軸上的最小值處,如圖所示三热。
這是個等高線圖鼓择,就是說在我們描述的三維空間里,你的視角在正上方康铭,看到一圈一圈z軸值相同的點構成的線惯退。在上圖中,隨機選擇的點在處从藤,經(jīng)過多次迭代后催跪,慢慢地靠近圓心處,即z軸上最小值附近夷野。
問題來了懊蒸,(由描述)怎么知道往哪個方向移動,才能靠近z軸上最小值附近悯搔?答案是往成本函數(shù)逐漸變小的方向移動骑丸。怎么表達成本函數(shù)逐漸變小的方向呢?答案是偏導數(shù)妒貌。
可以簡單地把偏導數(shù)理解為斜率通危。我們要讓不停地迭代,由當前的值灌曙,根據(jù)的偏導數(shù)函數(shù)菊碟,算出在上的斜率,然后在乘以學習率在刺,就可以讓往前變小的方向邁一小步逆害。
用數(shù)學來描述上述過程,梯度下降的公式為:
公式中蚣驼,下標j就是參數(shù)的序號魄幕,針對單變量線性回歸,即0和1颖杏。稱為學習率纯陨,它決定每次要移動的幅度大小,它會乘以成本函數(shù)對參數(shù)的偏導數(shù),以這個結果作為參數(shù)移動的幅度队丝。如果幅度太小靡馁,就意味著要計算很多次才能到達目的地;如果幅度太大机久,可能會直接跨過目的地臭墨,從而無法收斂。
把成本函數(shù)的定義代入上面的公式中膘盖,不難推導出梯度下降算法公式:
公式中胧弛,是學習率;m是訓練樣本的個數(shù)侠畔;是模型預測值和真實值的誤差结缚。需要注意的是,針對和分別求出了其迭代公式软棺,在的迭代公式里红竭,累加器中還需要乘以。對公式推導感興趣的讀者喘落,可以參考本章擴展部分的內容茵宪。
2.多變量線性回歸算法
工程應用中往往不止一個輸入特征。熟悉了單變量線性回歸算法后瘦棋,我們來探討一下多變量線性回歸算法稀火。
1.預測函數(shù)
上文介紹的線性回歸模型里只有一個輸入特征,我們推廣到更一般的情況赌朋,即多個輸入特征凰狞。此時輸出y的值由n個輸入特征x1,x2,x3,...,xn決定。那么預測函數(shù)模型可以改寫如下:
假設x0=1沛慢,那么上面的公式可以重寫為:
其中赡若,,团甲,...斩熊,統(tǒng)稱為,是預測函數(shù)的參數(shù)伐庭。即一組值就決定了一個預測函數(shù),記為分冈,為了簡便起見圾另,在不引起誤解的情況下可以簡寫為。理論上雕沉,預測函數(shù)有無窮多個集乔,我們求解的目標就是找出一個最優(yōu)的值。
思考:當有n個變量,扰路,...尤溜,決定y值的時候,訓練數(shù)據(jù)集應該長什么樣呢汗唱?
(1)向量形式的預測函數(shù)
根據(jù)向量乘法運算法則宫莱,成本函數(shù)可重寫為:
此處,依然假設哩罪,稱為模型偏置(bias)授霸。
為什么要寫成向量形式的預測函數(shù)呢?一是因為簡潔际插,而是因為在實現(xiàn)算法時碘耳,要用到數(shù)值計算里的矩陣運算來提高效率,比如Numpy庫里的矩陣運算框弛。
(2)向量形式的訓練樣本
假設辛辨,輸入特征的個數(shù)是n,即瑟枫,斗搞,...,力奋,我們總共有m個訓練樣本榜旦,為了書寫方便,假設景殷。這樣訓練樣本可以寫成矩陣的形式溅呢,即矩陣里每一行都是一個訓練樣本,總共有m行猿挚,每行有n+1列咐旧。
思考:為什么不是n列而是n+1列?答案是:把模型偏置也加入了訓練樣本里绩蜻。最后把訓練樣本寫成一個矩陣铣墨,如下:
理解訓練樣本矩陣的關鍵在于理解這些上標和下標的含義。其中办绝,帶括號的上標表示樣本序號伊约,從1到m;下標表示特征序號孕蝉,從0到n屡律,其中為常數(shù)1。比如降淮,表示第i個訓練樣本的第j個特征的值超埋。而只有上標,則表示第i個訓練樣本所構成的列向量。
熟悉矩陣乘法的話不難得出結論霍殴,如果要一次性計算出所有訓練樣本的預測值媒惕,可以使用下面的矩陣運算公式:
從這個公式也可以看到矩陣形式表達的優(yōu)勢。實際上来庭,在scikit-learn里妒蔚,訓練樣本就是用這種方式表達的,即使用維的矩陣來表達訓練樣本巾腕,可以回顧一下scikit-learn里模型的fit()函數(shù)的參數(shù)面睛。
2.成本函數(shù)
多變量線性回歸算法的成本函數(shù):
其中,模型參數(shù)為n+1維的向量尊搬,是預測值和實際值的差叁鉴。這個形式和單變量線性回歸算法的類似。
成本函數(shù)有其對應的矩陣形式:
其中佛寿,X為維的訓練樣本矩陣幌墓;上標T表示轉置矩陣;表示由所有的訓練樣本的輸出構成的向量冀泻。這個公式的優(yōu)勢是:沒有累加器常侣,不需要循環(huán),直接使用矩陣運算弹渔,就可以一次性計算出對特定的參數(shù)下模型的擬合成本胳施。
思考:矩陣運算真的不需要循環(huán)嗎?
這里所說的不需要循環(huán)肢专,是指不需要在算法實現(xiàn)層使用循環(huán)舞肆,但在數(shù)值運算庫如Numpy里,實現(xiàn)的矩陣運算還是要用到循環(huán)博杖。雖然都是循環(huán)椿胯,但是有差別,一是在數(shù)值運算庫里實現(xiàn)的循環(huán)效率更高剃根,而是矩陣運算的循環(huán)可以使用分布式來實現(xiàn)哩盲。一個大矩陣運算可以拆成多個子矩陣運算,然后在不同的計算機上執(zhí)行運算狈醉,最終再把運算結果匯合起來廉油。這種分布式計算對大型矩陣運算來說是一種必要的手段。
3.梯度下降算法
根據(jù)單變量線性回歸算法的介紹苗傅,梯度下降的公式為:
公式中抒线,下標j是參數(shù)的序號,其值從0到n金吗;為學習率。把成本函數(shù)代入上式,利用偏導數(shù)計算法則摇庙,不難推導出梯度下降算法的參數(shù)迭代公式:
我們可以對比一下單變量線性回歸函數(shù)的參數(shù)迭代公式旱物。實際上和多變量線性回歸函數(shù)的參數(shù)迭代公式是一模一樣的。惟一的區(qū)別就是因為為常數(shù)1卫袒,在單變量線性回歸算法的參數(shù)迭代公式中省去了宵呛。
這個公式怎么樣用編程語言來實現(xiàn)呢?在編寫機器學習算法的時候夕凝,一般步驟如下宝穗。
(1)確定學習率:太大可能會使成本函數(shù)無法收斂,太小則計算太多码秉,機器學習算法效率就比較低逮矛。
(2)確定參數(shù)起始點:比如讓所有的參數(shù)都以1作為起始點,即转砖,须鼎,...,府蔗。這樣就得到了我們的預測函數(shù)晋控。根據(jù)預測值和成本函數(shù),就可以算出在參數(shù)起始位置的成本姓赤。需要注意的是赡译,參數(shù)起始點可以根據(jù)實際情況靈活選擇,以便讓機器學習算法的性能更高不铆,比如選擇比較靠近極點的位置蝌焚。
(3)計算參數(shù)的下一組值:根據(jù)梯度下降參數(shù)迭代公式,分別同時計算出新的的值狂男。然后用新的值得到新的預測函數(shù)综看。再根據(jù)新的預測函數(shù),代入成本函數(shù)就可以算出新的成本岖食。
(4)確定成本函數(shù)是否收斂:拿新的成本和舊的成本進行比較红碑,看成本是不是變得越來越小。如果兩次成本之間的差異小于誤差范圍泡垃,即說明已經(jīng)非澄錾海靠近最小成本了,就可以近似地認為我們找到了最小成本蔑穴。如果兩次成本之間的差異在誤差范圍之外忠寻,重復步驟(3)繼續(xù)計算下一組參數(shù),直到找到最優(yōu)解存和。
3.模型優(yōu)化
線性回歸模型常用的優(yōu)化方法奕剃,包括增加多項式特征以及數(shù)據(jù)歸一化處理等衷旅。
1.多項式與線性回歸
當線性回歸模型太簡單導致欠擬合時,我們可以增加特征多項式來讓線性回歸模型更好地擬合數(shù)據(jù)纵朋。比如有兩個特征和柿顶,可以增加兩個特征的乘積作為新特征。同理操软,我們也可以增加和分別作為新特征和嘁锯。
在scikit-learn里,線性回歸是由類sklearn.learn_model.LinearRegression實現(xiàn)的聂薪,多項式由類sklearn.preprocessing.PolynomialFeatures實現(xiàn)家乘。那么要怎樣添加多項式特征呢?我們需要用一個管道把兩個類串起來藏澳,即用sklearn.pipeline.Pipeline把這兩個模型串起來仁锯。
比如下面的函數(shù)就可以創(chuàng)建一個多項式擬合:
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures
from sklearn.pipeline import Pipeline
def polynomial_model(degree=1):
polynomial_features = PolynomialFeatures(degree=degree,include_bias=False)
linear_regression = LinearRegression(normalize=True)
# 這是一個流水線,先增加多項式階數(shù)笆载,然后再用線性回歸算法來擬合數(shù)據(jù)
pipeline = Pipeline([("polynomial_features", polynomial_features),
("linear_regression", linear_regression)])
return pipeline
一個Pipeline可以包含多個處理節(jié)點扑馁,在scikit-learn里,除了最后一個節(jié)點外凉驻,其他的節(jié)點都必須實現(xiàn)fit()方法和transform()方法腻要,最后一個節(jié)點只需要實現(xiàn)fit()方法即可。當訓練樣本數(shù)據(jù)送進Pipeline里進行處理時涝登,它會逐個調用節(jié)點的fit()方法和transform()方法雄家,最后調用最后一個節(jié)點的fit()方法來擬合數(shù)據(jù)。管道的示意圖如下所示:
2.數(shù)據(jù)歸一化
當線性回歸模型有多個輸入特征時胀滚,特別是使用多項式添加特征時趟济,需要對數(shù)據(jù)進行歸一化處理。比如咽笼,特征的范圍在[1,4]之間顷编,特征的范文在[1,2000]之間,這種情況下剑刑,可以讓除以4來作為新特征媳纬,同時讓除以2000來作為新特征,該過程稱為特征縮放(feature scaling)施掏∨セ荩可以使用特征縮放來對訓練樣本進行歸一化處理,處理后的特征范圍在[0,1]之間七芭。
為什么要進行數(shù)據(jù)歸一化處理素挽?以及歸一化處理有哪些注意事項?
歸一化處理的目的是讓算法收斂更快狸驳,提升模型擬合過程中的計算效率预明。進行歸一化處理后缩赛,當有個新的樣本需要計算預測值時,也需要先進行歸一化處理撰糠,再通過模型來計算預測值峦筒,計算出來的預測值要再乘以歸一化處理的系數(shù),這樣得到的數(shù)據(jù)才是真正的預測數(shù)據(jù)窗慎。
在scikit-learn里,使用LinearRegression進行線性回歸時卤材,可以指定normalize=True來對數(shù)據(jù)進行歸一化處理遮斥。具體可以查閱scikit-learn文檔。
4.示例:使用線性回歸算法擬合正弦函數(shù)
本節(jié)用線性回歸算法來模擬正弦函數(shù)扇丛。
首先生成200個在區(qū)間內的正弦函數(shù)上的點术吗,并給這些點加上一些隨機的噪聲。
import numpy as np
n_dots = 200
X = np.linspace(-2 * np.pi, 2 * np.pi, n_dots)
Y = np.sin(X) + 0.2 * np.random.rand(n_dots) - 0.1
X = X.reshape(-1, 1)
Y = Y.reshape(-1, 1);
其中帆精,reshape()函數(shù)的作用是把Numpy的數(shù)組轉換成符合scikit-learn輸入格式的數(shù)組(這里是把一個n維向量轉換成一個n*1維的矩陣)较屿,否則scikit-learn會報錯。
接著我們使用PolynomialFeatures和Pipeline創(chuàng)建一個多項式擬合模型:
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures
from sklearn.pipeline import Pipeline
def polynomial_model(degree=1):
polynomial_features = PolynomialFeatures(degree=degree,include_bias=False)
linear_regression = LinearRegression(normalize=True)
pipeline = Pipeline([("polynomial_features", polynomial_features),
("linear_regression", linear_regression)])
return pipeline
分別用2/3/5/10階多項式來擬合數(shù)據(jù)集:
from sklearn.metrics import mean_squared_error
degrees = [2, 3, 5, 10]
results = []
for d in degrees:
model = polynomial_model(degree=d)
model.fit(X, Y)
train_score = model.score(X, Y)
mse = mean_squared_error(Y, model.predict(X))
results.append({"model": model, "degree": d, "score": train_score, "mse": mse})
for r in results:
print("degree: {}; train score: {}; mean squared error: {}"
.format(r["degree"], r["score"], r["mse"]))
算出每個模型擬合的評分卓练,此外隘蝎,使用mean_squared_error算出均方根誤差,即實際的點和模型預點之間的距離襟企,均方根誤差越小說明模型擬合效果越好——上述代碼的輸出結果為:
degree: 2; train score: 0.14988484858514306; mean squared error: 0.4346164668605285
degree: 3; train score: 0.27617240286045885; mean squared error: 0.37005268328809543
degree: 5; train score: 0.89946485219823; mean squared error: 0.05139801432804186
degree: 10; train score: 0.9941202722467253; mean squared error: 0.0030059768938090893
從輸出結果可以看出嘱么,多項式階數(shù)越高,擬合評分越高顽悼,均方根誤差越小曼振,擬合效果越好。
最后我們把不同模型的擬合效果在二維坐標上畫出來蔚龙,可以清楚地看到不同階數(shù)的多項式的擬合效果:
import matplotlib.pyplot as plt
from matplotlib.figure import SubplotParams
plt.figure(figsize=(12,6),dpi=200,subplotpars=SubplotParams(hspace=0.3))
for i,r in enumerate(results):
fig = plt.subplot(2,2,i+1)
plt.xlim(-8,8)
plt.title("LinearRegression degree={}".format(r["degree"]))
plt.scatter(X,Y,s=5,c='b',alpha=0.5)
plt.plot(X,r["model"].predict(X),'r-')
plt.show()
我們使用SubplotParams調整了子圖的豎直間距冰评,并且使用subplot()函數(shù)把4個模型的擬合情況都畫在同一個圖形上。上述代碼的輸出結果如下圖所示:
思考:在[-2π木羹,2π]區(qū)間內甲雅,10階多項式對數(shù)據(jù)擬合得非常好,我們可以試著畫出這10階模型在[-20,20]的區(qū)域內的曲線汇跨,觀察一下該模型的曲線和正弦函數(shù)的差異务荆。代碼如下:
plt.figure(figsize=(12,6),dpi=200)
X = np.linspace(-20,20,2000).reshape(-1, 1)
Y = np.sin(X).reshape(-1, 1)
model_10 = results[3]["model"]
plt.xlim(-20,20)
plt.ylim(-2,2)
plt.plot(X,Y,'b-')
plt.plot(X,model_10.predict(X),'r-')
dot1 = [-2*np.pi,0]
dot2 = [2*np.pi,0]
plt.scatter(dot1[0],dot1[1],s=50,c='r')
plt.scatter(dot2[0],dot2[1],s=50,c='r')
plt.show()
從圖中可以看出,10階多項式模型只有在區(qū)間[-2π,2π]之間對正弦曲線擬合較好穷遂,在此區(qū)間以外函匕,兩者相差甚遠。此案例告訴我們蚪黑,每個模型都有自己的適用范圍盅惜,在滿足適用范圍的基本前提下中剩,要盡可能尋找擬合程度最高的模型來使用。
5.示例:預測房價
本節(jié)使用scikit-learn自帶的波士頓房價數(shù)據(jù)來訓練模型抒寂,然后用模型來預測房價结啼。
1.輸入特征
房價和哪些因素有關?很多人可能對這個問題特別敏感屈芜,隨時可以列出很多郊愧,如房子面子、房子地理位置井佑、周邊教育資源属铁、周邊商業(yè)資源、房子朝向躬翁、年限焦蘑、小區(qū)情況等。在scikit-learn的波士頓房價數(shù)據(jù)集里盒发,它總共收集了13個特征例嘱,具體如下:
- CRIM:城鎮(zhèn)人均犯罪率。
- ZN:城鎮(zhèn)超過25000平方英尺的住宅區(qū)域的占地比例宁舰。
- INDUS:城鎮(zhèn)非零售用地占地比例拼卵。
- CHAS:是否靠近河邊,1為靠近蛮艰,0為遠離间学。
- NOX:一氧化氮濃度
- RM:每套房產的平均房間個數(shù)。
- AGE:在1940年之前就蓋好印荔,且業(yè)主自住的房子的比例低葫。
- DIS:與波士頓市中心的距離。
- RAD:周邊高速公路的便利性指數(shù)仍律。
- TAX:每10000美元的財產稅率嘿悬。
- PTRATIO:小學老師的比例。
- B:城鎮(zhèn)黑人的比例水泉。
- LSTAT:地位較低的人口比例善涨。
從這些指標里可以看到中美指標的一些差異。當然草则,這個數(shù)據(jù)是在1993年之前收集的钢拧,可能和現(xiàn)在會有差異。不要小看了這些指標炕横,實際上一個模型的好壞和輸入特征的選擇關系密切源内。大家可以思考一下,如果要在中國預測房價份殿,你會收集哪些特征數(shù)據(jù)膜钓?這些特征數(shù)據(jù)的可獲得性如何嗽交?收集成本多高?
我們先導入數(shù)據(jù):
from sklearn.datasets import load_boston
boston = load_boston()
X = boston.data
y = boston.target
X.shape
輸出如下:
(506, 13)
表明這個數(shù)據(jù)集有506個樣本颂斜,每個樣本有13個特征夫壁。整個訓練樣本放在一個506*13的矩陣里∥执可以通過X[0]來查看一個樣本數(shù)據(jù):
X[0]
輸出如下:
array([6.320e-03, 1.800e+01, 2.310e+00, 0.000e+00, 5.380e-01, 6.575e+00,
6.520e+01, 4.090e+00, 1.000e+00, 2.960e+02, 1.530e+01, 3.969e+02,
4.980e+00])
還可以通過boston.features_names來查看這些特征的標簽:
boston.feature_names
輸出如下:
array(['CRIM', 'ZN', 'INDUS', 'CHAS', 'NOX', 'RM', 'AGE', 'DIS', 'RAD',
'TAX', 'PTRATIO', 'B', 'LSTAT'], dtype='<U7')
我們可以把特征和數(shù)值對應起來盒让,觀察一下數(shù)據(jù)。
2.模型訓練
在scikit-learn里司蔬,LinearRegression類實現(xiàn)了線性回歸算法糯彬。在對模型進行訓練之前,我們需要先把數(shù)據(jù)集分成兩份葱她,以便評估算法的準確性。
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=3)
由于數(shù)據(jù)量比較小似扔,我們只選了20%的樣本來作為測試數(shù)據(jù)集吨些。接著,訓練模型并測試模型的準確性評分:
import time
from sklearn.linear_model import LinearRegression
model = LinearRegression()
start = time.process_time()
model.fit(X_train,y_train)
train_score = model.score(X_train,y_train)
cv_score = model.score(X_test,y_test)
print("elaspe:{0:.6f};train_score:{1:0.6f};cv_score:{2:.6f}"
.format(time.process_time()-start,train_score,cv_score))
我們順便統(tǒng)計了模型的訓練時間炒辉,除此之外豪墅,統(tǒng)計模型對訓練樣本的準確性得分(即對訓練樣本擬合的好壞程度)train_score,還測試了模型對測試樣本的得分sv_score黔寇。運行結果如下:
elaspe:0.000000;train_score:0.723941;cv_score:0.794958
從得分情況來看偶器,模型的擬合效果一般,還有沒有辦法來優(yōu)化模型的擬合效果呢缝裤?
3.模型優(yōu)化
首先觀察一下數(shù)據(jù)屏轰,特征數(shù)據(jù)的范圍相差比較大,最小的在級別憋飞,而最大的在級別霎苗,看來我們需要先把數(shù)據(jù)進行歸一化處理。歸一化處理最簡單的方式是榛做,創(chuàng)建線性回歸模型時增加normalize=True參數(shù):
model = LinearRegression(normalize=True)
當然唁盏,數(shù)據(jù)歸一化處理只會加快算法收斂速度,優(yōu)化算法訓練的效率检眯,無法提升算法的準確性厘擂。
怎么樣優(yōu)化模型的準確性呢?我們回到訓練分數(shù)上來锰瘸,可以觀察到模型針對訓練樣本的評分比較低(train_score:0.723941)刽严,即模型對訓練樣本的擬合成本比較高,這是一個典型的欠擬合現(xiàn)象避凝「圩回憶我們之前介紹的優(yōu)化欠擬合模型的方法倔既,一是挖掘更多的輸入特征,而是增加多項式特征鹏氧。在我們這個例子里渤涌,通過使用低成本的方案——即增加多項式特征來看能否優(yōu)化模型的性能匈勋。增加多項式特征眠砾,其實就是增加模型的復雜度。
我們編寫創(chuàng)建多項式模型的函數(shù):
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures
from sklearn.pipeline import Pipeline
def polynomial_model(degress=1):
polynomial_features = PolynomialFeatures(degree=degree,include_bias=False)
linear_regression = LinearRegression(normalize=True)
pipeline = Pipeline([("polynomial_features",polynomial_features),
("linear_regression",linear_regression)])
return pipeline
接著全谤,我們使用二階多項式來擬合數(shù)據(jù):
model = polynomial_model(degree=2)
start = time.process_time()
model.fit(X_train,y_train)
train_score = model.score(X_train,y_train)
cv_score = model.score(X_test,y_test)
print("elaspe:{0:.6f};train_score:{1:0.6f};cv_score:{2:.6f}"
.format(time.process_time()-start,train_score,cv_score))
輸出結果是:
elaspe:0.437500;train_score:0.930547;cv_score:0.860465
訓練樣本分數(shù)和測試分數(shù)都提高了吊履,看來模型確實得到了優(yōu)化安皱。我們可以把多項式改為3階看一下效果:
elaspe:0.343750;train_score:1.000000;cv_score:-105.483692
改為3階多項式后,針對訓練樣本的分數(shù)達到了1艇炎,而針對測試樣本的分數(shù)確實負數(shù)酌伊,說明這個模型過擬合了。
思考:我們總共有13個輸入特征缀踪,從一階多項式變?yōu)槎A多項式居砖,輸入特征個數(shù)增加了多少個?
參考:二階多項式共有:13個單一的特征驴娃,個兩兩配對的特征奏候,13個各自平方的特征,共計104個特征唇敞。比一階多項式的13個特征增加了91個特征蔗草。(如果有其他答案,歡迎在評論區(qū)留言)
4.學習曲線
更好的方法是畫出學習曲線疆柔,這樣對模型的狀態(tài)以及優(yōu)化的方向就一目了然咒精。
import matplotlib.pyplot as plt
from common.utils import plot_learning_curve
from sklearn.model_selection import ShuffleSplit
cv = ShuffleSplit(n_splits=10,test_size=0.2,random_state=0)
plt.figure(figsize=(18,4),dpi=200)
title = 'Learning Curves (degree={0})'
degrees = [1,2,3]
start = time.process_time()
for i in range(len(degrees)):
plt.subplot(1,3,i+1)
plot_learning_curve(plt,polynomial_model(degrees[i]),title.format(degrees[i]),
X,y,ylim=(0.01,1.01),cv=cv)
print('elaspe:{0:.6f}'.format(time.process_time()-start))
其中,common.utils包里的plot_learning_curve()函數(shù)是對sklearn.model_selection.learning_curve()函數(shù)的封裝旷档,代碼如下:
from sklearn.model_selection import learning_curve
import numpy as np
def plot_learning_curve(plt, estimator, title, X, y, ylim=None, cv=None,
n_jobs=1, train_sizes=np.linspace(.1, 1.0, 5)):
"""
Generate a simple plot of the test and training learning curve.
Parameters
----------
estimator : object type that implements the "fit" and "predict" methods
An object of that type which is cloned for each validation.
title : string
Title for the chart.
X : array-like, shape (n_samples, n_features)
Training vector, where n_samples is the number of samples and
n_features is the number of features.
y : array-like, shape (n_samples) or (n_samples, n_features), optional
Target relative to X for classification or regression;
None for unsupervised learning.
ylim : tuple, shape (ymin, ymax), optional
Defines minimum and maximum yvalues plotted.
cv : int, cross-validation generator or an iterable, optional
Determines the cross-validation splitting strategy.
Possible inputs for cv are:
- None, to use the default 3-fold cross-validation,
- integer, to specify the number of folds.
- An object to be used as a cross-validation generator.
- An iterable yielding train/test splits.
For integer/None inputs, if ``y`` is binary or multiclass,
:class:`StratifiedKFold` used. If the estimator is not a classifier
or if ``y`` is neither binary nor multiclass, :class:`KFold` is used.
Refer :ref:`User Guide <cross_validation>` for the various
cross-validators that can be used here.
n_jobs : integer, optional
Number of jobs to run in parallel (default 1).
"""
plt.title(title)
if ylim is not None:
plt.ylim(*ylim)
plt.xlabel("Training examples")
plt.ylabel("Score")
train_sizes, train_scores, test_scores = learning_curve(
estimator, X, y, cv=cv, n_jobs=n_jobs, train_sizes=train_sizes)
train_scores_mean = np.mean(train_scores, axis=1)
train_scores_std = np.std(train_scores, axis=1)
test_scores_mean = np.mean(test_scores, axis=1)
test_scores_std = np.std(test_scores, axis=1)
plt.grid()
plt.fill_between(train_sizes, train_scores_mean - train_scores_std,
train_scores_mean + train_scores_std, alpha=0.1,
color="r")
plt.fill_between(train_sizes, test_scores_mean - test_scores_std,
test_scores_mean + test_scores_std, alpha=0.1, color="g")
plt.plot(train_sizes, train_scores_mean, 'o--', color="r",
label="Training score")
plt.plot(train_sizes, test_scores_mean, 'o-', color="g",
label="Cross-validation score")
plt.legend(loc="best")
return plt
輸出的學習曲線如下圖所示:
從學習曲線中可以看出狠轻,一階多項式欠擬合,因為針對訓練樣本的分數(shù)比較低彬犯;而三階多項式過擬合向楼,因為針對訓練樣本的分數(shù)達到1,卻看不到交叉驗證數(shù)據(jù)集的分數(shù)谐区。針對二階多項式擬合的情況湖蜕,雖然比一階多項式的效果好,但從圖中可以明顯地看出來宋列,針對訓練數(shù)據(jù)集的分數(shù)和針對交叉驗證數(shù)據(jù)集的分數(shù)之間的間隔比較大昭抒,這說明訓練樣本數(shù)量不夠,我們應該去采集更多的數(shù)據(jù),以提高模型的準確性灭返。
6.拓展閱讀
本節(jié)內容涉及到較多的數(shù)學知識盗迟,特別是矩陣和偏導數(shù)運算法則。如果閱讀起來有困難熙含,可以先跳過罚缕。如果有一定數(shù)學基礎,這些知識對理解算法的實現(xiàn)細節(jié)及算法的效率有較大的幫助怎静。
1.梯度下降迭代公式推導
關于梯度下降算法迭代公式的推導過程邮弹,可以參考博客:http://blog.kamidox.com/gradient-descent.html,或者直接搜索“線性回歸算法kamidox.com”蚓聘。博客里詳細介紹了公式推導過程中用到的偏導數(shù)運算法則腌乡。
2.隨機梯度下降算法
本章介紹的梯度下降算法迭代公式稱為批量梯度下降算法(Batch Gradient Descent,簡稱BGD)夜牡,用它對參數(shù)進行一次迭代運算与纽,需要遍歷所有的訓練數(shù)據(jù)集。當訓練數(shù)據(jù)集比較大時塘装,其算法的效率會比較低急迂。考慮另外一個算法:
這個算法的關鍵點是把累加器去掉氢哮,不去遍歷所有的數(shù)據(jù)集,而是改成每次隨機地從訓練數(shù)據(jù)集中取一個數(shù)據(jù)進行參數(shù)迭代計算型檀,這就是隨機梯度下降算法(Stochastic Gradient Descent冗尤,簡稱SGD)。隨機梯度下降算法可以大大提高模型訓練的效率胀溺。
思考:為什么隨機取一個樣本進行參數(shù)迭代是可行的裂七?
從數(shù)學上證明批量梯度下降算法和隨機梯度下降算法的等價性涉及到復雜的數(shù)學知識。這里有個直觀的解釋可以幫助理解兩者的等價性仓坞”沉悖回到成本函數(shù)的定義:
我們說過无埃,這里累加后除以2是為了計算方便,那么除以m是什么意思呢侦镇?答案是平均值,即所有訓練數(shù)據(jù)集上的點到預測函數(shù)的距離的平均值壳繁。再回到隨機選取訓練數(shù)據(jù)集里的一個數(shù)據(jù)這個做法來看,如果計算次數(shù)足夠多蒿赢,并且是真正隨機渣触,那么隨機選取出來的這組數(shù)據(jù)從概率的角度來看,和平均值是相當?shù)年枪邸4騻€比方晾腔,儲錢罐里有1角的硬幣10枚,5角的硬幣2枚啊犬,1元的硬幣1枚灼擂,總計3元、13枚硬幣觉至。隨機從里面取1000次剔应,把每次取出來的硬幣幣值記錄下來,然后將硬幣放回儲錢罐里语御。這樣最后去算這1000次取出來的錢的平均值(1000次取出來的幣值總和除以1000)和儲錢罐里每枚硬幣的平均值(3/13元)應該是近似相等的峻贮。
3.標準方程
梯度下降算法通過不斷地迭代,從而不停地逼近成本函數(shù)的最小值來求解模型的參數(shù)应闯。另外一個方法是直接計算成本函數(shù)的微分纤控,令微分算子為0,求解這個方程碉纺,即可得到線性回歸的解船万。
線性回歸算法的成本函數(shù):
成本函數(shù)的“斜率”為0的點,即為模型參數(shù)的解骨田。令耿导,求解這個方程最終可以得到模型參數(shù):
方程求解過程可參閱https://en.wikipedia.org/wiki/Linear_least_squares_(mathematics)#Derivation_of_the_normal_equations。
這就是我們的標準方程态贤。它通過矩陣運算舱呻,直接從訓練樣本里求出參數(shù)θ的值。其中X為訓練樣本的矩陣形式悠汽,它是m×n的矩陣箱吕,y是訓練樣本的結果數(shù)據(jù),它是個m維列向量柿冲。