机器学习 | 特征工程- 超参数调优方法整理

特征工程是机器学习当中很重要的部分,可以帮助我们设计、创建新特征,以便模型从中提取重要相关性。本文将记录并持续更新相关特征工程的工具包介绍,包括自动模型选择和超参数调优等各方面。

· Featuretools

Featuretools 是一个开源的Python 库,用于自动化特征工程。自动特征工程能够缩减时间成本,构建更优秀的预测模型,生成更有意义的特征,还能防止数据泄漏(data leakage)。
目标任务:从数据表格集中获取所有的数据信息并整合到一张表中,再创建特征。
解决方案:采用深度特征合成方法(DFS),通过调用一个函数来构建数千个特征。
# Deep feature synthesis
feature_matrix, features = ft.dfs(entityset=es, 
                                  target_entity='clients',
                                  agg_primitives = agg_primitives,
                                  trans_primitives = trans_primitives)
其中ft 代表导入的 featuretools 库。
经测试,自动生成特征在数量和效率上均比手动特征工程性能提高很多。
 
此外,Featuretools还有以下优势:
· 构建特征时自动地考虑时间
· 可调用RF模型自动获取特征重要性

详细内容请见:

· 随机森林超参数优化- RandomSearch和GridSearch

rf中的超参数主要为森林中的决策树的数量(n_estimators) 以及每个树在分割节点时考虑的特征的数量(max_features)。 并且超参数优化的标准程序是通过交叉验证来解决过度拟合问题。

1)Random Search with Cross Validation

通常,对于最佳超参数的范围比较模糊,因此缩小搜索范围的最佳方法是为每个超参数评估各种值。使用Scikit-Learn的RandomizedSearchCV方法,我们可以定义超参数范围的网格,并从网格中随机抽样,使用每个值组合执行K-Fold CV。
Step 1: 执行前,先获取目前模型的参数
rf = RandomForestRegressor(random_state = 42)

from pprint import pprint
# Look at parameters used by our current forest
print('Parameters currently in use:\n')
pprint(rf.get_params())

Step 2:为了使用RandomizedSearchCV,我们首先需要创建一个参数网格在拟合过程中进行采样:

from sklearn.model_selection import RandomizedSearchCV
# Number of trees in random forest
n_estimators = [int(x) for x in np.linspace(start = 200, stop = 2000, num = 10)]
# Number of features to consider at every split
max_features = ['auto', 'sqrt']
# Maximum number of levels in tree
max_depth = [int(x) for x in np.linspace(10, 110, num = 11)]
max_depth.append(None)
# Minimum number of samples required to split a node
min_samples_split = [2, 5, 10]
# Minimum number of samples required at each leaf node
min_samples_leaf = [1, 2, 4]
# Method of selecting samples for training each tree
bootstrap = [True, False]
# Create the random grid
random_grid = {'n_estimators': n_estimators,
'max_features': max_features,
'max_depth': max_depth,
'min_samples_split': min_samples_split,
'min_samples_leaf': min_samples_leaf,
'bootstrap': bootstrap}
pprint(random_grid)

Step 3:训练

# 使用随机网格搜索最佳超参数
# 首先创建要调优的基本模型
rf = RandomForestRegressor()
# 随机搜索参数,使用3倍交叉验证 
# 采用100种不同的组合进行搜索,并使用所有可用的核心
rf_random = RandomizedSearchCV(estimator = rf, param_distributions = random_grid, n_iter = 100, cv = 3, verbose=2, random_state=42, n_jobs = -1)
# Fit模型
rf_random.fit(train_features, train_labels)

Step 4:得到最佳参数

rf_random.best_params_

Step 5:将优化后的参数进行训练和比较验证。

2) GridSearch

随机搜索允许我们缩小每个超参数的范围。现在我们知道在哪里集中搜索,我们可以明确指定要尝试的每个设置组合。GridSearchCV可以评估我们定义的所有组合。
Step 1:要使用网格搜索,我们根据随机搜索提供的最佳值创建另一个网格
from sklearn.model_selection import GridSearchCV
# Create the parameter grid based on the results of random search 
param_grid = {
    'bootstrap': [True],
    'max_depth': [80, 90, 100, 110],
    'max_features': [2, 3],
    'min_samples_leaf': [3, 4, 5],
    'min_samples_split': [8, 10, 12],
    'n_estimators': [100, 200, 300, 1000]
}
# Create a based model
rf = RandomForestRegressor()
# Instantiate the grid search model
grid_search = GridSearchCV(estimator = rf, param_grid = param_grid, cv = 3, n_jobs = -1, verbose = 2)

Step 2: Fit模型并重新训练和比较验证

grid_search.fit(train_features, train_labels)
grid_search.best_params_

best_grid = grid_search.best_estimator_
grid_accuracy = evaluate(best_grid, test_features, test_labels)
当性能的小幅下降表明我们已经达到了超参数调整的收益递减。
 
本部分代码请见:

详细内容请见:https://towardsdatascience.com/hyperparameter-tuning-the-random-forest-in-python-using-scikit-learn-28d2aa77dd74

· Hyperopt自动化超参数调优- 贝叶斯优化

网格搜索和随机搜索则对ml模型超参数的优化能取得不错的效果,但是需要大量运行时间去评估搜索空间中并不太可能找到最优点的区域。因此越来越多的的超参数调优过程都是通过自动化的方法完成的,它们旨在使用带有策略的启发式搜索(informed search)在更短的时间内找到最优超参数。
 
