机器学习——超参数搜索
2019-08-30

基础概念

超参数是在开始学习过程之前设置值的参数,而不是通过训练得到的参数数据。通常情况下,在机器学习过程中需要对超参数进行优化,给学习器选择一组最优超参数,以提高学习的性能和效果。比如,树的数量或树的深度,学习率(多种模式)以及k均值聚类中的簇数等都是超参数。

与超参数区别的概念是参数,它是模型训练过程中学习到的一部分,比如回归系数,神经网络权重等。简单的描述参数是模型训练获得的,超参数是人工配置参数(本质上是参数的参数,每次改变超参数,模型都要重新训练)。

超参数调优

在模型训练过程中的参数最优化,一般都是对参数的可能值进行有效搜索,然后用评价函数选取出最优参数,比如梯度下降法。

同理,人工的超参数选择过程,我们也可以采取类似参数搜索的办法,来提高效率,如果进行人工试错的方式,会非常浪费时间。

超参数搜索过程:

将数据集分为训练集,验证集及测试集。选择模型性能评价指标用训练集对模型进行训练在验证集上对模型进行参数进行搜索,用性能指标评价参数好坏选出最优参数

常见超参数搜索算法:

网格搜索随机搜索启发式搜索

网格搜索

网格搜索是在所有候选的参数选择中,通过循环遍历,尝试每一种可能性,表现最好的参数就是最终的结果(暴力搜索)。

原理:在一定的区间内,通过循环遍历,尝试每一种可能性,并计算其约束函数和目标函数的值,对满足约束条件的点,逐个比较其目标函数的值,将坏的点抛弃,保留好的点,最后便得到最优解的近似解。

为了评价每次选出的参数的好坏,我们需要选择评价指标,评价指标可以根据自己的需要选择accuracy、f1-score、f-beta、percision、recall等。

同时,为了避免初始数据的划分对结果的影响,我们需要采用交叉验证的方式来减少偶然性,一般情况下网格搜索需要和交叉验证相结合使用。

python的sklearn包中网格搜索函数GridSearchCV:

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)estimator:所使用的分类器param_grid:值为字典或者列表,需要最优化的参数的取值范围,如paramters = {"n_estimators":range(10,100,10)}。scoring :准确度评价指标,默认None,这时需要使用score函数;或者如scoring="roc_auc"。fit_params:字典类型数据,主要用于给fit方法传递参数。n_jobs: 并行数,int:个数,-1:跟CPU核数一致, 1:默认值。pre_dispatch:指定总共分发的并行任务数。当n_jobs大于1时,数据将在每个运行点进行复制,这可能导致OOM,而设置pre_dispatch参数,则可以预先划分总共的job数量,使数据最多被复制pre_dispatch次。iid:默认True,为True时,默认为各个样本fold概率分布一致,误差估计为所有样本之和,而非各个fold的平均。cv :交叉验证参数,默认None,使用三折交叉验证。指定fold数量,默认为3,也可以是yield训练/测试数据的生成器。refit :默认为True,程序将会以交叉验证训练集得到的最佳参数,重新对所有可用的训练集与验证集进行训练,作为最终用于性能评估的最佳模型参数。即在搜索参数结束后,用最佳参数结果再次fit一遍全部数据集。verbose:日志冗长度,int:冗长度,0:不输出训练过程,1:偶尔输出,>1:对每个子模型都输出。error_score: 默认为raise,可选择参数numeric,在模型拟合过程中如果产生误差,在raise情况下,误差分数将会提高,如果选择numeric,则fitfailedwarning会提高。return_train_score:布尔类型数据,默认为Ture ,为False时交叉验证的结果不包含训练得分。

网格搜索python简单实现

import pandas as pdfrom sklearn import datasetsfrom sklearn.model_selection import GridSearchCVfrom sklearn.metrics import classification_reportfrom xgboost.sklearn import XGBClassifieriris = datasets.load_iris()parameters = {"n_estimators":range(100,150,10),"max_depth":range(3,5,1)}xgc=XGBClassifier()clf = GridSearchCV(xgc, parameters)clf.fit(iris.data, iris.target)cv_result = pd.DataFrame.from_dict(clf.cv_results_)best_param=clf.best_params_best_score=clf.best_score_y_pred = clf.predict(iris.data)print(classification_report(y_true=iris.target, y_pred=y_pred))

随机搜索

随机搜索(random search)是利用随机数去求函数近似的最优解的方法,区别于网格搜索的暴力搜索方式。

原理:在一定的区间内,不断随机地而不是有倾向性产生随机点,并计算其约束函数和目标函数的值,对满足约束条件的点,逐个比较其目标函数的值,将坏的点抛弃,保留好的点,最后便得到最优解的近似解。

这种方法是建立在概率论的基础上,所取随机点越多,则得到最优解的概率也就越大。这种方法存在精度较差的问题,找到近似最优解的效率高于网格搜索。随机搜索一般用于粗选或普查。

python的sklearn包中随机搜索函数RandomizedSearchCV:

