机器学习 --- 参数优化与模型选择

一 交叉验证

交叉验证的目的

在实际训练中,模型一般对训练数据好,可是对训练数据以外的数据拟合程度差。用于评价模型的泛化能力,从而进行模型选择。html

交叉验证的基本思想

把在某种意义下将原始数据(dataset)进行分组,一部分作为训练集(train set),另外一部分作为验证集(validation set or test set),首先用训练集对模型进行训练,再利用验证集来测试模型的泛化偏差。另外,现实中数据老是有限的,为了对数据造成重用,从而提出k-折叠交叉验证。python

对于个分类或回归问题,假设可选的模型为M={M1,M2,M3……Md}。k-折叠交叉验证就是将训练集的1/k做为测试集,每一个模型训练k次,测试k次,错误率为k次的平均,最终选择平均率最小的模型Mi。git

一、 将所有训练集S分红k个不相交的子集,假设S中的训练样例个数为m,那么每个子集有m/k个训练样例,相应的子集称做{S1,S2,S3……Sk}。github

二、 每次从模型集合M中拿出来一个Mi,而后在训练子集中选择出k-1个算法

{S1,S2,Sj-1,Sj+1,Sk}(也就是每次只留下一个Sj),使用这k-1个子集训练Mi后,获得假设函数hij。最后使用剩下的一份Sj做测试,获得经验错误。数组

三、 因为咱们每次留下一个Sj(j从1到k),所以会获得k个经验错误,那么对于一个Mi,它的经验错误是这k个经验错误的平均。dom

四、 选出平均经验错误率最小的Mi,而后使用所有的S再作一次训练,获得最后的hi。机器学习


K折交叉验证函数

  1. 将数据集平均分割成K个等份
  2. 使用1份数据做为测试数据,其他做为训练数据
  3. 计算测试准确率
  4. 使用不一样的测试集,重复二、3步骤
  5. 测试准确率作平均,做为对未知数据预测准确率的估计



网格搜索GridSearchCV

GridSearchCV用于系统地遍历多种参数组合,经过交叉验证肯定最佳效果参数。工具

classsklearn.model_selection.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:所使用的分类器,如estimator=RandomForestClassifier(min_samples_split=100, min_samples_leaf=20, max_depth=8, max_features='sqrt', random_state=10) 而且传入除须要肯定最佳的参数以外的其余参数。每个分类器都须要一个scoring参数,或者score方法。

param_grid:值为字典或者列表,即须要最优化的参数的取值,param_item = {'n_estimators':range(10,71,10)}  param_grid =param_item

scoring :准确度评价标准,默认None,这时须要使用score函数;或者如scoring=’roc_auc’,根据所选模型不一样,评价准则不一样。字符串(函数名),或是可调用对象,须要其函数签名形如:scorer(estimator, X, y);若是是None,则使用estimator的偏差估计函数。

cv :交叉验证参数,默认None,使用三折交叉验证。指定fold数量,默认为3,也能够是yield训练/测试数据的生成器。

refit :默认为True,程序将会以交叉验证训练集获得的最佳参数,从新对全部可用的训练集与开发集进行,做为最终用于性能评估的最佳模型参数。即在搜索参数结束后,用最佳参数结果再次fit一遍所有数据集。

iid:默认True,为True时,默认为各个样本fold几率分布一致,偏差估计为全部样本之和,而非各个fold的平均。

verbose:日志冗长度,int:冗长度,0:不输出训练过程,1:偶尔输出,>1:对每一个子模型都输出。

n_jobs: 并行数,int:个数,-1:跟CPU核数一致, 1:默认值。

pre_dispatch:指定总共分发的并行任务数。当n_jobs大于1时,数据将在每一个运行点进行复制,这可能致使OOM,而设置pre_dispatch参数,则能够预先划分总共的job数量,使数据最多被复制pre_dispatch次


随机参数优化RandomizedSearchCV

尽管使用参数设置的网格法是目前最普遍使用的参数优化方法, 其余搜索方法也具备更有利的性能。 RandomizedSearchCV 实现了对参数的随机搜索, 其中每一个设置都是从可能的参数值的分布中进行取样。 这对于穷举搜索有两个主要优点:

  • 能够选择独立于参数个数和可能值的预算
  • 添加不影响性能的参数不会下降效率

