lightGBM参数解析及其参数调优

lightGBM官网中文文档!
LightGBM是打比赛如CCF大数据与天池大数据竞赛的经常被使用的模型

一:模型简介

lightgbm是xgboost的加强升级版.
LightGBM=XGBoost+Histogram+GOSS+EFB
其中,Histogram算法是直方图算法,作用:减少后选分类点的算法
GOSS是基于梯度的单边采样算法,作用减少样本数量
EFB算法是互斥特征捆绑算法,作用是减少特征数量
基于以上三个算法,LightGBM生产一片叶子需要的复杂度大大降低了,从而极大节约了计算时间。同时Histogram算法还将特征浮点数转换成0~255位的证书进行存储,从而集打节约了内存存储空间。
(1)基于leaf-wise的决策树的生长策略
对于大部分决策树算法使用的试level-wise生长策略,即同一层的叶子节点每次都一起分裂,但是实际上一些叶子节点的分裂增益较低,这样分裂会增加不小的开销。lightGBM使用的leaf-wise策略,每次在当前的叶子节点中,找出分裂增益最大的叶子节点进行分裂,而不是所有节点都进行分裂,这样可以提高精度。
(2)直方图算法
简答来说,就是先对特征值进行装箱处理,把连续的浮点特征值离散化成k个整数,形成一个一个的箱体,同时构造一个宽度为k的直方图,在遍历数据的时候,根据离散化后的值作为索引在直方图中累积统计值(因此这里试频数直方图),当遍历一次数据后,直方图累积了需要的统计量,然后根据直方图的离散值,遍历寻找最优的分割点。
对于连续的特征来说,装箱处理时特征工程中的离散化:如[0,10)区间的值都可以幅值为0,[10,20)区间的值都可以赋值为1等,这样就可以把众多的数据值划分为到有限的分箱中,在LightGBM中默认分箱数(bins)为256。对于分类的特征来说,则是每一种取值放入一个分箱中,且当取值的个数大于最大分箱数是,会忽略哪些很少出现的分类值。
(3)GOSS算法
单边梯度采样GOSS算法是通过对样本采样的方法来减少计算目标函数增益时候的复杂度。
在GOOS算法中,梯度更大的样本点在计算信息增益的时候占有更重要的作用,当我们对样本进行下采样的时候,保留这些体大较大的样本点,并随机取掉梯度小的样本。
首先把样本按照梯度排序,选出梯度最大的a%个样本,然后在剩下的小梯度样本数据中随机选取b%个样本,在计算信息增益的时候,将选出来的b%小梯度样本的信息增益扩大1-a/b的倍数。
(如果一个样本的梯度很小,证明该样本拟合较好,所以不需要再计算新的增益了。但是如果全部舍去梯度小的样本,那么精度就会下降,所以随机去掉梯度小的样本。)
(4)EFB算法:
互斥特征绑定算法,即将互斥特征绑定在一起以减少特征维度;EFB算法可以有效减少用于构建直方图的特征数量,从而降低计算复杂度,尤其是特征中包含大量稀疏特征的时候。LightGBM可以直接将每个类别取值和一个bin关联,从而自动处理它们,而无需预处理成onehot编码。
(5)并行学习
LightGBM支持特征并行和数据并行两种。传统的特征并行主要思想是在并行化决策树中寻找最佳切分点,在数据量大时难以加速,同时需要切分结果进行通信整合。LightGBM则是使用分散规则,它将直方图合并的任务分给不同的机器,降低通信和计算的开销,并利用直方图加速训练,进一步减少开销。
(6)支持类别特征
传统的机器学习一般不能支持直接输入类别特征,需要先转化成多维的0-1特征,这样无论在空间上还是时间上效率都不高。LightGBM通过更改决策树算法的决策规则,直接原生支持类别特征,不需要转化,提高了近8倍的速度。
(7)支持并行学习
LightGBM原生支持并行学习,目前支持特征并行(Featrue Parallelization)和数据并行(Data Parallelization)两种,还有一种是基于投票的数据并行(Voting Parallelization)
特征并行的主要思想是在不同机器、在不同的特征集合上分别寻找最优的分割点,然后在机器间同步最优的分割点。
数据并行则是让不同的机器先在本地构造直方图,然后进行全局的合并,最后在合并的直方图上面寻找最优分割点。
LightGBM针对这两种并行方法都做了优化。
(8)支持增量学习
每次读取文件的一部分,用于训练模型,并保存模型的训练结果;然后读取文件的另一部分,再对模型进行更新训练;迭代读取全部数据完毕,最终完成整个文件数据的训练过程。

二、代码举例

import json
import lightgbm as lgb
import pandas as pd
from sklearn.metrics import mean_squared_error
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.datasets import  make_classification
 
iris = load_iris()   # 载入鸢尾花数据集
data=iris.data
target = iris.target
X_train,X_test,y_train,y_test =train_test_split(data,target,test_size=0.2)
 
# 创建成lgb特征的数据集格式
lgb_train = lgb.Dataset(X_train, y_train) # 将数据保存到LightGBM二进制文件将使加载更快
lgb_eval = lgb.Dataset(X_test, y_test, reference=lgb_train)  # 创建验证数据
 
