MachineLearning小汇总----持续更新......

6 篇文章 1 订阅
5 篇文章 0 订阅

目标函数定义 : 目标函数

1. GBDT(Gradient Boosting Decision Tree)

Gradient Boosting是一种Boosting的方法 , 它的主要思想是每一次建立模型是在之前建立模型损失函数的梯度下降方向。损失函数式评价模型性能(一般为拟合程度+正则项) , 认为损失函数越小 , 性能越好 。而让损失函数持续下降 , 就能使得模型不断提升性能 , 其最好的方法就是使损失函数沿着梯度方向下降。

Gradient Boosting Decision Tree每一次建立树模型是在之前建立损失函数的梯度下降方向。 即利用了损失函数的负梯度在当前模型的值作为回归问题提升树算法的残差近似值 , 去拟合一棵回归树。

具体算法算理:GBDT原理-Gradient Boosting Decision Tree

为什么基分类器选用决策树 ? ===>决策树可以认为是if-then规则的集合 , 易于理解 , 可解释性强 , 预测速度快。同时 , 决策树算法相比于其他的算法需要更少的特征工程 , 比如可以不用做特征标准化 , 可以很好的处理字段缺失的数据 , 也可以不用关心特征间是否相互依赖等。决策树能够自动组合多个特征 , 它可以毫无压力地处理特征间的交互关系并且是非参数化的 , 因此不必担心异常值或者数据是否线性可分。不过 , 单独使用决策树算法时 , 有容易过拟合缺点。 所幸的是 , 通过各种方法 , 抑制决策树的复杂性 , 降低单棵决策树的拟合能力 , 再通过梯度提升的方法集成多个决策树 , 最终能够很好的解决过拟合的问题。

由此可见 , 梯度提升方法和决策树学习算法可以互相取长补短, 是一对完美的搭档。至于抑制单棵决策树的复杂度的方法有很多 , 比如限制树的最大深度、限制叶子节点的最少样本数量、限制节点分裂时的最少样本数量、吸收Bagging的思想对驯良样本采样(subsample) , 在学习单棵决策树时只使用一部分训练样本、借鉴森林的思路在学习单棵决策树时只采样一部分特征 、在目标函数中添加正则项惩罚复杂的树结构等。

GBDT二分类采用的损失函数是对数损失 : 因为损失函数是将所有样本的损失函数加起来 , 面对对数损失函数便是将和转变为乘的 , 故采用对数损失函数。

GBDT应用–回归和分类

GBDT分类 : 每一棵树拟合当前整个模型的损失函数的负梯度 , 构建新的树加到当前模型中形成新模型 , 下一棵树拟合新模型的损失函数的负梯度。

from sklearn import ensemble
clf = ensemble.GradientBoostingClassifier()
gbdt_model = clf.fit(X_train,y_trian) # Traning model
pred_x = gbdt_model.predict_proba(test1217_x)[:,1] # predict : probably of 1
# 包含的参数
# loss = loss(损失函数), learning_rate = learning_rate(学习率), n_estimators = n_estimators(迭代次数),
# min_samples_split = min_samples_split(最小样本切分),
# min_samples_leaf = min_samples_leaf(最小叶子节点数),
# min_weight_fraction_leaf = min_weight_fraction_leaf(最小叶子节点切分权重),
# max_depth = max_depth(最大深度), init = init, subsample = subsample,
# max_features = max_features(最大特征数),
# random_state = random_state(随机切分比例), verbose = verbose,
# max_leaf_nodes = max_leaf_nodes, warm_start = warm_start,
# presort = presort

GBDT回归 : 每一棵树拟合当前模型的残差 , 构建新的树加到当前模型中形成新模型 , 下一棵树拟合新模型的损失函数的负梯度。

from sklearn import ensemble
clf = ensemble.GradientBoostingRegressor()
gbdt_model = clf.fit(X_train,y_train) # Training model
y_upper = gbdt_model.predict(x_test) # predict
# 包含的参数和上面一致

GBDT调参问题 : sklearn中GBDT调参
GBDT运用的正则化技巧 , 防止模型过于复杂 , 参考文章GBDT运用的正则化技巧

GBDT构建新的特征的思想

特征决定模型性能上界 , 例如深度学习方法也是将数据如何更好的表达为特征。如果能够将数据表达成为线性可分的数据 , 那么使用简单的线性模型就可以取得很好的效果。GBDT构建新的特征也是使特征更好地表达数据。

**主要思想 ?*GBDT每棵树的路径直接作为LR输入特征使用。

用已有特征训练GBDT模型,然后利用GBDT模型学习到的树来构造新特征,最后把这些新特征加入原有特征一起训练模型。构造的新特征向量是取值0/1的,向量的每个元素对应于GBDT模型中树的叶子结点。当一个样本点通过某棵树最终落在这棵树的一个叶子结点上,那么在新特征向量中这个叶子结点对应的元素值为1,而这棵树的其他叶子结点对应的元素值为0。新特征向量的长度等于GBDT模型里所有树包含的叶子结点数之和。

GBDT原理及利用GBDT构造新的特征

上图为混合模型结构。输入特征通过增强的决策树进行转换。每个单独树的输出被视为稀疏性分类器的分类输入特征。增强的决策树被证明是非常强大的特征转换。
例子1 : 上图有两棵树 , 左树有三个叶子节点 , 右树有两个叶子节点 , 最终的特征即为五维的向量。对于输入x , 假设他落在左树的第一个节点 , 编码[1,0,0] , 落在右树第二个节点则编码[0,1] , 所以整体的编码为[1,0,0,0,1] , 这类编码作为特征, 输入到线性分类模型(LR or FM)中进行分类。
从另外一篇博客中我的理解是 : 这里两棵树给的是同一个样本 , 只是对样本进行了不同的划分 , 比如:样本为[3.4,5.6,7.8] , 我们得知该样本类别为点击(记为01) , 则我们可将样本写为[3.4,5.6,7.8,0]和[3.4,5.6,7.8,1]分别进入两棵树 , 继而得到最终的编码.针对样本有三类的情况 , 我们实质上是在每轮的训练的时候是同时训练三棵树。假设这个样本属于第二类 , 第一棵树针对样本x的第一类 , 输入为(x,0)。第二颗树针对样本x的第二类 , 输入为(x,1)。第三棵树针对样本x的第三类 , 输入为(x,0)

在这里每棵树的训练过程其实就是我们之前已经提到过的Cart Tree的生成过程。在此处我们参照之前的生成树程序 , 就可以解出三棵树以及三棵树对x类别的预测值f1(x) , f2(x) , f3(x) 。那么在此类训练中 , 我们仿照多分类的逻辑回归 , 使用softmax(映射函数)来产生概率 , 则属于类别1的概率是softmax的概率公式并且我们可以针对类别1求出残差y11(x) = 0-p1(x) ; 类别2求出残差y22(x) = 1-p2(x) ; 类别3求出残差y33(x) = 0-p3(x) .

然后开始第二轮针对第一类输入为(x,y11(x)) , 针对第二类输入为(x,y22(x)) , 针对第三类输入为(x,y33(x)) , 继续训练出第三棵树 , 一直迭代M轮 , 每轮构建3棵树。

所以当K=3 , 我们其实应该有三个式子Cart树之GBDT公式(Cim表示预测值的均值 = 1/N*sum(yi) , I属于Rim就为1 , 不属于为0)当训练完毕以后 , 新来一个样本x1 , 我们需要预测该样本的类别的时候 , 便可以有这三个产生三个值 , f1(x) , f2(x) , f3(x) 。样本属于某个类别c的概率为类别之公式

GBDT多分类算法流程图解 : GBDT多分类算法流程图解


GBDT与LR融合方案

在CTR预估中 , 如何利用AD ID是一个问题。
直接将AD ID作为特征建树不可行 , 而onehot编码过于稀疏 , 为每一个AD ID建GBDT树 , 相当于发掘出区分每个广告的特征。而对于曝光不充分的样本即长尾部分 , 无法单独建树。
综合方案为 : 使用GBDT对非ID和ID分别建一类树。

  • 非ID类树 : 不以细粒度的ID建树 , 此类树作为base , 即这些ID一起构建GBDT。即便曝光少的广告、广告主 , 扔可以通过此类树得到有区分性的特征、特征组合。
  • ID类树 : 以细粒度的ID建一类树(每个ID构建GBDT) , 用于发现曝光充分的ID对应有区分性的特征、特征组合。当一条样本x进来之后 , 遍历两类树到叶子节点 , 得到的特征作为LR输入。当AD曝光不充分不足以训练树时 , 其他树恰好作为补充。

