有小伙伴催更了...
今天给大家分享一下机器学习调参的几种方法。
首次给大家介绍一下什么是调参。调参其实是为了找到更好更优的超参数,而超参数呢,是算法或者模型在开始学习之前设置值的初始参数。
超参数是在建立模型时用于控制算法行为的参数。这些参数不能从常规训练过程中获得。在对模型进行训练之前,需要对它们进行赋值。
下图主要列举了三种算法需要的超参数,例如决策树需要max_depth,随机森林需要N_estimator,LR需要C等等。
传统方式
最简单也是最“笨”的方式,就是手工搜索。下边用代码举例说明。
#importing required librariesfrom sklearn.neighbors import KNeighborsClassifierfrom sklearn.model_selection import train_test_splitfrom sklearn.model_selection import KFold , cross_val_scorefrom sklearn.datasets import load_winewine = load_wine()X = wine.datay = wine.target#splitting the data into train and test setX_train,X_test,y_train,y_test = train_test_split(X,y,test_size = 0.3,random_state = 14)#declaring parameters gridk_value = list(range(2,11))algorithm = ['auto','ball_tree','kd_tree','brute']scores = []best_comb = []kfold = KFold(n_splits=5)#hyperparameter tunningfor algo in algorithm: for k in k_value: knn = KNeighborsClassifier(n_neighbors=k,algorithm=algo) results = cross_val_score(knn,X_train,y_train,cv = kfold) print(f'Score:{round(results.mean(),4)} with algo = {algo} , K = {k}') scores.append(results.mean()) best_comb.append((k,algo))best_param = best_comb[scores.index(max(scores))]print(f'\\nThe Best Score : {max(scores)}')print(f"['algorithm': {best_param[1]} ,'n_neighbors': {best_param[0]}]")
这个方式其实很适合新手练手用,但是也有缺点,缺点就是不一定会得到最优的超参组合,而且工作量很大。
网格搜索
网格搜索适用于三四个(或者更少)的超参数(当超参数的数量增长时,网格搜索的计算复杂度会呈现指数增长,这时要换用随机搜索),用户列出一个较小的超参数值域,这些超参数值域的笛卡尔集(排列组合)为一组组超参数。网格搜索算法使用每组超参数训练模型并挑选验证集误差最小的超参数组合。
以SVM为例,挑选SVM的超参数C值、kernel类型和gamma值。下面的配置表示我们要搜索两种网格:一种是linear kernel和不同C值;一种是RBF kernel以及不同的C和gamma值。Grid Search会挑选最适合的超参数值。
param_grid = [ {'C': [1, 10, 100, 1000], 'kernel': ['linear']}, {'C': [1, 10, 100, 1000], 'gamma': [0.001, 0.0001], 'kernel': ['rbf']}, ]
同时,Grid Search适合重复的、迭代的进行。以上面的数据为例,如果在linear kernel的情况下,最合适的C值显示为1,那么说明我们高估了C值的范围,我们应该降低C值的搜索区域,开始第二次搜索:
param_grid = [ {'C': [0.1, 0.5,0.8,1], 'kernel': ['linear']} ]
如果超参数数量过多,则会增加计算的复杂度,此时需要考虑使用随机搜索。
随机搜索
随机搜索提供了一种更高效的解决方法(特别是参数数量多的情况下),Randomized Search为每个参数定义了一个分布函数并在该空间中采样(sampling)。上文对SVM的网格搜索在此处可以表示为:
param_grid = [ {'C': scipy.stats.expon(scale=100), 'gamma': scipy.stats.expon(scale=.1), 'kernel': ['rbf'], 'class_weight':['balanced', None]}]
Randomized Search指数级高效于Grid Search,因为Grid Search将大量的计算浪费在了指数级的对结果无影响的参数中,而Randomized Search几乎每次都搜索了对结果有影响的参数的值。
贝叶斯搜索
贝叶斯优化当然用到了贝叶斯公式,这里不作详细证明了,它要求已经存在几个样本点(同样存在冷启动问题,后面介绍解决方案),并且通过高斯过程回归(假设超参数间符合联合高斯分布)计算前面n个点的后验概率分布,得到每一个超参数在每一个取值点的期望均值和方差,其中均值代表这个点最终的期望效果,均值越大表示模型最终指标越大,方差表示这个点的效果不确定性,方差越大表示这个点不确定是否可能取得最大值非常值得去探索。因此实现贝叶斯优化第一步就是实现高斯过程回归算法,并且这里可以通过类似SVM的kernel trick来优化计算,在后面的开源项目Advisor介绍中我们就使用Scikit-learn提供的GaussianProgressRegressor,最终效果如下图,在只有3个初始样本的情况下我们(通过100000个点的采样)计算出每个点的均值和方差。
代码实例如下:
from skopt import BayesSearchCVimport warningswarnings.filterwarnings("ignore")# parameter ranges are specified by one of belowfrom skopt.space import Real, Categorical, Integerknn = KNeighborsClassifier()#defining hyper-parameter gridgrid_param = { 'n_neighbors' : list(range(2,11)) , 'algorithm' : ['auto','ball_tree','kd_tree','brute'] }#initializing Bayesian SearchBayes = BayesSearchCV(knn , grid_param , n_iter=30 , random_state=14)Bayes.fit(X_train,y_train)#best parameter combinationBayes.best_params_#score achieved with best parameter combinationBayes.best_score_#all combinations of hyperparametersBayes.cv_results_['params']#average scores of cross-validationBayes.cv_results_['mean_test_score']
总结
其实在科研之外的工程项目的开发和应用上,我们一般采用两种或者多种调参方式进行组合,用以在最优解和效率之间找到平衡。
本文来自投稿,不代表重蔚自留地立场,如若转载,请注明出处https://www.cwhello.com/35709.html
如有侵犯您的合法权益请发邮件951076433@qq.com联系删除