在机器学习模型中,需要人工选择的参数称为超参数。比如随机森林中决策树的个数,人工神经网络模型中隐藏层层数和每层的节点个数,正则项中常数大小等等,他们都需要事先指定。超参数选择不恰当,就会出现欠拟合或者过拟合的问题。而在选择超参数的时候,有两个途径,一个是凭经验微调,另一个就是选择不同大小的参数,带入模型中,挑选表现最好的参数。
微调的一种方法是手工调制超参数,直到找到一个好的超参数组合,这么做的话会非常冗长,你也可能没有时间探索多种组合,所以可以使用Scikit-Learn的GridSearchCV来做这项搜索工作。下面让我们一一探索。

1,为什么叫网格搜索(GridSearchCV)?

GridSearchCV的名字其实可以拆分为两部分,GridSearch和CV,即网格搜索和交叉验证。这两个名字都非常好理解。网格搜索,搜索的是参数,即在指定的参数范围内,按步长依次调整参数,利用调整的参数训练学习器,从所有的参数中找到在验证集上精度最高的参数,这其实是一个训练和比较的过程。
GridSearchCV可以保证在指定的参数范围内找到精度最高的参数,但是这也是网格搜索的缺陷所在,他要求遍历所有可能参数的组合,在面对大数据集和多参数的情况下,非常耗时。

2,什么是Grid Search网格搜索?

Grid Search:一种调参手段;穷举搜索:在所有候选的参数选择中,通过循环遍历,尝试每一种可能性,表现最好的参数就是最终的结果。其原理就像是在数组里找到最大值。这种方法的主要缺点是比较耗时!
所以网格搜索适用于三四个(或者更少)的超参数(当超参数的数量增长时,网格搜索的计算复杂度会呈现指数增长,这时候则使用随机搜索),用户列出一个较小的超参数值域,这些超参数至于的笛卡尔积(排列组合)为一组组超参数。网格搜索算法使用每组超参数训练模型并挑选验证集误差最小的超参数组合。
2.1,以随机森林为例说明GridSearch网格搜索
下面代码,我们要搜索两种网格,一种是n_estimators,一种是max_features。GridSearch会挑选出最适合的超参数值。

  1. from sklearn.model_selection import GridSearchCV
  2. param_grid = [
  3. {'n_estimators': [3, 10, 30], 'max_features': [2, 4, 6, 8]},
  4. {'bootstrap': [False], 'n_estimators': [3, 10], 'max_features': [2, 3, 4]},
  5. ]
  6. forest_reg = RandomForestRegressor()
  7. grid_search = GridSearchCV(forest_reg, param_grid, cv=5,
  8. scoring='neg_mean_squared_error')
  9. grid_search.fit(housing_prepared, housing_labels)

sklearn 根据paramgrid的值,首先会评估3×4=12种n_estimators和max_features的组合方式,接下来在会在bootstrap=False的情况下(默认该值为True),评估2×3=6种12种n_estimators和max_features的组合方式,所以最终会有12+6=18种不同的超参数组合方式,而每一种组合方式要在训练集上训练5次, 所以一共要训练18×5=90 次,当训练结束后,你可以通过best_params获得最好的组合方式。

gridsearch.best_params


输出结果如下:

1 {‘max_features’: 8, ‘n_estimators’: 30}

得到最好的模型:

1 gridsearch.best_estimator

输出如下:

  1. RandomForestRegressor(bootstrap=True, criterion=‘mse’, max_depth=None,
  2. max_features=8, max_leaf_nodes=None, min_impurity_decrease=0.0,
  3. min_impurity_split=None, min_samples_leaf=1,
  4. min_samples_split=2, min_weight_fraction_leaf=0.0,
  5. n_estimators=30, n_jobs=1, oob_score=False, random_state=None,
  6. verbose=0, warm_start=False)