GBDT原理及利用GBDT构造新的特征2

例子2: 下图假设训练了3棵深度为2的树模型 , 对于输入X , 在第一棵树属于节点4 , 第二棵树属于节点7,第三棵树属于节点6 , 所以生成的特征为 : "1:4 2:7 3:6"

GBDT原理及利用GBDT构造新的特征3

FFM详细资料>>

Python实现

libFFM适用于例子2的情况 , 即只用使用每棵树的index
LR适用于例子1的情况 , 须将节点使用one-hot编码

# 弱分类器的数目
n_estimator = 10
# 随机生成分类数据。
X, y = make_classification(n_samples=80000)  
# 切分为测试集和训练集,比例0.5
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.5)
# 将训练集切分为两部分,一部分用于训练GBDT模型,另一部分输入到训练好的GBDT模型生成GBDT特征,然后作为LR的特征。这样分成两部分是为了防止过拟合。
X_train, X_train_lr, y_train, y_train_lr = train_test_split(X_train, y_train, test_size=0.5)
# 调用GBDT分类模型。
grd = GradientBoostingClassifier(n_estimators=n_estimator)
# 调用one-hot编码。
grd_enc = OneHotEncoder()
# 调用LR分类模型。
grd_lm = LogisticRegression()


'''使用X_train训练GBDT模型,后面用此模型构造特征'''
grd.fit(X_train, y_train)

# fit one-hot编码器
grd_enc.fit(grd.apply(X_train)[:, :, 0])

''' 
使用训练好的GBDT模型构建特征,然后将特征经过one-hot编码作为新的特征输入到LR模型训练。
'''
grd_lm.fit(grd_enc.transform(grd.apply(X_train_lr)[:, :, 0]), y_train_lr)
# 用训练好的LR模型多X_test做预测
y_pred_grd_lm = grd_lm.predict_proba(grd_enc.transform(grd.apply(X_test)[:, :, 0]))[:, 1]
# 根据预测结果输出
fpr_grd_lm, tpr_grd_lm, _ = roc_curve(y_test, y_pred_grd_lm)

总结 :

对于样本量大的数据 , 线性模型具有训练速度快的特点 , 但线性模型学习能力限于线性可分数据 , 所以就需要特征工程将数据尽可能地从输入空间转换到线性可分的特征空间。GBDT与LR的融合模型 , 其实使用GBDT来发掘有区分度的特征以及组合特征 , 来替代人工组合特征。工业中GBDT+LR、GBDT+FM都是应用比较广泛。

参考文档 : http://blog.csdn.net/shine19930820/article/details/71713680


GBDT&&RF

GBDT和随机森林都是基于决策树而得到的。决策树比较不易受到离群点和缺失值的影响。决策树不考虑空间分布 , 也不考虑分类器的结构 吗是一种无参算法。但是决策树比较容易过拟合 , 另外 , 决策树不易处理连续型变量。


GBDT&RF&XGBoost&LightGBM

RF (随机森林)

RF是Bagging算法的优化版本 , 改进一 : 基学习器限定为决策树 , 改进二 : 除了bagging在样本上加上扰动 , 同时属性上也加上了扰动 , 即在决策树学习的过程中引入了随机属性选择 , 即不止有样本的随机选择还有属性的随机选择。

RF和Bagging对比 :

RF的起始性能较差 , 特别当只有一个基学习器时 , 随着学习器数目增多 , 随机森林通常会收敛到更低的泛化误差。随机森林的训练效率也会高于Bagging , 因为单个决策树在构建中 , Bagging使用的是’确定性’决策树 , 在选择特征划分结点时 , 要对所有的特征进行考虑 , 而随机森林使用的是’随机性’特征数 , 只需要考虑特征的子集。

RF优缺点 :

**优点 : ** 训练可以高度并行化 , 对于大数据时代的大样本训练速度有优势 ; 能够处理高维的数据 , 并且不用特征选择 , 而且在训练完后 , 给出特征的重要性 ; 相对于Boosting系列的Adaboost和GBDT , RF实现比较简单。

**缺点 : **在噪声较大的分类或者回归问题上容易过拟合。

ET(极端随机数) Extra-Trees :

**ET的原理 : **算法与随机算法十分相似 , 都是由许多决策树构成。但是与随机森林有两点主要的区别 :

  • 对于每个决策树的训练集 , RF采用的是随机采样bootstrap来选择采样集作为每一个决策树的训练集 , 而extra trees一般不采用随机采样 , 即每个决策树采用原始训练集
  • 在选定了划分特征后 , RF的决策树会基于信息增益 , 基尼系数 , 均方差之类的原则 , 选择一个最优的特征划分点 , 这和传统的决策树相同。但是extra trees比较激进 , 它会随机的选择一个特征值来划分决策树。
ET随机选择特征值的解释 :

当特征属性是类别的形式时,随机选择具有某些类别的样本为左分支,而把具有其他类别的样本作为右分支;当特征属性是数值的形式时,随机选择一个处于该特征属性的最大值和最小值之间的任意数,当样本的该特征属性值大于该值时,作为左分支,当小于该值时,作为右分支。这样就实现了在该特征属性下把样本随机分配到两个分支上的目的。然后计算此时的分叉值(如果特征属性是类别的形式,可以应用基尼指数;如果特征属性是数值的形式,可以应用均方误差)。遍历节点内的所有特征属性,按上述方法得到所有特征属性的分叉值,我们选择分叉值最大的那种形式实现对该节点的分叉。

ET与RF的对比 :

由于随机选择了特征值的划分点位 , 而不是最优点位 , 这样会导致生成的决策树的规模一般会大于RF所生成的决策树。也就是说 , **模型的方差相对于RF进一步减少 , **但是偏差相对于RF进一步增大。在某些时候 , extra trees的泛化能力比RF更好。

GBDT(梯度提升决策树) :

GBDT原理 :

GBDT是集成学习Boosting家族的成员,是对提升树的改进。提升树是加法模型、学习算法为前向分布算法时的算法。不过它限定基本学习器为决策树。对于二分类问题,损失函数为指数函数,就是把AdaBoost算法中的基本学习器限定为二叉决策树就可以了;对于回归问题,损失函数为平方误差,此时,拟合的是当前模型的残差。梯度提升树GBDT是对提升树算法的改进,提升树算法只适合误差函数为指数函数和平方误差,对于一般的损失函数,梯度提升树算法利用损失函数的负梯度在当前模型的值,作为残差的近似值。

**GBDT建树关键点 : **采用ensemble决策树而非单棵树 ; 建树采用GBDT而非RF

  • 为什么建树采用ensemble决策树?
    • 一棵树的表达能力很弱 , 不足以表达多个有区分性的特征组合 , 多棵树的表达能力更强一些。GBDT每棵树都在学习前面树尚存的不足 , 迭代多少次就会生成多少棵树。多棵树正好满足LR每条训练样本可以通过GBDT映射成多个特征的需求。
  • 为什么采用GBDT而非RF?
    • RF也是多棵树 , 但从效果上有实践证明不如GBDT。而且GBDT前面的树 , 特征分裂主要体现对多数样本有区分度的特征 ; 后面的树 , 主要体现在是经过前N棵树 , 残差仍然较大的少数样本。有限选用在整体上有区分度的特征 , 在选用针对少数样本有区分度的特征 , 思路更加合理 , 这应该也是用GBDT的原因。
GBDT与Boosting的区别 :

GBDT与传统Boosting区别较大 , 它的每一次计算都是为了减少上一次的残差 , 而为了消除残差 , 我们可以在残差减小的梯度方向上建立模型 , 所以说 , 在GradienBoost中 , 每个新的模型的建立是为了使得之前的模型的残差往残差梯度下降的方向 , 与传统的Boosting中关注正确错误样本的加权有着很大的区别。

GBDT优缺点 :

**优点 : **它能灵活的处理各种类型的数据 ; 在相对较少的调参时间下 , 预测的准确度较高 , 相对于SVM来说的。

**缺点 : **基学习器之间存在串行关系 , 难以并行训练数据。

XGBoost

XGBoost的原理认识 :

XGBoost是Boosting算法的其中一种 , Boosting算法的思想是将许多弱分类器集成在一起 , 形成一个强分类器。因为XGBoost是一种提升树模型 , 所以它是将许多书模型集成在一起 , 形成一个很强的分类器。XGBoost是极端梯度提升树 , 其基本思想 : 一棵树一棵树逐渐地往模型里面加 , 每加一棵Cart决策树时 , 要使得整体的效果(目标函数有所下降)有所提升。使用多棵决策树(多个单一的弱分类器)构成组合分类器 , 并且给每个叶子节点赋予一定的权值。