指定如何取样的参数是使用字典完成的, 很是相似于为 GridSearchCV 指定参数。 此外, 经过 n_iter 参数指定计算预算, 即取样候选项数或取样迭代次数。 对于每一个参数, 能够指定在可能值上的分布或离散选择的列表 (均匀取样):

{'C': scipy.stats.expon(scale=100), 'gamma': scipy.stats.expon(scale=.1),
  'kernel': ['rbf'], 'class_weight':['balanced', None]}

搜索的输出值

cv_results_:给出不一样参数状况下的评价结果的记录

best_params_:描述了已取得最佳结果的参数的组合

best_score_:成员提供优化过程期间观察到的最好的评分

from sklearn.datasets import load_iris  # 自带的样本数据集
from sklearn.neighbors import KNeighborsClassifier  # 要估计的是knn里面的参数,包括k的取值和样本权重分布方式
import matplotlib.pyplot as plt  # 可视化绘图
from sklearn.model_selection import GridSearchCV,RandomizedSearchCV  # 网格搜索和随机搜索

iris = load_iris()

X = iris.data  # 150个样本,4个属性
y = iris.target # 150个类标号

#######################################################

k_range = range(1, 30)  # 优化参数k的取值范围
weight_options = ['uniform', 'distance']  # 代估参数权重的取值范围。uniform为统一取权值,distance表示距离倒数取权值
# 下面是构建parameter grid,其结构是key为参数名称,value是待搜索的数值列表的一个字典结构
params = {'n_neighbors':k_range,'weights':weight_options}  # 定义优化参数字典,字典中的key值必须是分类算法的函数的参数名
knn = KNeighborsClassifier(n_neighbors=5)  # 定义分类算法。n_neighbors和weights的参数名称和params字典中的key名对应
# ================================网格搜索=======================================
# 这里GridSearchCV的参数形式和cross_val_score的形式差很少,其中params是 parameter grid所对应的参数
# GridSearchCV中的n_jobs设置为-1时,能够实现并行计算(若是你的电脑支持的状况下)
grid = GridSearchCV(estimator = knn, param_grid = params, cv=10, scoring='accuracy') #针对每一个参数对进行了10次交叉验证。scoring='accuracy'使用准确率为结果的度量指标。能够添加多个度量指标
grid.fit(X, y)

#print('网格搜索-度量记录:',grid.cv_results_)  # 包含每次训练的相关信息
print('网格搜索-最佳度量值:',grid.best_score_)  # 获取最佳度量值
print('网格搜索-最佳参数:',grid.best_params_)  # 获取最佳度量值时的代定参数的值。是一个字典
print('网格搜索-最佳模型:',grid.best_estimator_)  # 获取最佳度量时的分类器模型


# 使用获取的最佳参数生成模型,预测数据
knn_grid = KNeighborsClassifier(n_neighbors=grid.best_params_['n_neighbors'], weights=grid.best_params_['weights'])  # 取出最佳参数进行建模
knn_grid.fit(X, y)  # 训练模型
print(knn_grid.predict([[3, 5, 4, 2]]))  # 预测新对象
# =====================================随机搜索===========================================
rand = RandomizedSearchCV(knn, params, cv=10, scoring='accuracy', n_iter=10, random_state=5)  #
rand.fit(X, y)

#print('随机搜索-度量记录:',grid.cv_results_)  # 包含每次训练的相关信息
print('随机搜索-最佳度量值:',grid.best_score_)  # 获取最佳度量值
print('随机搜索-最佳参数:',grid.best_params_)  # 获取最佳度量值时的代定参数的值。是一个字典
print('随机搜索-最佳模型:',grid.best_estimator_)  # 获取最佳度量时的分类器模型


# 使用获取的最佳参数生成模型,预测数据
knn_random = KNeighborsClassifier(n_neighbors=grid.best_params_['n_neighbors'], weights=grid.best_params_['weights'])  # 取出最佳参数进行建模
knn_random.fit(X, y)  # 训练模型
print(knn_random.predict([[3, 5, 4, 2]]))  # 预测新对象



当你的调节参数是连续的,好比回归问题的正则化参数,有必要指定一个连续分布而不是可能值的列表,这样RandomizeSearchCV就能够执行更好的grid search。