贝叶斯优化是一种基于模型的用于寻找函数最小值的方法。近段时间以来,贝叶斯优化开始被用于机器学习超参数调优,结果表明,该方法在测试集上的表现更加优异,并且需要的迭代次数小于随机搜索。
Python 环境下有一些贝叶斯优化程序库,它们目标函数的代理算法有所区别。本部分主要介绍「Hyperopt」库,它使用树形 Parzen 评估器(TPE,https://papers.nips.cc/paper/4443-algorithms-for-hyper-parameter-optimization.pdf)作为搜索算法,其他的 Python 库还包含「Spearmint」(高斯过程代理)和「SMAC」(随即森林回归)。
 
贝叶斯优化问题有四个组成部分:
1)目标函数:我们想要最小化的对象,这里指带超参数的机器学习模型的验证误差
2)域空间:待搜索的超参数值
3)优化算法:构造代理模型和选择接下来要评估的超参数值的方法
4)结果的历史数据:存储下来的目标函数评估结果,包含超参数和验证损失
通过以上四个步骤,我们可以对任意实值函数进行优化(找到最小值)。
 
详解:

1)目标函数

模型训练目的是最小化目标函数,所以输出为需要最小化的实值——交叉验证损失。Hyperopt 将目标函数作为黑盒处理,因为这个库只关心输入和输出是什么。为了找到使损失最小的输入值
伪代码:
def objective(hyperparameters):
    """Returns validation score from hyperparameters"""

    model = Classifier(hyperparameters)
    validation_loss = cross_validation(model, training_data)    
    return validation_loss
实际GBM完整的目标函数
import lightgbm as lgb
from hyperopt import STATUS_OK

N_FOLDS = 10

# Create the dataset
train_set = lgb.Dataset(train_features, train_labels)

def objective(params, n_folds = N_FOLDS):
    """Objective function for Gradient Boosting Machine Hyperparameter Tuning"""

    # Perform n_fold cross validation with hyperparameters
    # Use early stopping and evalute based on ROC AUC
    cv_results = lgb.cv(params, train_set, nfold = n_folds, num_boost_round = 10000, early_stopping_rounds = 100, metrics = 'auc', seed = 50)
#此部分为核心代码,

    # Extract the best score
    best_score = max(cv_results['auc-mean'])

    # Loss must be minimized
    loss = 1 - best_score

    # Dictionary with information for evaluation
    return {'loss': loss, 'params': params, 'status': STATUS_OK}

2)域空间

贝叶斯优化中,域空间对每个超参数来说是一个概率分布而不是离散的值。因为很难确定不同数据集之间的最佳模型设定区间,此处主要采用贝叶斯算法进行推理。
此外,模型中有些参数是不需要调优的。以GBM为例,除了n_estimator之外,还有10个左右的参数需要调整。因此我们采用不同的分布来定义每个参数的域空间
from hyperopt import hp
# Define the search space
space = {
    'class_weight': hp.choice('class_weight', [None, 'balanced']),
    'boosting_type': hp.choice('boosting_type', 
                               [{'boosting_type': 'gbdt', 
                                    'subsample': hp.uniform('gdbt_subsample', 0.5, 1)}, 
                                {'boosting_type': 'dart', 
                                     'subsample': hp.uniform('dart_subsample', 0.5, 1)},
                                {'boosting_type': 'goss'}]),
    'num_leaves': hp.quniform('num_leaves', 30, 150, 1),
    'learning_rate': hp.loguniform('learning_rate', np.log(0.01), np.log(0.2)),
    'subsample_for_bin': hp.quniform('subsample_for_bin', 20000, 300000, 20000),
    'min_child_samples': hp.quniform('min_child_samples', 20, 500, 5),
    'reg_alpha': hp.uniform('reg_alpha', 0.0, 1.0),
    'reg_lambda': hp.uniform('reg_lambda', 0.0, 1.0),
    'colsample_bytree': hp.uniform('colsample_by_tree', 0.6, 1.0)
}
不同分布名称含义:
 choice:类别变量
   quniform:离散均匀分布(在整数空间上均匀分布)
   uniform:连续均匀分布(在浮点数空间上均匀分布)
   loguniform:连续对数均匀分布(在浮点数空间中的对数尺度上均匀分布)
 
定义与空间后,可以选择一个样本来查看典型样本形式
# Sample from the full space
example = sample(space)

# Dictionary get method with default
subsample = example['boosting_type'].get('subsample', 1.0)

# Assign top-level keys
example['boosting_type'] = example['boosting_type']['boosting_type']
example['subsample'] = subsample

example

3)优化算法

尽管从概念上来说,这是贝叶斯优化最难的一部分,但在 Hyperopt 中创建优化算法只需一行代码。使用树形 Parzen 评估器(Tree Parzen Estimation,以下简称 TPE)的代码如下:
from hyperopt import tpe
# Algorithm
tpe_algorithm = tpe.suggest

4)结果历史数据

想知道背后的发展进程,可以使用「Trials」对象,它将存储基本的训练信息,还可以使用目标函数返回的字典(包含损失「loss」和参数「params」)
from hyperopt import Trials
# Trials object to track progress
bayes_trials = Trials()
或者通过csv记录迭代过程

5)优化算法

from hyperopt import fmin
MAX_EVALS = 500

# Optimize
best = fmin(fn = objective, space = space, algo = tpe.suggest, 
            max_evals = MAX_EVALS, trials = bayes_trials)
此部分代码请见:
 
详细内容参考:
 
持续更新...

 

posted @ 2018-09-27 20:04  eo_will  阅读(7689)  评论(0编辑  收藏  举报