XGBoost的优点 :

XGBoost算法可以给预测模型带来能力的提升。

  • 正则化 : XGBoost是以"正则化提升(regularized boosting)"技术而闻名。XGBoost早代价函数里加入了正则项 , 用于控制模型的复杂度。正则项里包含了树的叶子节点个数 , 每个叶子节点上输出的score的L2模的平方和。从Bias-variance tradeoff(偏差-方差权衡)角度来讲 , 正则项降低了模型的方差 , 使学习出来的模型更加简单 , 防止过拟合 , 这也是XGBoost优于传统GBDT的一个特征。
  • 并行处理 : XGBoost工具支持并行。决策树的学习最耗时的一个步骤就是对特征的值进行排序(因为要确定最佳分割点),Xgboost在训练之前,预先对数据进行了排序,然后保存为block结构,后面的迭代中重复使用这个结构,大大减小计算量。这个block结构也使得并行成为了可能,在进行节点的分类时,需要计算每个特征的增益,大大减少计算量。这个block结构也使得并行成为了可能,在进行节点的分裂的时候,需要计算每个特征的增益,最终选增益最大的那个特征去做分裂,那么各个特征的增益计算就可以开多线程进行。
  • 灵活性 : ** XGBoost支持用户自定义目标函数和评估函数** , 只要目标函数二阶可导就行。它对模型增加了一个全新的维度 , 所以我们的处理不会受到任何限制。
  • **缺失值处理 : **对于特征的值有缺失的样本 , XGBoost可以自动学习出它的分裂方向。XGBoost内置处理缺失值的规则。用户需要提供一个和其他样本不同的值 , 然后把它作为一个参数传进去 , 以此来作为缺失值的取值。XGBoost在不同节点遇到缺失值时采用不同的处理方法 , 并且会学习未来遇到缺失值时的处理方法。
  • **剪枝 : **XGBoost先从顶到底建立所有可以建立的子树 , 再从底到顶反向进行剪枝 , 比起GBM , 这样不容易陷入局部最优解
  • **内置交叉验证 : **XGBoost允许在每一轮Boosting迭代中使用交叉验证。因此可以方便的获得最优Boosting迭代次数 , 而GBM使用网格搜索 , 只能检测有限个值。
XGBoost的离线安装 :

1,点击此处,下载对应自己Python版本的网址。

XGBoost安装1

2 , 输入安装代码 : pip install xgboost-0.81-cp37-cp37m-win_amd64.whl

XGBoost安装2

XGBoost目标函数 = 训练误差 + 正则化惩罚项 ===> Obj(θ) = L(θ) + Ω(θ)

XGBoost也是拟合的在数据上的残差,但是它是用泰勒展式对模型损失残差的近似;同时XGBoost对模型的损失函数进行的改进,并加入了模型复杂度的正则项

Boosting的最大好处在于,每一步的残差计算其实变相地增大了分错instance的权重,而已经分对的instance则都趋向于0。这样后面的树就能越来越专注那些前面被分错的instance。

XGBoost模型详解
1 , XGBoost能加载的各种数据格式解析

​ XGBoost可以加载多种数据格式的训练数据 :

libsvm 格式的文本数据 ; 
Numpy 的二维数组 ; 
XGBoost 的二进制的缓存文件。加载的数据存储对象在DMatrix中。

加载libsvm格式的数据 : dtrain1 = xgb.DMatrix('train.svm.txt')
加载二进制的缓存文件 : dtrain2 = xgb.DMatrix('train.svm.buffer')
加载numpy的数组 :

data = np.random.rand(5,10) # 5改行10列数据集
label = np.random.randint(2,size=5) # 二分类目标值
dtrain = xgb.DMatrix(data, label = label) # 组成训练集

将scipy.sparse格式的数据转化为DMatrix格式

dtrain = xgb.DMatrix('train.svm.txt')
dtrain.save_binary("train.buffer")

可以使用如下方式处理DMatrix中的缺失值dtrain = xgb.DMatrix(data , label=label , missing=-999.0)

当需要给样本设置权重时 , 可以用如下方式 :

w = np.random.rand(5,1)
dtrain = xgb.DMatrix(data,label = label,missing=-999.0,weight=w)
2 , XGBoost的模型参数**

​ XGBoost使用key-value字典的方式存储参数

在运行XGBoost之前 , 必须设置三种类型参数 : general parameters,booster parameters和task parameters

**通用参数(General Parameters) : **该参数控制在提升(boosting)过程中使用哪种booster,常用的booster有树模型(tree)和线性模型(linear model)
Booster参数(Booster Parameters):这取决于使用哪种booster
学习目标参数(Task Parameters):控制学习的场景,例如在回归问题中会使用不同的参数控制排序

