通過學習曲線識別過擬合和欠擬合
本文將介紹如何通過學習曲線來有效識別機器學習模型中的過擬合和欠擬合。
欠擬合和過擬合
1、過擬合
如果一個模型對數據進行了過度訓練,以至于它從中學習了噪聲,那么這個模型就被稱為過擬合。過擬合模型非常完美地學習了每一個例子,所以它會錯誤地分類一個看不見的/新的例子。對于一個過擬合的模型,我們會得到一個完美/接近完美的訓練集分數和一個糟糕的測試/驗證分數。
過擬合的原因:用一個復雜的模型來解決一個簡單的問題,從數據中提取噪聲。因為小數據集作為訓練集可能無法代表所有數據的正確表示。
2、欠擬合
如果一個模型不能正確地學習數據中的模式,我們就說它是欠擬合的。欠擬合模型并不能完全學習數據集中的每一個例子。在這種情況下,我們看到訓練集和測試/驗證集的分數都很低。
欠擬合的原因:使用一個簡單的模型來解決一個復雜的問題,這個模型不能學習數據中的所有模式,或者模型錯誤的學習了底層數據的模式。
學習曲線
學習曲線通過增量增加新的訓練樣例來繪制訓練樣例樣本的訓練和驗證損失。可以幫助我們確定添加額外的訓練示例是否會提高驗證分數(在未見過的數據上得分)。如果模型是過擬合的,那么添加額外的訓練示例可能會提高模型在未見數據上的性能。同理如果一個模型是欠擬合的,那么添加訓練樣本也沒有什么用。' learning_curve '方法可以從Scikit-Learn的' model_selection '模塊導入。
from sklearn.model_selection import learning_curve
我們將使用邏輯回歸和Iris數據進行演示。創建一個名為“learn_curve”的函數,它將擬合邏輯回歸模型,并返回交叉驗證分數、訓練分數和學習曲線數據。
#The function below builds the model and returns cross validation scores, train score and learning curve data
def learn_curve(X,y,c):
''' param X: Matrix of input features
param y: Vector of Target/Label
c: Inverse Regularization variable to control overfitting (high value causes overfitting, low value causes underfitting)
'''
'''We aren't splitting the data into train and test because we will use StratifiedKFoldCV.
KFold CV is a preferred method compared to hold out CV, since the model is tested on all the examples.
Hold out CV is preferred when the model takes too long to train and we have a huge test set that truly represents the universe
'''
le = LabelEncoder() # Label encoding the target
sc = StandardScaler() # Scaling the input features
y = le.fit_transform(y)#Label Encoding the target
log_reg = LogisticRegression(max_iter=200,random_state=11,C=c) # LogisticRegression model
# Pipeline with scaling and classification as steps, must use a pipelne since we are using KFoldCV
lr = Pipeline(steps=(['scaler',sc],
['classifier',log_reg]))
cv = StratifiedKFold(n_splits=5,random_state=11,shuffle=True) # Creating a StratifiedKFold object with 5 folds
cv_scores = cross_val_score(lr,X,y,scoring="accuracy",cv=cv) # Storing the CV scores (accuracy) of each fold
lr.fit(X,y) # Fitting the model
train_score = lr.score(X,y) # Scoring the model on train set
#Building the learning curve
train_size,train_scores,test_scores =learning_curve(estimator=lr,X=X,y=y,cv=cv,scoring="accuracy",random_state=11)
train_scores = 1-np.mean(train_scores,axis=1)#converting the accuracy score to misclassification rate
test_scores = 1-np.mean(test_scores,axis=1)#converting the accuracy score to misclassification rate
lc =pd.DataFrame({"Training_size":train_size,"Training_loss":train_scores,"Validation_loss":test_scores}).melt(id_vars="Training_size")
return {"cv_scores":cv_scores,
"train_score":train_score,
"learning_curve":lc}
上面代碼很簡單,就是我們日常的訓練過程,下面我們開始介紹學習曲線的用處
1、擬合模型的學習曲線
我們將使用' learn_curve '函數通過將反正則化變量/參數' c '設置為1來獲得一個良好的擬合模型(即我們不執行任何正則化)。
lc = learn_curve(X,y,1)
print(f'Cross Validation Accuracies:\n{"-"*25}\n{list(lc["cv_scores"])}\n\n\
Mean Cross Validation Accuracy:\n{"-"*25}\n{np.mean(lc["cv_scores"])}\n\n\
Standard Deviation of Deep HUB Cross Validation Accuracy:\n{"-"*25}\n{np.std(lc["cv_scores"])}\n\n\
Training Accuracy:\n{"-"*15}\n{lc["train_score"]}\n\n')
sns.lineplot(data=lc["learning_curve"],x="Training_size",y="value",hue="variable")
plt.title("Learning Curve of Good Fit Model")
plt.ylabel("Misclassification Rate/Loss");
上面的結果中,交叉驗證準確率與訓練準確率接近。
訓練的損失(藍色):一個好的擬合模型的學習曲線會隨著訓練樣例的增加逐漸減小并逐漸趨于平坦,說明增加更多的訓練樣例并不能提高模型在訓練數據上的性能。
驗證的損失(黃色):一個好的擬合模型的學習曲線在開始時具有較高的驗證損失,隨著訓練樣例的增加逐漸減小并逐漸趨于平坦,說明樣本越多,就能夠學習到更多的模式,這些模式對于”看不到“的數據會有幫助
最后還可以看到,在增加合理數量的訓練樣例后,訓練損失和驗證損失彼此接近。
2、過擬合模型的學習曲線
我們將使用' learn_curve '函數通過將反正則化變量/參數' c '設置為10000來獲得過擬合模型(' c '的高值導致過擬合)。
lc = learn_curve(X,y,10000)
print(f'Cross Validation Accuracies:\n{"-"*25}\n{list(lc["cv_scores"])}\n\n\
Mean Cross Validation Deep HUB Accuracy:\n{"-"*25}\n{np.mean(lc["cv_scores"])}\n\n\
Standard Deviation of Cross Validation Accuracy:\n{"-"*25}\n{np.std(lc["cv_scores"])} (High Variance)\n\n\
Training Accuracy:\n{"-"*15}\n{lc["train_score"]}\n\n')
sns.lineplot(data=lc["learning_curve"],x="Training_size",y="value",hue="variable")
plt.title("Learning Curve of an Overfit Model")
plt.ylabel("Misclassification Rate/Loss");
與擬合模型相比,交叉驗證精度的標準差較高。
過擬合模型的學習曲線一開始的訓練損失很低,隨著訓練樣例的增加,學習曲線逐漸增加,但不會變平。過擬合模型的學習曲線在開始時具有較高的驗證損失,隨著訓練樣例的增加逐漸減小并且不趨于平坦,說明增加更多的訓練樣例可以提高模型在未知數據上的性能。同時還可以看到,訓練損失和驗證損失彼此相差很遠,在增加額外的訓練數據時,它們可能會彼此接近。
3、欠擬合模型的學習曲線
將反正則化變量/參數' c '設置為1/10000來獲得欠擬合模型(' c '的低值導致欠擬合)。
lc = learn_curve(X,y,1/10000)
print(f'Cross Validation Accuracies:\n{"-"*25}\n{list(lc["cv_scores"])}\n\n\
Mean Cross Validation Accuracy:\n{"-"*25}\n{np.mean(lc["cv_scores"])}\n\n\
Standard Deviation of Cross Validation Accuracy:\n{"-"*25}\n{np.std(lc["cv_scores"])} (Low variance)\n\n\
Training Deep HUB Accuracy:\n{"-"*15}\n{lc["train_score"]}\n\n')
sns.lineplot(data=lc["learning_curve"],x="Training_size",y="value",hue="variable")
plt.title("Learning Curve of an Underfit Model")
plt.ylabel("Misclassification Rate/Loss");
與過擬合和良好擬合模型相比,交叉驗證精度的標準差較低。
欠擬合模型的學習曲線在開始時具有較低的訓練損失,隨著訓練樣例的增加逐漸增加,并在最后突然下降到任意最小點(最小并不意味著零損失)。這種最后的突然下跌可能并不總是會發生。這表明增加更多的訓練樣例并不能提高模型在未知數據上的性能。
總結
在機器學習和統計建模中,過擬合(Overfitting)和欠擬合(Underfitting)是兩種常見的問題,它們描述了模型與訓練數據的擬合程度如何影響模型在新數據上的表現。
分析生成的學習曲線時,可以關注以下幾個方面:
- 欠擬合:如果學習曲線顯示訓練集和驗證集的性能都比較低,或者兩者都隨著訓練樣本數量的增加而緩慢提升,這通常表明模型欠擬合。這種情況下,模型可能太簡單,無法捕捉數據中的基本模式。
- 過擬合:如果訓練集的性能隨著樣本數量的增加而提高,而驗證集的性能在一定點后開始下降或停滯不前,這通常表示模型過擬合。在這種情況下,模型可能太復雜,過度適應了訓練數據中的噪聲而非潛在的數據模式。
根據學習曲線的分析,你可以采取以下策略進行調整:
- 對于欠擬合:
- 增加模型復雜度,例如使用更多的特征、更深的網絡或更多的參數。
- 改善特征工程,嘗試不同的特征組合或轉換。
- 增加迭代次數或調整學習率。
- 對于過擬合:
使用正則化技術(如L1、L2正則化)。
減少模型的復雜性,比如減少參數數量、層數或特征數量。
增加更多的訓練數據。
應用數據增強技術。
使用早停(early stopping)等技術來避免過度訓練。
通過這樣的分析和調整,學習曲線能夠幫助你更有效地優化模型,并提高其在未知數據上的泛化能力。