# 将参数写成字典下形式
params = {
    'task': 'train',
    'boosting_type': 'gbdt',  # 设置提升类型
    'objective': 'regression', # 目标函数
    'metric': {'l2', 'auc'},  # 评估函数
    'num_leaves': 31,   # 叶子节点数
    'learning_rate': 0.05,  # 学习速率
    'feature_fraction': 0.9, # 建树的特征选择比例
    'bagging_fraction': 0.8, # 建树的样本采样比例
    'bagging_freq': 5,  # k 意味着每 k 次迭代执行bagging
    'verbose': 1 # <0 显示致命的, =0 显示错误 (警告), >0 显示信息
}
 
print('Start training...')
# 训练 cv and train
gbm = lgb.train(params,lgb_train,num_boost_round=20,valid_sets=lgb_eval,early_stopping_rounds=5) # 训练数据需要参数列表和数据集
 
print('Save model...') 
 
gbm.save_model('model.txt')   # 训练后保存模型到文件
 
print('Start predicting...')
# 预测数据集
y_pred = gbm.predict(X_test, num_iteration=gbm.best_iteration) #如果在训练期间启用了早期停止,可以通过best_iteration方式从最佳迭代中获得预测
# 评估模型
print('The rmse of prediction is:', mean_squared_error(y_test, y_pred) ** 0.5) # 计算真实值和预测值之间的均方根误差

三、参数解析:

1、用于控制模型学习过程的参数:

(每个参数有可能有较多个别名(alias))
max_depth, default=-1, type=int
限制树模型的最大深度. 这可以在 #data 小的情况下防止过拟合. 树仍然可以通过 leaf-wise 生长。< 0 意味着没有限制。
min_data_in_leaf
default=20, type=int, alias=min_data_per_leaf, min_data, min_child_samples
一个叶子上数据的最小数量. 可以用来处理过拟合.
min_sum_hessian_in_leaf
default=1e-3, type=double, alias=min_sum_hessian_per_leaf,
min_sum_hessian, min_hessian, min_child_weight
一个叶子上的最小hessian和。类似于 min_data_in_leaf, 可以用来处理过拟合.
feature_fraction:
default=1.0, type=double, 0.0 < feature_fraction < 1.0,
alias=sub_feature, colsample_bytree
如果 feature_fraction 小于 1.0, LightGBM 将会在每次迭代中随机选择部分特征. 例如, 如果设置为 0.8, 将会在每棵树训练之前选择 80% 的特征。可以用来加速训练,可以用来处理过拟合。
feature_fraction_seed:
default=2, type=int.
feature_fraction 的随机数种子
bagging_fraction:
default=1.0, type=double, 0.0 < bagging_fraction < 1.0, alias=sub_row, subsample
类似于 feature_fraction, 但是它将在不进行重采样的情况下随机选择部分数据。可以用来加速训练,可以用来处理过拟合。
Note: 为了启用 bagging, bagging_freq 应该设置为非零值
bagging_freq:
default=0, type=int, alias=subsample_freq
bagging 的频率, 0 意味着禁用 bagging. k 意味着每 k 次迭代执行bagging
Note: 为了启用 bagging, bagging_fraction 设置适当
bagging_seed :
default=3, type=int, alias=bagging_fraction_seed
bagging 随机数种子
early_stopping_round:
default=0, type=int, alias=early_stopping_rounds, early_stopping
如果一个验证集的度量在 early_stopping_round 循环中没有提升, 将停止训练
lambda_l1 :
L1 正则,default=0, type=double, alias=reg_alpha
lambda_l2:
L2 正则,default=0, type=double, alias=reg_lambda
min_split_gain:
default=0, type=double, alias=min_gain_to_split
执行切分的最小增益
drop_rate:
default=0.1, type=double
仅仅在 dart 时使用
skip_drop:
default=0.5, type=double
仅仅在 dart 时使用, 跳过 drop 的概率
max_drop:
default=50, type=int
仅仅在 dart 时使用, 一次迭代中删除树的最大数量
<=0 意味着没有限制
uniform_drop:
default=false, type=bool
仅仅在 dart 时使用, 如果想要均匀的删除, 将它设置为 true
xgboost_dart_mode:
default=false, type=bool
仅仅在 dart 时使用, 如果想要使用 xgboost dart 模式, 将它设置为 true
drop_seed:
default=4, type=int
仅仅在 dart 时使用, 选择 dropping models 的随机数种子
top_rate:
default=0.2, type=double
仅仅在 goss 时使用, 大梯度数据的保留比例
other_rate:
default=0.1, type=int
仅仅在 goss 时使用, 小梯度数据的保留比例
min_data_per_group:
default=100, type=int
每个分类组的最小数据量
max_cat_threshold:
default=32, type=int
用于分类特征,限制分类特征的最大阈值
cat_smooth:
default=10, type=double
用于分类特征,这可以降低噪声在分类特征中的影响, 尤其是对数据很少的类别
cat_l2:
default=10, type=double
分类切分中的 L2 正则
max_cat_to_onehot:
default=4, type=int
当一个特征的类别数小于或等于 max_cat_to_onehot 时, one-vs-other 切分算法将会被使用
top_k:
default=20, type=int, alias=topk
被使用在 Voting parallel 中,将它设置为更大的值可以获得更精确的结果, 但会减慢训练速度

2、IO参数:

max_bin,:
default=255, type=int
工具箱的最大数特征值决定了容量,工具箱的最小数特征值可能会降低训练的准确性, 但是可能会增加一些一般的影响(处理过度学习)。LightGBM 将根据 max_bin 自动压缩内存。例如,如果maxbin=255, 那么 LightGBM 将使用uint8t的特性值。
min_data_in_bin
default=3, type=int
单个数据箱的最小数, 使用此方法避免 one-data-one-bin(可能会过度学习)
data_random_seed
default=1, type=int
并行学习数据分隔中的随机种子 (不包括并行功能)
output_model
default=LightGBM_model.txt, type=string, alias=model_output, model_out
培训中输出的模型文件名
input_model
default="", type=string, alias=model_input, model_in
输入模型的文件名
对于 prediction 任务, 该模型将用于预测数据
对于 train 任务, 培训将从该模型继续
output_result
default=LightGBM_predict_result.txt, type=string, alias=predict_result, prediction_result
prediction 任务的预测结果文件名
model_format
default=text, type=multi-enum, 可选项=text, proto
保存和加载模型的格式
text, 使用文本字符串
proto, 使用协议缓冲二进制格式
您可以通过使用逗号来进行多种格式的保存, 例如 text,proto. 在这种情况下, model_format 将作为后缀添加
output_model
Note: 不支持多种格式的加载
Note: 要使用这个参数, 您需要使用 build 版本
pre_partition
default=false, type=bool, alias=is_pre_partition
用于并行学习(不包括功能并行)
true 如果训练数据 pre-partitioned, 不同的机器使用不同的分区
is_sparse
default=true, type=bool, alias=is_enable_sparse, enable_sparse
用于 enable/disable 稀疏优化. 设置 false 就禁用稀疏优化
two_round
default=false, type=bool, alias=two_round_loading, use_two_round_loading
默认情况下, LightGBM 将把数据文件映射到内存, 并从内存加载特性。 这将提供更快的数据加载速度。但当数据文件很大时, 内存可能会耗尽,如果数据文件太大, 不能放在内存中, 就把它设置为 true
save_binary
default=false, type=bool, alias=is_save_binary, is_save_binary_file
如果设置为 true, LightGBM 则将数据集(包括验证数据)保存到二进制文件中。 可以加快数据加载速度。
verbosity
default=1, type=int, alias=verbose
表示是否输出中间信息,小于0 ,仅仅输出致命的, 等于0 ,还会输出错误 (警告)信息, 大于0 ,则还会输出info信息.
header: default=false, type=bool, alias=has_header
如果输入数据有标识头, 则在此处设置 true
label
default="", type=string, alias=label_column
指定标签列
用于索引的数字, e.g. label=0 意味着 column_0 是标签列
为列名添加前缀 name: , e.g. label=name:is_click
weight
default="", type=string, alias=weight_column
列的指定
用于索引的数字, e.g. weight=0 表示 column_0 是权重点
为列名添加前缀 name:, e.g. weight=name:weight
Note: 索引从 0 开始. 当传递 type 为索引时, 它不计算标签列, 例如当标签为 0 时, 权重为列 1, 正确的参数是权重值为 0
query
default="", type=string, alias=query_column, group, group_column
指定 query/group ID 列
用数字做索引, e.g. query=0 意味着 column_0 是这个查询的 Id
为列名添加前缀 name: , e.g. query=name:query_id
Note: 数据应按照 query_id. 索引从 0 开始. 当传递 type 为索引时, 它不计算标签列, 例如当标签为列 0, 查询 id 为列 1 时, 正确的参数是查询 =0
ignore_column
default="", type=string, alias=ignore_feature, blacklist
在培训中指定一些忽略的列
用数字做索引, e.g. ignore_column=0,1,2 意味着 column_0, column_1 和 column_2 将被忽略
为列名添加前缀 name: , e.g. ignore_column=name:c1,c2,c3 意味着 c1, c2 和 c3 将被忽略
Note: 只在从文件直接加载数据的情况下工作
Note: 索引从 0 开始. 它不包括标签栏
categorical_feature
default="", type=string, alias=categorical_column, cat_feature, cat_column
指定分类特征
用数字做索引, e.g. categorical_feature=0,1,2 意味着 column_0, column_1 和 column_2 是分类特征
为列名添加前缀 name:, e.g. categorical_feature=name:c1,c2,c3 意味着 c1, c2 和 c3 是分类特征
Note: 只支持分类与 int type. 索引从 0 开始. 同时它不包括标签栏
Note: 负值的值将被视为 missing values
predict_raw_score
default=false, type=bool, alias=raw_score, is_predict_raw_score
只用于 prediction 任务
设置为 true 只预测原始分数
设置为 false 只预测分数
predict_leaf_index
default=false, type=bool, alias=leaf_index, is_predict_leaf_index
只用于 prediction 任务
设置为 true to predict with leaf index of all trees
predict_contrib
default=false, type=bool, alias=contrib, is_predict_contrib
只用于 prediction 任务
设置为 true 预估 SHAP values, 这代表了每个特征对每个预测的贡献. 生成的特征+1的值, 其中最后一个值是模型输出的预期值, 而不是训练数据
bin_construct_sample_cnt
default=200000, type=int, alias=subsample_for_bin
用来构建直方图的数据的数量
在设置更大的数据时, 会提供更好的培训效果, 但会增加数据加载时间
如果数据非常稀疏, 则将其设置为更大的值
num_iteration_predict
default=-1, type=int
只用于 prediction 任务
用于指定在预测中使用多少经过培训的迭代
<= 0 意味着没有限制
pred_early_stop
default=false, type=bool
如果 true 将使用提前停止来加速预测。可能影响精度
pred_early_stop_freq
default=10, type=int
检查早期early-stopping的频率
pred_early_stop_margin
default=10.0, type=double
t提前early-stopping的边际阈值
use_missing
default=true, type=bool
设置为 false 禁用丢失值的特殊句柄
zero_as_missing
default=false, type=bool
设置为 true 将所有的0都视为缺失的值 (包括 libsvm/sparse 矩阵中未显示的值)
设置为 false 使用 na 代表缺失值
init_score_file
default="", type=string
训练初始分数文件的路径, “” 将使用 train_data_file + .init (如果存在)
valid_init_score_file
default="", type=multi-string
验证初始分数文件的路径, “” 将使用 valid_data_file + .init (如果存在)
通过 , 对multi-validation进行分离