# xgboost模型
params = {
    ##### 2.1通用参数
    'booster':'gbtree', # 有两种模型可以选择gbtree和gblinear。gbtree使用基于树的模型进行提升计算 , gblinear使用线性模型进行提升计算。缺省(默认)值为gbtree
    'silent':0,  # 设置成1 则没有运行信息输入,最好是设置成0 ; 默认为0
    'nthread':7,  #CPU线程数 , 默认值为当前系统可以获得的最大线程数
    # verbosity:[默认 =1] 打印消息的详细程度,有效值为0(静默),1(警告),2(信息),3(调试)。有时,XGBoost会尝试根据启发式更改配置,启动式显示为警告消息。如果出现意外,请尝试增加详细程度。
    
    # num_pbuffer : 预测缓冲区大小 , 通常设置为训练实例的数目。缓冲用于保存最后一步提升的预测结果 , 无需人为设置。
    # num_feature : Boosting过程中用到的特征维数 , 设置为特征个数 , XGBoost会自动设置 , 无需人为设置
    # disable_default_eval_metric :[默认 = 0] 标记以禁止默认度量标准,设置为> 0 以禁止。
    
    ##### 2.2 tree booster参数
    'eta':0.007,  # 如同学习率 , 为了防止过拟合 , 更新过程中用到的收缩步长。在每次提升计算之后 , 算法会直接获得新特征的权重。eta通过缩减特征的权重式计算过程更加保守。默认值为0.3 , 取值范围:[0,1] , 典型值为:0.01-0.2
    'gamma':0.1,    # 用于控制是否后剪枝的参数,越大越保守,一般0.1 0.2的样子。在节点分裂时 , 只有分裂后损失函数的值下降了 , 才会分裂这个节点。Gamma指定了节点分裂所需的最小损失函数下降值。这个参数值越大 , 算法越保守。取值范围为:[0,∞)
    'max_depth':12,  # 构建树的深度,越大越容易过拟合。默认值为6 , 取值范围为 :[0,∞) , 要使用CV函数来进行调优。典型值:3-10
    # max_delta_step [default=0] :我们允许每个树的权重被估计的值。如果它的值被设置为0,意味着没有约束;如果它被设置为一个正值,它能够使得更新的步骤更加保守。通常这个参数是没有必要的,但是如果在逻辑回归中类极其不平衡这时候他有可能会起到帮助作用。把它范围设置为1-10之间也许能控制更新。 取值范围为:[0,∞)
    # min_child_weight [default=1] :孩子节点中最小的样本权重和。如果一个叶子节点的样本权重和小于min_child_weight则拆分过程结束。在现行回归模型中,这个参数是指建立每个模型所需要的最小样本数。这个参数用于避免过拟合。当它的值较大时,可以避免模型学习到局部的特殊样本。但是如果这个值过高,会导致欠拟合。这个参数需要使用CV来调整。取值范围为:[0,∞]
    'subsample':0.7, # 随机采样训练样本 , 用于训练模型的子样本占整个样本集合的比例。取值范围是:(0,1] ,默认为1
    'colsample_bytree':3,# 这个参数默认为1,是每个叶子里面h的和至少是多少
    # 对于正负样本不均衡时的0-1分类而言,假设h在0.01附近,min_child_weight为1
    #意味着叶子节点中最少需要包含100个样本。这个参数非常影响结果,
    # 控制叶子节点中二阶导的和的最小值,该参数值越小,越容易过拟合
    # colsample_bytree , colsample_bylevel , colsample_bynode [默认 =1] 这是对列的子采样参数家族,所有的colsample_by* 参数的范围均为(0,1),默认值为1.

  # colsample_bytree [default=1] :在建立树时对特征采样的比例。缺省值为1 。取值范围为:(0,1]

  # colsample_bylevel :是每个级别的列的子采样率,对于树中达到的每个新深度级别,子采样都会发生一次,列是从当前树的列集中进行子采样的。

  # colsample_bynode:是每个节点(拆分)的列的子采样率。每次评估新的拆分时,都会发生一次子采样。列是从为当前级别选择的列集中进行二次采样的。

  # colsample_by* :参数累积起作用。例如,与64个功能的组合将在每个拆分中留下4个功能可供选择。
{'colsample_bytree':0.5, 'colsample_bylevel':0.5, 'colsample_bynode':0.5}

    
    ##### 2.3 Linear Booster参数
    'lambda':2,  # 控制模型复杂度的权重值的L2 正则化项参数,参数越大,模型越不容易过拟合 , 默认值1
    # 'alpha'[default = 1]:L1正则的惩罚系数,增加此值可以使模型更加保守
    # lambda_bias:在偏置上的L2正则。缺省(默认)值为0(在L1上没有偏置项的正则,因为L1偏置时不需要)
    
    ##### 2.4 学习目标参数
    'objective':'multi:softmax',   # 多分类问题
    # objective[default = reg:linear] :定义学习任务及相应的学习目标,可选择的目标函数如下:

    # "reg:linear" —— 线性回归。
    # "reg:logistic"—— 逻辑回归。
    # "binary:logistic"—— 二分类的逻辑回归问题,输出为概率。
    # "binary:logitraw"—— 二分类的逻辑回归问题,输出的结果为wTx。
    # "count:poisson"—— 计数问题的poisson回归,输出结果为poisson分布。在poisson回归中,max_delta_step的缺省值为0.7。(used to safeguard optimization)
    # "multi:softmax" –让XGBoost采用softmax目标函数处理多分类问题,同时需要设置参数num_class(类别个数)
    # "multi:softprob" –和softmax一样,但是输出的是ndata * nclass的向量,可以将该向量reshape成ndata行nclass列的矩阵。没行数据表示样本所属于每个类别的概率。
    # "rank:pairwise" –set XGBoost to do ranking task by minimizing the pairwise loss
	# base_score[default = 0.5]: 所有实例的初始化预测分数,全局偏置;当有足够的迭代次数时,改变这个值将不会有太大的影响
    'seed':1000,# 随机数的种子 , 默认为0
    #'eval_metric':'auc' 校验数据所需要的评价指标,不同的目标函数将会有缺省的评价指标(rmse for regression and error for classification  mean average precision for ranking)
    'num_class':10,  # 类别数,与multi softmax并用
       
}
    # 3 基本方法和默认参数
    # xgboost.train(params,dtrain,num_boost_round=10,evals(),obj=None,feval=None,maximize=False,early_stopping_rounds=None,evals_result=None,verbose_eval=True,learning_rates=None,xgb_model=None)
	# parms:这是一个字典,里面包含着训练中的参数关键字和对应的值,形式是parms = {'booster':'gbtree','eta':0.1}
  # dtrain:训练的数据
  # num_boost_round:这是指提升迭代的个数
  # evals:这是一个列表,用于对训练过程中进行评估列表中的元素。形式是evals = [(dtrain,'train'),(dval,'val')] 或者是 evals =[(dtrain,'train')] ,对于第一种情况,它使得我们可以在训练过程中观察验证集的效果。
  # obj :自定义目的函数
  # feval:自定义评估函数
  # maximize:是否对评估函数进行最大化
  # early_stopping_rounds:早期停止次数,假设为100,验证集的误差迭代到一定程度在100次内不能再继续降低,就停止迭代。这要求evals里至少有一个元素,如果有多个,按照最后一个去执行。返回的是最后的迭代次数(不是最好的)。如果early_stopping_rounds存在,则模型会生成三个属性,bst.best_score ,bst.best_iteration和bst.best_ntree_limit
  # evals_result:字典,存储在watchlist中的元素的评估结果
  # verbose_eval(可以输入布尔型或者数值型):也要求evals里至少有一个元素,如果为True,则对evals中元素的评估结果会输出在结果中;如果输入数字,假设为5,则每隔5个迭代输出一次。
  # learning_rates:每一次提升的学习率的列表
  # xgb_model:在训练之前用于加载的xgb_model

#### 4 模型训练
num_round = 10
bst = xgb.train( plst, dtrain, num_round, evallist )

#### 5 模型预测
# X_test类型可以是二维List,也可以是numpy的数组
dtest = DMatrix(X_test)
ans = model.predict(dtest)

#### 6 保存模型
bst.save_model('test.model')

# 6.1 导出模型和特征映射(Map)
# 6.2 导出模型到文件
bst.dump_model('dump.raw.txt')
# 6.3 导出模型和特征映射
bst.dump_model('dump.raw.txt','featmap.txt')

#### 7 加载模型
bst = xgb.Booster({'nthread':4})#init model
bst.load_model("model.bin")   # load data

常见的损失函数

XGBoost实战代码 :

Xgboost有两大类接口:Xgboost原生接口 和sklearn接口,并且Xgboost能够实现分类回归两种任务。下面对这四种情况做以解析。

1,基于Xgboost原生接口的分类
from sklearn.datasets import load_iris
import xgboost as xgb
from xgboost import plot_importance
import matplotlib.pyplot  as plt
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score  # 准确率
 
# 记载样本数据集
iris = load_iris()
X,y = iris.data,iris.target
# 数据集分割
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.2,random_state=123457)
 
# 算法参数
params = {
    'booster':'gbtree',
    'objective':'multi:softmax',
    'num_class':3,
    'gamma':0.1,
    'max_depth':6,
    'lambda':2,
    'subsample':0.7,
    'colsample_bytree':0.7,
    'min_child_weight':3,
    'slient':1,
    'eta':0.1,
    'seed':1000,
    'nthread':4,
}
 
plst = params.items()
 
# 生成数据集格式
dtrain = xgb.DMatrix(X_train,y_train)
num_rounds = 500
# xgboost模型训练
model = xgb.train(plst,dtrain,num_rounds)
 
# 对测试集进行预测
dtest = xgb.DMatrix(X_test)
y_pred = model.predict(dtest)
 
# 计算准确率
accuracy = accuracy_score(y_test,y_pred)
print('accuarcy:%.2f%%'%(accuracy*100))
 
# 显示重要特征
plot_importance(model)
plt.show()
#  输出预测正确率以及特征重要性:
# 	accuarcy:93.33%
2,基于Xgboost原生接口的回归

import xgboost as xgb
from xgboost import plot_importance
from matplotlib import pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_boston
from sklearn.metrics import mean_squared_error
 
# 加载数据集,此数据集时做回归的
boston = load_boston()
X,y = boston.data,boston.target
 
# Xgboost训练过程
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.2,random_state=0)
 
# 算法参数
params = {
    'booster':'gbtree',
    'objective':'reg:gamma',
    'gamma':0.1,
    'max_depth':5,
    'lambda':3,
    'subsample':0.7,
    'colsample_bytree':0.7,
    'min_child_weight':3,
    'slient':1,
    'eta':0.1,
    'seed':1000,
    'nthread':4,
}
 
dtrain = xgb.DMatrix(X_train,y_train)
num_rounds = 300
plst = params.items()
model = xgb.train(plst,dtrain,num_rounds)
 
# 对测试集进行预测
dtest = xgb.DMatrix(X_test)
ans = model.predict(dtest)
 
# 显示重要特征
plot_importance(model)
plt.show()
# 重要特征(值越大,说明该特征越重要
3,Xgboost使用sklearn接口的分类(推荐) ---- XGBClassifier
from xgboost.sklearn import XGBClassifier
 
clf = XGBClassifier(
    silent=0,  # 设置成1则没有运行信息输出,最好是设置为0,是否在运行升级时打印消息
    # nthread = 4  # CPU 线程数 默认最大
    learning_rate=0.3 , # 如同学习率
    min_child_weight = 1,
    # 这个参数默认为1,是每个叶子里面h的和至少是多少,对正负样本不均衡时的0-1分类而言
    # 假设h在0.01附近,min_child_weight为1 意味着叶子节点中最少需要包含100个样本
    # 这个参数非常影响结果,控制叶子节点中二阶导的和的最小值,该参数值越小,越容易过拟合
    max_depth=6, # 构建树的深度,越大越容易过拟合
    gamma = 0,# 树的叶子节点上做进一步分区所需的最小损失减少,越大越保守,一般0.1 0.2这样子
    subsample=1, # 随机采样训练样本,训练实例的子采样比
    max_delta_step=0,  # 最大增量步长,我们允许每个树的权重估计
    colsample_bytree=1, # 生成树时进行的列采样
    reg_lambda=1, #控制模型复杂度的权重值的L2正则化项参数,参数越大,模型越不容易过拟合
    # reg_alpha=0, # L1正则项参数
    # scale_pos_weight =1 # 如果取值大于0的话,在类别样本不平衡的情况下有助于快速收敛,平衡正负权重
    # objective = 'multi:softmax', # 多分类问题,指定学习任务和响应的学习目标
    # num_class = 10,  # 类别数,多分类与multisoftmax并用
    n_estimators=100,  # 树的个数
    seed = 1000,  # 随机种子
    # eval_metric ='auc'
)

