3

gcForest 算法原理及 Python 与 R 实现

 2 years ago
source link: https://cosx.org/2018/10/python-and-r-implementation-of-gcforest/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

gcForest 算法原理及 Python 与 R 实现

关键词:gcForest Model; Python; R

审稿:何通

1. 背景介绍

从目前来看深度学习大多建立在多层的神经网络基础上,即一些参数化的多层可微的非线性模块,这样就可以通过后向传播去训练,Zhi-Hua Zhou 和 Ji Feng 在 Deep Forest [1,2] 论文中基于不可微的模块建立深度模块,这就是 gcForest。

传统的深度学习有一定的弊端:

  • 超参数个数较多,训练前需要大量初始化,主要靠经验调整,使得 DNN 更像一门艺术而非科学(有人说深度学习就是在调参);
  • DNN 的训练要求大量的训练数据,数据的标注成本太高;
  • DNN 是一个黑盒子,训练的结果很难去解释描述,并且学习行为很难用理论去分析解释;
  • DNN 在训练之前就得确定具体的网络结构(虽然 dropout 等的技术可以增加网络结构的一些不确定性),模型的复杂度也是提前设定好的。

但是有一点是我们相信的,在处理更复杂的学习问题时,算法的学习模块应该要变的更深(论文 The Power of Depth for Feedforward Neural Networks 从理论上已经证明了加深网络比加宽网络更有效),现在的深层网络结构依旧依赖于神经网络(我们这里把像 CNN,RNN 的基本结构单元也归结为神经网络单元), 周志华团队考虑深度学习的结构可不可以用其他不可微的模块组成:

Can deep learning be realized with non-differentiable modules?

这个问题会使我们想到一些其他问题:

  • 深度学习是否就等同于 DNN(深度模型是不是只能通过可微的函数结构去构建)?
  • 是否可以不通过后向传播训练深度模型?
  • 在一些 RandomForest,XGBoost,lightGBM,catBoost 成功的学习任务上, 深度模型是否也能够战胜它们?

下面我们就来看一下,如何从传统的机器学习和深度学习中获得灵感,构建 gcForest Model。

2.gcForest 模型灵感来源

我们本节首先介绍 gcForest 模型构建的灵感来源,论文提到了两个方面,一个是在 DNN 中,一个是在集成学习(Ensemble Learning)中。

2.1 DNN 中的灵感来源

深度学习在一些学习任务上之所以成功,关键在于特征的表示学习上(representation learning),如下图所示,随着一层一层的处理(layer-by-layer processing),马的图像数据的特征被高度抽象出来用于动物分类任务。

pic1

也就是说 DNN 在做分类任务时最终使用的是生成抽象出来的关键高层特征,而决策树,Boosting,SVM 等传统机器学习模型只是使用初始的特征表示去学习,换句话说并没有做模型内部的特征变换(in-model feature transformation)。

除此之外,DNN 可以设计成任意的复杂度,而决策树和 Boosting 模型的复杂度是有限的,充足的模型复杂度虽然不是 DNN 必要的取胜法宝,但是也是非常重要的。

总结在设计 gcForest 来自 DNN 的灵感:

  • layer-by-layer processing
  • in-model feature transformation
  • suffcient model complexity

2.2 集成学习中的灵感来源

众所周知,集成学习通常比起单模型往往会有一个比较不错的表现,在构建一个好的集成模型时,要同时考虑子模型预测的准确度和子模型之间的多样性,尽量做到模型之间的互补,即理论上来说,子模型的准确度越高且子模型之间差异性越大(diversity),集成的效果越好。

但是,what is diversity? 怎样去度量模型之间的多样性?

在实践中,增强多样性的基本策略是在训练过程中引入基于启发式的随机性。粗略地说有 4 个机制:

  • 数据样本机制:通过产生不同的数据样本来训练子模型;
  • 输入特征机制:通过选取不同特征子空间来训练子模型;
  • 学习参数机制:通过设定不同参数来使得子模型具有多样性;
  • 输出表示机制:通过不同的输出表示增加子模型的多样性;

下一节通过本节的一些灵感构建 gcForest 模型。

