mGBDT论文研究

这篇文章 是看了冯霁博士的论文总结,一部分是个人看法,如果错误欢迎指正

简介

在过去的十年中,深度神经网络的发展在机器学习领域取得了显著的进步。通过构建层次结构或 “深度” 结构,该模型能够从监督和无监督设置的原始数据中学习良好的表示,这被认为是其关键成分。成功的应用领域包括计算机视觉、语音识别、自然语言处理等.

目前,几乎所有的深度神经网络都是利用随机梯度下降的反向传播作为训练过程中对训练更新参数的主力。的确,当模型由可微组件组成(例如,带有非线性**函数的加权和)时,反向传播仍然是目前的最佳选择。其他一些方法如目标传播作为神经网络训练的一种替代方法已经被提出,但其有效性和普及程度仍处于早期阶段。例如,已有的工作证明了目标传播最多可以和反向传播一样好,并且在实践中,经常需要额外的反向传播来进行微调。换句话说,旧的、好的反向传播仍然是训练可微学习系统(如神经网络)最有效的方法。另一方面,探索利用不可微模块构建多层或深层模型的可能性不仅具有学术意义,而且具有重要的应用潜力。例如,

诸如随机森林或梯度提升决策树(GBDT)之类的树集成仍然是在各种领域中对离散或表格数据进行建模的主要方式,因此将在树集成的数据中获得所学习的分层分布式表示。

由于没有机会使用链式法则传播误差,因此不可能进行反向传播。这就产生了两个基本问题:首先,我们能否构造一个具有不可微组件的多层模型,使中间层中的输出可以被视为分布式表示?第二,如果是这样的,如何在不借助反向传播的情况下共同训练这些模型?本文的目的就是提供这样的一种尝试。

这是第一次尝试用树集成来构建多层模型,该模型能够构建具有自适应模型复杂性的多层结构,并在广泛的任务范围内有竞争性表现。与此同时,如何利用forest构建多层次模型,明确地检验其表示学习能力,目前还不清楚。由于前人的许多研究表明,多层分布表示法可能是深度神经网络成功的关键原因,因此对表示学习法进行探索是必要的

之前的模式

考虑具有m-1中间层和一个最终输出的多层前馈结构层. 表示 o i where i ∈ {0,1,2,...,M} 作为每一层的输入层和输出层 o M . 对于特定的输入数据x , 相关的每一层的输入在Rdi, where i ∈ {0,1,2,...,M} .因此,学习任务是学习 F i : R d i−1 → R d i的映射,对于每层 i > 0 , 使得最终输出Om最小化训练集的经验损失L,L一般选择带均方误差或交叉熵带额外的正则化项,在无监督设置中,所需要的输出Y可以是训练数据本身,导致自编码和损失函数的是输出和原始输入之间的重建误差.

当每个训练任务Fi是参数和可微的,这种学习任务可以用一种有效的方式利用反向传播。其基本例程是利用链规则计算各层各参数损失函数的梯度,然后对参数进行梯度下降更新。一旦训练完成,中间层的输出可以看作是模型学习到的新表示。这种分层的密集表示可以解释为原始输入的多层抽象,并被认为是深度模型成功的关键。

本文的模式

然而,每个训练任务Fi不可微甚至是非参数时,反向传播不再适用,因为不能计算损失函数和对其参数的导数,下面会解决这个问题当每一个训练任务是增强决策树时.

mGBDT论文研究

 

首先,在每一次迭代t时,假设Fi t-1 是从先前的迭代中得到的,我们需要得到一个"伪逆"映射Git与每一个Fi t-1 配对就像Git(Fi t-1(oi-1)) ~~oi-1,这可以通过最小化重建损失函数的期望值来实现:

mGBDT论文研究

 

这里损失loss L(inverse)可以被重建loss,像自动编码器一样,随机噪声注入经常被建议,也就是说,如果利用纯重构误差测度,最好设置L(inverse)为

mGBDT论文研究

通过这样做模型在反向映射被强制学习如何将相邻的训练数据映射到右侧歧管。此外,这种随机性注入也有助于通过将逆映射方向视为一条可作为未来工作的生成路径用于探索。

第二,一旦我们更新 GiT我,我们可以使用它作为给定和更新前向映射的前向层 F我1。这里的关键是分配一个伪标签Z(i-1),每层的伪标签定义为Z(i-1)t=Gi(zit)。也就是说,在迭代 t 中,对于所有中间层,每层的伪标签可以 “对齐”,并从输出层传播到输入层。然后,计算每一层的伪标签,Fi(t-1)可以遵循一个可以沿着梯度上升的一步向mGBDT论文研究伪残差移动,就像一个标准的GBDT.换句话说,在迭代t时,输出层f m计算其伪标签。然后通过反函数生成所有其他层的伪标签,然后每个F i,因此可以相应地更新。一旦所有的f i都更新了,程序就可以转到下一次迭代更新g i。在实践中,建议自下而上更新,每个f i可以向其当前伪标签执行几轮加法增强步骤。

在训练神经网络时,可以通过分配随机高斯噪声来完成初始化。

