数据挖掘--模型参数调优

Deadly 提交于 2020-04-06 22:02:39

模型评估

使用metric函数来进行评分

  sklearn.metrics里面提供了一些函数来帮助我们进行评分。其中里面以_score结尾的函数的返回值越大,模型的性能越好。而以_error或_loss结尾的函数,返回值越小,表示模型性能越好。从命名上来看,这一点不难理解。

  metrics里面的很多函数名不直接传入scoring后面,因为有一些函数需要传入特定的参数才能够使用。比如在使用fbeta_score的时候需要传入bata参数等。 在这个时候,我们的做法是把函数名和参数封装一下,封装成为一个新的函数,然后传入scoring后面。封装的方法是使用metrics的make_scorer方法。

from sklearn.metrics import make_scorer, fbeta_score
#我们用make_scorer封装了fbeta_score,它需要传入一个参数
ftwo_score = make_scorer(fbeta_score, beta=2)
from sklearn.model_selection import GridSearchCV
from sklearn.svm import LinearSVC
grid = GridSearchCV(LinearSVC(), param_grid={‘C‘:[1, 10]}, scoring=ftwo_score)

  使用metrics的make_score方法,我们可以制定出我们自己的方法,这个方法需要满足下面一些条件:

  • 需要传入一个我们自定义的函数的名称
  • 需说明greater_is_better的值是True还是False。 当值为正的时候,返回的是score的值,值越高性能越好。当为False的时候,返回的为score的负值,值越低越好。
  • 是否是针对分类问题的。 传入needs_threshold=True来说明是针对分类问题的,默认情况为False。
  • 其余的参数。如在f1_score中的bata,labels。
import numpy as np
from sklearn.metrics import make_scorer

def my_custom_loss_func(ground_truth, predictions):
    diff = np.abs(ground_truth - predictions).max()
    return np.log(1 + diff)

loss  = make_scorer(my_custom_loss_func, greater_is_better=False)
score = make_scorer(my_custom_loss_func, greater_is_better=True)
ground_truth = [[1], [1]]
predictions  = [0, 1]
from sklearn.dummy import DummyClassifier
clf = DummyClassifier(strategy=‘most_frequent‘, random_state=0)
clf = clf.fit(ground_truth, predictions)
#这段代码的原理是这样的,我们的clf使用ground_truth 和predictions进行训练
#使用clf.predict(ground_truth) 进行预测以后的结果为[0,0],
#my_custom_loss_func(np.array([0,0], np.array[0,1]) 最后得到log(2) = 0.69314
print(score(clf, ground_truth, predictions))
print(loss(clf, ground_truth, predictions))

  

  我们还可以实现我们自己的评分对象.为了实现更高的自由度,我们可以不使用上述的make_scorer方法,完全定制出来我们自己的评分对象,这需要遵循两点

  • 能够调用(estimater, X, y)参数,estimater是我们的模型,X为数据集, y为X数据集的真实结果。
  • 需要返回一个分数作为评分的结果。该分数能够从某个方面反映出我们模型的好坏。

 

模型调参

1.贪心调参

  采用贪心算法选取局部最优的思想,每次选择一个参数(其他参数为默认或者为已经调至最优的参数)调至最优。代码如下:

## LGB的参数集合:

objective = ['regression', 'regression_l1', 'mape', 'huber', 'fair']

num_leaves = [3,5,10,15,20,40, 55]
max_depth = [3,5,10,15,20,40, 55]
bagging_fraction = []
feature_fraction = []
drop_rate = []

# 贪心调参  每一轮针对某个特征找到最优的值,

#  key-参数名字 value为对应的参数值
best_obj = dict()
for obj in objective:
    model = LGBMRegressor(objective=obj)
    score = np.mean(cross_val_score(model, X=train_X, y=train_y_ln, verbose=0, cv = 5, scoring=make_scorer(mean_absolute_error)))
    best_obj[obj] = score
    