3.gcForest 模型

3.1 级联森林结构

基于上一节中的一些灵感,gcForest 被构造成如下图所示的级联结构

pic2

  • 每一个 Level 包含若干个集成学习的分类器(这里是决策树森林,你可以换成 XGBoost,lightGBM,catBoost 等),这是一种集成中的集成的结构;
  • 为了体现多样性,黑色和蓝色的 Forest 代表了若干不同的集成学习器,为了说明这里用了两种,黑色的是完全随机森林:由 500 棵决策树组成,每棵树随机选取一个特征作为分裂树的分裂节点,然后一直生长,直到每个叶节点细分到只有一个类或者不多于 10 个样本,蓝色的表示普通的随机森林:由 500 棵决策树组成,每棵树通过随机选取 sqrt(d)(d 表示输入特征的维度) 个候选特征然后通过 gini 系数筛选分裂节点;
  • gcForest 采用了 DNN 中的 layer-by-layer 结构,从前一层输入的数据和输出结果数据做 concat 作为下一层的输入;
  • 为了防止过拟合的情况出现,每个森林的训练都使用了 k - 折交叉验证,也即每一个训练样本在 Forest 中都被使用 k-1 次,产生 k-1 个类别列表,取平均作为下一个 Level 级联的输入的一部分;
  • 当级联扩展到新的 Level 后,通过验证集去评估之前所有级联结构的表现,如果评估结果没有太大的改变或提升则训练过程结束,因此级联结构的 Level 的个数被训练过程决定;
  • 这里要注注意的是,当我们想控制训练过程的 loss 或限制计算资源的时候,我们也可以使用训练误差,而不是交叉验证的误差同样能够用于控制级联的增长;
  • 以上这一点就是 gcForest 和神经网络的区别,gcForest 可以通过训练自适应的调整模型的复杂度,并且在恰当的时候停止增加我们的 Level;

3.2 多粒度扫描

为了增强级联森林结构我们增加了多粒度扫描,这实际上来源于深度网络中强大的处理特征之间关系能力,多粒度扫描的结构如下图所示

pic3

其过程可描述如下:

  • 先输入一个完整的 p 维样本(p 是样本特征的维度),然后通过一个长度为 k 的采样窗口进行滑动采样,得到 s=(p-k)+1 个(这个过程就类似于 CNN 中的卷积核的滑动,这里假设窗口移动的步长为 1)k 维特征子样本向量;
  • 接着每个子样本都用于完全随机森林和普通随机森林的训练,并在每个森林都获得一个长度为 c 的概率向量(c 是分类类别个数,上图中 c=3),这样每个森林都产生一个 s*c 的表征向量,最后把每层的 F 个森林的结果拼接在一起,得到样本输出。

上图是一个滑动窗口的简单情形,下图我们展示了多滑动窗口的过程:

pic4

这里的过程和上图中的过程是一致的,这里要特别注意在每个 Level 特征维度的变化,并且上图中的结构可以称为级联中的级联,也即每个级联结构中有多个级联子结构。

除了这种结构,我们的多粒度扫描的结构还可以有其他的形式,可以入下图所示:

pic5

这也是一种多粒度扫描结构,被称为 gcForest_conc 结构,即在多粒度扫描后进行了特征的 concatenate, 然后再去做级联结构,从实验结果上来看,该结构要稍微逊色于第一种结构。

3.3 小结

以上就是 gcForset 的模型结构和构建过程,我们可以清晰的看到 gcForest 的主要超参数:

级联结构中的超参数:

  • 级联的每层的森林数
  • 每个森林的决策树的数量
  • 树停止生长的规则

多粒度扫描中的超参数:

  • 多粒度扫描的森林数
  • 每个森林的决策树数
  • 树的停止生长规则
  • 滑动窗口的数量和大小

其优点如下:

  • 计算开销小,单机就相当于 DNN 中使用 GPU(如果你没有钱购买 GPU, 可以考虑使用 gcForest)
  • 模型效果好(论文中 [1,2] 实验对比了不同模型的效果,gcForest 在传统的机器学习,图像,文本,语音上都表现不俗)
  • 超参数少,模型对超参数不敏感,一套超参数可以应用到不同的数据集
  • 可以适用于不同大小的数据集,模型复杂度可自适用的伸缩
  • 每个级联生成使用交叉验证,避免过拟合
  • 相对于 DNN,gcForest 更容易进行理论分析

