机器学习(七)——LightGBM算法

LightGBM算法

  • 1 算法原理
  • 2 LightGBM重要参数
    • 2.1 基本参数调整
    • 2.2 针对训练速度的参数调整
    • 2.3 针对准确率的参数调整
    • 2.4 针对过拟合的参数调整
  • 3 代码实践
  • 4 参考

主要为自己学习的笔记,如有错误请大家勘正

1 算法原理

LightGBM也是像XGBoost一样,是一类集成算法,他跟XGBoost总体来说是一样的,同样使用了CART回归树,算法本质上与Xgboost没有出入,只是在XGBoost的基础上进行了优化(XGBoost算法参考)。它增加了一些新特性:

  • 优化速度和内存使用
    1)基于直方图算法进行优化,使数据存储更加方便、运算更快、鲁棒性强、模型更加稳定等;
    2)降低计算每个分割增益的成本,提出了单边梯度采样算法,排除大部分小梯度的样本,仅用剩下的样本计算信息增益,它是一种在减少数据量和保证精度上平衡的算法;
    3)减少内存使用;
    4)减少并行学习的计算成本。
  • 精度优化
    1)使用以叶子数为导向(带深度限制的 Leaf-wise 算法)的决策树建立算法而不是大多数GBDT工具使用的决策树深度导向(按层生长 (level-wise) )的生长策略,可以降低误差,得到更好的精度;
    2)分类特征的编码方式的优化,提出了互斥特征捆绑算法,高维度的数据往往是稀疏的,这种稀疏性启发我们设计一种无损的方法来减少特征的维度。通常被捆绑的特征都是互斥的(即特征不会同时为非零值,像one-hot),这样两个特征捆绑起来就不会丢失信息;
    3)通信网络的优化;
    4)并行学习的优化;
    4)GPU支持。
  • 稀疏优化
    1)用离散的bin替换连续的值。如果#bins较小,则可以使用较小的数据类型(例如uint8_t)来存储训练数据 ;
    2)无需存储其他信息即可对特征数值进行预排序 。

LightGBM的优点:
  1)更快的训练效率
  2)低内存使用
  3)更高的准确率
  4)支持并行化学习
  5)可以处理大规模数据
  
LightGBM的缺点:
  1)相对于深度学习模型无法对时空位置建模,不能很好地捕获图像、语音、文本等高维数据
  2)在拥有海量训练数据,并能找到合适的深度学习模型时,深度学习的精度可以遥遥领先LightGBM

2 LightGBM重要参数

2.1 基本参数调整

  1. num_leaves(num_leaf,max_leaves,max_leaf) :默认=31,一棵树上的最大叶子数。这是控制树模型复杂度的主要参数,一般令num_leaves小于(2的max_depth次方),以防止过拟合。
  2. min_data_in_leaf: 默认=20,一个叶子上数据的最小数量. 可以用来处理过拟合。它的值取决于训练数据的样本个树和 num_leaves参数.。将其设置的较大可以避免生成一个过深的树, 但有可能导致欠拟合。
  3. max_depth:限制树模型的最大深度. 这可以在 #data 小的情况下防止过拟合. 树仍然可以通过 leaf-wise 生长。depth 的概念在 leaf-wise 树中并没有多大作用, 因为并不存在一个从 leaves 到 depth 的合理映射。

2.2 针对训练速度的参数调整

  1. bagging_fraction(sub_row, subsample):默认=1,不进行重采样的情况下随机选择部分数据。可用来设置使用特征的子抽样。
  2. bagging_freq(subsample_freq):bagging 的频率, 0 意味着禁用 bagging. k 意味着每 k 次迭代执行bagging
  3. 选择较小的 max_bin 参数
  4. 使用 save_binary 在未来的学习过程对数据加载进行加速。

2.3 针对准确率的参数调整

  1. 设置较大的 max_bin (学习速度可能变慢)
  2. 设置较小的 learning_rate 和较大的 num_iterations
  3. 设置较大的 num_leaves (可能导致过拟合)
  4. 设置较大的训练集
  5. boosting :默认gbdt,设置提升类型。可以尝试使用dart:多个加性回归树的DROPOUT方法。

2.4 针对过拟合的参数调整

  1. 设置较小的 max_bin
  2. 设置较小的 num_leaves
  3. min_data_in_leaf: 默认=20,一个叶子上数据的最小数量. 可以用来处理过拟合
  4. min_sum_hessian_in_leaf(min_sum_hessian_per_leaf, min_sum_hessian, min_hessian):默认=1e-3,一个叶子上的最小 hessian 和. 类似于 min_data_in_leaf, 可以用来处理过拟合
  5. 设置 bagging_fraction 和 bagging_freq 来使用 bagging
  6. 设置 feature_fraction 来使用特征子抽样
  7. 使用更大的数据集以训练
  8. 使用正则化方法
  9. max_depth:限制树模型的最大深度。

进一步的调参可参考《LightGBM调参笔记》

3 代码实践

代码参考自Kaggle开源项目,我将其搬运过来以学习参考。

import lightgbm as lgb
from sklearn import metrics
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
 
canceData=load_breast_cancer()
X=canceData.data
y=canceData.target
X_train,X_test,y_train,y_test=train_test_split(X,y,random_state=0,test_size=0.2)
 
### 数据转换
print('数据转换')
lgb_train = lgb.Dataset(X_train, y_train, free_raw_data=False)
lgb_eval = lgb.Dataset(X_test, y_test, reference=lgb_train,free_raw_data=False)
 
