模型參數選擇方法——GridSearch網格搜索
在日常模型訓練過程中,模型有多種選擇,模型的參數同樣也有多種選擇,如何根據同一批數據選出最適合的模型和參數呢?
一般情況下,模型還比較好選擇,是選用機器學習中分類模型例如 LR、SVM或XGBoost等,還是使用深度學習模型CNN、LSTM等。但是參數的選擇就讓人很頭疼,每個模型都有一堆參數,參數值又有許多,如何不費人力而費機器的選擇模型參數呢,我今天看到了一種方法叫做:GridSearch,叫做網格搜索,準備記錄一下。
什麽是Grid Search 網格搜索?
Grid Search:一種調參手段,看起來很高大上,其實原理並不是很高大上(汗)。
窮舉搜索:在所有候選的參數選擇中,通過循環遍歷,嘗試每一種可能性,表現最好的參數就是最終的結果。其原理就像是在數組裏找最大值。(為什麽叫網格搜索?以有兩個參數的模型為例,參數a有3種可能,參數b有4種可能,把所有可能性列出來,可以表示成一個3*4的表格,其中每個cell就是一個網格,循環過程就像是在每個網格裏遍歷、搜索,所以叫grid search)
例如支持向量機中的SVC模型,一般調參的參數有 C 和 gamma。
其中 C是懲罰系數,即對誤差的寬容度。c越高,說明越不能容忍出現誤差,容易過擬合。C越小,容易欠擬合。C過大或過小,泛化能力變差
gamma是選擇RBF函數作為kernel後,該函數自帶的一個參數。隱含地決定了數據映射到新的特征空間後的分布,gamma越大,支持向量越少,gamma值越小,支持向量越多。支持向量的個數影響訓練與預測的速度。
而網格搜索就像是在下面這個由多個參數構成的網格裏搜索一樣:
Simple Grid Search 簡單的網格搜索
其實在了解了網格搜索的原理後,我們實現網格搜索可以通過兩層for循環,遍歷每一個參數組合並返回模型在測試集上的分數,選擇最高分數訓練出來的模型參數即可。例如我們僅將SVC模型中的C和gamma作為待調參數:
from sklearn.datasets import load_iris from sklearn.svm import SVC from sklearn.model_selection import train_test_split iris = load_iris() X_train,X_test,y_train,y_test = train_test_split(iris.data,iris.target,random_state=0) print("Size of training set:{} size of testing set:{}".format(X_train.shape[0],X_test.shape[0]))#### grid search start best_score = 0 for gamma in [0.001,0.01,0.1,1,10,100]: for C in [0.001,0.01,0.1,1,10,100]: svm = SVC(gamma=gamma,C=C)#對於每種參數可能的組合,進行一次訓練; svm.fit(X_train,y_train) score = svm.score(X_test,y_test) if score > best_score:#找到表現最好的參數 best_score = score best_parameters = {‘gamma‘:gamma,‘C‘:C} #### grid search end print("Best score:{:.2f}".format(best_score)) print("Best parameters:{}".format(best_parameters))
可以得到上面結果輸出為:
Size of training set:112 size of testing set:38 Best score:0.973684 Best parameters:{‘gamma‘: 0.001, ‘C‘: 100}
存在的問題:
上述操作中,我們使用訓練集對模型進行訓練,其中測試集除了用作參數調整,也用來測量模型的好壞;這樣做導致最終的評分結果比實際效果要好。(因為測試集在調參過程中,送到了模型裏,而我們的目的是將訓練模型應用在unseen data上)
解決方法:
對訓練集再進行一次劃分,分成訓練集和驗證集,這樣劃分的結果就是:原始數據劃分為3份,分別為:訓練集、驗證集和測試集;其中訓練集用來模型訓練,驗證集用來調整參數,而測試集用來衡量模型表現好壞。
X_trainval,X_test,y_trainval,y_test = train_test_split(iris.data,iris.target,random_state=0) X_train,X_val,y_train,y_val = train_test_split(X_trainval,y_trainval,random_state=1) print("Size of training set:{} size of validation set:{} size of teseting set:{}".format(X_train.shape[0],X_val.shape[0],X_test.shape[0])) best_score = 0.0 for gamma in [0.001,0.01,0.1,1,10,100]: for C in [0.001,0.01,0.1,1,10,100]: svm = SVC(gamma=gamma,C=C) svm.fit(X_train,y_train) score = svm.score(X_val,y_val) if score > best_score: best_score = score best_parameters = {‘gamma‘:gamma,‘C‘:C} svm = SVC(**best_parameters) #使用最佳參數,構建新的模型 svm.fit(X_trainval,y_trainval) #使用訓練集和驗證集進行訓練,more data always results in good performance. test_score = svm.score(X_test,y_test) # evaluation模型評估 print("Best score on validation set:{:.2f}".format(best_score)) print("Best parameters:{}".format(best_parameters)) print("Best score on test set:{:.2f}".format(test_score))
通過對訓練集進行再次分割,使用驗證集進行參數選擇,得到的結果如下:
Size of training set:84 size of validation set:28 size of teseting set:38 Best score on validation set:0.96 Best parameters:{‘gamma‘: 0.001, ‘C‘: 10} Best score on test set:0.92
可見,在驗證集上進行了參數選擇後,模型選擇的最優參數發生了改變,但最起碼能(大概)知道這個參數組合的泛化效果如何,因為也受數據分布影響,因為這只是一個劃分結果,這個score of test set,還是有一定的偶然性,所以需要使用交叉驗證來減少偶然性。
Grid Search with Cross Validation
from sklearn.model_selection import cross_val_score best_score = 0.0 for gamma in [0.001,0.01,0.1,1,10,100]: for C in [0.001,0.01,0.1,1,10,100]: svm = SVC(gamma=gamma,C=C) scores = cross_val_score(svm,X_trainval,y_trainval,cv=5) #5折交叉驗證 score = scores.mean() #取平均數 if score > best_score: best_score = score best_parameters = {"gamma":gamma,"C":C} svm = SVC(**best_parameters) svm.fit(X_trainval,y_trainval) test_score = svm.score(X_test,y_test) print("Best score on validation set:{:.2f}".format(best_score)) print("Best parameters:{}".format(best_parameters)) print("Score on testing set:{:.2f}".format(test_score))
我們在模型訓練過程中,沒有單獨劃分出某一部分作為專門的驗證集,而是對訓練集進行了5折交叉驗證,並且取出5個劃分好的數據集訓練出的結果取平均來嘗試得到最優參數組合。(這樣更加穩定),結果可見下:
Best score on validation set:0.97 Best parameters:{‘gamma‘: 0.01, ‘C‘: 100} Score on testing set:0.97
可以看到參數又改變了。
在使用交叉驗證+網格搜索操作中,我們使用cross_val_score,方法來判斷每一次交叉驗證的結果,再使用均值來判斷這組參數的效果,同樣在sklearn中就有這麽一個類GridSearchCV,這個類實現了fit,predict,score等方法,被當做了一個estimator,使用fit方法,該過程中:(1)搜索到最佳參數;(2)實例化了一個最佳參數的estimator;
from sklearn.model_selection import GridSearchCV #把要調整的參數以及其候選值 列出來; param_grid = {"gamma":[0.001,0.01,0.1,1,10,100], "C":[0.001,0.01,0.1,1,10,100]} print("Parameters:{}".format(param_grid)) grid_search = GridSearchCV(SVC(),param_grid,cv=5) #實例化一個GridSearchCV類 X_train,X_test,y_train,y_test = train_test_split(iris.data,iris.target,random_state=10) # 在這裏劃分好數據後,下面的grid_search自動進行訓練集和驗證集劃分並取出最優參數組合,返回的grid_search就是一個帶該參數組合的模型 grid_search.fit(X_train,y_train) #訓練,找到最優的參數,同時使用最優的參數實例化一個新的SVC estimator。 print("Test set score:{:.2f}".format(grid_search.score(X_test,y_test))) print("Best parameters:{}".format(grid_search.best_params_)) print("Best score on train set:{:.2f}".format(grid_search.best_score_))
最後得到輸出結果:
Parameters:{‘gamma‘: [0.001, 0.01, 0.1, 1, 10, 100], ‘C‘: [0.001, 0.01, 0.1, 1, 10, 100]} Test set score:0.97 Best parameters:{‘C‘: 10, ‘gamma‘: 0.1} Best score on train set:0.98
Grid Search 調參方法存在的共性弊端就是:耗時;參數越多,候選值越多,耗費時間越長!所以,一般情況下,先定一個大範圍,然後再細化。
總結
Grid Search:一種調優方法,在參數列表中進行窮舉搜索,對每種情況進行訓練,找到最優的參數;由此可知,這種方法的主要缺點是 比較耗時!
模型參數選擇方法——GridSearch網格搜索