機器學習中的過擬合和欠擬合
1、機器學習算法對於整體的數據訓練和擬合,以典型的多元線性回歸的方式為例,通過設定擬合的最高次數,然后對比輸出的曲線結果可以看出,隨着擬合函數次數的增大,其擬合線性回歸模型的R2的值在不斷地增大,均方差也在不斷地減小,看起來擬合的結果越來越准確,其實質只是對於所存在原始數據的擬合誤差越來越小,而對於新的數據樣本則並不一定適合,這就是說存在過擬合(overfitting)的現象;而如果設定的多項式次數太小,又會使得整體的R2值太小,均方誤差太大,從而使得擬合結果不足,這又是欠擬合(under fitting)的情況。
其中過擬合和欠擬合的輸出准確度一般可以用均方差來進行對比和衡量,將不同機器學習算法的學習曲線定義為函數輸出如下所示:
#將不同的機器學習算法的學習曲線封裝成為函數可以方便輸出
def plot_learning_curve(algo,x_train,x_test,y_train,y_test):
train_score = []
test_score = []
for i in range(1, len(x_train)):
algo.fit(x_train[:i], y_train[:i])
y_train_pre = algo.predict(x_train[:i])
y_test_pre =algo.predict(x_test)
train_score.append(mean_squared_error(y_train[:i], y_train_pre))
test_score.append(mean_squared_error(y_test, y_test_pre))
plt.figure()
plt.plot([i for i in range(1, len(x_train))], np.sqrt(train_score), "g", label="train_error")
plt.plot([i for i in range(1, len(x_train))], np.sqrt(test_score), "r", label="test_error")
plt.legend()
plt.axis([0,len(x_train)+1,0,5])
plt.show()
plot_learning_curve(LinearRegression(),x_train,x_test,y_train,y_test)
plot_learning_curve(polynomialRegression(degree=1),x_train,x_test,y_train,y_test) #欠擬合的情況
plot_learning_curve(polynomialRegression(degree=2),x_train,x_test,y_train,y_test) #最佳擬合的情況
plot_learning_curve(polynomialRegression(degree=10),x_train,x_test,y_train,y_test) #過擬合的情況
其中機器學習算法存在過擬合和欠擬合情況時的均方差輸出如下圖所示:
從上面兩張圖對比可以明顯看出,機器學習算法過擬合和欠擬合時學習曲線的特點總結如下:
(1)欠擬合:學習曲線的訓練均方差一直增大,而測試數據集的均方差一直減小,最終都趨於一定的定值不變,並且這兩個定值也大致趨近,但是總體明顯要大於1;
(2)最佳擬合:學習曲線的訓練均方差一直增大,而測試數據集的均方差一直減小,最終都趨於一定的定值不變,並且這兩個定值相近,都在1附近;
(3)過擬合:學習曲線的訓練均方差剛開始幾乎為0,后續隨着數據集的增多一直增大,趨於定值;而測試數據集的均方差波動起伏,整體趨勢一直減小,最終也趨於一定的定值不變,但是這兩者定值之間存在一定的差距,並且過擬合的測試數據集均方差在開始和后續一些地方是趨於無窮的;
因此,對於機器學習算法的使用,在數據訓練的過程中可能會出現訓練的欠擬合和過擬合現象,這個必須盡量注意和避免!
2、機器學習算法對於數據的處理主要是解決過擬合的現象,對於過擬合的訓練模型,其實質是指模型的泛化能力不足,而要解決數據訓練模型的過擬合現象,比較有效的方法就是將其數據集分為訓練數據集合測試數據集進行訓練和相關測試,這就是機器學習算法中利用sklearn中的train_test_split函數的意義所在。
3、對於數據的擬合過程,其訓練模型復雜度越高,其訓練數據的准確率會越高,但是對於新的測試數據集的預測准確度呈現先增后降的變化趨勢,主要是因為基礎數據集存在數據噪音,隨着模型復雜度的增高,其算法過多的表達了數據間噪音的關系,因此我們需要找到對於測試數據集預測率最高的那個數據訓練模型,也就是所說的泛化能力最好的數據模型。
4、對於機器學習算法的過擬合現象,將數據分割成為訓練集和測試集進行模型的訓練是一種有效避免的方式,但是並不是最好的方式,因為這樣的方式可能會使得訓練數據集出現過擬合的現象。為了更好地去提高模型的准確度,避免訓練過程中出現的過擬合和欠擬合現象,更加有效地方式是采用驗證數據集進行交叉驗證的方式,這樣可以最大程度地避免訓練過程中存在過擬合的現象,提高訓練模型的准確度。
5、機器學習算法的交叉驗證的方式可以使用sklearn庫中的cross_var_score(knn1,x_train,y_train,cv)函數來進行,其中cv參數就是指交叉驗證時將訓練數據集划分的份數,它的本質其實和GridSearch網格搜索的驗證方式是一致的,都是交叉驗證,並且GridSearch網格搜索函數也含有一個自定義參數cv,它和cross_var_score的cv參數是一致的。
6、采用交叉驗證的方式對於模型進行訓練的結果是相對比較靠譜的一種訓練方式,並且將訓練數據集划分為k份進行交叉驗證的方式一般可以稱為k-fold cross validation,隨着k的增大,其驗證的結果是越來越可靠的。不過也有它的缺點:每次訓練k個模型,相當於整體的性能慢了k倍。
7、留一法(LOO CV):Leaves-One-Out cross validation是指訓練數據x_train集含有多少個數據樣本(即數據的長度m),則將其划分為多少份進行交叉驗證,其中m-1份數據用來訓練,剩余一份用來數據的驗證,它的最大優點在於這種方式將完全不受隨機的影響,得到的性能指標最接近真正的性能指標,不過因為數據量的巨大,整體的計算量巨大,計算過程非常慢。一般很少用這種方法,不過在學術研究過程中為了結果的可靠和准確也會經常用到。
其中關於交叉驗證的具體的代碼如下所示:
#機器學習算法的交叉驗證方式實現代碼:
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
digits=datasets.load_digits()
x=digits.data
y=digits.target
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)
#1-1普通的訓練測試方式
from sklearn.neighbors import KNeighborsClassifier
best_score=0
best_p=0
best_k=0
for k in range(2,11):
for p in range(1,6):
knn1=KNeighborsClassifier(weights="distance",p=p,n_neighbors=k)
knn1.fit(x_train,y_train)
score=knn1.score(x_test,y_test)
if score>best_score:
best_p=p
best_k=k
best_score=score
print("best_score:",best_score)
print("best_k:",best_k)
print("best_p:",best_p)
#1-2交叉驗證方式
from sklearn.model_selection import cross_val_score
best_p=0
best_k=0
best_score=0
for k in range(2,11):
for p in range(1,6):
knn2=KNeighborsClassifier(weights="distance",p=p,n_neighbors=k)
knn2.fit(x_train,y_train)
scores=cross_val_score(knn2,x_train,y_train,cv=5) #這里的cv參數就是指將訓練數據集分為幾份進行交叉驗證,默認為3
score=np.mean(scores)
if score>best_score:
best_p=p
best_k=k
best_score=score
print("best_score:",best_score)
print("best_k:",best_k)
print("best_p:",best_p)
knn11=KNeighborsClassifier(weights="distance",p=2,n_neighbors=2)
knn11.fit(x_train,y_train)
print(knn11.score(x_test,y_test))
#1-3利用網格搜索的方式尋找最優的超參數組合就是對於訓練數據集進行交叉驗證尋找最優
from sklearn.model_selection import GridSearchCV
knn3=KNeighborsClassifier()
param=[
{
"weights":["distance"],
"n_neighbors":[i for i in range(2,11)],
"p":[k for k in range(1,6)]
}
]
grid1=GridSearchCV(knn3,param,verbose=1,cv=5) #這里的cv參數就是指將訓練數據集分為幾份進行交叉驗證,默認為3
grid1.fit(x_train,y_train)
print(grid1.best_score_)
print(grid1.best_params_)
kn2=grid1.best_estimator_
print(kn2.score(x_test,y_test))
其具體的結果如下所示:
(1)過擬合和最佳擬合學習曲線實際輸出
(2)欠擬合和最佳擬合學習曲線實際輸出