基于Sckit-learn接口的分类

from sklearn.datasets import load_iris
import xgboost as xgb
from xgboost import plot_importance
from matplotlib import pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
 
# 加载样本数据集
iris = load_iris()
X,y = iris.data,iris.target
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.2,random_state=12343)
 
# 训练模型
model = xgb.XGBClassifier(max_depth=5,learning_rate=0.1,n_estimators=160,silent=True,objective='multi:softmax')
model.fit(X_train,y_train)
 
# 对测试集进行预测
y_pred = model.predict(X_test)
 
#计算准确率
accuracy = accuracy_score(y_test,y_pred)
print('accuracy:%2.f%%'%(accuracy*100))
 
# 显示重要特征
plot_importance(model)
plt.show()
4,基于Scikit-learn接口的回归

import xgboost as xgb
from xgboost import plot_importance
from matplotlib import pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_boston
 
# 导入数据集
boston = load_boston()
X ,y = boston.data,boston.target
 
# Xgboost训练过程
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.2,random_state=0)
 
model = xgb.XGBRegressor(max_depth=5,learning_rate=0.1,n_estimators=160,silent=True,objective='reg:gamma')
model.fit(X_train,y_train)
 
# 对测试集进行预测
ans = model.predict(X_test)
 
# 显示重要特征
plot_importance(model)
plt.show()

Xgboost参数调优的一般方法

调参步骤:

1,选择较高的学习速率(learning rate)。一般情况下,学习速率的值为0.1.但是,对于不同的问题,理想的学习速率有时候会在0.05~0.3之间波动。选择对应于此学习速率的理想决策树数量。Xgboost有一个很有用的函数“cv”,这个函数可以在每一次迭代中使用交叉验证,并返回理想的决策树数量。

2,对于给定的学习速率和决策树数量,进行决策树特定参数调优(max_depth , min_child_weight , gamma , subsample,colsample_bytree)在确定一棵树的过程中,我们可以选择不同的参数。

3,Xgboost的正则化参数的调优。(lambda , alpha)。这些参数可以降低模型的复杂度,从而提高模型的表现。

4,降低学习速率,确定理想参数。

下面详细的进行这些操作。

第一步:确定学习速率和tree_based参数调优的估计器数目

为了确定Boosting参数,我们要先给其他参数一个初始值。咱们先按照如下方法取值:

  • 1,max_depth = 5:这个参数的取值最好在3-10之间,我选的起始值为5,但是你可以选择其他的值。起始值在4-6之间都是不错的选择。
  • 2,min_child_weight = 1 :这里选择了一个比较小的值,因为这是一个极不平衡的分类问题。因此,某些叶子节点下的值会比较小。
  • 3,gamma = 0 :起始值也可以选择其它比较小的值,在0.1到0.2之间就可以,这个参数后继也是要调整的。
  • 4,subsample,colsample_bytree = 0.8 这个是最常见的初始值了。典型值的范围在0.5-0.9之间。
  • 5,scale_pos_weight =1 这个值时因为类别十分不平衡。

注意,上面这些参数的值知识一个初始的估计值,后继需要调优。这里把学习速率就设成默认的0.1。然后用Xgboost中的cv函数来确定最佳的决策树数量。

第二步:max_depth和min_weight参数调优

我们先对这两个参数调优,是因为他们对最终结果有很大的影响。首先,我们先大范围地粗略参数,然后再小范围的微调。

注意:在这一节我会进行高负荷的栅格搜索(grid search),这个过程大约需要15-30分钟甚至更久,具体取决于你系统的性能,你也可以根据自己系统的性能选择不同的值。

第三步:gamma参数调优

在已经调整好其他参数的基础上,我们可以进行gamma参数的调优了。Gamma参数取值范围很大,这里我们设置为5,其实你也可以取更精确的gamma值。


param_test3 = {
 'gamma':[i/10.0 for i in range(0,5)]
}
 
gsearch3 = GridSearchCV(estimator = XGBClassifier( learning_rate =0.1,
 n_estimators=140, max_depth=4,min_child_weight=6, gamma=0,
subsample=0.8, colsample_bytree=0.8,objective= 'binary:logistic',
nthread=4, scale_pos_weight=1,seed=27),  param_grid = param_test3, scoring='roc_auc',n_jobs=4,iid=False, cv=5)
 
gsearch3.fit(train[predictors],train[target])
 
gsearch3.grid_scores_, gsearch3.best_params_, gsearch3.best_score_
第四步:调整subsample 和 colsample_bytree参数

尝试不同的subsample 和 colsample_bytree 参数。我们分两个阶段来进行这个步骤。这两个步骤都取0.6,0.7,0.8,0.9作为起始值。

第五步:正则化参数调优

由于gamma函数提供了一种更加有效的降低过拟合的方法,大部分人很少会用到这个参数,但是我们可以尝试用一下这个参数。

第六步:降低学习速率

最后,我们使用较低的学习速率,以及使用更多的决策树,我们可以用Xgboost中CV函数来进行这一步工作。

总结一下,要想模型的表现有大幅的提升,调整每个参数带来的影响也必须清楚,仅仅靠着参数的调整和模型的小幅优化,想要让模型的表现有个大幅度提升是不可能的。要想模型的表现有质的飞跃,需要依靠其他的手段。诸如,特征工程(feature egineering) ,模型组合(ensemble of model),以及堆叠(stacking)等。


XGBoost输出特征重要性以及筛选特征
1,梯度提升算法是如何计算特征重要性的?

使用梯度提升算法的好处是在提升树被创建后,可以相对直接地得到每个属性的重要性得分。一般来说,重要性分数,衡量了特征在模型中的提升决策树构建中的价值。一个属性越多的被用来在模型中构建决策树,它的重要性就相对越高。

属性重要性是通过对数据集中的每个属性进行计算,并进行排序得到。在单个决策树中通过每个属性分裂点改进性能度量的量来计算属性重要性。由节点负责加权和记录次数,也就是说一个属性对分裂点改进性能度量越大(越靠近根节点),权值越大;被越多提升树所选择,属性越重要。性能度量可以是选择分裂节点的Gini纯度,也可以是其他度量函数。

最终将一个属性在所有提升树中的结果进行加权求和后然后平均,得到重要性得分。

2,绘制特征重要性

一个已训练的Xgboost模型能够自动计算特征重要性,这些重要性得分可以通过成员变量feature_importances_得到。可以通过如下命令打印:

print(model.feature_importances_)

我们可以直接在条形图上绘制这些分数 , 以便获得数据集中每个特征的相对重要性的直观显示 , 例如 :

# plot
pyplot.bar(range(len(model.feature_importances_)),model.feature_importances_)
pyplot.show()

我们可以通过在the Pima Indians onset of diabetes 数据集上训练XGBoost模型来演示,并从计算的特征重要性中绘制条形图。

# plot feature importance manually
from numpy import loadtxt
from xgboost import XGBClassifier
from matplotlib import pyplot
from sklearn.datasets import load_iris
# load data
dataset = load_iris()
# split data into X and y
X = dataset.data
y = dataset.target
# fit model no training data
model = XGBClassifier()
model.fit(X, y)
# feature importance
print(model.feature_importances_)
# plot
pyplot.bar(range(len(model.feature_importances_)), model.feature_importances_)
pyplot.show()

运行这个实例 , 首先输出特征重要性分数以及相对重要性条形图
这种绘制的缺点在于 , 只显示了特征重要性而没有排序 , 可以在绘制之前对特征重要性得分进行排序。通过内建的绘制函数进行特征重要性得分排序后的绘制 , 这个函数就是plot_importance() , 示例如下 :


# plot feature importance manually
from numpy import loadtxt
from xgboost import XGBClassifier
from matplotlib import pyplot
from sklearn.datasets import load_iris
from xgboost import plot_importance
 
# load data
dataset = load_iris()
# split data into X and y
X = dataset.data
y = dataset.target
# fit model no training data
model = XGBClassifier()
model.fit(X, y)
# feature importance
print(model.feature_importances_)
# plot feature importance
 
plot_importance(model)
pyplot.show()