3、目标参数:

sigmoid,:
default=1.0, type=double
sigmoid 函数的参数. 将用于 binary 分类 和 lambdarank
alpha:
default=0.9, type=double
Huber loss 和 Quantile regression 的参数. 将用于 regression 任务
fair_c:
default=1.0, type=double
Fair loss 的参数. 将用于 regression 任务
gaussian_eta, default=1.0, type=double
控制高斯函数的宽度的参数. 将用于 regression_l1 和 huber losses
poisson_max_delta_step:
default=0.7, type=double
Poisson regression 的参数用于维护优化
scale_pos_weight:
default=1.0, type=double
正值的权重 binary 分类 任务
boost_from_average:
default=true, type=bool
只用于 regression 任务
将初始分数调整为更快收敛速度的平均值
is_unbalance:
default=false, type=bool, alias=unbalanced_sets
用于 binary 分类
如果培训数据不平衡 设置为 true
max_position:
default=20, type=int
用于 lambdarank
将在这个 NDCG 位置优化
label_gain:
default=0,1,3,7,15,31,63,…, type=multi-double
用于 lambdarank
有关获得标签. 列如, 如果使用默认标签增益 这个 2 的标签则是 3
使用 , 分隔
num_class:
default=1, type=int, alias=num_classes
只用于 multiclass 分类
reg_sqrt:
default=false, type=bool
只用于 regression
适合sqrt(label) 相反, 预测结果也会自动转换成 pow2(prediction)

4、度量参数:

metric :default={l2 for regression},{binary_logloss for binary classification},{ndcg for lambdarank}
    l1: absolute loss, alias=mean_absolute_error, mae
    l2: square loss, alias=mean_squared_error, mse
    l2_root: root square loss, alias=root_mean_squared_error, rmse
    quantile: Quantile regression
    huber: Huber loss
    fair: Fair loss
    possion Poisson regression
    ndcg: NDCG
    map: MAP
    auc: AUC
    binary_logloss: log loss
    binary_error: 样本: 0 的正确分类, 1 错误分类
    multi_logloss: mulit-class 损失日志分类
    multi_error: error rate for mulit-class 出错率分类
    xentropy: cross-entropy (与可选的线性权重), alias=cross_entropy
    xentlambda: “intensity-weighted” 交叉熵, alias=cross_entropy_lambda
    kldiv: Kullback-Leibler divergence, alias=kullback_leibler
    支持多指标,使用,分隔
metric_freq:
default=1, type=int
频率指标输出
train_metric:
default=false, type=bool, alias=training_metric, is_training_metric
如果你需要输出训练的度量结果则设置 true
ndcg_at:
default=1,2,3,4,5, type=multi-int, alias=ndcg_eval_at, eval_at
NDCG 职位评估, 使用 , 分隔

模型的训练过程参数:

def train(params, train_set, num_boost_round=100,
          valid_sets=None, valid_names=None,
          fobj=None, feval=None, init_model=None,
          feature_name='auto', categorical_feature='auto',
          early_stopping_rounds=None, evals_result=None,
          verbose_eval=True, learning_rates=None,
          keep_training_booster=False, callbacks=None):
