超参数是在开始学习过程之前设置值的参数,而不是通过训练得到的参数数据。超参数可以分为两种类型:定义模型及结构本身的参数,目标函数与与优化算法所需的参数,前者用于训练和预测阶段,后者用于训练阶段。
在实战过程中,需要对机器学习模型进行优化以确保模型的最高准确率,因此,超参数调优参数应运而生,常见的超参数搜索算法有网格搜索、随机搜索、贝叶斯优化等。超参数搜索算法一般包括三个要素:
-
目标函数,即算法需要最大化/最小化目标;
-
搜索范围,一般通过上限和下限来确定;
-
算法的其他参数,如搜索步长。
1、传统的机器学习超参优化
1.1 网格搜索
1.1.1 算法原理
网格搜索是较简单、应用最广泛的超参数搜索算法,它通过查找搜索范围内的所有的点来确定最优值(穷举)。然而,这种方法比较消耗计算资源和时间。在实际应用中,网格搜索一般会先使用较大的搜索范围和步长,来寻找全局最优值可能的位置;然后逐渐缩小搜索范围和步长,来寻找更精确的最优值。
1.1.2 参数说明
函数:sklearn.model_selection.GridSearchCV
参数:
- estimator:sklearn的估计器
- param_grid:(字典 or 字典列表 )超参数
- scoring:(str)在测试集进行交叉验证的评估策略
- n_jobs:(int)并行处理几个job
- refit:(bool)使用最佳参数重新调整estimator
- cv:(int)确定交叉验证的策略,None:默认使用5-fold交叉验证
属性:
- cv_results_:字典的keys是列名,values是列的DataFrame
- best_estimator_:最佳参数的估计器
- best_score_:最佳参数的估计器交叉验证的平均得分
- best_params_:最佳参数
1.1.3算法实现
from sklearn import svm, datasets
from sklearn.model_selection import GridSearchCV#获取数据
iris = datasets.load_iris()#超参数
parameters = {'kernel':('linear', 'rbf'), 'C':[1, 10]}
#估计器
svc = svm.SVC()
#网格搜索
clf = GridSearchCV(svc, parameters)
clf.fit(iris.data, iris.target)
sorted(clf.cv_results_.keys())
1.2 随机搜索
1.2.1 算法原理
随机搜索和网格搜索类似,只是不再测试上界和下界之间的所有值,而是在搜索范围中随机选取样本点,如果样本点集足够大,通过随机采样也能大概率找到全局最优值或其近似值。
比网格搜索的好处:一是独立于参数数量和可能的值被选中;二是添加不影响性能的参数不会降低效率
1.2.2 参数说明
函数:sklearn.model_selection.RandomizedSearchCV
参数:
- estimator:sklearn的估计器
- param_distributions:(字典 or 字典列表 )超参数的样本分布
- scoring:(str)在测试集进行交叉验证的评估策略
- n_iter:(int)采样参数的设置数
- n_jobs:(int)并行处理几个job
- refit:(bool)使用最佳参数重新调整estimator
- cv:(int)确定交叉验证的策略,None:默认使用5-fold交叉验证
属性:
- cv_results_:字典的keys是列名,values是列的DataFrame
- best_estimator_:最佳参数的估计器
- best_score_:最佳参数的估计器交叉验证的平均得分
- best_params_:最佳参数
1.2.3算法实现
from sklearn.datasets import load_iris
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import RandomizedSearchCV
from scipy.stats import uniform
iris = load_iris()
logistic = LogisticRegression(solver='saga', tol=1e-2, max_iter=200,random_state=0)
distributions = dict(C=uniform(loc=0, scale=4),penalty=['l2', 'l1'])
clf = RandomizedSearchCV(logistic, distributions, random_state=0)
search = clf.fit(iris.data, iris.target)
print(search.best_params_)
2 基于模型的序列超参优化
基于模型的序列超参优化的核心成分是:代理模型选择、代理模型更新、基于后验分布下新的超参组合的选择。
(1)代理模型选择
模型选择是使用一个概率模型作为目标函数的代理函数,通过这个模型对目标函数进行预估评价。
(2)代理模型更新
刚开始模型随机选择一个超参组合,但随着观测的增加,可以基于新的观测去更新模型进而增强模型能力。
(3)新超参组的选择
代理模型都采用概率代理模型,可以提供更高的灵活性,每一个超参组合的评估结果都可以用一个概率分布表达。,其次,基于概率分布的模型可以用于选取期望最大的新超参组合并支持模型的更新迭代。通过新超参组选择的方法定义采集函数来计算超参组合优劣程度,常见方法有:
- PI(Probability Improvement)
PI即概率提升法,它的核心思路是寻找在期望上最有可能获得更好结果的超参组合。
效用函数:
是目标函数(误差率),是当前最小误差率的值,计算效用函数的期望。
- EI(Expected Improvement)
EI即期望提升法,PI没有考虑优化的幅度,因此EI引入了优化幅度从而计算期望优化度。
效用函数:
- UCB(Upper Confidence Bound)
UCB的核心思想是统计学的置信区间,如果目标函数是往最大值优化,就使用上边界,如果目标函数是往最小值优化,就使用下边界。
2.1 贝叶斯优化
2.1.1 算法原理
贝叶斯优化是一种黑盒优化算法,用于求解表达式未知的函数的极值问题。算法根据一组采样点处的函数值预测出任意点处函数值的概率分布,这通过高斯过程回归而实现。根据高斯过程回归的结果构造采集函数,用于衡量每一个点值得探索的程度,求解采集函数的极值从而确定下一个采样点。最后返回这组采样点的极值作为函数的极值。
其核心由两部分构成:
- 1. 高斯过程回归。计算每一点处函数值的均值和方差;
- 2. 根据均值和方差构造采集函数,用于决定本次迭代时在哪个点处进行采样。
(1)高斯分布
若随机变量服从一个位置参数为、尺度参数为的概率分布,且其概率密度函数为
则这个随机变量就称为正态随机变量,正态随机变量服从的分布就称为正态分布,记作,读作服从,或服从正态分布。
(2)高斯过程
高斯过程(Gaussian Process, GP)是概率论和数理统计中随机过程(stochastic process)的一种,是一系列服从正态分布的随机变量(random variable)在一指数集(index set)内的组合,这个集合里面的任意有限个随机变量都服从联合正态分布。
对于一个连续域T,我们在连续域任选n个时刻,使得获得一个n维向量都满足其是一个n维高斯分布,则这个就是高斯过程。一个高斯过程可以通过一个均值函数和一个协方差函数完全确定。
高斯过程和高斯混合模型的区别?
高斯过程是无限个高斯分布的变量,高斯混合模型是有限个高斯分布的随机变量。
(3)高斯过程回归
高斯过程回归模型是一个不太依赖于m(x)但是比较依赖核函数选择的模型。(详解另写)
(4)采集函数
采集函数用于确定在何处采集下一个样本点,它需要满足下面的条件。
- 在已有的采样点处采集函数的值更小,因为这些点已经被探索过,再在这些点处计算函数值对解决问题没有什么用;
- 在置信区间更宽的点处采集函数的值更大,因为这些点具有更大的不确定性,更值得探索;
- 在函数均值更大的点处采集函数的值更大,因为均值是对该点处函数值的估计值,这些点更可能在极值点附近。
2.1.2 参数说明
目标函数:
fmin(fn=目标函数)
搜索空间:
fmin(fn=目标函数,space=搜索空间)
hp.choice返回一个选项,选项可以是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)前闭后开的区间内的随机整数。
搜索算法:
fmin(fn=目标函数,space=搜索空间,algo=搜索算法)
随机搜索(对应是hyperopt.rand.suggest),模拟退火(对应是hyperopt.anneal.suggest),TPE算法
可视化(Trials)伪代码:
trials = Trials()
best = fmin(fn=fn_dtree, space=space_dtree, algo=tpe.suggest, max_evals=1000, trials=trials)parameters = ['max_depth', 'max_features', 'criterion', 'scale','normalize'] # decision tree
cols = len(parameters)
f, axes = plt.subplots(nrows=1, ncols=cols, figsize=(20, 5))
cmap = plt.cm.jet
for i, val in enumerate(parameters):xs = np.array([t['misc']['vals'][val] for t in trials.trials]).ravel()ys = [-t['result']['loss'] for t in trials.trials]ys = np.array(ys)axes[i].scatter(xs,ys,s=20,linewidth=0.01,alpha=0.5,c=cmap(float(i) / len(parameters)))axes[i].set_title(val)
2.1.3算法实现
from sklearn.datasets import load_digits
from sklearn.tree import DecisionTreeClassifier
from sklearn.svm import SVC
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import cross_val_score
from hyperopt import fmin, tpe, hp,STATUS_OK, Trials
#from sklearn.model_selection import RandomizedSearchCV
#from scipy.stats import uniform
digits = load_digits()
X = digits.data
y = digits.target
print (X.shape, y.shape)def hyperopt_train_test(params):t = params['type']del params['type']if t == 'svm':clf = SVC(**params)elif t == 'dtree':clf = DecisionTreeClassifier(**params)elif t == 'knn':clf = KNeighborsClassifier(**params)else:return 0return cross_val_score(clf, X, y).mean()space = hp.choice('classifier_type', [{'type': 'naive_bayes','alpha': hp.uniform('alpha', 0.0, 2.0)},{'type': 'svm','C': hp.uniform('C', 0, 10.0),'kernel': hp.choice('kernel', ['linear', 'rbf']),'gamma': hp.uniform('gamma', 0, 20.0)},{'type': 'randomforest','max_depth': hp.choice('max_depth', range(1,20)),'max_features': hp.choice('max_features', range(1,5)),'n_estimators': hp.choice('n_estimators', range(1,20)),'criterion': hp.choice('criterion', ["gini", "entropy"]),'scale': hp.choice('scale', [0, 1]),'normalize': hp.choice('normalize', [0, 1])},{'type': 'knn','n_neighbors': hp.choice('knn_n_neighbors', range(1,50))}
])count = 0
best = 0
def f(params):global best, countcount += 1acc = hyperopt_train_test(params.copy())if acc > best:print ('new best:', acc, 'using', params['type'])best = accif count % 50 == 0:print ('iters:', count, ', acc:', acc, 'using', params)return {'loss': -acc, 'status': STATUS_OK}trials = Trials()
best = fmin(f, space, algo=tpe.suggest, max_evals=1500, trials=trials)
print ('best:')
print (best)
除了高斯过程回归的序列超参优化之外,还有基于随机森林、TPE的序列超参优化
参考:
一文秒懂贝叶斯优化/Bayesian Optimization
Hyperopt 入门指南
......