得到特征重要性的条形图并且是排序好的 , 根据其在输入数组的索引 , 特征自动命名为f0~f3 , 在问题描述中手动的将这些索引映射到名称 , 我们可以看到 , f2具有最高的重要性 , f1最低。

3,根据Xgboost特征重要性得分进行特征选择

特征重要性得分,可以用于在scikit-learn中进行特征选择。通过SelectFromModel类实现,该类采用模型并将数据集转换为具有选定特征的子集。这个类可以采取预先训练的模型,例如在整个数据集上训练的模型。然后,它可以阈值来决定选择哪些特征。当在SelectFromModel实例上调用transform()方法时,该阈值被用于在训练集和测试集上一致性选择相同特征。

在下面的示例中,我们首先在训练集上训练xgboost模型,然后在测试上评估。使用从训练数据集计算的特征重要性,然后,将模型封装在一个SelectFromModel实例中。我们使用这个来选择训练集上的特征,用所选择的特征子集训练模型,然后在相同的特征方案下对测试集进行评估。

# select features using threshold
selection = SelectFromModel(model, threshold=thresh, prefit=True)
select_X_train = selection.transform(X_train)
# train model
selection_model = XGBClassifier()
selection_model.fit(select_X_train, y_train)
# eval model
select_X_test = selection.transform(X_test)
y_pred = selection_model.predict(select_X_test)

我们可以通过测试多个阈值,来从特征重要性中选择特征。具体而言,每个输入变量的特征重要性,本质上允许我们通过重要性来测试每个特征子集。

完整代码如下 :

# plot feature importance manually
import numpy as np
from xgboost import XGBClassifier
from matplotlib import pyplot
from sklearn.datasets import load_iris
from xgboost import plot_importance
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.feature_selection import SelectFromModel
 
# load data
dataset = load_iris()
# split data into X and y
X = dataset.data
y = dataset.target
 
# split data into train and test sets
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.33,random_state=7)
 
# fit model no training data
model = XGBClassifier()
model.fit(X_train, y_train)
# feature importance
print(model.feature_importances_)
 
# make predictions for test data and evaluate
y_pred = model.predict(X_test)
predictions = [round(value) for value in y_pred]
accuracy = accuracy_score(y_test,predictions)
print("Accuracy:%.2f%%"%(accuracy*100.0))
 
#fit model using each importance as a threshold
thresholds = np.sort(model.feature_importances_)
for thresh in thresholds:
    # select features using threshold
    selection = SelectFromModel(model,threshold=thresh,prefit=True )
    select_X_train = selection.transform(X_train)
    # train model
    selection_model = XGBClassifier()
    selection_model.fit(select_X_train, y_train)
    # eval model
    select_X_test = selection.transform(X_test)
    y_pred = selection_model.predict(select_X_test)
    predictions = [round(value) for value in y_pred]
    accuracy = accuracy_score(y_test,predictions)
    print("Thresh=%.3f, n=%d, Accuracy: %.2f%%" % (thresh, select_X_train.shape[1], accuracy * 100.0))
    
    # 结果如下 : 
    # [0.20993228 0.09029345 0.54176074 0.15801354]
    # Accuracy:92.00%
    # Thresh=0.090, n=4, Accuracy: 92.00%
    # Thresh=0.158, n=3, Accuracy: 92.00%
    # Thresh=0.210, n=2, Accuracy: 86.00%
    # Thresh=0.542, n=1, Accuracy: 90.00%

我们可以看到,模型的性能通常随着所选择的特征的数量减少,在这一问题上,可以对测试集准确率和模型复杂度做一个权衡,例如选择三个特征,接受准确率为92%,这可能是对这样一个小数据集的清洗,但是对于更大的数据集和使用交叉验证作为模型评估方案可能是更有用的策略。

参考文献:
https://blog.csdn.net/waitingzby/article/details/81610495
https://blog.csdn.net/u011089523/article/details/72812019
https://blog.csdn.net/luanpeng825485697/article/details/79907149
https://xgboost.readthedocs.io/en/latest/parameter.html#general-parameters
原文地址https://www.cnblogs.com/wj-1314/p/9402324.html


XGBoost和传统GBDT的区别 :

  • 传统GBDT以CART作为基分类器,xgboost还支持线性分类器,这个时候xgboost相当于带L1和L2正则化项的逻辑斯蒂回归(分类问题)或者线性回归(回归问题)。
  • 传统GBDT在优化时只用到一阶导数信息,xgboost则对代价函数进行了二阶泰勒展开,同时用到了一阶和二阶导数。顺便提一下,xgboost工具支持自定义代价函数,只要函数可一阶和二阶求导。
  • xgboost在代价函数里加入了正则项,用于控制模型的复杂度。正则项里包含了树的叶子节点个数、每个叶子节点上输出的score的L2模的平方和。从Bias-variance tradeoff角度来讲,正则项降低了模型的variance,使学习出来的模型更加简单,防止过拟合,这也是xgboost优于传统GBDT的一个特性。
  • Shrinkage(缩减),相当于学习速率(xgboost中的eta)。xgboost在进行完一次迭代后,会将叶子节点的权重乘上该系数,主要是为了削弱每棵树的影响,让后面有更大的学习空间。实际应用中,一般把eta设置得小一点,然后迭代次数设置得大一点。(补充:传统GBDT的实现也有学习速率)
  • 列抽样(column subsampling)。xgboost借鉴了随机森林的做法,支持列抽样,不仅能降低过拟合,还能减少计算,这也是xgboost异于传统gbdt的一个特性。
  • 对缺失值的处理。对于特征的值有缺失的样本,xgboost可以自动学习出它的分裂方向。XGBoost对于确实值能预先学习一个默认的分裂方向
  • xgboost工具支持并行。boosting不是一种串行的结构吗?怎么并行的?注意xgboost的并行不是tree粒度的并行,xgboost也是一次迭代完才能进行下一次迭代的(第t次迭代的代价函数里包含了前面t-1次迭代的预测值)。xgboost的并行是在特征粒度上的。我们知道,决策树的学习最耗时的一个步骤就是对特征的值进行排序(因为要确定最佳分割点),xgboost在训练之前,预先对数据进行了排序,然后保存为block结构,后面的迭代中重复地使用这个结构,大大减小计算量。这个block结构也使得并行成为了可能,在进行节点的分裂时,需要计算每个特征的增益,最终选增益最大的那个特征去做分裂,那么各个特征的增益计算就可以开多线程进行。
  • 可并行的近似直方图算法。树节点在进行分裂时,我们需要计算每个特征的每个分割点对应的增益,即用贪心法枚举所有可能的分割点。当数据无法一次载入内存或者在分布式情况下,贪心算法效率就会变得很低,所以xgboost还提出了一种可并行的近似直方图算法,用于高效地生成候选的分割点

XGBoost使用经验总结

  • 多类别分类时,类别需要从0开始编码
  • Watchlist不会影响模型训练。
  • 类别特征必须编码,因为xgboost把特征默认都当成数值型的
  • 调参:Notes on Parameter Tuning 以及 Complete Guide to Parameter Tuning in XGBoost (with codes in Python)
  • 训练的时候,为了结果可复现,记得设置随机数种子。
  • XGBoost的特征重要性是如何得到的?某个特征的重要性(feature score),等于它被选中为树节点分裂特征的次数的和,比如特征A在第一次迭代中(即第一棵树)被选中了1次去分裂树节点,在第二次迭代被选中2次……那么最终特征A的feature score就是 1+2+….

