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