但 Zhi-Hua Zhou 和 Ji Feng 在 Deep Forest [1,2] 论文中 CIFAR-10 数据集的训练上 gcForest 在简单的结构设定下显然逊色于 AlexNet 和 ResNet(可以通过增加网络结构的复杂度和增强特征表示来优化 gcForest),未来仍需要在特征表示和内存消耗上做进一步的优化。

4.gcForest 模型的 Python 实现

GitHub 上有两个 star 比较多的 gcForest 项目,在参考文献中已经列出,下面我们就使用这两个 gcForest 的 Python 模块去尝试使用 gcForest 模型去解决一些问题,这里要说明的是其中参考文献 [3] 是官方提供(由 gcForest 的作者之一 Ji Feng 维护)的一个 Python 版本。目前 gcForest 并没有相对好用的 R 语言接口,我们已经开发了基于参考文献 [4] 的 R 接口包(目前已托管在 GitHub 和 CRAN)。这里我们仅介绍参考文献 [4] 中的 gcForest 模块,如果感兴趣,可以自行研究官方提供的 gcForest 模块(官方模块,集成模型的选择除了随机森林外还可以选择其他的结构像 XGBoost,ExtraTreesClassifier LogisticRegression,SGDClassifier 等,因此建议使用该模块)。这里仅介绍参考文献 [4] 中 gcForest 的模块使用。

Example1:Digits 数据集预测

from GCForest import gcForest
from sklearn.datasets import load_iris, load_digits
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

import pandas as pd
import numpy as np

digits = load_digits()
X = digits.data
y = digits.target
X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.4)

# 注意参数设定
gcf = gcForest(shape_1X=[8,8], window=[4,6], tolerance=0.0, min_samples_mgs=10, min_samples_cascade=7)
gcf.fit(X_tr, y_tr)

# 类别预测
pred_X = gcf.predict(X_te)
print(pred_X)

# 模型评估
accuracy = accuracy_score(y_true=y_te, y_pred=pred_X)
print('gcForest accuracy : {}'.format(accuracy))

Example2: 多粒度扫描和级联结构分离

因为多粒度扫描和级联结构是相对独立的模块,因此是可以分开的,如果代码中提供了参数 y,则会自动去做训练,如果参数 y 是 None,代码会回调最后训练的随机森林进行数据切片。

# 模型根据y_tr进行训练
gcf = gcForest(shape_1X=[8,8], window=5, min_samples_mgs=10, min_samples_cascade=7)
X_tr_mgs = gcf.mg_scanning(X_tr, y_tr)

# 回调最后训练的随机森林模型
X_te_mgs = gcf.mg_scanning(X_te)

# 使用多粒度扫描的输出作为级联结构的输入,这里要注意
# 级联结构不能直接返回预测,而是最后一层级联Level的结果
# 因此,需要求平均并且取做大作为预测值

gcf = gcForest(tolerance=0.0, min_samples_mgs=10, min_samples_cascade=7)
_ = gcf.cascade_forest(X_tr_mgs, y_tr)

pred_proba = gcf.cascade_forest(X_te_mgs)
tmp = np.mean(pred_proba, axis=0)
preds = np.argmax(tmp, axis=1)
accuracy_score(y_true=y_te, y_pred=preds)

Example3: 去除多粒度扫描的级联结构

我们也可以使用最原始的不带多粒度扫描的级联结构进行预测

gcf = gcForest(tolerance=0.0, min_samples_cascade=20)
_ = gcf.cascade_forest(X_tr, y_tr)

pred_proba = gcf.cascade_forest(X_te)
tmp = np.mean(pred_proba, axis=0)
preds = np.argmax(tmp, axis=1)
accuracy_score(y_true=y_te, y_pred=preds)