如果GridSearchCV初始化时,refit=True(默认的初始化值),在交叉验证时,一旦发现最好的模型(estimator),将会在整个训练集上重新训练,这通常是一个好主意,因为使用更多的数据集会提升模型的性能。
以上面有两个参数的模型为例,参数a有3中可能,参数b有4种可能,把所有可能性列出来,可以表示成一个3*4的表格,其中每个cell就是一个网格,循环过程就像是在每个网格里遍历,搜索,所以叫grid search。

3,RandomizedSearchCV——(随机搜索)


所谓的模型配置,一般统称为模型的超参数(Hyperparameters),比如KNN算法中的K值,SVM中不同的核函数(Kernal)等。多数情况下,超参数等选择是无限的。在有限的时间内,除了可以验证人工预设几种超参数组合以外,也可以通过启发式的搜索方法对超参数组合进行调优。称这种启发式的超参数搜索方法为网格搜索。
我们在搜索超参数的时候,如果超参数个数较少(三四个或者更少),那么我们可以采用网格搜索,一种穷尽式的搜索方法。但是当超参数个数比较多的时候,我们仍然采用网格搜索,那么搜索所需时间将会指数级上升。
所以有人就提出了随机搜索的方法,随机在超参数空间中搜索几十几百个点,其中就有可能有比较小的值。这种做法比上面稀疏化网格的做法快,而且实验证明,随机搜索法结果比稀疏网格法稍好。
RandomizedSearchCV使用方法和类GridSearchCV 很相似,但他不是尝试所有可能的组合,而是通过选择每一个超参数的一个随机值的特定数量的随机组合,这个方法有两个优点:

  • 如果你让随机搜索运行, 比如1000次,它会探索每个超参数的1000个不同的值(而不是像网格搜索那样,只搜索每个超参数的几个值)
  • 你可以方便的通过设定搜索次数,控制超参数搜索的计算量。

RandomizedSearchCV的使用方法其实是和GridSearchCV一致的,但它以随机在参数空间中采样的方式代替了GridSearchCV对于参数的网格搜索,在对于有连续变量的参数时,RandomizedSearchCV会将其当做一个分布进行采样进行这是网格搜索做不到的,它的搜索能力取决于设定的n_iter参数,同样的给出代码。image.png

  1. import numpy as np
  2. import pandas as pd
  3. import xgboost as xgb
  4. from sklearn.grid_search import RandomizedSearchCV
  5. #导入训练数据
  6. traindata = pd.read_csv("/traindata.txt",sep = ',')
  7. traindata = traindata.set_index('instance_id')
  8. trainlabel = traindata['is_trade']
  9. del traindata['is_trade']
  10. print(traindata.shape,trainlabel.shape)
  11. #分类器使用 xgboost
  12. clf1 = xgb.XGBClassifier()
  13. #设定搜索的xgboost参数搜索范围,值搜索XGBoost的主要6个参数
  14. param_dist = {
  15. 'n_estimators':range(80,200,4),
  16. 'max_depth':range(2,15,1),
  17. 'learning_rate':np.linspace(0.01,2,20),
  18. 'subsample':np.linspace(0.7,0.9,20),
  19. 'colsample_bytree':np.linspace(0.5,0.98,10),
  20. 'min_child_weight':range(1,9,1)
  21. }
  22. #RandomizedSearchCV参数说明,clf1设置训练的学习器
  23. #param_dist字典类型,放入参数搜索范围
  24. #scoring = 'neg_log_loss',精度评价方式设定为“neg_log_loss“
  25. #n_iter=300,训练300次,数值越大,获得的参数精度越大,但是搜索时间越长
  26. #n_jobs = -1,使用所有的CPU进行训练,默认为1,使用1个CPU
  27. grid = RandomizedSearchCV(clf1,param_dist,cv = 3,scoring = 'neg_log_loss',n_iter=300,n_jobs = -1)
  28. #在训练集上训练
  29. grid.fit(traindata.values,np.ravel(trainlabel.values))
  30. #返回最优的训练器
  31. best_estimator = grid.best_estimator_
  32. print(best_estimator)
  33. #输出最优训练器的精度
  34. print(grid.best_score_)

4, 超参数估计的随机搜索和网格搜索的比较