"""Perform the training with given parameters.
Parameters
----------
params : dict
    Parameters for training.
train_set : Dataset
    Data to be trained on.
num_boost_round : int, optional (default=100)
    Number of boosting iterations.
valid_sets : list of Datasets or None, optional (default=None)
    List of data to be evaluated on during training.
valid_names : list of strings or None, optional (default=None)
    Names of ``valid_sets``.
fobj : callable or None, optional (default=None)
    Customized objective function.
feval : callable or None, optional (default=None)
    Customized evaluation function.
    Should accept two parameters: preds, train_data,
    and return (eval_name, eval_result, is_higher_better) or list of such tuples.
    For multi-class task, the preds is group by class_id first, then group by row_id.
    If you want to get i-th row preds in j-th class, the access way is preds[j * num_data + i].
    To ignore the default metric corresponding to the used objective,
    set the ``metric`` parameter to the string ``"None"`` in ``params``.
init_model : string, Booster or None, optional (default=None)
    Filename of LightGBM model or Booster instance used for continue training.
feature_name : list of strings or 'auto', optional (default="auto")
    Feature names.
    If 'auto' and data is pandas DataFrame, data columns names are used.
categorical_feature : list of strings or int, or 'auto', optional (default="auto")
    Categorical features.
    If list of int, interpreted as indices.
    If list of strings, interpreted as feature names (need to specify ``feature_name`` as well).
    If 'auto' and data is pandas DataFrame, pandas categorical columns are used.
    All values in categorical features should be less than int32 max value (2147483647).
    Large values could be memory consuming. Consider using consecutive integers starting from zero.
    All negative values in categorical features will be treated as missing values.
early_stopping_rounds : int or None, optional (default=None)
    Activates early stopping. The model will train until the validation score stops improving.
    Validation score needs to improve at least every ``early_stopping_rounds`` round(s)
    to continue training.
    Requires at least one validation data and one metric.
    If there's more than one, will check all of them. But the training data is ignored anyway.
    The index of iteration that has the best performance will be saved in the ``best_iteration`` field
    if early stopping logic is enabled by setting ``early_stopping_rounds``.
evals_result: dict or None, optional (default=None)
    This dictionary used to store all evaluation results of all the items in ``valid_sets``.

    Example
    -------
    With a ``valid_sets`` = [valid_set, train_set],
    ``valid_names`` = ['eval', 'train']
    and a ``params`` = {'metric': 'logloss'}
    returns {'train': {'logloss': ['0.48253', '0.35953', ...]},
    'eval': {'logloss': ['0.480385', '0.357756', ...]}}.

verbose_eval : bool or int, optional (default=True)
    Requires at least one validation data.
    If True, the eval metric on the valid set is printed at each boosting stage.
    If int, the eval metric on the valid set is printed at every ``verbose_eval`` boosting stage.
    The last boosting stage or the boosting stage found by using ``early_stopping_rounds`` is also printed.

    Example
    -------
    With ``verbose_eval`` = 4 and at least one item in ``valid_sets``,
    an evaluation metric is printed every 4 (instead of 1) boosting stages.

learning_rates : list, callable or None, optional (default=None)
    List of learning rates for each boosting round
    or a customized function that calculates ``learning_rate``
    in terms of current number of round (e.g. yields learning rate decay).
keep_training_booster : bool, optional (default=False)
    Whether the returned Booster will be used to keep training.
    If False, the returned value will be converted into _InnerPredictor before returning.
    You can still use _InnerPredictor as ``init_model`` for future continue training.
callbacks : list of callables or None, optional (default=None)
    List of callback functions that are applied at each iteration.
    See Callbacks in Python API for more information.

Returns
-------
booster : Booster
    The trained Booster model.
"""

四:模型属性

lgb_model. train(params, 
				train_set, 
				num_boost_round=100,
          		valid_sets=None,
				valid_names=None,
          		fobj=None, 
				feval=None, 
				init_model=None,
          		feature_name='auto', 
				categorical_feature='auto',
          		early_stopping_rounds=None, 
				evals_result=None,
          		verbose_eval=True, 
				learning_rates=None,
          		keep_training_booster=False, 
				callbacks=None):

含义:使用lgb模型对训练集进行学习训练,

lgb_model.eval_train()

含义:使用训练出来的模型对训练集的估计,返回评估结果的列表
评估的依据为train()训练过程中的参数params中的’metric’进行评估的,如’metric’=
[‘l1’,’l2’,’rmse’]

lgb_model.predict(test_x,  num_iteration=lgb_model.best_iteration)

含义:使用训练好的模型对test_x进行预测。
模型存储:

joblib.dump(lgb_model, 'lgb_model.pkl')

模型加载:

lgb_model = joblib.load('lgb_model.pkl')

五:参数调优

调参方法:
对于基于决策树的模型,调参的方法都是大同小异。一般都需要如下步骤:
1、首先选择较高的学习率,大概0.1附近,这样是为了加快收敛的速度。这对于调参是很有必要的。
2、对决策树基本参数调参
3、正则化参数调参
4、最后降低学习率,这里是为了最后提高准确率
所以,下面的调参例子是基于上述步骤来操作。数据集为一个(4400+, 1000+)的数据集,全是数值特征,metric采用均方根误差。
(同义的参数用/划开,方便查看)

Step1. 学习率learning_rate和估计器boosting/boot/booting_type及其数目
不管怎么样,我们先把学习率先定一个较高的值,这里取 learning_rate = 0.1,其次确定估计器boosting/boost/boosting_type的类型,不过默认都会选gbdt。
为了确定估计器的数目(boosting迭代的次数),也可以说是残差树的数目,参数名为n_estimators/num_iterations/num_round/num_boost_round。我们可以先将该参数设成一个较大的数,然后在cv结果中查看最优的迭代次数,具体如代码。
在这之前,我们必须给其他重要的参数一个初始值。初始值的意义不大,只是为了方便确定其他参数。下面先给定一下初始值,以下参数根据具体项目要求定:

'boosting_type'/'boosting': 'gbdt'
'objective': 'regression'
'metric': 'rmse'
'max_depth': 6     #根据问题来定咯,由于我的数据集不是很大,所以选择了一个适中的值,其实4-10都无所谓。
'num_leaves': 50  #由于lightGBM是leaves_wise生长,官方说法是要小于2^max_depth
'subsample'/'bagging_fraction':0.8           
###  数据采样
'colsample_bytree'/'feature_fraction': 0.8  