调参神器hyperopt

         http://hyperopt.github.io/hyperopt-sklearn/     

       Hyperopt库为python中的模型选择和参数优化提供了算法和并行方案。机器学习常见的模型有KNN, SVM,PCA,决策树,GBDT等一系列的算法,可是在实际应用中,咱们须要选取合适的模型,并对模型调参,获得一组合适的参数。尤为是在模型的调参阶段,须要花费大量的时间和精力,却又效率低下。可是咱们能够换一个角度来看待这个问题,模型的选取,以及模型中须要调节的参数,能够看作是一组变量,模型的质量标准(好比正确率,AUC)等等能够看作是目标函数,这个问题就是超参数的优化的问题。咱们可使用搜索算法来解决。

若是咱们要肯定某个算法模型的最佳参数,一般会使用网格搜索(GridSearch),即假若有两个参数A和B,它们分别有NANB个取值(人为设定),那么咱们则须要依次枚举这些取值的全部组合状况(在这里是共NANB

种),而后取准确率最高的那一个做为最终这个算法模型的最优参数。

显然,若是要枚举的组合状况很是多,网格搜索将变得十分低效,甚至不可接受。那么本文要介绍的hyperopt能够理解为一个智能化的网格搜索,能大大缩短调参所需的时间。

#!/usr/bin/env python  
# encoding: utf-8  

""" 
@version: v1.0 
@author: zwqjoy 
@contact: zwqjoy@163.com 
@site: https://blog.csdn.net/zwqjoy 
@file: para
@time: 2018/7/7 17:00 
""" 

from hyperopt import fmin, tpe, hp, rand
import numpy as np
from sklearn.metrics import accuracy_score
from sklearn import svm
from sklearn import datasets

# SVM的三个超参数:C为惩罚因子,kernel为核函数类型,gamma为核函数的额外参数(对于不一样类型的核函数有不一样的含义)
# 有别于传统的网格搜索(GridSearch),这里只须要给出最优参数的几率分布便可,而不须要按照步长把具体的值给一个个枚举出来
parameter_space_svc ={
    # loguniform表示该参数取对数后符合均匀分布
    'C':hp.loguniform("C", np.log(1), np.log(100)),
    'kernel':hp.choice('kernel',['rbf','poly']),
    'gamma': hp.loguniform("gamma", np.log(0.001), np.log(0.1)),
}

# 鸢尾花卉数据集,是一类多重变量分析的数据集
# 经过花萼长度,花萼宽度,花瓣长度,花瓣宽度4个属性预测鸢尾花卉属于(Setosa,Versicolour,Virginica)三个种类中的哪一类
iris = datasets.load_digits()

#--------------------划分训练集和测试集--------------------
train_data = iris.data[0:1300]
train_target = iris.target[0:1300]
test_data = iris.data[1300:-1]
test_target = iris.target[1300:-1]
#-----------------------------------------------------------

# 计数器,每一次参数组合的枚举都会使它加1
count = 0

def function(args):
    print(args)

    # **能够把dict转换为关键字参数,能够大大简化复杂的函数调用
    clf = svm.SVC(**args)

    # 训练模型
    clf.fit(train_data,train_target)

    # 预测测试集
    prediction = clf.predict(test_data)

    global count
    count = count + 1
    score = accuracy_score(test_target,prediction)
    print("[{0}], Test acc: {1})".format(str(count), score))

    # 因为hyperopt仅提供fmin接口,所以若是要求最大值,则须要取相反数
    return -score

# algo指定搜索算法,目前支持如下算法:
# ①随机搜索(hyperopt.rand.suggest)
# ②模拟退火(hyperopt.anneal.suggest)
# ③TPE算法(hyperopt.tpe.suggest,算法全称为Tree-structured Parzen Estimator Approach)
# max_evals指定枚举次数上限,即便第max_evals次枚举仍未能肯定全局最优解,也要结束搜索,返回目前搜索到的最优解
best = fmin(function, parameter_space_svc, algo=tpe.suggest, max_evals=100)

# best["kernel"]返回的是数组下标,所以须要把它还原回来
kernel_list = ['rbf','poly']
best["kernel"] = kernel_list[best["kernel"]]

print("best params: ",best)

clf = svm.SVC(**best)
print(clf)


 

注意:

     tuning with Hyperopt- TypeError: 'generator' object has no attribute '__getitem__'

      The issue is incompatibility of Hyperopt with networkxx2. One needs to downgrade to "networkx 1.11". (pip intall networkx==1.11)


参考:

python机器学习模型选择&调参工具Hyperopt-sklearn

python调参神器hyperopt

相关文章
相关标签/搜索