使用的数据集是小数据集 手写数字数据集 load_digits() 分类 数据规模 5620*64
(sklearn中的小数据可以直接使用,大数据集在第一次使用的时候会自动下载)
比较随机森林超参数优化的随机搜索和网格搜索。所有影响学习的参数都是同时搜索的(除了估计值的数量,它会造成时间/质量的权衡)。
随机搜索和网格搜索探索的是完全相同的参数空间。参数设置的结果非常相似,而随机搜索的运行时间要低的多。
随机搜索的性能稍差,不过这很可能是噪声效应,不会延续到外置测试集
注意:在实践中,人们不会使用网格搜索同时搜索这么多不同的参数,而是只选择那些被认为最重要的参数。
代码如下:

  1. #_*_coding:utf-8_*_
  2. # 输出文件开头注释的内容 __doc__的作用
  3. '''
  4. Python有个特性叫做文档字符串,即DocString ,这个特性可以让你的程序文档更加清晰易懂
  5. '''
  6. print(__doc__)
  7. import numpy as np
  8. from time import time
  9. from scipy.stats import randint as sp_randint
  10. from sklearn.model_selection import GridSearchCV
  11. from sklearn.model_selection import RandomizedSearchCV
  12. from sklearn.datasets import load_digits
  13. from sklearn.ensemble import RandomForestClassifier
  14. # get some data
  15. digits = load_digits()
  16. X, y = digits.data , digits.target
  17. # build a classifier
  18. clf = RandomForestClassifier(n_estimators=20)
  19. # utility function to report best scores
  20. def report(results, n_top= 3):
  21. for i in range(1, n_top + 1):
  22. candidates = np.flatnonzero(results['rank_test_score'] == i)
  23. for candidate in candidates:
  24. print("Model with rank:{0}".format(i))
  25. print("Mean validation score : {0:.3f} (std: {1:.3f})".
  26. format(results['mean_test_score'][candidate],
  27. results['std_test_score'][candidate]))
  28. print("Parameters: {0}".format(results['params'][candidate]))
  29. print("")
  30. # 指定取样的参数和分布 specify parameters and distributions to sample from
  31. param_dist = {"max_depth":[3,None],
  32. "max_features":sp_randint(1,11),
  33. "min_samples_split":sp_randint(2,11),
  34. "bootstrap":[True, False],
  35. "criterion":["gini","entropy"]
  36. }
  37. # run randomized search
  38. n_iter_search = 20
  39. random_search = RandomizedSearchCV(clf,param_distributions=param_dist,
  40. n_iter=n_iter_search,cv =5)
  41. start = time()
  42. random_search.fit(X, y)
  43. print("RandomizedSearchCV took %.2f seconds for %d candidates"
  44. " parameter settings." % ((time() - start), n_iter_search))
  45. report(random_search.cv_results_)
  46. # use a full grid over all parameters
  47. param_grid = {"max_depth":[3,None],
  48. "max_features":[1, 3, 10],
  49. "min_samples_split":[2, 3, 10],
  50. "bootstrap":[True, False],
  51. "criterion":["gini","entropy"]
  52. }
  53. # run grid search
  54. grid_search = GridSearchCV(clf, param_grid=param_grid, cv =5)
  55. start = time()
  56. grid_search.fit(X , y)
  57. print("GridSearchCV took %.2f seconds for %d candidate parameter settings."
  58. % (time() - start, len(grid_search.cv_results_['params'])))
  59. report(grid_search.cv_results_)

1,GridSearchCV简介

GridSearchCV,它存在的意义就是自动调参,只要把参数输进去,就能给出最优化结果和参数。但是这个方法适合于小数据集,一旦数据的量级上去了,很难得到结果。这个时候就需要动脑筋了。数据量比较大的时候可以使用一个快速调优的方法——坐标下降。它其实是一种贪心算法:拿当前对模型影响最大的参数调参,直到最优化;再拿下一个影响最大的参数调优,如此下去,直到所有的参数调整完毕。这个方法的缺点就是可能会跳到局部最优而不是全局最优,但是省时间省力,巨大的优势面前,还是试一试,后续可以再拿bagging再优化。
通常算法不够好,需要调试参数时必不可少。比如SVM的惩罚因子C,核函数kernel,gamma参数等,对于不同的数据使用不同的参数,结果效果可能差1~5个点,sklearn为我们专门调试参数的函数grid_search。

