[机器学习] --- 参数优化与模型选择
?
?
一 交叉驗證
交叉驗證的目的
在實際訓練中,模型通常對訓練數據好,但是對訓練數據之外的數據擬合程度差。用于評價模型的泛化能力,從而進行模型選擇。
交叉驗證的基本思想
把在某種意義下將原始數據(dataset)進行分組,一部分做為訓練集(train set),另一部分做為驗證集(validation set or test set),首先用訓練集對模型進行訓練,再利用驗證集來測試模型的泛化誤差。另外,現實中數據總是有限的,為了對數據形成重用,從而提出k-折疊交叉驗證。
對于個分類或回歸問題,假設可選的模型為M={M1,M2,M3……Md}。k-折疊交叉驗證就是將訓練集的1/k作為測試集,每個模型訓練k次,測試k次,錯誤率為k次的平均,最終選擇平均率最小的模型Mi。
1、 將全部訓練集S分成k個不相交的子集,假設S中的訓練樣例個數為m,那么每一個子集有m/k個訓練樣例,相應的子集稱作{S1,S2,S3……Sk}。
2、 每次從模型集合M中拿出來一個Mi,然后在訓練子集中選擇出k-1個
{S1,S2,Sj-1,Sj+1,Sk}(也就是每次只留下一個Sj),使用這k-1個子集訓練Mi后,得到假設函數hij。最后使用剩下的一份Sj作測試,得到經驗錯誤。
3、 由于我們每次留下一個Sj(j從1到k),因此會得到k個經驗錯誤,那么對于一個Mi,它的經驗錯誤是這k個經驗錯誤的平均。
4、 選出平均經驗錯誤率最小的Mi,然后使用全部的S再做一次訓練,得到最后的hi。
?
K折交叉驗證
?
?
網格搜索GridSearchCV
GridSearchCV用于系統地遍歷多種參數組合,通過交叉驗證確定最佳效果參數。
classsklearn.model_selection.GridSearchCV(estimator,param_grid, scoring=None, fit_params=None, n_jobs=1, iid=True, refit=True,cv=None, verbose=0, pre_dispatch='2*n_jobs', error_score='raise',return_train_score=True)常用參數解讀
estimator:所使用的分類器,如estimator=RandomForestClassifier(min_samples_split=100, min_samples_leaf=20, max_depth=8, max_features='sqrt', random_state=10)?并且傳入除需要確定最佳的參數之外的其他參數。每一個分類器都需要一個scoring參數,或者score方法。
param_grid:值為字典或者列表,即需要最優化的參數的取值,param_item = {'n_estimators':range(10,71,10)}??param_grid =param_item
scoring :準確度評價標準,默認None,這時需要使用score函數;或者如scoring=’roc_auc’,根據所選模型不同,評價準則不同。字符串(函數名),或是可調用對象,需要其函數簽名形如:scorer(estimator, X, y);如果是None,則使用estimator的誤差估計函數。
cv :交叉驗證參數,默認None,使用三折交叉驗證。指定fold數量,默認為3,也可以是yield訓練/測試數據的生成器。
refit :默認為True,程序將會以交叉驗證訓練集得到的最佳參數,重新對所有可用的訓練集與開發集進行,作為最終用于性能評估的最佳模型參數。即在搜索參數結束后,用最佳參數結果再次fit一遍全部數據集。
iid:默認True,為True時,默認為各個樣本fold概率分布一致,誤差估計為所有樣本之和,而非各個fold的平均。
verbose:日志冗長度,int:冗長度,0:不輸出訓練過程,1:偶爾輸出,>1:對每個子模型都輸出。
n_jobs: 并行數,int:個數,-1:跟CPU核數一致, 1:默認值。
pre_dispatch:指定總共分發的并行任務數。當n_jobs大于1時,數據將在每個運行點進行復制,這可能導致OOM,而設置pre_dispatch參數,則可以預先劃分總共的job數量,使數據最多被復制pre_dispatch次
?
隨機參數優化RandomizedSearchCV
盡管使用參數設置的網格法是目前最廣泛使用的參數優化方法, 其他搜索方法也具有更有利的性能。 RandomizedSearchCV 實現了對參數的隨機搜索, 其中每個設置都是從可能的參數值的分布中進行取樣。 這對于窮舉搜索有兩個主要優勢:
- 可以選擇獨立于參數個數和可能值的預算
- 添加不影響性能的參數不會降低效率
指定如何取樣的參數是使用字典完成的, 非常類似于為 GridSearchCV 指定參數。 此外, 通過 n_iter 參數指定計算預算, 即取樣候選項數或取樣迭代次數。 對于每個參數, 可以指定在可能值上的分布或離散選擇的列表 (均勻取樣):
{'C': scipy.stats.expon(scale=100), 'gamma': scipy.stats.expon(scale=.1),'kernel': ['rbf'], 'class_weight':['balanced', None]}搜索的輸出值
cv_results_:給出不同參數情況下的評價結果的記錄
best_params_:描述了已取得最佳結果的參數的組合
best_score_:成員提供優化過程期間觀察到的最好的評分
from sklearn.datasets import load_iris # 自帶的樣本數據集 from sklearn.neighbors import KNeighborsClassifier # 要估計的是knn里面的參數,包括k的取值和樣本權重分布方式 import matplotlib.pyplot as plt # 可視化繪圖 from sklearn.model_selection import GridSearchCV,RandomizedSearchCV # 網格搜索和隨機搜索iris = load_iris()X = iris.data # 150個樣本,4個屬性 y = iris.target # 150個類標號#######################################################k_range = range(1, 30) # 優化參數k的取值范圍 weight_options = ['uniform', 'distance'] # 代估參數權重的取值范圍。uniform為統一取權值,distance表示距離倒數取權值 # 下面是構建parameter grid,其結構是key為參數名稱,value是待搜索的數值列表的一個字典結構 params = {'n_neighbors':k_range,'weights':weight_options} # 定義優化參數字典,字典中的key值必須是分類算法的函數的參數名 knn = KNeighborsClassifier(n_neighbors=5) # 定義分類算法。n_neighbors和weights的參數名稱和params字典中的key名對應 # ================================網格搜索======================================= # 這里GridSearchCV的參數形式和cross_val_score的形式差不多,其中params是 parameter grid所對應的參數 # GridSearchCV中的n_jobs設置為-1時,可以實現并行計算(如果你的電腦支持的情況下) grid = GridSearchCV(estimator = knn, param_grid = params, cv=10, scoring='accuracy') #針對每個參數對進行了10次交叉驗證。scoring='accuracy'使用準確率為結果的度量指標。可以添加多個度量指標 grid.fit(X, y)#print('網格搜索-度量記錄:',grid.cv_results_) # 包含每次訓練的相關信息 print('網格搜索-最佳度量值:',grid.best_score_) # 獲取最佳度量值 print('網格搜索-最佳參數:',grid.best_params_) # 獲取最佳度量值時的代定參數的值。是一個字典 print('網格搜索-最佳模型:',grid.best_estimator_) # 獲取最佳度量時的分類器模型# 使用獲取的最佳參數生成模型,預測數據 knn_grid = KNeighborsClassifier(n_neighbors=grid.best_params_['n_neighbors'], weights=grid.best_params_['weights']) # 取出最佳參數進行建模 knn_grid.fit(X, y) # 訓練模型 print(knn_grid.predict([[3, 5, 4, 2]])) # 預測新對象 # =====================================隨機搜索=========================================== rand = RandomizedSearchCV(knn, params, cv=10, scoring='accuracy', n_iter=10, random_state=5) # rand.fit(X, y)#print('隨機搜索-度量記錄:',grid.cv_results_) # 包含每次訓練的相關信息 print('隨機搜索-最佳度量值:',grid.best_score_) # 獲取最佳度量值 print('隨機搜索-最佳參數:',grid.best_params_) # 獲取最佳度量值時的代定參數的值。是一個字典 print('隨機搜索-最佳模型:',grid.best_estimator_) # 獲取最佳度量時的分類器模型# 使用獲取的最佳參數生成模型,預測數據 knn_random = KNeighborsClassifier(n_neighbors=grid.best_params_['n_neighbors'], weights=grid.best_params_['weights']) # 取出最佳參數進行建模 knn_random.fit(X, y) # 訓練模型 print(knn_random.predict([[3, 5, 4, 2]])) # 預測新對象
?
當你的調節參數是連續的,比如回歸問題的正則化參數,有必要指定一個連續分布而不是可能值的列表,這樣RandomizeSearchCV就可以執行更好的grid search。
?
調參神器hyperopt
? ? ? ? ?http://hyperopt.github.io/hyperopt-sklearn/? ? ?
? ? ? ?Hyperopt庫為python中的模型選擇和參數優化提供了算法和并行方案。機器學習常見的模型有KNN, SVM,PCA,決策樹,GBDT等一系列的算法,但是在實際應用中,我們需要選取合適的模型,并對模型調參,得到一組合適的參數。尤其是在模型的調參階段,需要花費大量的時間和精力,卻又效率低下。但是我們可以換一個角度來看待這個問題,模型的選取,以及模型中需要調節的參數,可以看做是一組變量,模型的質量標準(比如正確率,AUC)等等可以看做是目標函數,這個問題就是超參數的優化的問題。我們可以使用搜索算法來解決。
如果我們要確定某個算法模型的最佳參數,通常會使用網格搜索(GridSearch),即假如有兩個參數A和B,它們分別有NA和NB個取值(人為設定),那么我們則需要依次枚舉這些取值的所有組合情況(在這里是共NA?NB
種),然后取準確率最高的那一個作為最終這個算法模型的最優參數。
顯然,如果要枚舉的組合情況非常多,網格搜索將變得十分低效,甚至不可接受。那么本文要介紹的hyperopt可以理解為一個智能化的網格搜索,能大大縮短調參所需的時間。
#!/usr/bin/env python # encoding: utf-8 """ @version: v1.0 @author: zwqjoy @contact: zwqjoy@163.com @site: https://blog.csdn.net/zwqjoy @file: para @time: 2018/7/7 17:00 """ from hyperopt import fmin, tpe, hp, rand import numpy as np from sklearn.metrics import accuracy_score from sklearn import svm from sklearn import datasets# SVM的三個超參數:C為懲罰因子,kernel為核函數類型,gamma為核函數的額外參數(對于不同類型的核函數有不同的含義) # 有別于傳統的網格搜索(GridSearch),這里只需要給出最優參數的概率分布即可,而不需要按照步長把具體的值給一個個枚舉出來 parameter_space_svc ={# loguniform表示該參數取對數后符合均勻分布'C':hp.loguniform("C", np.log(1), np.log(100)),'kernel':hp.choice('kernel',['rbf','poly']),'gamma': hp.loguniform("gamma", np.log(0.001), np.log(0.1)), }# 鳶尾花卉數據集,是一類多重變量分析的數據集 # 通過花萼長度,花萼寬度,花瓣長度,花瓣寬度4個屬性預測鳶尾花卉屬于(Setosa,Versicolour,Virginica)三個種類中的哪一類 iris = datasets.load_digits()#--------------------劃分訓練集和測試集-------------------- train_data = iris.data[0:1300] train_target = iris.target[0:1300] test_data = iris.data[1300:-1] test_target = iris.target[1300:-1] #-----------------------------------------------------------# 計數器,每一次參數組合的枚舉都會使它加1 count = 0def function(args):print(args)# **可以把dict轉換為關鍵字參數,可以大大簡化復雜的函數調用clf = svm.SVC(**args)# 訓練模型clf.fit(train_data,train_target)# 預測測試集prediction = clf.predict(test_data)global countcount = count + 1score = accuracy_score(test_target,prediction)print("[{0}], Test acc: {1})".format(str(count), score))# 由于hyperopt僅提供fmin接口,因此如果要求最大值,則需要取相反數return -score# algo指定搜索算法,目前支持以下算法: # ①隨機搜索(hyperopt.rand.suggest) # ②模擬退火(hyperopt.anneal.suggest) # ③TPE算法(hyperopt.tpe.suggest,算法全稱為Tree-structured Parzen Estimator Approach) # max_evals指定枚舉次數上限,即使第max_evals次枚舉仍未能確定全局最優解,也要結束搜索,返回目前搜索到的最優解 best = fmin(function, parameter_space_svc, algo=tpe.suggest, max_evals=100)# best["kernel"]返回的是數組下標,因此需要把它還原回來 kernel_list = ['rbf','poly'] best["kernel"] = kernel_list[best["kernel"]]print("best params: ",best)clf = svm.SVC(**best) print(clf)
注意:
? ? ?tuning with Hyperopt- TypeError: 'generator' object has no attribute '__getitem__'
? ? ? The issue is incompatibility of Hyperopt with networkxx2. One needs to downgrade to "networkx 1.11".?(pip intall networkx==1.11)
?
參考:
python機器學習模型選擇&調參工具Hyperopt-sklearn
python調參神器hyperopt
?
?
?
總結
以上是生活随笔為你收集整理的[机器学习] --- 参数优化与模型选择的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Keras 构建DNN 对用户名检测判断
- 下一篇: [机器学习] --- Getting S