感兴趣的小伙伴可以研究一下,官方提供(由 gcForest 的作者之一 Ji Feng 维护的)的模块,其实都差不多,如果感兴趣,我们后期也会做相关的介绍。

5.gcForest 模型的 R 实现

我们已经基于 [4] 开发了其 R 版本的 gcForest 包(该包已托管在 GitHub 和 CRAN)[5,6],如果你习惯用 R 语言,可以尝试该包,这里演示 gcForest 的 Cheat Sheet 和一些官方的 Demo。

gcforest_r

Example1:iris 数据集预测

# install from CRAN
# install.packages('gcForest')

# install from github
# install.packages("devtools")
# devtools::install_github('DataXujing/gcForest_r')

# 在运行gcForest包前,请确保系统中已经安装了Python3.X及相应的模块包括:
# Numpy >= 1.12.0
# Scikit-learn >= 0.18.1

# 如果你不想手动安装可以通过req_py()函数,进行Python环境的包的监测和安装:
# library(gcForest)
# req_py()

library(gcForest)

sk <- reticulate::import('sklearn')
train_test_split <- sk$model_selection$train_test_split

data <- sk$datasets$load_iris
iris <- data()
X = iris$data
y = iris$target
data_split = train_test_split(X, y, test_size=0.33)

X_tr <- data_split[[1]]
X_te <- data_split[[2]]
y_tr <- data_split[[3]]
y_te <- data_split[[4]]

gcforest_m <- gcforest(shape_1X=4L, window=2L, tolerance=0.0)
gcforest_m$fit(X_tr,y_tr)
gcf_model <- model_save(gcforest_m,'../gcforest_model.model')

gcf <- model_load('../gcforest_model.model')
gcf$predict(X_te)

Example2: 多粒度扫描和级联结构分离

# mg-scanning
gcforest_m <- gcForest(shape_1X=c(8L,8L), window=5L, min_samples_mgs=10L, min_samples_cascade=7L)
X_tr_mgs <- gcforest_m$mg_scanning(X_tr, y_tr)

X_te_mgs <- gcforest_m$mg_scanning(X_te)

# cascade_forest
gcforest_m <- gcForest(tolerance=0.0, min_samples_mgs=10L, min_samples_cascade=7L)
cf <- gcforest_m$cascade_forest(X_tr_mgs, y_tr)

pred_proba <- gcforest_m$cascade_forest(X_te_mgs)
pred_proba <- reticulate::py_to_r(pred_proba)

# then do mean and max

详细的 gcForest R 包文档可以参考 https://cran.r-project.org/web/packages/gcForest/vignettes/gcForest-docs.html

文中我们介绍了 gcForest 的模型算法和 Python 及 R 的实现,目前 gcForest 算法的官方 Python 包 [3] 并未托管在 Pypi,但 v1.1.1 支持 Python3.5,也有开源实现基于 Python3.x 的 gcForest version0.1.6 版本 (上一节中已经演示)[4],但其功能要相对弱于 [3]; 如果你是 R 语言的忠实用户,那么可以使用我们开发的 R 版本的 gcForest 包 [5,6]。

7. 参考文献

[1]. Z.-H. Zhou and J. Feng. Deep Forest: Towards an Alternative to Deep Neural Networks. In IJCAI-2017. (https://arxiv.org/abs/1702.08835v2 )

[2]. Z.-H. Zhou and J. Feng. Deep Forest. In IJCAI-2017. (https://arxiv.org/abs/1702.08835 )

[3].gcForest v1.1.1(https://github.com/kingfengji/gcForest)

[4].gcForest version 0.1.6(https://github.com/pylablanche/gcForest)

[5].gcForestr(https://github.com/DataXujing/gcForestr)

[6].CRAN gcForest(https://CRAN.R-project.org/package=gcForest)

敬告各位友媒,如需转载,请与统计之都小编联系(直接留言或发至邮箱:[email protected]),获准转载的请在显著位置注明作者和出处(转载自:统计之都),并在文章结尾处附上统计之都微信二维码。

统计之都微信二维码

← 第十一届中国 R 会议(广州)暨华南地区数据科学会议通知 电子表格中的数据整理 →

发表 / 查看评论


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK