深度森林gcForest的原理及实现
gcForest基本原理
周志华教授团队认为模型复杂度并不是DNN成功的关键,而逐层处理才是DNN成功的关键因素。提升集成算法及其决策树模型只是在数据的原始特征上做处理,没有对模型内的特征做特征变换,且只有有限的模型复杂度。因此,他们推测深度模型成功的三个因素为逐层处理、特征变换和巨大的模型复杂度。
gcForest受逐层处理的启发,采用级联结构,并采用不同种类的树提高模型的多样性。下面详细介绍gcForest的基本原理。
1 级联森林
可将级联结构理解为类似神经网络,它对原始数据进行逐层处理,每个级联层都将上一层的输出作为输入,并将处理的信息输出到下一层。级联森林如下图所示。

-
完全随机森林由若干棵CART决策树构成,森林中每棵树分裂时随机选取一个属性作为当前节点的最优分裂属性。一直生长到每个叶子节点只包含一个类别或者每个叶子节点包含的类别数低于参数指定的最小样本划分数目。
-
普通随机森林也由若干CART决策树构成,森林中每棵树分裂时先随机选取一个大小为sqrt(m)的候选属性集(m表示特征属性的个数),然后在候选属性集中选择最优的分裂属性(分类问题计算基尼指数,回归问题计算均方差)。
两者的不同在于完全随机森林是在完整的特征空间随机选取特征来分裂,而普通随机森林在特征空间的一个子空间根据基尼指数或者均方差选取最优分类属性。 -
决策树其实是在特征空间中不断划分子空间,并且给每个子空间打上标签(分类问题就是一个类别,回归问题就是一个目标值),例如一条测试样本,每棵树会根据样本所在子空间的训练样本的类别占比生成一个类别的概率分布,然后对森林内所有树的各类别比例取平均,输出整个森林中各类别的比例。
-
一层结果输出之后,会用此模型来对一个检验即进行评估,若得到的结果准确率或者误差达到了可接受的阈值,训练就会终止。这个操作非常关键,相当于自动决定了层数,对模型复杂度的自适应调节使得gcForest能够可伸缩的应用于不同规模的数据集上。
2 多粒度扫描
gcForest的多粒度扫描类似于CNN和RNN的采样窗口,增强了模型的特征提取能力。
多粒度扫描具体过程
首先输入一个P维样本,然后通过一个长度为k的采样窗口进行滑动采样,设定滑动步长s,得到N=(P-k)/s+1个K维的特征子样本向量。每个子样本都用于所有的随机森林训练,并且每个森林都会获得一个长度为C的概率向量(C为类别数),这样一个森林就能获得一个N*C的表征向量。最后把每层的F个森林的结果拼接在一起得到本层输出。上述描述了一种滑动窗口大小的扫描过程,实际上可以利用多种大小的滑动窗口进行采样,这样会获得更多的特征子样本,真正达到“多粒度”扫描的。
3 gcForest挑战
由于深度森林不能像深度学习那样使用GPU进行加速,树结构难以像矩阵操作一样在GPU上运行,所以任务的规模很大程度上限制了深度森林的表现。周志华教授对gcForest的发展现状表示:需要克服的问题还有很多,不过所有这些付出都是值得的。因为“没有免费的午餐”,没有哪个学习模型永远是最好的,尽管深度神经网络很成功,但我们还是要继续探索其他类型的模型。我们猜测深度神经网络确实是适用于数值建模问题的,但当你面对的是符号化、离散、表格数据时,深度森林可以做的更好。
代码实现
# -*- coding: utf-8 -*-
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
from deepforest import CascadeForestRegressor
import pandas as pd
import numpy as np
# 载入训练数据集
train_path = input('请输入训练数据集路径(含文件名):')
data = pd.read_csv(train_path,
encoding='utf-8')
x = data.iloc[:, :5].values
y = data.iloc[:, 6:7].values
# 深度随机森林模型
model = CascadeForestRegressor()
# 模型训练
model.fit(x, y)
# 载入测试数据集
test_path = input('请输入测试数据集路径(含文件名):')
data = pd.read_csv(test_path,
encoding='utf-8')
x = data.iloc[:, :5].values
y_true = data.iloc[:, 6:7].values
y_true_np = np.array(y_true)
# 模型测试
y_pred = model.predict(x)
# 模型评估
mse = mean_squared_error(y_true_np, y_pred)
print('mse:', mse)
mae = mean_absolute_error(y_true_np, y_pred)
print('mae', mae)
r2_score = r2_score(y_true_np, y_pred)
print('r2_score', r2_score)