噪声对每个参数,然后程序可以进入下一阶段进行参数更新。为了这里描述的树结构模型,从分发所有可能的树配置,因此,而不是在随机的,我们产生一些高斯噪声作为中间层的输出,训练一些非常小树获得F 0i,其中索引0表示在此初始化阶段获得的树结构。然后,训练过程可以继续迭代更新正向映射和反向映射。

mGBDT论文研究

 

伪代码的步骤详解:

1.数据X经过m层树后可以得到预测值Om,计算OM即最后一层的伪标签

mGBDT论文研究

 

2.根据损失更新反函数G,在数据O中添加噪音,利用损失去更新G函数

mGBDT论文研究

 

3.G函数更新后根据新的最后一层伪标签计算每一层新的伪标签

mGBDT论文研究

 

4.根据新的伪标签去更新F树参数,得到新的预测值Om

mGBDT论文研究

 

5.下轮循环,到达迭代次数结束

 

源码地址:

https://github.com/kingfengji/mGBDT

 

官方要求依赖包

termcolor
xgboost==0.71
joblib==0.11
numpy<=1.12.1
scikit-learn==0.18.2

(实际使用包的最近版本没有报错)

rom sklearn import datasets
from sklearn.model_selection import train_test_split
import sys
sys.path.insert(0, "lib")
​
from mgbdt import MGBDT, MultiXGBModel
​
# 使用sklearn生成Sythetic Circle数据集
n_samples = 15000
x_all, y_all = datasets.make_circles(n_samples=n_samples, factor=.5, noise=.04, random_state=0)
x_train, x_test, y_train, y_test = train_test_split(x_all, y_all, test_size=0.3, random_state=0, stratify=y_all)
​
# 创建多层GBDT模型 
net = MGBDT(loss="CrossEntropyLoss", target_lr=1.0, epsilon=0.1)
​
# 添加多个层
# f,g代表正向映射和反向映射(本文使用梯度增强决策树)
net.add_layer("tp_layer",
    F=MultiXGBModel(input_size=2, output_size=5, learning_rate=0.1, max_depth=5, num_boost_round=5),
    G=None)
net.add_layer("tp_layer",
    F=MultiXGBModel(input_size=5, output_size=3, learning_rate=0.1, max_depth=5, num_boost_round=5),
    G=MultiXGBModel(input_size=3, output_size=5, learning_rate=0.1, max_depth=5, num_boost_round=5))
net.add_layer("tp_layer",
    F=MultiXGBModel(input_size=3, output_size=2, learning_rate=0.1, max_depth=5, num_boost_round=5),
    G=MultiXGBModel(input_size=2, output_size=3, learning_rate=0.1, max_depth=5, num_boost_round=5))
​
# 初始化向前映射
net.init(x_train, n_rounds=5)
​
# 训练数据集
net.fit(x_train, y_train, n_epochs=50, eval_sets=[(x_test, y_test)], eval_metric="accuracy")
​
# 预测
y_pred = net.forward(x_test)
​
# 获得隐藏层数输出
# hiddens[0] represent the input data
# hiddens[1] represent the output of the first layer
# hiddens[2] represent the output of the second layer
# hiddens[3] represent the output of the final layer (same as y_pred)
hiddens = net.get_hiddens(x_test)
# 输入的参数
x_test [[ 0.98629219  0.37425717]
 [-0.78112229  0.67636561]
 [-0.99009668 -0.09422216]
 ...
 [-0.07366793  0.39242077]
 [ 0.49469412 -0.03646068]
 [ 0.44998581  0.12221243]]
# 得到预测结果 二分类每个分类的分数
[ 1.31211066 -0.80238914]
 [ 1.22297394 -0.79598117]
 [ 1.28149652 -0.80238914]
 ...
 [-0.59332609  1.14914155]
 [-0.67818439  1.21024263]
 [-0.59332609  1.15299153]]
# 隐藏层每层的输出

[array([[ 0.98629219,  0.37425717],
       [-0.78112229,  0.67636561],
       [-0.99009668, -0.09422216],
       ...,
       [-0.07366793,  0.39242077],
       [ 0.49469412, -0.03646068],
       [ 0.44998581,  0.12221243]]), array([[0.24195877, 0.33463973, 0.43626902, 0.43110707, 0.43486381],
       [0.25372952, 0.32291317, 0.42654103, 0.43159989, 0.42881507],
       [0.22903574, 0.32872641, 0.41438475, 0.42890173, 0.42163324],
       ...,
       [0.35433355, 0.2935842 , 0.33562356, 0.45755211, 0.44033578],
       [0.36669326, 0.29807991, 0.33253574, 0.45570359, 0.43310291],
       [0.36669326, 0.2972962 , 0.33253574, 0.45570359, 0.43310291]]),
 array([[0.50276399, 0.6679424 , 0.27878499],
       [0.51799071, 0.69514143, 0.27562791],
       [0.51008165, 0.68187857, 0.27736026],
       ...,
       [0.54104149, 0.34162545, 0.32582015],
       [0.49520546, 0.34162545, 0.32657376],
       [0.52884918, 0.34162545, 0.32657376]]), array([[ 1.42716765, -0.68587327],
       [ 1.38501275, -0.72134578],
       [ 1.39800358, -0.70963466],
       ...,
       [-0.69251084,  1.39149308],
       [-0.65996385,  1.22131491],
       [-0.65070701,  1.43479514]])]