2,GridSearchCV参数说明

参数如下:

  1. class sklearn.model_selection.GridSearchCV(estimator, param_grid, scoring=None,
  2. fit_params=None, n_jobs=None, iid=’warn’, refit=True, cv=’warn’, verbose=0,
  3. pre_dispatch=‘2*n_jobs’, error_score=’raise-deprecating’, return_train_score=’warn’)

说明如下:
1)estimator:选择使用的分类器,并且传入除需要确定最佳的参数之外的其他参数。每一个分类器都需要一个scoring参数,或者score方法:如estimator = RandomForestClassifier(min_sample_split=100,min_samples_leaf = 20,max_depth = 8,max_features = ‘sqrt’ , random_state =10),
2)param_grid:需要最优化的参数的取值,值为字典或者列表,例如:param_grid = param_test1,param_test1 = {‘n_estimators’ : range(10,71,10)}
3)scoring = None :模型评价标准,默认为None,这时需要使用score函数;或者如scoring = ‘roc_auc’,根据所选模型不同,评价准则不同,字符串(函数名),或是可调用对象,需要其函数签名,形如:scorer(estimator,X,y);如果是None,则使用estimator的误差估计函数。
4)fit_para,s = None
5)n_jobs = 1 : n_jobs:并行数,int:个数,-1:跟CPU核数一致,1:默认值
6)iid = True:iid:默认为True,为True时,默认为各个样本fold概率分布一致,误差估计为所有样本之和,而非各个fold的平均。
7)refit = True :默认为True,程序将会以交叉验证训练集得到的最佳参数,重新对所有可能的训练集与开发集进行,作为最终用于性能评估的最佳模型参数。即在搜索参数结束后,用最佳参数结果再次fit一遍全部数据集。
8)cv = None:交叉验证参数,默认None,使用三折交叉验证。指定fold数量,默认为3,也可以是yield训练/测试数据的生成器。
9)verbose = 0 ,scoring = None verbose:日志冗长度,int:冗长度,0:不输出训练过程,1:偶尔输出,>1:对每个子模型都输出。
10)pre_dispatch = ‘2*n_jobs’ :指定总共发的并行任务数,当n_jobs大于1时候,数据将在每个运行点进行复制,这可能导致OOM,而设置pre_dispatch参数,则可以预先划分总共的job数量,使数据最多被复制pre_dispatch次。

3,进行预测的常用方法和属性

  • grid.fit() :运行网格搜索
  • gridscores :给出不同参数情况下的评价结果
  • bestparams :描述了已取得最佳结果的参数的组合
  • bestscore :提供优化过程期间观察到的最好的评分
  • cvresults :具体用法模型不同参数下交叉验证的结果

    4,GridSearchCV属性说明

    (1) cvresults : dict of numpy (masked) ndarrays
    具有键作为列标题和值作为列的dict,可以导入到DataFrame中。注意,“params”键用于存储所有参数候选项的参数设置列表。
    (2) bestestimator : estimator
    通过搜索选择的估计器,即在左侧数据上给出最高分数(或指定的最小损失)的估计器。如果refit = False,则不可用。
    (3)bestscore :float bestestimator的分数
    (4)best_parmas
    : dict 在保存数据上给出最佳结果的参数设置
    (5) bestindex : int 对应于最佳候选参数设置的索引(cvresults数组)
    search.cvresults [‘params’] [search.bestindex]中的dict给出了最佳模型的参数设置,给出了最高的平均分数(search.bestscore)。
    (6)scorer : function
    Scorer function used on the held out data to choose the best parameters for the model.
    (7)n_splits
    : int
    The number of cross-validation splits (folds/iterations).