Python实例介绍正则化贪心森林算法(附代码)

时间:2022-05-05
本文章向大家介绍Python实例介绍正则化贪心森林算法(附代码),主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
作者:Ankit Chaoudhary

翻译:笪洁琼

校对:梁傅淇

通过本文与大家讨论一个被称为正则化的贪心森林算法。

引言

作为一名参与多个机器学习竞赛数据科学家,我总是在寻找“尚未流行”的算法。我是这样定义这些算法的:它们本身最终不会成为竞赛里的赢家,但是它们会给的预测带来不同。

为什么我对这些算法感兴趣?

关键在于“它们本身”。这些算法能够用在集成模型之中,来获取超过大多数流行的梯度提升算法(XGBoost,LightGBM等等)的额外优势。

这篇文章讨论的是一种被称为正则化的贪心森林(RGF)的算法。它在大量的数据集里的表现都和Boosting算法相当(如果没有优于它们的话)。它们产生更少的预测,并且在与其他树提升模型集成时表现更好。

目录

1. 正则化贪心森林算法vs. 梯度提升

  • 权重优化
  • 树的大小
  • 模型大小

2. 使用Python实现正则化贪心算法

正则化贪心森林算法(RGF) vs. 梯度提升

在Boosting算法之中,每个分类器/回归因子都使用数据进行训练,同时也会将之前分类器和回归因子的成功经验纳入考量。每一次训练之后,都会重新分配权重。被错误分类的数据所占的权重将会上升,用以突出最困难的案例。这样,后续的学习器将会专注于这些数据。

但是,Boosting方法只是把决策树基学习器视为一个黑盒子,并没有利用树结构本身。从某种意义上说,在每次迭代中,Boosting都会对模型进行部分纠正。

相比之下,正则化贪心森林算法(RGF)执行两个步骤:

找出对目前的森林可进行的结构上的一步改造,以使得新森林损失(例如,最小二乘或对数损失)最小化。

调整整个森林的叶子重量,使损失函数最小化。

寻找最优结构变化:

1.为了计算效率,在搜索新的森林时只执行两种操作 :

  • 要分割一个现有的叶节点,
  • 开始生长新树(即:在森林里增加上一个新的树桩)。

2.通过不断地评估所有可能的结构变化下最大的损失减少量,直到所有现有的叶子权重都确定后,搜索结束。

3.搜索整个森林是很昂贵的(通常情况下,这是实际应用的情况)。

因此,搜索被限制在最近创建的“t”棵树中,默认选项是t=1。

让我用一个例子来解释这个问题。

图3显示在与图2相同的阶段,我们可以考虑将一个叶子节点分裂成两个标记为X的节点,或者生出一棵新树T4。


权重优化

参数可以用作指定损失函数和权重优化的间隔。在每增加100个新叶子节点时校正一次权重的效果最好,因此k=100通常被用作正则化贪心森林模型训练时的默认参数。

如果k很大的话,那么跟最后才做一次权重更新是一样的;如果k很小的话(例如k=1的情况下),训练将会非常缓慢。

正则化

对于这个算法来说,对损失函数明确的正则化非常重要,因为它很快就会过拟合。

在森林生长过程和权重优化过程中,可能有不同的L2正则化参数。

正则化有三种方法:

一种是对仅包含叶子的模型的L2正则化,在这种模型中,正则化罚项G(F)是:

另外两种被称为最小惩罚,它们对每棵树的正则化罚项都是这样的形式:

γ > 1对更深的节点(对应于更复杂的函数)进行了更严厉的惩罚。可以通过λ 或γ来对正则化程度进行调整。在森林生长的过程和权重调整的过程中,有可能会使用不同的L2正则化参数。

树的大小

正则化贪心森林算法需要树的大小参数(例如,树的数量,最大深度),在梯度提升决策树中需要。

对于正则化贪心森林算法而言,树的大小是由正则化损失最小化的结果所决定的。我们需要定义的是森林中的最大叶子数和正则化参数(L1和L2)。

模型大小

由于正则化贪心森林算法在模型和森林上执行充分的优化措施,因此和需要低学习率/收缩率以及更多子模型的Boosting算法相比,它可以使用更简单的模型获得良好的结果。

使用Python实现正则化贪心森林算法

最初正则化贪心森林算法来进行二分类和回归是在C++中实现的,由初始研究论文作者Rie Johnson和Tong Zhang完成;而对该算法最广为流行的、支持多分类的封装是在MLWave工作的基础上,由fukatani所完成的。

超参数

我们来谈谈影响模型准确性、训练速度或者同时影响两者的最重要的参数:

max_leaf:当森林中的叶节点数达到此值时,训练将终止。它应该足够大,以便在训练点可以获得一个好的模型,而较小的值可以缩短训练时间。

  • loss:损失函数
  • LS:平方损失(p-y)^ 2/2,
  • Expo:指数损失exp(py)
  • Log:对数损失 log(1 + exp(py))

算法:

RGF:在仅包含树叶的模型上进行L2正则化的正则化贪心森林算法

RGF opt:最小惩罚的正则化贪心森林算法

RGF Sib:最小惩罚、同层级零和约束的正则化贪心森林算法

reg_depth:必须小于1。用于算法为RGF opt和RGF Sib的情况。更大的值意味着对更深的节点执行更严厉的惩罚

