一、介紹
hyperopt 是一個自動調參工具,與 sklearn 的 GridSearchCV 相比,hyperopt 具有更加完善的功能,且模型不必符合 sklearn 接口規范。
1.1. 項目地址
https://github.com/hyperopt/hyperopt
1.2. 安裝方法
pip install hyperopt
二、使用
2.1. 官方樣例解讀
# 定義一個需要優化的目標函數,比如可以封裝成xgb的一個評估函數,以給定超參訓練完xgb后,對回歸問題返回 MAE 的值
def objective(args):
case, val = args
if case == 'case 1':
return val
else:
return val ** 2
# 定義搜索空間
from hyperopt import hp
space = hp.choice('a',
[
('case 1', 1 + hp.lognormal('c1', 0, 1)),
('case 2', hp.uniform('c2', -10, 10))
])
# 在搜索空間內進線搜索,去最小化目標函數(也可以最大化,見官方文檔)
from hyperopt import fmin, tpe, space_eval
best = fmin(objective, space, algo=tpe.suggest, max_evals=100)
print(best)
# -> {'a': 1, 'c2': 0.01420615366247227}
print(space_eval(space, best))
# -> ('case 2', 0.01420615366247227}
2.2. 搜索空間設置
- hp.choice(label,[options])
- 返回一個選項,選項可以是list或者tuple.options可以是嵌套的表達式,用於組成條件參數。
- hp.pchoice(label,p_options)
- 以一定的概率返回一個p_options的一個選項。這個選項使得函數在搜索過程中對每個選項的可能性不均勻。
- hp.uniform(label,low,high)
- 參數在low和high之間均勻分布。
- hp.quniform(label,low,high,q)
- 參數的取值是round(uniform(low,high)/q)*q,適用於那些離散的取值。
- hp.loguniform(label,low,high)
- 繪制exp(uniform(low,high)),變量的取值范圍是[exp(low),exp(high)]
- hp.randint(label,upper)
- 返回一個在[0,upper)前閉后開的區間內的隨機整數。
2.3. XGBoost Hyperopt Demo
代碼樣例來自 《python調參神器hyperopt》
#coding:utf-8
import numpy as np
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
import xgboost as xgb
from random import shuffle
from xgboost.sklearn import XGBClassifier
from sklearn.cross_validation import cross_val_score
import pickle
import time
from hyperopt import fmin, tpe, hp,space_eval,rand,Trials,partial,STATUS_OK
def loadFile(fileName = "E://zalei//browsetop200Pca.csv"):
data = pd.read_csv(fileName,header=None)
data = data.values
return data
data = loadFile()
label = data[:,-1]
attrs = data[:,:-1]
labels = label.reshape((1,-1))
label = labels.tolist()[0]
minmaxscaler = MinMaxScaler()
attrs = minmaxscaler.fit_transform(attrs)
index = range(0,len(label))
shuffle(index)
trainIndex = index[:int(len(label)*0.7)]
print len(trainIndex)
testIndex = index[int(len(label)*0.7):]
print len(testIndex)
attr_train = attrs[trainIndex,:]
print attr_train.shape
attr_test = attrs[testIndex,:]
print attr_test.shape
label_train = labels[:,trainIndex].tolist()[0]
print len(label_train)
label_test = labels[:,testIndex].tolist()[0]
print len(label_test)
print np.mat(label_train).reshape((-1,1)).shape
def GBM(argsDict):
max_depth = argsDict["max_depth"] + 5
n_estimators = argsDict['n_estimators'] * 5 + 50
learning_rate = argsDict["learning_rate"] * 0.02 + 0.05
subsample = argsDict["subsample"] * 0.1 + 0.7
min_child_weight = argsDict["min_child_weight"]+1
print "max_depth:" + str(max_depth)
print "n_estimator:" + str(n_estimators)
print "learning_rate:" + str(learning_rate)
print "subsample:" + str(subsample)
print "min_child_weight:" + str(min_child_weight)
global attr_train,label_train
gbm = xgb.XGBClassifier(nthread=4, #進程數
max_depth=max_depth, #最大深度
n_estimators=n_estimators, #樹的數量
learning_rate=learning_rate, #學習率
subsample=subsample, #采樣數
min_child_weight=min_child_weight, #孩子數
max_delta_step = 10, #10步不降則停止
objective="binary:logistic")
metric = cross_val_score(gbm,attr_train,label_train,cv=5,scoring="roc_auc").mean()
print metric
return -metric
space = {"max_depth":hp.randint("max_depth",15),
"n_estimators":hp.randint("n_estimators",10), #[0,1,2,3,4,5] -> [50,]
"learning_rate":hp.randint("learning_rate",6), #[0,1,2,3,4,5] -> 0.05,0.06
"subsample":hp.randint("subsample",4),#[0,1,2,3] -> [0.7,0.8,0.9,1.0]
"min_child_weight":hp.randint("min_child_weight",5), #
}
algo = partial(tpe.suggest,n_startup_jobs=1)
best = fmin(GBM,space,algo=algo,max_evals=4)#max_evals表示想要訓練的最大模型數量,越大越容易找到最優解
print best
print GBM(best)
三、調參經驗
一般來說,算法模型會對重要的超參進行適度搜索。比如xgb的learning_rate,最大深度,損失函數等。
為什么要做超參搜索?我認為對於“煉丹”新人,或缺乏足夠數據了解的情況,這類工具可以幫忙規避非常不合理的參數設置。
但特征決定了 " \(天花板_1\) ",模型結構決定了 " \(天花板_2\) ",調參只能在訓練集內逼近 " \(天花板_3\) ",調參實在落了下乘。所以像是 hyperopt 這類自動調參工具,不一定僅僅對learning_rate這種超參做搜索,比如在 cnn 中我們可以對模型結構做搜索,可能會有更高的提升空間。其實這也是近幾年很火的 NAS 方向 (Neural Architecture Search)。