LighGBM与XGBoost的区别 :

  • (1)xgboost采用的是level-wise的分裂策略,而lightGBM采用了leaf-wise的策略,区别是xgboost对每一层所有节点做无差别分裂,可能有些节点的增益非常小,对结果影响不大,但是xgboost也进行了分裂,带来了务必要的开销。
    leaft-wise的做法是在当前所有叶子节点中选择分裂收益最大的节点进行分裂,如此递归进行,很明显leaf-wise这种做法容易过拟合,因为容易陷入比较高的深度中,因此需要对最大深度做限制,从而避免过拟合。
  • (2)lightgbm使用了基于histogram的决策树算法,这一点不同与xgboost中的 exact 算法,histogram算法在内存和计算代价上都有不小优势。
    • -. 内存上优势:很明显,直方图算法的内存消耗为(#data* #features * 1Bytes)(因为对特征分桶后只需保存特征离散化之后的值),而xgboost的exact算法内存消耗为:(2 * #data * #features* 4Bytes),因为xgboost既要保存原始feature的值,也要保存这个值的顺序索引,这些值需要32位的浮点数来保存。
    • -. 计算上的优势,预排序算法在选择好分裂特征计算分裂收益时需要遍历所有样本的特征值,时间为(#data),而直方图算法只需要遍历桶就行了,时间为(#bin)
  • (3)直方图做差加速 : 一个子节点的直方图可以通过父节点的直方图减去兄弟节点的直方图得到,从而加速计算。
  • (4)lightgbm支持直接输入categorical 的feature : 在对离散特征分裂时,每个取值都当作一个桶,分裂时的增益算的是”是否属于某个category“的gain。类似于one-hot编码。
  • (5)但实际上xgboost的近似直方图算法也类似于lightgbm这里的直方图算法,为什么xgboost的近似算法比lightgbm还是慢很多呢?
    • xgboost在每一层都动态构建直方图, 因为xgboost的直方图算法不是针对某个特定的feature,而是所有feature共享一个直方图(每个样本的权重是二阶导),所以每一层都要重新构建直方图,而lightgbm中对每个特征都有一个直方图,所以构建一次直方图就够了
  • (6)lightgbm哪些方面做了并行?
    • feature parallel : 一般的feature parallel就是对数据做垂直分割(partiion data vertically,就是对属性分割),然后将分割后的数据分散到各个workder上,各个workers计算其拥有的数据的best splits point, 之后再汇总得到全局最优分割点。但是lightgbm说这种方法通讯开销比较大,lightgbm的做法是每个worker都拥有所有数据,再分割?(没懂,既然每个worker都有所有数据了,再汇总有什么意义?这个并行体现在哪里??)
    • data parallel : 传统的data parallel是将对数据集进行划分,也叫 平行分割(partion data horizontally), 分散到各个workers上之后,workers对得到的数据做直方图,汇总各个workers的直方图得到全局的直方图。 lightgbm也claim这个操作的通讯开销较大,lightgbm的做法是使用”Reduce Scatter“机制,不汇总所有直方图,只汇总不同worker的不同feature的直方图(原理?),在这个汇总的直方图上做split,最后同步。

LR(逻辑斯特回归)

逻辑回归假设数据服从伯努利分布,通过极大化似然函数的方法,运用梯度下降来求解参数,来达到将数据二分类的目的。

LR是广义线性模型,与传统线性模型相比,LR使用了Logit变换将函数值映射到0~1区间[2],映射后的函数值就是CTR的预估值。LR这种线性模型很容易并行化,处理上亿条训练样本不是问题,但线性模型学习能力有限,需要大量特征工程预先分析出有效的特征、特征组合,从而去间接增强LR的非线性学习能力。

逻辑回归从以下5方面来叙述 :
  • 逻辑回归的假设
  • 逻辑回归的损失函数
  • 逻辑回归的求解方法
  • 逻辑回归的目的
  • 逻辑回归如何分类
逻辑回归的基本假设
  • 任何模型都是有自己的假设 , 在这个假设模型下才是适用的。逻辑回归的第一个基本假设是假设数据服从伯努利分布。伯努利分布有一个简单的例子是抛硬币 , 抛中为正面的概率为p , 为负面的概率为1-p. 在逻辑回归这个模型里面是假设hθ(x)为样本为正的概率 , 1-hθ(x)为样本为负的概率。那么整个模型可以描述为hθ(x;θ)=p
  • 逻辑回归的第二个假设是假设样本为正的概率是p=1/(1+exp(−θTx))
  • 所以逻辑回归的最终形式 : hθ(x;θ)=1/(1+exp(−θTx))
逻辑回归的损失函数
  • 逻辑回归的损失函数是它的极大似然函数
    • Lθ(x)=∏hθ(xi; θ)yi∗(1−hθ(xi;θ))(1−yi) (i=1->m)
逻辑回归的求解方法
  • 由于该极大似然函数无法直接求解 , 我们一般通过对该函数进行梯度下降来不断逼近最优解。梯度下降分为随机梯度下降 , 批梯度下降 , small batch梯度下降三种方式
    • BGD(批量梯度下降)会获得全局最优解 , 缺点是在更新每个参数的时候需要遍历所有的数据 , 计算量会很大 , 并且有很多的冗余计算 , 导致的结果是当数据量大的时候 , 每个参数的更新都会很慢。
    • SGD(随机梯度下降)是以高方差频繁更新 , 优点是使得SGD会调到新的和潜在更好的局部最优解 , 缺点是使得收敛局部最优解的过程更加复杂
    • min-batch(小批量梯度)结合了SGD和BGD的优点 , 每次更新的时候使用n个样本。减少了参数更新的次数 , 可以达到更加稳定收敛结果 , 一般在深度学习中我们采用这种方法。
  • Adam , 动量法等优化方法—自适应优化算法 , 由于上述方法有两个致命问题
    • 如何对模型选择合适的学习率。自始至终保持同样的学习率其实不太合适。因为一开始参数刚刚开始学习的时候 , 此时的参数和最优解隔得比较远 , 需要保持一个较大的学习率尽快逼近最优解。但是学习到后面的时候 , 参数和最优解已经隔得比较近了 , 你还保持最初的学习率 , 容易越过嘴有点 , 在最优点附近来回振荡 , 通俗点说 , 就很容易学过头了 , 跑偏了。
    • 如何对参数选择合适的学习率。对每个参数都保持同样的学习率也是很不合理的。有些参数更新频繁 , 那么学习率可以适当小一点 。有些参数更新缓慢 , 那么学习率就应该大一点。
逻辑回归的目的
  • 该函数的目的便是将数据二分类 , 提高准确率。
逻辑回归如何分类
  • 逻辑回归作为一个回归(也就是y值是连续的) , 如何应用到分类上去呢?y值确实是一个连续的变量。逻辑回归的做法是划定一个阈值 , y值大于这个阈值的是一类 , y值小于这个阈值是另外一类。阈值具体如何调整根据实际情况选择.一般会选择0.5作为阈值来划分。
关于LR的疑问
  • 逻辑回归的损失函数为什么要使用极大似然函数作为损失函数??
    • 损失函数一般有四种 , 平方损失函数 , 对数损失函数 , HingeLoss0-1损失函数 , 绝对值损失函数。将极大似然函数取对数以后等同于对数损失函数。在逻辑回归这个模型下 , 对数损失函数的训练求解参数的速度是比较快的。
      • θj=θj−(yi−hθ(xi;θ))∗xij
      • 这个式子的更新速度只跟xij, yi相关。和sigmod函数本身的梯度是无关的。这样更新的速度是可以自始至终都比较的稳定。
    • 为什么不选平方损失函数 : 其一是因为如果你是用平方损失函数 , 你会发现梯度更新的速度和sigmod函数本身的梯度是很相关的。sigmod函数在它的定义域内的梯度都不大于0.25。这样训练会非常慢。
  • 逻辑回归在训练的过程当中 , 如果有很多的特征高度相关或者说有一个特征重复了100遍 , 会造成怎样的影响?
    • 如果在损失函数最终收敛的情况下 , 其实就算有很多特征高度相关也不会影响分类器的效果。
    • 但是对于特征本身来说的话 , 假设只有一个特征 , 在不考虑采样的情况下 , 将其重复100遍。训练完以后 , 数据还是这么多 , 但是这个特征本身重复了100遍 , 实质上将原来的特征分成了100份 , 每一个特征都是原来特征权重值的百分之一。
    • 如果在随机采样的情况下 , 其实训练收敛完以后 , 还是可以认为这100个特征和原来那一个特征扮演的效果一样 , 只是可能中间很多特征的值正负相消了。
  • 为什么我们还是会在训练的过程当中将高度相关的特征去掉?
    • 去掉高度相关的特征会让模型的可解释性更好
    • 可以大大提高训练的速度 。如果模型当中有很多特征高度相关的话。就算损失函数本身收敛了 , 但实际上参数是没有收敛的 , 这样会拉低训练的速度。其次是特征多了 , 本身就会增大训练的时间。
逻辑回归的优缺点总结
  • **优点 : **
    • 形式简单 , 模型的可解释性非常好。从特征的权重可以看到不同的特征对最后结果的影响 , 某个特征的权重值比较高 , 那么这个特征最后对结果的影响会比较大。
    • 模型效果不错。在工程上是可以接受的(作为baseline) , 如果特征工程做的好 , 效果不会太差 , 并且特征工程可以大家并行开发 , 大大加快开发的速度。
    • 训练速度较快 。分类的时候 , 计算量仅仅只和特征的数目相关。并且逻辑回归的分布式优化SGD发展比较成熟 , 训练的速度可以通过堆机器进一步提高 , 这样我们可以在短时间内迭代还几个版本的模型
    • 资源占用小 , 尤其是内存。因为只需要存储各个维度的特征值。
    • 方便输出结果调整 。逻辑回归可以很方便的的到最后的分类结果 , 因为输出的是每个样本的概率分数 , 我们可以很容易的对这些概率分数进行cutoff , 也就是划分阈值(大于某个阈值的哪一类 , 小于某个阈值的是哪一类)
  • 缺点
    • 准确率并不是很高 。因为形式非常的简单(非常类似线性模型) , 很难去拟合数据的真实分布。
    • 很难处理数据不平衡的问题。 如果我们对于一个正负样本非常不平衡的问题比如正负样本比10000:1 , 我们把所有样本都预测为正也能使损失函数的值比较小 , 但是作为一个分类器 , 它对正负样本的区分能力不会很好。
    • 处理非线性数据比较麻烦。逻辑回归在不引入其他方法的情况下 , 只能处理线性可分的数据 , 或者进一步说 , 处理二分类的问题。
    • 逻辑回归本身无法筛选特征。有时候我们会用gbdt来筛选特征 , 然后再进入逻辑回归。

Regression问题的常规步骤为 :

  • 寻找h函数(即假设估计的函数)
  • 构造J函数(损失函数)
  • 想办法使得J函数最小并求得回归参数(θ)
  • 数据拟合问题
  1. 当梯度下降到一定数值后 , 每次迭代的变化很小 , 这时可以设定一个阈值 , 只要变化小于该阈值 , 就停止迭代 , 而得到的结果也近似于最优解。

  2. 若损失函数的值不断变大 , 则有可能是步长速率a太大 , 导致算法不收敛 , 这时可适当调整a值

对于样本数量非常之多的情况 , 普通的批量梯度下降算法(BGD)会非常耗时 , 靠近极小值是收敛速度减慢 , 因为每次迭代都要遍历所有样本 , 这时可以选择随机梯度下降算法(SGD)

梯度下降需要把m个样本全部带入计算 , 迭代一次计算量为m*n^2 ; 随机梯度下降每次只使用一个样本 , 迭代一次计算量为n^2 , 当m很大时 , 随机梯度下降迭代一次的速度要远高于梯度下降 , 虽然不是每次迭代得到的损失函数都想着全局最优方向 , 但是**整体的方向都是向全局最优解的 , 最终的结果往往是在全局最优解附近 **。

数据拟合问题 :

  • 欠拟合 , 通常是因为特征量选少了
    • 增加特征量
  • 过拟合 , 通常是因为特征量选多了
    • 减少特征量或者正则化
  • 需要的数据

CTR预估中GBDT&LR融合

在CTR预估中 , 如何使用ad_id是一个问题 :
直接将ad_id作为特征建树不可行 , 而one-hot编码过于稀疏 , 为每个ad_id建GBDT树 , 相当于发掘出区分每个广告的特征。而对于曝光不充分的样本即长尾分布 , 无法单独建树。

综合方案为 : 使用GBDT对非ID和ID分别建一类树。

  • 非ID类树 : 不以细粒度的ID建树 , 此类树作为base , 即这些ID一起构建GBDT。即便曝光少的广告、广告主 , 仍可以通过此类树得到有区分的特征、特征组合。
  • ID类树 : 以细粒度的ID建一类树(每个ID构建GBDT) , 用于发现曝光充分的ID对应有区分性的特征、特征组合。如下图 , 当一条样本x进来之后 , 遍历两类树到叶子节点 , 得到的特征作为LR的输入。当AD曝光不充分不足以训练树时 , 其他树恰好作为补充。

总结

对于样本量大的数据 , 线性模型具有训练速度快的特点 , 但线性模型学习能力限于线性可分数据 , 所以就需要特征工程将数据尽可能地从输入空间转换到线性可分的特征空间。GBDT与LR的融合模型 , 其实使用GBDT来发掘有区分度的特征以及组合特征 , 来替代人工组合特征。

融合代码实现 :

import numpy as np # 快速操作结构数组的工具
import matplotlib.pyplot as plt  # 可视化绘制
from sklearn.linear_model import LinearRegression  # 线性回归
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.ensemble import GradientBoostingClassifier,RandomForestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import roc_auc_score,roc_curve,auc
from sklearn.preprocessing import OneHotEncoder


# 弱分类器的数目
n_estimator = 10
# 随机生成分类数据。
X, y = make_classification(n_samples=80000,n_features=20,n_classes=2)

# 切分为测试集和训练集,比例0.5
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.5)
# 将训练集切分为两部分,一部分用于训练GBDT模型,另一部分输入到训练好的GBDT模型生成GBDT特征,然后作为LR的特征。这样分成两部分是为了防止过拟合。
X_train_gbdt, X_train_lr, y_train_gbdt, y_train_lr = train_test_split(X_train, y_train, test_size=0.5)
# 调用GBDT分类模型。
gbdt = GradientBoostingClassifier(n_estimators=n_estimator)
# 调用one-hot编码。
one_hot = OneHotEncoder()
# 调用LR分类模型。
lr = LogisticRegression()


'''使用X_train训练GBDT模型,后面用此模型构造特征'''
gbdt.fit(X_train_gbdt, y_train_gbdt)

X_leaf_index = gbdt.apply(X_train_gbdt)[:, :, 0]  # apply返回每个样本在每科树中所属的叶子节点索引。行数为样本数,列数为树数目。值为在每个数的叶子索引
X_lr_leaf_index = gbdt.apply(X_train_lr)[:, :, 0] # apply返回每个样本在每科树中所属的叶子节点索引。行数为样本数,列数为树数目。值为在每个数的叶子索引
print('每个样本在每个树中所属的叶子索引\n',X_leaf_index)
# fit one-hot编码器
one_hot.fit(X_leaf_index)  # 训练one-hot编码,就是识别每列有多少可取值
X_lr_one_hot = one_hot.transform(X_lr_leaf_index)  # 将训练数据,通过gbdt树,形成的叶子节点(每个叶子代表了原始特征的一种组合)索引,编码成one0-hot特征。
# 编码后的每个特征代表原来的一批特征的组合。

''' 
使用训练好的GBDT模型构建特征,然后将特征经过one-hot编码作为新的特征输入到LR模型训练。
'''

# 使用lr训练gbdt的特征组合
print('使用逻辑回归训练GBDT组合特征的结果')
lr.fit(X_lr_one_hot, y_train_lr)
# 用训练好的LR模型多X_test做预测
y_pred_grd_lm = lr.predict_proba(one_hot.transform(gbdt.apply(X_test)[:, :, 0]))[:, 1]  # 获取测试集正样本的概率
# 根据预测结果输出
fpr, tpr, thresholds = roc_curve(y_test, y_pred_grd_lm)  # 获取真正率和假正率以及门限
roc_auc = auc(fpr, tpr)
print('auc值为\n',roc_auc)
#画图,只需要plt.plot(fpr,tpr),变量roc_auc只是记录auc的值,通过auc()函数能计算出来
plt.plot(fpr, tpr, lw=1, label='area = %0.2f' %  roc_auc)
plt.show()



# 使用lr直接训练原始数据
print('使用逻辑回归训练原始数据集的结果')
lr.fit(X_train_lr, y_train_lr)
# 用训练好的LR模型多X_test做预测
y_pred_grd_lm = lr.predict_proba(X_test)[:, 1]  # 获取测试集正样本的概率
# 根据预测结果输出
fpr, tpr, thresholds = roc_curve(y_test, y_pred_grd_lm)  # 获取真正率和假正率以及门限
roc_auc = auc(fpr, tpr)
print('auc值为\n',roc_auc)
#画图,只需要plt.plot(fpr,tpr),变量roc_auc只是记录auc的值,通过auc()函数能计算出来
plt.plot(fpr, tpr, lw=1, label='area = %0.2f' %  roc_auc)
plt.show()

补充1 : L1和L2正则化的区别 :

L1正则项的等值线是方形,方形与J0的等值线相交时,相交点为顶点的概率很大,w1或w2等于零的概率很大。所以使用L1正则项的解具有稀疏性

L2正则项的等值线是圆,圆与J0的等值线相交时,w1或w2等于零的概率很小。所以使用L2正则项的解不具有稀疏性。

L1和L2正则是比较常见和常用的正则化项,都可以达到防止过拟合的效果。


补充2 : 方差 , 协方差 , 偏差

方差 是 x自身的波动情况
协方差,是x1,x2两个的相关情况 ,若是 x1 = x2 ,那么协方差就是方差了


补充3 : cos , pearson , Jaccard应用场景

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值