l2:用于控制l2正则化的程度。对算法的表现至关重要,而恰当的取值依赖于数据。1、0.1或0.01通常会产生良好表现,体现在指数损失、对数损失上,一些数据需要更小的值,比如1e-10或1e-20。

sl2:在森林生长过程中,覆盖L2规则化参数λ。也就是说,如果设定了具体的参数,那么权重优化的过程就会使用λ,而森林生长的过程则会使用λg;如果省略该参数的话,整个过程中都会使用λ而不会覆盖它。在某些数据上,λ/100表现良好。

test_interval:正则化贪心森林算法在指定的间隔(interval)和训练结束时会对所有树的所有叶子的权重进行优化。

由于这个原因,如果我们保存了250棵树的模型,那么这250棵树只能用于测试250棵树的附加模型。如果我们在获得“k”树的时候停止训练,分配给这“k”棵树的权重将与500棵树的模型前“k”棵树的权重完全不同。

如果test_interval=500,那么每500个节点被添加到森林的时候,都会模拟一次训练结束,这个模型会被测试或存储以备后续测试。

normalize:如果(打开这个参数),训练目标就会被标准化以使得平均数为零。

使用Python装饰器进行训练和评估

让我们尝试使用正则化贪心森林算法来解决Big Mart销售预测问题。数据集可以从此链接下载。在这篇文章中,我已经引入了某些预处理步骤。

import pandas as pd
import numpy as np
#Read files:
train =pd.read_csv("Train_UWu5bXk.csv")
test =pd.read_csv("Test_u94Q5KV.csv")
train['source']='train'
test['source']='test'
data = pd.concat([train,test],ignore_index=True)
#Filter categorical variables
categorical_columns = [x for x indata.dtypes.index if data.dtypes[x]=='object']
#Exclude ID cols and source:
categorical_columns = [x for x in categorical_columnsif x not in ['Item_Identifier','Outlet_Identifier','source']]
#Get the first two characters of ID:
data['Item_Type_Combined'] =data['Item_Identifier'].apply(lambda x: x[0:2])
#Rename them to more intuitive categories:
data['Item_Type_Combined'] =data['Item_Type_Combined'].map({'FD':'Food',
 'NC':'Non-Consumable',
 'DR':'Drinks'})
#Years
data['Outlet_Years'] = 2013 -data['Outlet_Establishment_Year']
#Change categories of low fat:
data['Item_Fat_Content'] = data['Item_Fat_Content'].replace({'LF':'LowFat',
 'reg':'Regular',
 'lowfat':'Low Fat'})
#Mark non-consumables as separate categoryin low_fat:
data.loc[data['Item_Type_Combined']=="Non-Consumable",'Item_Fat_Content']= "Non-Edible"
#Fill missing values by a very largenegative val
data.fillna(-9999,inplace = True)
#Import library:
from sklearn.preprocessing importLabelEncoder
le = LabelEncoder()
#New variable for outlet
data['Outlet'] =le.fit_transform(data['Outlet_Identifier'])
var_mod = ['Item_Fat_Content','Outlet_Location_Type','Outlet_Size','Item_Type_Combined','Outlet_Type','Outlet']
le = LabelEncoder()
for i in var_mod:
 data[i] =le.fit_transform(data[i].astype(str))
train_new =train.drop(['Item_Identifier','Outlet_Identifier','Item_Outlet_Sales'],axis=1)
test_new =test.drop(['Item_Identifier','Outlet_Identifier'],axis=1)
y_all = train['Item_Outlet_Sales']

Once we have the pre-processed the storeddata, we can then import RGF using the following command:

一旦我们已经预处理存储的数据,我们就可以使用以下代码导入正则化贪心森林算法。

from rgf.sklearn import RGFRegressor
from sklearn.model_selection importGridSearchCV

需要设置的最重要的两个参数时叶子最大数和L2正则化。我们可以使用网格搜索来找到具有经交叉验证的最优均方误差的参数。

parameters ={'max_leaf':[1000,1200,1300,1400,1500,1600,1700,1800,1900,2000],
              'l2':[0.1,0.2,0.3],
              'min_samples_leaf':[5,10]}
clf = GridSearchCV(estimator=rgf,
                   param_grid=parameters,
                  scoring='neg_mean_squared_error',
                   n_jobs = -1,
                   cv = 3)

看起来我们使用太多的叶子构造出一个复杂的模型了,高惩罚项需要匹配更小的max_Leaf,让我们使用更小的max_leaf来做网格搜索。

parameters ={'max_leaf':[100,200,300,400,500,800,900,1000],
 'algorithm':("RGF_Sib","RGF"),
 'l2':[0.1,0.2,0.3],
 'min_samples_leaf':[5,10]}

看起来这些参数是最合适的。在公共排行榜上,这些参数的均方根误差得分是1146。

后记

正则化贪心森林算法只是和梯度提升算法类似的另一种树集成技术,能够有效地应用于非线性关系建模。这个库的相关文档可以在这里链接中找到。Alpha版本包含多核支持的快速正则化贪心算法。这个算法还有其他几个参数可以调试。欢迎在评论中告诉我这个算法是如何为你解决问题的。

原文链接:https://www.analyticsvidhya.com/blog/2018/02/introductory-guide-regularized-greedy-forests-rgf-python/