RandomizedSearchCV(estimator, param_distributions, n_iter=10, scoring=None, fit_params=None, n_jobs=1, iid=True, refit=True, cv=None, verbose=0,pre_dispatch=‘2*n_jobs’, random_state=None, error_score=’raise’)estimator:所使用的分类器param_distributions:值为字典或者列表,需要最优化的参数的取值范围,同时需要选择一种rvs方法来进行抽样,比如scipy.stats.distributionsn_iter : 抽样参数,默认为10,具体的值选择需要根据模型的相应效果进行评估。scoring :准确度评价指标,默认None,这时需要使用score函数;或者如scoring="roc_auc"。fit_params:字典类型数据,主要用于给fit方法传递参数。n_jobs: 并行数,int:个数,-1:跟CPU核数一致, 1:默认值。pre_dispatch:指定总共分发的并行任务数。当n_jobs大于1时,数据将在每个运行点进行复制,这可能导致OOM,而设置pre_dispatch参数,则可以预先划分总共的job数量,使数据最多被复制pre_dispatch次。iid:默认True,为True时,默认为各个样本fold概率分布一致,误差估计为所有样本之和,而非各个fold的平均。cv :交叉验证参数,默认None,使用三折交叉验证。指定fold数量,默认为3,也可以是yield训练/测试数据的生成器。refit :默认为True,程序将会以交叉验证训练集得到的最佳参数,重新对所有可用的训练集与验证集进行训练,作为最终用于性能评估的最佳模型参数。即在搜索参数结束后,用最佳参数结果再次fit一遍全部数据集。verbose:日志冗长度,int:冗长度,0:不输出训练过程,1:偶尔输出,>1:对每个子模型都输出。random_state:随机种子,默认为None,int类型或者随机状态实例,伪随机数生成器状态用于从可能的值列表而不是scipy中随机抽样。统计分布。如果int,随机状态是随机数生成器所使用的种子;如果随机状态实例,随机状态是随机数生成器;如果没有,随机数生成器是np.random所使用的随机状态实例。error_score: 默认为raise,可选择参数numeric,在模型拟合过程中如果产生误差,在raise情况下,误差分数将会提高,如果选择numeric,则fitfailedwarning会提高。

随机搜索python简单实现

import pandas as pdfrom sklearn import datasetsfrom sklearn.model_selection import GridSearchCV,RandomizedSearchCVfrom sklearn.metrics import classification_reportfrom xgboost.sklearn import XGBClassifieriris = datasets.load_iris()parameters = {"n_estimators":range(100,150,10),"max_depth":range(3,5,1)}xgc=XGBClassifier()clf = RandomizedSearchCV(xgc, parameters,cv=5)clf.fit(iris.data, iris.target)cv_result = pd.DataFrame.from_dict(clf.cv_results_)best_param=clf.best_params_best_score=clf.best_score_y_pred = clf.predict(iris.data)print(classification_report(y_true=iris.target, y_pred=y_pred))

启发式搜索

启发式搜索(Heuristically Search)又称为有信息搜索(Informed Search),它是利用问题拥有的启发信息来引导搜索,达到减少搜索范围、降低问题复杂度的目的,这种利用启发信息的搜索过程称为启发式搜索。

原理:在状态空间中的搜索对每一个搜索的位置进行评估,得到最好的位置,再从这个位置进行搜索直到目标。这样可以省略大量无谓的搜索路径,提高了效率。在启发式搜索中,对位置的估价是十分重要的。采用了不同的估价可以有不同的效果。

启发式搜索有模拟退火算法(SA)、遗传算法(GA)、列表搜索算法(ST)、进化规划(EP)、进化策略(ES)、蚁群算法(ACA)、人工神经网络(ANN)...等。

启发式搜索非常多样化,而且在sklearn包中并没有现成的函数,如果有需要我们可以针对某一种启发式算法的实现过程进行了解,然后用python手动实现。

这里我们着手对遗传算法的实现过程做些了解。

遗传算法

遗传算法(Genetic Algorithm)是一种通过模拟自然进化过程搜索最优解的方法,它的思想来自于进化论,生物种群具有自我进化的能力,能够不断适应环境,优势劣汰之后得到最优的种群个体。进化的行为主要有选择,遗传,变异,遗传算法希望能够通过将初始解空间进化到一个较好的解空间。

原理:遗传算法是从代表问题可能潜在的解集的一个种群(population)开始的,而一个种群则由经过基因(gene)编码的一定数目的个体(individual)组成。每个个体实际上是染色体(chromosome)带有特征的实体。染色体作为遗传物质的主要载体,即多个基因的集合,其内部表现(即基因型)是某种基因组合,它决定了个体的形状的外部表现,如黑头发的特征是由染色体中控制这一特征的某种基因组合决定的。因此,在一开始需要实现从表现型到基因型的映射即编码工作。由于仿照基因编码的工作很复杂,我们往往进行简化,如二进制编码,初代种群产生之后,按照适者生存和优胜劣汰的原理,逐代(generation)演化产生出越来越好的近似解,在每一代,根据问题域中个体的适应度(fitness)大小选择(selection)个体,并借助于自然遗传学的遗传算子(genetic operators)进行组合交叉(crossover)和变异(mutation),产生出代表新的解集的种群。这个过程将导致种群像自然进化一样的后生代种群比前代更加适应于环境,末代种群中的最优个体经过解码(decoding),可以作为问题近似最优解。

遗传算法的实现步骤:

初始化候选参数集,并编码为基因序列(初始化种群,一组参数编码为一个种群个体,共n个种群个体 ),即对参数进行编码,同时设定进化代数m。个体评估,计算各个种群个体的适应度(适应度描述了该个体对自然环境的适应能力,表征了其个体存活能力和生殖机会),用适应度函数表示。选择运算,选择是模拟自然选择,把优秀的个体选择出来(基于适应度),以进行后续的遗传和变异。交叉运算,交叉是模拟繁殖后代的基因重组。变异运算,变异是模拟基因突变。经过选择,交叉,变异,生产下一代群体,重复此过程,直到停止条件。

从上面的实现步骤可以知道,遗传算法包含以下几个主要部分:

基因编码适应度函数遗传算子,包含选择,交叉,变异

有了基本遗传算法的实现过程,后续就是对每个算法实现过程的细节寻找合适的方法进行处理。换到机器学习参数最优化的问题上,就是使用遗传算法搜索参数空间获得最优的模型性能评价指标的过程。

启发式搜索先到这里,后续有时间可以尝试python代码实现参数最优化的过程。