特征采样
用LightGBM的cv函数进行演示:

params = {    
  'boosting_type': 'gbdt', 
    'objective': 'regression', 
    'learning_rate': 0.1, 
    'num_leaves': 50, 
	'max_depth': 6,    
	'subsample': 0.8, 
    'colsample_bytree': 0.8, 
    }
data_train = lgb.Dataset(df_train, y_train, silent=True)
#lightgbm.cv官方文档https://lightgbm.readthedocs.io/en/latest/pythonapi/lightgbm.cv.html
cv_results = lgb.cv(
params, 
data_train, 
num_boost_round=1000, 
nfold=5, 
stratified=False, 
shuffle=True, 
metrics='rmse',
early_stopping_rounds=50, 
verbose_eval=50, 
show_stdv=True, 
seed=0)
print('best n_estimators:', len(cv_results['rmse-mean']))
print('best cv score:', cv_results['rmse-mean'][-1])

输出:

best n_estimators: 43
best cv score: 1.3838664241

由于我的数据集不是很大,所以在学习率为0.1时,最优的迭代次数只有43。那么现在,我们就代入(0.1, 43)进入其他参数的tuning。但是还是建议,在硬件条件允许的条件下,学习率还是越小越好。

#lgb.cv()函数的使用方法:
#参数
params  基学习器的参数
train_set 训练集
nfold    n折交叉验证
metrics  评价标准。
num_boost_round 最大迭代次数。
early_stopping_rounds 早停轮数。
verbose_eval  每间隔n个迭代就显示一次进度
stratified 默认True,是否采用分层抽样,建议采用。
shuffle 默认True,是否洗牌,不建议采用。
seed 相当于random_state
param需要填写的参数
objective    树的类型。回归:regression;二分类:binary;多分类:multiclass;排序等。
boosting    有gbdt,rf,dart。
n_jobs
learning_rate
num_leaves
max_depth
subsample
colsample_bytree
返回值
返回值是一个字典,一般用到的方法有:
len(cv_results[‘rmse-mean’]) 确定基学习器的数量。
cv_results[‘rmse-mean’][-1] 确定最后得分
其中rmse由参数metric决定

Step2. max_depth 和 num_leaves
这是提高精确度的最重要的参数。
max_depth :设置树深度,深度越大可能过拟合
num_leaves:因为 LightGBM 使用的是 leaf-wise 的算法,因此在调节树的复杂程度时,使用的是 num_leaves 而不是 max_depth。大致换算关系:num_leaves = 2^(max_depth),但是它的值的设置应该小于 2^(max_depth),否则可能会导致过拟合。
我们也可以同时调节这两个参数,对于这两个参数调优,我们先粗调,再细调:
这里我们引入sklearn里的GridSearchCV()函数进行搜索。这个函数特别耗内存,特别耗时间,特别耗精力:

from sklearn.model_selection import GridSearchCV
### 我们可以创建lgb的sklearn模型,使用上面选择的(学习率,评估器数目)
model_lgb = lgb.LGBMRegressor(objective='regression',num_leaves=50,
                              learning_rate=0.1, n_estimators=43, max_depth=6,
                              metric='rmse', bagging_fraction = 0.8,
                              feature_fraction = 0.8)
params_test1={    
			'max_depth': range(3,8,2),       #3,5,7
			'num_leaves':range(50, 170, 30)   #50,80,110,140
			}   #一共12个组合
gsearch1 = GridSearchCV(estimator=model_lgb, 
						param_grid=params_test1, 
						scoring='neg_mean_squared_error', 
						cv=5, 
						verbose=1, 
						n_jobs=4)
gsearch1.fit(df_train, y_train)
print(gsearch1.grid_scores_, 
gsearch1.best_params_, 
gsearch1.best_score_)

输出结果:

Fitting 5 folds for each of 12 candidates, totalling 60 fits 
[Parallel(n_jobs=4)]: Done  42 tasks      | elapsed:  2.0min
[Parallel(n_jobs=4)]: Done  60 out of  60 | elapsed:  3.1min finished
(
[mean: -1.88629, std: 0.13750, params: {'max_depth': 3, 'num_leaves': 50}, 
 #一个组合中的5折交叉运算得分的均值和方差
mean: -1.88629, std: 0.13750, params: {'max_depth': 3, 'num_leaves': 80},  
......  
mean: -1.86024, std: 0.11364, params: {'max_depth': 7, 'num_leaves': 140}],
 {'max_depth': 7, 'num_leaves': 80}, 
-1.8602436718814157
)

运行了12个参数组合,得到的最优解是在max_depth为7,num_leaves为80的情况下,分数为-1.860
sklearn模型评估里的scoring参数都是采用的higher return values are better than lower return values(较高的返回值优于较低的返回值)。但是,我采用的metric策略采用的是均方误差(rmse),越低越好,所以sklearn就提供了neg_mean_squared_erro参数,也就是返回metric的负数,所以就均方差来说,也就变成负数越大越好了。
至此,我们将我们这步得到的最优解代入第三步。其实,我这里只进行了粗调,如果要得到更好的效果,可以将max_depth在7附近多取几个值,num_leaves在80附近多取几个值。千万不要怕麻烦,虽然这确实很麻烦。