### 设置初始参数--不含交叉验证参数
print('设置参数')
params = {
     
          'boosting_type': 'gbdt',
          'objective': 'binary',
          'metric': 'auc',
          'nthread':4,
          'learning_rate':0.1
          }
 
### 交叉验证(调参)
print('交叉验证')
max_auc = float('0')
best_params = {
     }
 
# 准确率
print("调参1:提高准确率")
for num_leaves in range(5,100,5):
    for max_depth in range(3,8,1):
        params['num_leaves'] = num_leaves
        params['max_depth'] = max_depth
 
        cv_results = lgb.cv(
                            params,
                            lgb_train,
                            seed=1,
                            nfold=5,
                            metrics=['auc'],
                            early_stopping_rounds=10,
                            verbose_eval=True
                            )
            
        mean_auc = pd.Series(cv_results['auc-mean']).max()
        boost_rounds = pd.Series(cv_results['auc-mean']).idxmax()
            
        if mean_auc >= max_auc:
            max_auc = mean_auc
            best_params['num_leaves'] = num_leaves
            best_params['max_depth'] = max_depth
if 'num_leaves' and 'max_depth' in best_params.keys():          
    params['num_leaves'] = best_params['num_leaves']
    params['max_depth'] = best_params['max_depth']
 
# 过拟合
print("调参2:降低过拟合")
for max_bin in range(5,256,10):
    for min_data_in_leaf in range(1,102,10):
            params['max_bin'] = max_bin
            params['min_data_in_leaf'] = min_data_in_leaf
            
            cv_results = lgb.cv(
                                params,
                                lgb_train,
                                seed=1,
                                nfold=5,
                                metrics=['auc'],
                                early_stopping_rounds=10,
                                verbose_eval=True
                                )
                    
            mean_auc = pd.Series(cv_results['auc-mean']).max()
            boost_rounds = pd.Series(cv_results['auc-mean']).idxmax()
 
            if mean_auc >= max_auc:
                max_auc = mean_auc
                best_params['max_bin']= max_bin
                best_params['min_data_in_leaf'] = min_data_in_leaf
if 'max_bin' and 'min_data_in_leaf' in best_params.keys():
    params['min_data_in_leaf'] = best_params['min_data_in_leaf']
    params['max_bin'] = best_params['max_bin']
 
print("调参3:降低过拟合")
for feature_fraction in [0.6,0.7,0.8,0.9,1.0]:
    for bagging_fraction in [0.6,0.7,0.8,0.9,1.0]:
        for bagging_freq in range(0,50,5):
            params['feature_fraction'] = feature_fraction
            params['bagging_fraction'] = bagging_fraction
            params['bagging_freq'] = bagging_freq
            
            cv_results = lgb.cv(
                                params,
                                lgb_train,
                                seed=1,
                                nfold=5,
                                metrics=['auc'],
                                early_stopping_rounds=10,
                                verbose_eval=True
                                )
                    
            mean_auc = pd.Series(cv_results['auc-mean']).max()
            boost_rounds = pd.Series(cv_results['auc-mean']).idxmax()
 
            if mean_auc >= max_auc:
                max_auc=mean_auc
                best_params['feature_fraction'] = feature_fraction
                best_params['bagging_fraction'] = bagging_fraction
                best_params['bagging_freq'] = bagging_freq
 
if 'feature_fraction' and 'bagging_fraction' and 'bagging_freq' in best_params.keys():
    params['feature_fraction'] = best_params['feature_fraction']
    params['bagging_fraction'] = best_params['bagging_fraction']
    params['bagging_freq'] = best_params['bagging_freq']
 
 
print("调参4:降低过拟合")
for lambda_l1 in [1e-5,1e-3,1e-1,0.0,0.1,0.3,0.5,0.7,0.9,1.0]:
    for lambda_l2 in [1e-5,1e-3,1e-1,0.0,0.1,0.4,0.6,0.7,0.9,1.0]:
        params['lambda_l1'] = lambda_l1
        params['lambda_l2'] = lambda_l2
        cv_results = lgb.cv(
                            params,
                            lgb_train,
                            seed=1,
                            nfold=5,
                            metrics=['auc'],
                            early_stopping_rounds=10,
                            verbose_eval=True
                            )
                
        mean_auc = pd.Series(cv_results['auc-mean']).max()
        boost_rounds = pd.Series(cv_results['auc-mean']).idxmax()
 
        if mean_auc >= max_auc:
            max_auc=mean_auc
            best_params['lambda_l1'] = lambda_l1
            best_params['lambda_l2'] = lambda_l2
if 'lambda_l1' and 'lambda_l2' in best_params.keys():
    params['lambda_l1'] = best_params['lambda_l1']
    params['lambda_l2'] = best_params['lambda_l2']
 
print("调参5:降低过拟合2")
for min_split_gain in [0.0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0]:
    params['min_split_gain'] = min_split_gain
    
    cv_results = lgb.cv(
                        params,
                        lgb_train,
                        seed=1,
                        nfold=5,
                        metrics=['auc'],
                        early_stopping_rounds=10,
                        verbose_eval=True
                        )
            
    mean_auc = pd.Series(cv_results['auc-mean']).max()
    boost_rounds = pd.Series(cv_results['auc-mean']).idxmax()
 
    if mean_auc >= max_auc:
        max_auc=mean_auc
        
        best_params['min_split_gain'] = min_split_gain
if 'min_split_gain' in best_params.keys():
    params['min_split_gain'] = best_params['min_split_gain']
 
print(best_params)

4 参考

  1. 《LightGBM调参笔记》
  2. Kaggle开源项目
  3. 《基于LightGBM的分类预测》

你可能感兴趣的:(机器学习,决策树,机器学习)