best_leaves = dict()
for leaves in num_leaves:
    model = LGBMRegressor(objective=min(best_obj.items(), key=lambda x:x[1])[0], num_leaves=leaves)
    score = np.mean(cross_val_score(model, X=train_X, y=train_y_ln, verbose=0, cv = 5, scoring=make_scorer(mean_absolute_error)))
    best_leaves[leaves] = score
    
best_depth = dict()
for depth in max_depth:
    model = LGBMRegressor(objective=min(best_obj.items(), key=lambda x:x[1])[0],
                          num_leaves=min(best_leaves.items(), key=lambda x:x[1])[0],
                          max_depth=depth)
    score = np.mean(cross_val_score(model, X=train_X, y=train_y_ln, verbose=0, cv = 5, scoring=make_scorer(mean_absolute_error)))
    best_depth[depth] = score

sns.lineplot(x=['0_initial','1_turning_obj','2_turning_leaves','3_turning_depth'], y=[0.143 ,min(best_obj.values()), min(best_leaves.values()), min(best_depth.values())])

 

2.网格调参(Grid Search)

  Sklearn-GridSearchCV:一种调参的方法,当你算法模型效果不是很好时,可以通过该方法来调整参数,通过循环遍历,尝试每一种参数组合,返回最好的得分值的参数组合。由于遍历的组合较多,在算力条件一般的情况下,适用于小数据集。代码如下:

# grid search 网格搜索调参 遍历所有给定的值进行调参  适用于小数据集
# 原来的数据集分割为训练集和测试集之后,其中测试集起到的作用有两个,一个是用来调整参数,一个是用来评价模型的好坏,这样会导致评分值会比实际效果要好。(因为我们将测试集送到了模型里面去测试模型的好坏,而我们目的是要将训练模型应用在没使用过的数据上。)
from sklearn.model_selection import GridSearchCV 
parameters = {'objective': objective , 'num_leaves': num_leaves, 'max_depth': max_depth}
model = LGBMRegressor()
# 交叉验证~
clf = GridSearchCV(model, parameters, cv=5)
clf = clf.fit(train_X, train_y)
clf.best_params_

 

 

3.贝叶斯调参:

  在搜索空间过大的时候,网格搜索的速度实在不太能让人接受,贝叶斯调参相对于网格搜索的优势如下

  • 贝叶斯调参采用高斯过程,会考虑到之前的参数信息,不断地更新先验;网格搜索则不会考虑先验信息。
  • 贝叶斯调参迭代次数少,速度快;网格搜索会遍历所有的可能的参数组合,所以速度慢,参数多时易导致维度爆炸
  • 贝叶斯调参针对非凸问题依然稳健;网格搜索针对非凸问题易得到局部最优。
  纠结原理的话可以看一看这篇文章(https://www.cnblogs.com/marsggbo/p/9866764.html). 核心的想法是利用已有的先验知识去找到是目标函数达到全局最优的参数.代码如下:
  
# 贝叶斯调参

from bayes_opt import BayesianOptimization
from sklearn.metrics import mean_absolute_error,  make_scorer

#  用 make_scorer封装metrics中的评分函数供scoring使用
def rf_cv(num_leaves, max_depth, subsample, min_child_samples):
    val = cross_val_score(
        LGBMRegressor(objective = 'regression_l1',
            num_leaves=int(num_leaves),
            max_depth=int(max_depth),
            subsample = subsample,
            min_child_samples = int(min_child_samples)
        ),
        X=train_X, y=train_y_ln, verbose=0, cv = 5, scoring=make_scorer(mean_absolute_error)
    ).mean()
    return 1 - val
# 核心三个部分 目标函数的设计:rf_cv,域空间的设定(参数范围),最优化search

rf_bo = BayesianOptimization(
    rf_cv,
    {
    'num_leaves': (2, 100),
    'max_depth': (2, 100),
    'subsample': (0.1, 1),
    'min_child_samples' : (2, 100)
    }
)

rf_bo.maximize()

# 输出最优参数组合
print(rf_bo.max)

https://github.com/fmfn/BayesianOptimization 有贝叶斯调参代码更加详细的一个介绍

 

 

 

 

附录:

常用模型的链接一个记录回头整理一下:

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!