params_test2={
	'max_depth':[6,7,8],
    'num_leaves':[68,74,80,86,92]
}
gsearch2 = GridSearchCV(estimator=model_lgb, 
						param_grid=params_test2, 
						scoring='neg_mean_squared_error', 
						cv=5, verbose=1, n_jobs=4)
						gsearch2.fit(df_train, y_train)
print(gsearch2.grid_scores_, 
gsearch2.best_params_, 
gsearch2.best_score_)

输出:

Fitting 5 folds for each of 15 candidates, totalling 75 fits 
[Parallel(n_jobs=4)]: Done  42 tasks      | elapsed:  2.8min
[Parallel(n_jobs=4)]: Done  75 out of  75 | elapsed:  5.1min finished
(
[mean: -1.87506, std: 0.11369, params: {'max_depth': 6, 'num_leaves': 68},  
mean: -1.87506, std: 0.11369, params: {'max_depth': 6, 'num_leaves': 74},  
......
mean: -1.86733, std: 0.12159, params: {'max_depth': 8, 'num_leaves': 86},  
mean: -1.86665, std: 0.12174, params: {'max_depth': 8, 'num_leaves': 92}],
 {'max_depth': 7, 'num_leaves': 68}, 
-1.8602436718814157
)

可见最大深度7是没问题的,但是看细节的话,发现在最大深度为7的情况下,叶结点的数量对分数并没有影响。
Step3: min_data_in_leaf 和 min_sum_hessian_in_leaf
说到这里,就该降低过拟合了。
min_data_in_leaf 是一个很重要的参数, 也叫min_child_samples,它的值取决于训练数据的样本个数和num_leaves. 将其设置的较大可以避免生成一个过深的树, 但有可能导致欠拟合。
min_sum_hessian_in_leaf:也叫min_child_weight,使一个结点分裂的最小海森值之和(Minimum sum of hessians in one leaf to allow a split. Higher values potentially decrease overfitting)
我们采用跟上面相同的方法进行:

params_test3={
    'min_child_samples': [18, 19, 20, 21, 22],
    'min_child_weight':[0.001, 0.002]
}
model_lgb = lgb.LGBMRegressor(objective='regression',num_leaves=80,
                              learning_rate=0.1, n_estimators=43, max_depth=7, 
                              metric='rmse', bagging_fraction = 0.8, feature_fraction = 0.8)
gsearch3 = GridSearchCV(estimator=model_lgb, 
param_grid=params_test3, 
scoring='neg_mean_squared_error', 
cv=5, verbose=1, n_jobs=4)
gsearch3.fit(df_train, y_train)
print(gsearch3.grid_scores_, 
gsearch3.best_params_, 
gsearch3.best_score_)

输出结果:

Fitting 5 folds for each of 10 candidates, totalling 50 fits
[Parallel(n_jobs=4)]: Done  42 tasks      | elapsed:  2.9min
[Parallel(n_jobs=4)]: Done  50 out of  50 | elapsed:  3.3min finished
(
[mean: -1.88057, std: 0.13948, params: {'min_child_samples': 18, 'min_child_weight': 0.001},  
mean: -1.88057, std: 0.13948, params: {'min_child_samples': 18, 'min_child_weight': 0.002},  
...... 
mean: -1.86750, std: 0.13898, params: {'min_child_samples': 22, 'min_child_weight': 0.001},  
mean: -1.86750, std: 0.13898, params: {'min_child_samples': 22, 'min_child_weight': 0.002}],
{'min_child_samples': 20, 'min_child_weight': 0.001}, 
-1.8602436718814157
)

这是我经过粗调后细调的结果,可以看到,min_data_in_leaf的最优值为20,而min_sum_hessian_in_leaf对最后的值几乎没有影响。且这里调参之后,最后的值没有进行优化,说明之前的默认值即为20,0.001。

Step4: feature_fraction 和 bagging_fraction
这两个参数都是为了降低过拟合的。
feature_fraction参数来进行特征的子抽样。这个参数可以用来防止过拟合及提高训练速度。
bagging_fraction+bagging_freq参数必须同时设置,bagging_fraction相当于subsample样本采样,可以使bagging更快的运行,同时也可以降拟合。bagging_freq默认0,表示bagging的频率,0意味着没有使用bagging,k意味着每k轮迭代进行一次bagging。
不同的参数,同样的方法。

params_test4={
		    'feature_fraction': [0.5, 0.6, 0.7, 0.8, 0.9],
		    'bagging_fraction': [0.6, 0.7, 0.8, 0.9, 1.0]
			}
model_lgb = lgb.LGBMRegressor(objective='regression',
								num_leaves=80,
                             	learning_rate=0.1, 
								n_estimators=43, 
								max_depth=7, 
                             	metric='rmse',
								bagging_freq = 5,  
								min_child_samples=20)
gsearch4 = GridSearchCV(estimator=model_lgb, 
param_grid=params_test4, 
scoring='neg_mean_squared_error', 
cv=5, verbose=1, n_jobs=4)
gsearch4.fit(df_train, y_train)
print(gsearch4.grid_scores_, gsearch4.best_params_, gsearch4.best_score_)

输出结果:

Fitting 5 folds for each of 25 candidates, totalling 125 fits
[Parallel(n_jobs=4)]: Done  42 tasks      | elapsed:  2.6min
[Parallel(n_jobs=4)]: Done 125 out of 125 | elapsed:  7.1min finished
([mean: -1.90447, std: 0.15841, params: {'bagging_fraction': 0.6, 'feature_fraction': 0.5},  
mean: -1.90846, std: 0.13925, params: {'bagging_fraction': 0.6, 'feature_fraction': 0.6},  
......
mean: -1.87266, std: 0.12271, params: {'bagging_fraction': 1.0, 'feature_fraction': 0.9}],
 {'bagging_fraction': 1.0, 'feature_fraction': 0.7}, 
-1.8541224387666373
)

从这里可以看出来,bagging_feaction和feature_fraction的理想值分别是1.0和0.7,一个很重要原因就是,我的样本数量比较小(4000+),但是特征数量很多(1000+)。所以,这里我们取更小的步长,对feature_fraction进行更细致的取值。

params_test5={
    'feature_fraction': [0.62, 0.65, 0.68, 0.7, 0.72, 0.75, 0.78 ]
			}
model_lgb = lgb.LGBMRegressor(objective='regression',	
								num_leaves=80,
                             	learning_rate=0.1, 
								n_estimators=43, 
								max_depth=7, 
                              	metric='rmse',  
								min_child_samples=20)
								gsearch5 = GridSearchCV(estimator=model_lgb, 
								param_grid=params_test5, 
								scoring='neg_mean_squared_error', 
								cv=5, verbose=1, n_jobs=4)
gsearch5.fit(df_train, y_train)
print(gsearch5.grid_scores_, gsearch5.best_params_, gsearch5.best_score_)

输出结果:

Fitting 5 folds for each of 7 candidates, totalling 35 fits
[Parallel(n_jobs=4)]: Done  35 out of  35 | elapsed:  2.3min finished
([mean: -1.86696, std: 0.12658, params: {'feature_fraction': 0.62},  
mean: -1.88337, std: 0.13215, params: {'feature_fraction': 0.65},  
mean: -1.87282, std: 0.13193, params: {'feature_fraction': 0.68},  
mean: -1.85412, std: 0.12698, params: {'feature_fraction': 0.7},  
mean: -1.88235, std: 0.12682, params: {'feature_fraction': 0.72},  
mean: -1.86329, std: 0.12757, params: {'feature_fraction': 0.75},  
mean: -1.87943, std: 0.12107, params: {'feature_fraction': 0.78}],
 {'feature_fraction': 0.7}, 
-1.8541224387666373)

feature_fraction就是0.7了。

Step5: 正则化参数
正则化参数lambda_l1(reg_alpha), lambda_l2(reg_lambda),毫无疑问,是降低过拟合的,两者分别对应l1正则化和l2正则化。我们也来尝试一下使用这两个参数。

params_test6={
    'reg_alpha': [0, 0.001, 0.01, 0.03, 0.08, 0.3, 0.5],
    'reg_lambda': [0, 0.001, 0.01, 0.03, 0.08, 0.3, 0.5]
}
model_lgb = lgb.LGBMRegressor(objective='regression',
								num_leaves=80,
								learning_rate=0.1, 
								n_estimators=43, 
								max_depth=7, 
								metric='rmse',  
								min_child_samples=20, 
								feature_fraction=0.7)
								gsearch6 = GridSearchCV(estimator=model_lgb, 
								param_grid=params_test6, 
								scoring='neg_mean_squared_error', 
								cv=5, verbose=1, n_jobs=4)
gsearch6.fit(df_train, y_train)
print(gsearch6.grid_scores_, gsearch6.best_params_, gsearch6.best_score_)

输出结果:

Fitting 5 folds for each of 49 candidates, totalling 245 fits
[Parallel(n_jobs=4)]: Done  42 tasks      | elapsed:  2.8min
[Parallel(n_jobs=4)]: Done 192 tasks      | elapsed: 10.6min
[Parallel(n_jobs=4)]: Done 245 out of 245 | elapsed: 13.3min finished
([mean: -1.85412, std: 0.12698, params: {'reg_alpha': 0, 'reg_lambda': 0},   
……
mean: -1.88148, std: 0.12622, params: {'reg_alpha': 0.5, 'reg_lambda': 0.5}],
 {'reg_alpha': 0, 'reg_lambda': 0}, 
-1.8541224387666373)

step6: 降低learning_rate
之前使用较高的学习速率是因为可以让收敛更快,但是准确度肯定没有细水长流来的好。最后,我们使用较低的学习速率,以及使用更多的决策树n_estimators来训练数据,代入之前优化好的参数。

params = {
		    'boosting_type': 'gbdt', 
		    'objective': 'regression', 
		    'learning_rate': 0.005, 
		    'num_leaves': 80, 
		    'max_depth': 7,    
		  	'min_data_in_leaf': 20,
		    'subsample': 1, 
		    'colsample_bytree': 0.7, 
    }
data_train = lgb.Dataset(df_train, y_train, silent=True)
cv_results = lgb.cv(params, data_train, 
					num_boost_round=10000, 
					nfold=5, 
					stratified=False, 
					shuffle=True, 
					metrics='rmse',
					early_stopping_rounds=50, 
					verbose_eval=100, 
					show_stdv=True)
print('best n_estimators:', len(cv_results['rmse-mean']))
print('best cv score:', cv_results['rmse-mean'][-1])

lightGBM其他的参数优化方法按照以上方式进行优化即可。

你可能感兴趣的:(python,机器学习,机器学习,python)