date: 2015-01-10 19:55
layout: 'post'
title: 'Blogging Like a Hacker'
tags:
    - "hello"
    - 'world'
status: 'public'

机器学习理论

常问问题

线性回归

# 测试代码:
import os

==逻辑回归(LR)==

考察点:把LR从头到脚都给讲一遍。建模,现场数学推导,每种解法的原理,正则化,LR和maxent模型啥关系,LR为啥比线性回归好。有不少会背答案的人,问逻辑细节就糊涂了。原理都会? 那就问工程,并行化怎么做,有几种并行化方式,读过哪些开源的实现。还会,那就准备收了吧,顺便逼问LR模型发展历史。

原理

cost function(极大似然函数)推导

img
img

如何做分类

逻辑回归作为一个回归函数,如何用于分类问题。 逻辑回归中,对于每个 x,其条件概率 y 的确是一个连续的变量。而逻辑回归中可以设定一个阈值,y 值大于这个阈值的是一类,y 值小于这个阈值的是另外一类。至于阈值的选择,通常是根据实际情况来确定,一般情况下选取 0.5 作为阈值来划分。

🚩为什么可以用似然函数做损失函数

因为目标是要让预测为正的的概率最大,且预测为负的概率也最大,即每一个样本预测都要得到最大的概率,将所有的样本预测后的概率进行相乘都最大,这就能到似然函数了。

w 使得 lnL(w) 最大,用梯度下降或者牛顿法求解都行。

🚩LR的cost function和梯度下降参数迭代公式

img
img

==LR参数求解的优化方法==

梯度下降法,随机梯度下降法,牛顿法,LBFGS,BFGS,OWLQN

极大似然函数无法直接求解,一般是通过对该函数进行梯度下降来不断逼近其最优解。这里需要注意的点是要对梯度下降有一定的了解,就梯度下降本身来看的话就有随机梯度下降,批梯度下降,small batch 梯度下降三种方式,面试官可能会问这三种方式的优劣以及如何选择最合适的梯度下降方式。

LR和线性回归的区别

  1. 线性回归用来做预测, LR用来做分类。

  2. 线性回归的拟合函数本质上是对 输出变量 y 的拟合, 而==逻辑回归的拟合函数是对 label 为1的样本的概率的拟合。==

  3. [公式]
    [公式]
  4. 线性回归用最小二乘法来计算参数,LR用最大似然估计来计算参数。

  5. 🚩 ==线性回归更容易受到异常值的影响,而LR对异常值有较好的稳定性。==

LR 如何实现多分类?

  1. 方式1: 修改逻辑回归的损失函数,使用softmax函数构造模型解决多分类问题

    softmax分类模型会有相同于类别数的输出,输出的值为对于样本属于各个类别的概率,最后对于样本进行预测的类型为概率值最高的那个类别。

  2. 方式2根据每个类别都建立一个二分类器,本类别的样本标签定义为0,其它分类样本标签定义为1

    则有多少个类别就构造多少个逻辑回归分类器。

  3. 若所有类别之间有明显的互斥则使用softmax分类器,若所有类别不互斥有交叉的情况则构造相应类别个数的逻辑回归分类器。

逻辑回归为什么一般性能差?

LR是线性的,不能得到非线性关系,实际问题并不完全能用线性关系就能拟合。

如何用LR解决非线性问题?

将特征离散成高维的01特征可以解决分类模型的非线性问题

🚩 LR 为何要对特征进行离散化

  1. 引入非线性。 逻辑回归属于广义线性模型,表达能力受限;单变量离散化为N个后,每个变量有单独的权重,相当于为模型引入了非线性,能够提升模型表达能力,加大拟合; 离散特征的增加和减少都很容易,易于模型的快速迭代;

  2. 运算速度快。 稀疏向量内积乘法运算速度快,计算结果方便存储,容易扩展

  3. 增加鲁棒性。 离散化后的特征对异常数据有很强的鲁棒性:

比如一个特征是年龄>30是1,否则0。如果特征没有离散化,一个异常数据“年龄300岁”会给模型造成很大的干扰;

  1. 方便交叉与特征组合: 离散化后可以进行特征交叉,

由M+N个变量变为M*N个变量,进一步引入非线性,提升表达能力。

  1. 增加稳定性: 特征离散化后,模型会更稳定,

比如如果对用户年龄离散化,20-30作为一个区间,不会因为一个用户年龄长了一岁就变成一个完全不同的人。当然处于区间相邻处的样本会刚好相反,所以怎么划分区间是门学问。

  1. 简化模型,降低过拟合风险: 特征离散化以后,起到了简化了逻辑回归模型的作用,降低了模型过拟合的风险。

为什么逻辑回归比线性回归要好?

逻辑回归和线性回归首先都是广义的线性回归,但是:

  1. 经典线性模型的优化目标函数是最小二乘,而逻辑回归则是似然函数,

  2. 另外线性回归在整个实数域范围内进行预测,敏感度一致,而分类范围,需要在[0,1]。逻辑回归就是一种减小预测范围,将预测值限定为[0,1]间的一种回归模型,因而对于这类问题来说,逻辑回归的鲁棒性比线性回归的要好

逻辑回归与最大熵模型MaxEnt的关系?

逻辑回归跟最大熵模型没有本质区别。逻辑回归是最大熵对应类别为二类时的特殊情况,也就是当逻辑回归类别扩展到多类别时,就是最大熵模型

逻辑回归的优缺点

优点:

缺点:

🚩工程上,怎么实现LR的并行化?有哪些并行化的工具

🚩LR和SVM的联系与区别?

SVM

【机器学习】支持向量机 SVM(非常详细)

【机器学习】支持向量机 SVM(非常详细)

K最近邻(KNN)分类算法

k-means聚类算法(无监督)

岭回归模型(Ridge)

决策树(DT)

决策树的生成过程就是使用满足划分准则的特征不断的将数据集划分为纯度更高,不确定性更小的子集 的过程。对于当前数据集D的每一次的划分,都希望根据某特征划分之后的各个子集的纯度更高,不确定性更小。

信息增益( ID3算法 )

在熵的理解那部分提到了,熵可以表示样本集合的不确定性,熵越大,样本的不确定性就越大。因此可以使用划分前后集合熵的差值来衡量使用当前特征对于样本集合D划分效果的好坏

做法:计算使用所有特征划分数据集D,得到多个特征划分数据集D的信息增益,从这些信息增益中选 择最大的,因而当前结点的划分特征便是使信息增益最大的划分所使用的特征。

熵的理解:为了精确地定义信息增益,我们先定义信息论中广泛使用的一个度量标准称为熵(entropy)

信息增益比( C4.5算法 )

基尼指数( CART算法 -分类树)

定义:基尼指数(基尼不纯度):表示在样本集合中一个随机选中的样本被分错的概率

公式:基尼指数(基尼不纯度)= 样本被选中的概率 * 样本被分错的概率

image-20210801213355246

注意: Gini指数越小表示集合中被选中的样本被分错的概率越小,也就是说集合的纯度越高,反之,集合越不纯。

  1. 🚩 pk表示选中的样本属于k类别的概率,则这个样本被分错的概率是(1-pk)
  2. 样本集合中有K个类别,一个随机选中的样本可以属于这k个类别中的任意一个,因而对类别就加和
  3. 当为二分类是,Gini(P) = 2p(1-p)

集成学习

Boosting方法的例子

bagging方法

bagging方法的例子

GBDT

RF和GBDT

  1. 首先gbdt 是通过采用加法模型(即基函数的线性组合),以及不断减小训练过程产生的残差来达到将数据分类或者回归的算法。
  2. gbdt通过多轮迭代, 每轮迭代产生一个弱分类器,每个分类器在上一轮分类器的梯度(如果损失函数是平方损失函数,则梯度就是残差值)基础上进行训练。
  3. 🚩 对弱分类器的要求一般是足够简单,并且是低方差和高偏差的。因为训练的过程是通过降低偏差来不断提高最终分类器的精度(此处是可以证明的)。
  4. 弱分类器一般会选择为==CART TREE==(也就是分类回归树)。
  5. 由于上述高偏差和简单的要求,每个分类回归树的深度不会很深。最终的总分类器是将每轮训练得到的弱分类器加权求和得到的(也就是加法模型)。

RF和GBDT的区别?

相同点

不同点

比较LR和GBDT

先说说LR和GBDT的区别

说说什么情景下GBDT不如LR?

当在高维稀疏特征的场景下,LR的效果一般会比GBDT好

原因如下:

先看一个例子: 假设一个二分类问题,label为0和1,特征有100维,如果有1w个样本,但其中只要10个正样本1,而这些样本的特征 f1的值全为1,而其余9990条样本的f1特征都为0(在高维稀疏的情况下这种情况很常见)。

我们都知道在这种情况下,树模型很容易优化出一个使用fyu’yuya’s’d’f’j’ka特征作为重要分裂节点的树,因为这个结点直接能够将训练数据划分的很好,但是当测试的时候,却会发现效果很差,因为这个特征f1只是刚好偶然间跟y拟合到了这个规律,这也是我们常说的过拟合。

那么这种情况下,如果采用LR的话,应该也会出现类似过拟合的情况呀:y = W1 x f1 + Wi x fi+….,其中 W1特别大以拟合这10个样本。为什么此时树模型就过拟合的更严重呢?

仔细想想发现,因为现在的模型普遍都会带着正则项,而 LR 等线性模型的正则项是对权重的惩罚,也就是 W1一旦过大,惩罚就会很大,进一步压缩 W1的值,使他不至于过大。但是,树模型则不一样,树模型的惩罚项通常为叶子节点数和深度等,而我们都知道,对于上面这种 case,树只需要一个节点就可以完美分割9990和10个样本,一个结点,最终产生的惩罚项极其之小

这也就是为什么在高维稀疏特征的时候,线性模型会比非线性模型好的原因了:带正则化的线性模型比较不容易对稀疏特征过拟合。

XGBoost

简单介绍一下XGBoost。

XGBoost与GBDT的不同?

XGBoost类似于GBDT的优化版,不论是精度还是效率上都有了提升。都是聚焦残差,不过与GBDT相比,具体的优点有:

XGBoost的优点

  1. 精度高:GBDT 只用到一阶泰勒展开,而 XGBoost 对损失函数进行了二阶泰勒展开。引入二阶导一方面是为了增加精度,另一方面也是为了能够自定义损失函数。
  2. 灵活性更强:传统GBDT以CART作为基分类器,xgboost还支持线性分类器,这时xgboost相当于带L1和L2正则化项的逻辑斯蒂回归(分类问题)或者线性回归(回归问题)。
  3. 正则化:在目标函数中加入了正则项,用于控制模型的复杂度。正则项里包含了树的叶子节点个数、叶子节点权重的 L2 范式。正则项降低了模型的方差,使学习出来的模型更加简单,有助于防止过拟合。
  4. Shrinkage:相当于学习速率。XGBoost 在进行完一次迭代后,会将叶子节点的权重乘上该系数,主要是为了削弱每棵树的影响,让后面有更大的学习空间
  5. 列抽样:借鉴了随机森林的做法,支持列抽样,不仅能降低过拟合,还能减少计算;
  6. 缺失值处理:对于特征的值有缺失的样本,xgboost可以自动学习出它的分裂方向。
  7. 可以并行化操作:块结构可以很好的支持并行计算。
  8. 可并行的近似直方图算法:树节点在进行分裂时,我们需要计算每个特征的每个分割点对应的增益,即用贪心法枚举所有可能的分割点。当数据无法一次载入内存或者在分布式情况下,贪心算法效率就会变得很低,所以xgboost还提出了一种可并行的近似直方图算法,用于高效地生成候选的分割点。
  9. 相当于预剪枝:当增益大于阈值时才让节点分裂,上式中的gamma即阈值,它是正则项里叶子节点数T的系数,所以xgboost在优化目标函数的同时相当于做了预剪枝。

XGBoost的缺点

  1. 节点分类需要遍历数据集:虽然利用预排序和近似算法可以降低寻找最佳分裂点的计算量,但在节点分裂过程中仍需要遍历数据集;
  2. 预排序空间复杂度高:预排序过程的空间复杂度过高,不仅需要存储特征值,还需要存储特征对应样本的梯度统计值的索引,相当于消耗了两倍的内存。
  3. 找到特别精确的分割点,可能存在过拟合。

为什么使用泰勒二阶展开?

为什么可以并行训练?

为什么快?

防止过拟合的方法?

XGBoost在设计时,为了防止过拟合做了很多优化,具体如下:

如何处理缺失值?

XGBoost模型的一个优点就是允许特征存在缺失值。对缺失值的处理方式如下:(稀疏感知算法)

🚩叶子结点的权重计算?

XGBoost目标函数最终推导形式如下:

图片
图片

利用一元二次函数求最值的知识,当目标函数达到最小值Obj星时,每个叶子结点的权重为wj*。

具体公式如下:

图片
图片

Gj代表在叶子节点j上,所有样本的一阶导数值之和;Hj代表在叶子节点j上,所有样本的二阶导数值之和。最终的obj叫做结构分数,结构分数越小,代表生成的树的结构就越好(让残差尽可能的小)。

一棵树的停止生长条件?

如何处理不平衡数据?

对于不平衡的数据集,例如用户的购买行为,肯定是极其不平衡的,这对XGBoost的训练有很大的影响,XGBoost有两种自带的方法来解决:

如何对树进行剪枝?

如何选择最佳分裂点?

XGBoost在训练前预先将特征按照特征值进行了排序,并存储为block结构,以后在结点分裂时可以重复使用该结构。

选择最佳分裂点的方法有两种:全局扫描法和加权分位数算法。

全局扫描法:可以采用特征并行的方法利用多个线程分别计算每个特征的最佳分割点(对每个特征的每个可能的分割点都计算其分裂增益),根据每次分裂后产生的增益,最终选择增益最大的那个特征的特征值作为最佳分裂点。

加权分位数算法:如果在计算每个特征的最佳分割点时,对每个样本都进行遍历,计算复杂度会很大,这种全局扫描的方法并不适用大数据和样本特征的取值较多的场景。XGBoost还提供了一种加权分位数算法(直方图近似算法),对特征排序后仅选择常数个候选分裂位置作为候选分裂点,选择最佳分裂点时,仅仅需要在候选分裂点中进行选择,极大提升了结点分裂时的计算效率。

(相当于对分裂点进行了分桶,尽量让左右子树的loss分布均匀(利用二阶导数值hi根据每一个样本对拟合残差的贡献程度进行加权),这样先选出了候选分割点,我们只需要保存每个桶中样本的一阶导数值的和、二阶导数值的和,然后对于每个候选分割点,计算分裂增益时,只需要将分割点左边的桶的值进行求和与右边的桶的值求和进行比较即可。因为桶的个数肯定小于样本的数量,所以得以加速)

可扩展性如何体现?

如何评价特征的重要性?

gain: 计算使用 特征i分裂节点的分裂增益之和/所有特征分裂节点的分裂增益之和 ,得到重要性。

注意:覆盖范围这里指的是一个特征用作分割点后,其影响的样本数量,即有多少样本经过该特征分割到两个子节点。

为什么对缺失值不敏感?

对存在缺失值的特征,一般的解决方法是:

一些模型如SVM和KNN,其模型原理中涉及到了对样本距离的度量,如果缺失值处理不当,最终会导致模型预测效果很差。

而树模型对缺失值的敏感度低,大部分时候可以在数据缺失时时使用。原因就是,一棵树中每个结点在分裂时,寻找的是某个特征的最佳分裂点(特征值),完全可以不考虑存在特征值缺失的样本,也就是说,如果某些样本缺失的特征值缺失,对寻找最佳分割点的影响不是很大。

XGBoost对缺失数据有特定的处理方法:训练时,根据叶子结点分裂增益公式,将该特征值missing的样本分别分配到左叶子结点和右叶子结点,两种情形都计算一遍后,选择放到分裂后增益最大的那个方向(左分支或是右分支),作为预测时特征值缺失样本的默认分支方向(右子节点)。

因此,对于有缺失值的数据在经过缺失处理后:

参数调优的一般步骤?

首先需要初始化一些基本变量,例如:

learning_rate:学习率(收缩系数)

n_estimators:树的个数 一般会设置early_stopping_rounds=200,如果新增加200棵树都不降低损失,那么就停止建树, verbose=50,每50次输出一次结果。

max_depth:构建树的深度,越大越容易过拟合;

min_child_weight:孩子节点中最小的样本权重和。当引入一次分裂后,重新计算新生成的左、右两个叶子结点的样本权重和。如果任一个叶子结点的样本权重低于某一个阈值,会放弃此次分裂。这涉及到一个超参数:最小样本权重和,是指如果一个叶子节点包含的样本数量太少也会放弃分裂,防止树分的太细。

gamma:损失下降多少才进行分裂(控制叶子节点的个数,分裂后节点就增加了),也称作最小划分损失min_split_loss,只有当划分后的损失函数损失下降值大于此阈值,当前叶子节点才继续划分。

subsample:对训练样本的随机采样比例

colsample_bytree:对特征的采样比例

scale_pos_weight:正样本在数据集中占得权重(可以解决正负样本比例不均衡的问题)

alpha :L1正则化系数,try 1e-5, 1e-2, 0.1, 1, 100

lambda :L2正则化系数

(1) 确定learning rate和estimator的数量

learning rate可以先用0.1,用cv来寻找最优的estimators

(2) max_depth和 min_child_weight

我们调整这两个参数是因为,这两个参数对输出结果的影响很大。我们首先将这两个参数设置为较大的数,然后通过迭代的方式不断修正,缩小范围。

max_depth,每棵子树的最大深度,check from range(3,10,2)。

min_child_weight,子节点的权重阈值,check from range(1,6,2)。

如果一个结点分裂后,它的所有子节点的权重之和都大于该阈值,该叶子节点才可以划分。

(3) gamma

也称作最小划分损失min_split_loss,check from 0.1 to 0.5,指的是,对于一个叶子节点,当对它采取划分之后,损失函数的降低值的阈值。

(4) subsample, colsample_bytree

subsample是对训练的采样比例

colsample_bytree是对特征的采样比例

both check from 0.6 to 0.9

(5) 正则化参数

alpha 是L1正则化系数,try 1e-5, 1e-2, 0.1, 1, 100

lambda 是L2正则化系数

(6) 降低学习率

降低学习率的同时增加树的数量,通常最后设置学习率为0.01~0.1

如果过拟合了怎么解决?

当出现过拟合时,有两类参数可以缓解:

第一类参数:用于直接控制模型的复杂度。包括max_depth,min_child_weight,gamma 等参数(即控制树的深度和叶子节点的个数);

第二类参数:用于增加随机性,从而使得模型在训练时对于噪音不敏感。包括subsample,colsample_bytree(即对样本集采样、对特征采样);

还有就是直接减小learning rate,但需要同时增加estimator 参数。(即减小收缩系数,让叶子节点的权重更小,让后面的树有更大的学习空间)。

XGBoost和LightGBM的区别?

图片
图片

(1)树生长策略:XGB采用level-wise的分裂策略,LGB采用leaf-wise的分裂策略。XGB对每一层所有节点做无差别分裂,(该策略遍历一次数据可以同时分裂同一层的叶子,容易进行多线程优化,也好控制模型的复杂度,不容易过拟合)但是可能有些节点增益非常小,对结果影响不大,带来不必要的开销(一层一层的走,不管它效果好不好)。Leaf-wise是在所有叶子节点中选取分裂收益最大的节点进行的,(该策略可以降低更多的误差,得到更好的精度),但是很容易出现过拟合问题,所以需要对最大深度做限制 。

(2)分割点查找算法:XGB使用特征预排序算法,LGB使用基于直方图的切分点算法,其优势如下:

(XGB寻找最优分裂点的复杂度=特征数量 X 分裂节点数量 X 样本的数量,LGB分别从这三个方面进行了优化)

但实际上xgboost的近似直方图算法(加权分位数算法)也类似于lightgbm这里的直方图算法,为什么xgboost的近似算法比lightgbm还是慢很多呢?

xgboost在每一层都动态构建直方图, 因为xgboost的直方图算法不是针对某个特定的feature,而是所有feature共享一个直方图(每个样本的权重是二阶导),所以每一层都要重新构建直方图,而lightgbm中对每个特征都有一个直方图,所以构建一次直方图就够了。(XGB在每一层的分裂过程中,都需要对所有的特征重新选择候选分割点(即重新构建直方图),因为XGB在选取候选分割点的时候,考虑的是想让loss在左右子树上分布均匀,(二阶导数代表样本对降低loss的贡献程度,所以可以根据二阶导数值累加来选取候选分割点),每次分裂之后,节点中的样本数量发生变化,每个节点的二阶导数和也是变化的,所以需要重新构建直方图。)

(3)支持离散变量:XGB无法直接输入类别型变量,因此需要事先对类别型变量进行编码(例如独热编码),而LightGBM可以直接处理类别型变量(采用many-vs-many的切分方式,将类别特征分为两个子集,实现类别特征的最优切分。基本思想为每次分组时,都会根据训练目标对类别特征进行分类:在枚举分割点之前,先把直方图按照每个类别对应的label均值进行排序,然后按照排序的结果依次枚举最优分割点)。

(4)缓存命中率:XGB使用Block结构的一个缺点是取梯度的时候,是通过索引来获取的,而这些梯度的获取顺序是按照特征的大小顺序的,(并且不同特征的访问顺序也不同),这将导致非连续的内存访问,可能使得CPU cache缓存命中率低,从而影响算法效率。而LGB是基于直方图分裂特征的,梯度信息都存储在一个个bin(桶:离散值索引?)中,所以访问梯度是连续的,缓存命中率高。

(5)LightGBM 与 XGboost 的并行策略不同:

LightGBM的调参过程?

learning_rate:学习率,预先设定一个较高的值,如0.1

boosting_type: 默认选择gbdt

n_estimators/num_boost_round:迭代次数(残差树的个数)

max_depth:树的深度

num_leaves:叶子个数,一般小于2**max_depth

min_data_in_leaf:节点中样本点最小个数

max_bin:最大分桶的个数(离散化时取值的最大值)

feature_fraction: 特征的采样率

bagging_fraction: 样本的采样率

bagging_freq: k,意味每k次迭代(每生成k棵树)执行bagging

lambda_l1: L1正则化参数

lambda_l2: L2正则化参数

min_split_gain: 分裂最小增益阈值,分裂增益小于阈值则终止分裂

LightGBM的调参步骤

第一步:学习率和迭代次数我们先把学习率先定一个较高的值,这里取 learning_rate = 0.1,其次确定估计器boosting/boost/boosting_type的类型,不过默认都会选gbdt。

'boosting_type'/'boosting': 'gbdt'
'objective': 'binary'
'metric': 'auc'
 
# 以下是选择的初始值
'max_depth': 5     # 由于数据集不是很大,所以选择了一个适中的值,其实4-10都无所谓。
'num_leaves': 30   # 由于lightGBM是leaves_wise生长,官方说法是要小于2^max_depth
'subsample'/'bagging_fraction':0.8           # 数据采样
'colsample_bytree'/'feature_fraction': 0.8   # 特征采样

迭代的次数,也可以说是残差树的数目,参数名为n_estimators/num_iterations/num_round/num_boost_round。我们可以先将该参数设成一个较大的数,然后在cv结果中查看最优的迭代次数lgb.cv(params, data_train, num_boost_round=1000, nfold=5, stratified=False, shuffle=True, metrics='auc',early_stopping_rounds=50,seed=0)

我们根据以上结果,就可以取n_estimators=188

第二步:确定max_depth和num_leaves这是提高精确度的最重要的参数。这里我们引入sklearn里的GridSearchCV()函数进行搜索。

from sklearn.grid_search import GridSearchCV

params_test1={'max_depth': range(3,8,1), 'num_leaves':range(5, 100, 5)}
              
gsearch1 = GridSearchCV(estimator = lgb.LGBMClassifier(boosting_type='gbdt',objective='binary',metrics='auc',learning_rate=0.1, n_estimators=188, max_depth=6, bagging_fraction = 0.8,feature_fraction = 0.8), 
                       param_grid = params_test1, scoring='roc_auc',cv=5,n_jobs=-1)
gsearch1.fit(X_train,y_train)
gsearch1.grid_scores_, gsearch1.best_params_, gsearch1.best_score_

根据结果,我们取max_depth=4, num_leaves=10

第三步:确定min_data_in_leaf和max_bin

params_test2={'max_bin': range(5,256,10), 'min_data_in_leaf':range(1,102,10)}
              
gsearch2 = GridSearchCV(estimator = lgb.LGBMClassifier(boosting_type='gbdt',objective='binary',metrics='auc',learning_rate=0.1, n_estimators=188, max_depth=4, num_leaves=10,bagging_fraction = 0.8,feature_fraction = 0.8), 
                       param_grid = params_test2, scoring='roc_auc',cv=5,n_jobs=-1)
gsearch2.fit(X_train,y_train)
gsearch2.grid_scores_, gsearch2.best_params_, gsearch2.best_score_

这个结果就不显示了,根据结果,我们取min_data_in_leaf=51,max_bin in=15。

第四步:确定feature_fraction、bagging_fraction、bagging_freq

第五步:确定lambda_l1和lambda_l2

第六步:确定 min_split_gain

第七步:降低学习率,增加迭代次数,验证模型

model=lgb.LGBMClassifier(boosting_type='gbdt',objective='binary',metrics='auc',learning_rate=0.01, n_estimators=1000, max_depth=4, num_leaves=10,max_bin=15,min_data_in_leaf=51,bagging_fraction=0.6,bagging_freq= 0, feature_fraction= 0.8,
lambda_l1=1e-05,lambda_l2=1e-05,min_split_gain=0)
model.fit(X_train,y_train)
y_pre=model.predict(X_test)
print("acc:",metrics.accuracy_score(y_test,y_pre))
print("auc:",metrics.roc_auc_score(y_test,y_pre))

这样会发现, 调完参之后, 比使用默认参数的acc,auc都会有所提高。还有一种是LightGBM的cv函数调参, 这个比较省事,写好代码自动寻优,但是需要调参经验,如何设置一个好的参数范围, 这个不写了,篇幅太长,具体的看最后面那个链接吧。

sklearn里的GridSearchCV()函数

XGBoost其他问题(极端梯度提升)

XGBoost怎么调参

(1) XGBoost重要参数

xgboost主要分为三类参数:

1.通用参数 general parameter

booster: 每次迭代的模型选择,gbtree或者gbliner

silent: 控制打印信息,(过时,被verbosity代替)

nthread:并行的线程数,默认使用最大的核数

2.Booster参数:Parameters for Tree Booster

eta: shinkage防止过拟合,默认0.3

gamma:公式中叶子节点个数的参数,也就是分类后损失函数的增益,增益大于这个阈值,才会对节点进行分裂。

max_depth:默认为0,树的最大深度,用来避免过拟合

min_child_weight: 最小的叶子节点样本权重和,如果节点分裂造成一个叶子节点的样本权重和小于该值,则放弃分裂。

max_delta_step: 该参数仙子每棵树权重改变的最大步长。一般用不到,但是在数据样本极度不平衡时,可能对逻辑回归有帮助。

subsample:训练样本随机采样的比例。

colsample_bytree, colsample_bylevel, colsample_bynode: 列采样的参数设置。bytree表示在构建每棵树的时候使用。bylevel表示构建每层节点的时候使用,bynode在每次分裂的时候使用。

lambda: L2正则化项。默认为1.

alpha:L1的正则化项.

scale_pos_weight: 控制正负样本的平衡用于不平衡数据

3.学习任务参数:控制训练目标的表现

objective:定义损失函数。常用值有binary:logistic; multi:softmax; multi:softprob

eval_metic: 验证集的评估指标。rmse, mae, logloss, error, merror, mlogloss, auc

seed:随机数的种子,设置它可以复现随机数的结果,也可以用于调整参数。

(2) XGBoost调参技巧

a.当出现过拟合时,有两类参数可以缓解:

也可以直接检查步长eta,此时需要增加num_round参数.

b.当遇到数据不平衡时(如广告点击率预测任务),有两种方式提高模型的预测性能:

可以通过scale_pos_weight参数来平衡正负样本的权重; 使用AUC来评估。

不能重新平衡正负样本**;设置max_delta_step为一个有限的值(如1),从而有助于收敛

LightGBM

原理

LightGBM (Light Gradient Boosting Machine)是一个实现GBDT算法的框架,支持高效率的并行训练。

LigthGBM也是一种boosting算法,它由微软开源,是XGB的一种轻量级实现,用于分类或者回归问题。在准确率与XGB相近的情况下,大大减少运行时长和内存占用

LightGBM提出的主要原因就是为了解决GBDT在海量数据遇到的问题,让GBDT可以更好更快地用于工业实践。

优点

🚩与XGB相比的优化

  1. 单边梯度抽样算法 - 过滤掉梯度小的样本,减少了大量的计算;
  2. 直方图算法 - 将遍历样本转变为遍历直方图,极大的降低了时间复杂度;
  3. 互斥特征捆绑算法(EFB)
  4. 带深度限制的 Leaf-wise 算法 - 减少了很多不必要的计算量
img
img

Histogram算法

直方图算法的基本思想是先把连续的浮点特征值离散化成k个整数(其实又是分桶的思想,而这些桶称为bin,比如[0,0.1)→0, [0.1,0.3)→1),同时构造一个宽度为k的直方图。

在遍历数据的时候,根据离散化后的值作为索引在直方图中累积统计量,当遍历一次数据后,直方图累积了需要的统计量,然后根据直方图的离散值,遍历寻找最优的分割点。

img
img

使用直方图算法有很多优点。首先,最明显就是内存消耗的降低,直方图算法不仅不需要额外存储预排序的结果,而且可以只保存特征离散化后的值,而这个值一般用8位整型存储就足够了,内存消耗可以降低为原来的1/8。然后在计算上的代价也大幅降低,预排序算法每遍历一个特征值就需要计算一次分裂的增益,而直方图算法只需要计算k次(k可以认为是常数),时间复杂度从O(#data#feature)优化到O(k#features)。

带深度限制的Leaf-wise的叶子生长策略

在XGBoost中,树是按层生长的,称为Level-wise tree growth,同一层的所有节点都做分裂,最后剪枝,如下图所示:

img
img

Level-wise过一次数据可以同时分裂同一层的叶子,容易进行多线程优化,也好控制模型复杂度,不容易过拟合。但实际上Level-wise是一种低效的算法,因为它不加区分的对待同一层的叶子,带来了很多没必要的开销,因为实际上很多叶子的分裂增益较低,没必要进行搜索和分裂。

在Histogram算法之上,LightGBM进行进一步的优化。首先它抛弃了大多数GBDT工具使用的按层生长 (level-wise) 的决策树生长策略,而使用了带有深度限制的按叶子生长 (leaf-wise)算法。

img
img

Leaf-wise则是一种更为高效的策略,每次从当前所有叶子中,找到分裂增益最大的一个叶子,然后分裂,如此循环。因此同Level-wise相比,在分裂次数相同的情况下,Leaf-wise可以降低更多的误差,得到更好的精度。Leaf-wise的缺点是可能会长出比较深的决策树,产生过拟合。因此LightGBM在Leaf-wise之上增加了一个最大深度的限制,在保证高效率的同时防止过拟合。

直方图差加速

LightGBM另一个优化是Histogram(直方图)做差加速。一个容易观察到的现象:一个叶子的直方图可以由它的父亲节点的直方图与它兄弟的直方图做差得到。通常构造直方图,需要遍历该叶子上的所有数据,但直方图做差仅需遍历直方图的k个桶。

利用这个方法,LightGBM可以在构造一个叶子的直方图后,可以用非常微小的代价得到它兄弟叶子的直方图,在速度上可以提升一倍。

直接支持类别特征

实际上大多数机器学习工具都无法直接支持类别特征,一般需要把类别特征,转化到多维的0/1特征,降低了空间和时间的效率。而类别特征的使用是在实践中很常用的。基于这个考虑,LightGBM优化了对类别特征的支持,可以直接输入类别特征,不需要额外的0/1展开。并在决策树算法上增加了类别特征的决策规则。在Expo数据集上的实验,相比0/1展开的方法,训练速度可以加速8倍,并且精度一致。据我们所知,LightGBM是第一个直接支持类别特征的GBDT工具。

CatBoost

极简示例:

import numpy as np
import catboost as cb

# 读数据
train_data = np.random.randint(0, 100, size=(100, 10))
train_label = np.random.randint(0, 2, size=(100))
test_data = np.random.randint(0,100, size=(50,10))
print(train_data,train_label,test_data)

# 建立模型
model = cb.CatBoostClassifier(iterations=2, depth=2, learning_rate=0.5, loss_function='Logloss',
                              logging_level='Verbose')
# 模型拟合
model.fit(train_data, train_label, cat_features=[0,2,5])
# 预测
preds_class = model.predict(test_data)
preds_probs = model.predict_proba(test_data)
print('class = ',preds_class)
print('proba = ',preds_probs)

机器学习中的距离计算方法

设空间中两个点为(x1,y1),(x2,y2):

欧式距离:

img
img

曼哈顿距离(城区距离):

​ d(i,j)=|X1-X2|+|Y1-Y2|

余弦距离:

img
img

欧式距离与余弦距离的对比?

1.欧式距离的数值受到维度的影响,余弦相似度不受维度影响, 在高维的情况下也依然保持低维完全相同时相似度为1等性质。

2.欧式距离体现的是距离上的绝对差异,余弦距离体现的是方向上的相对差异。

切比雪夫距离(横向纵向最大距离):

​ dis=max(abs(x1-x2),abs(y1-y2))

其他问题

如何做特征组合

特征组合的思想很简单,通过将单独的特征进行组合(相乘或求笛卡尔积)而形成的合成特征。比如属性A有三个特征,属性B有两个特征,笛卡尔积后就有六个组合特征,然后用one hot 或其他embedding方式给新的特征编码

为什么朴素贝叶斯如此“朴素”?

因为它假定所有的特征在数据集中的作用是同样重要和独立的。正如我们所知,这个假设在现实世界中是很不真实的,因此,说朴素贝叶斯真的很“朴素”。

启发式搜索算法总结

启发式搜索算法蕴含着许多人生哲学,它虽不是数学方法,其思想更类似于人类解决问题的思想和一些人生中总结的道理,值得好好体会。最后用网上一段描述各种搜索算法的例子来作为总结:

为了找出地球上最高的山,一群有志气的兔子们开始想办法。 (1)兔子朝着比现在高的地方跳去。他们找到了不远处的最高山峰。但是这座山不一定是珠穆朗玛峰。这就是爬山法,它不能保证局部最优值就是全局最优值。 (2)兔子喝醉了。他随机地跳了很长时间。这期间,它可能走向高处,也可能踏入平地。但是,他渐渐清醒了并朝他踏过的最高方向跳去。这就是模拟退火。 (3)兔子们知道一个兔的力量是渺小的。他们互相转告着,哪里的山已经找过,并且找过的每一座山他们都留下一只兔子做记号。他们制定了下一步去哪里寻找的策略。这就是禁忌搜索。 (4)兔子们吃了失忆药片,并被发射到太空,然后随机落到了地球上的某些地方。他们不知道自己的使命是什么。但是,如果你过几年就杀死一部分海拔低的兔子,多产的兔子们自己就会找到珠穆朗玛峰。这就是遗传算法。

如何用根据特征重要性筛选特征

利用模型的 feature_importances_方法。

阈值的选择会进行遍历并选择最佳的阈值

from numpy import sort
from sklearn.feature_selection import SelectFromModel # Fit model using each importance as a threshold
thresholds = sort(model_XGB.feature_importances_)
for thresh in thresholds:
  # select features using threshold
  selection = SelectFromModel(model_XGB, 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))

谈谈判别式模型和生成式模型?

判别方法:由数据直接学习决策函数 Y = f(X),或者由条件分布概率 P(Y|X)作为预测模型,即判别模型。

生成方法:由数据学习联合概率密度分布函数 P(X,Y),然后求出条件概率分布P(Y|X)作为预测的模型,即生成模型。

由生成模型可以得到判别模型,但由判别模型得不到生成模型。

常见的判别模型有:K近邻、SVM、决策树、感知机、线性判别分析(LDA)、线性回归、传统的神经网络、逻辑斯蒂回归、boosting、条件随机场

常见的生成模型有:朴素贝叶斯、隐马尔可夫模型、高斯混合模型、文档主题生成模型(LDA)、限制玻尔兹曼机