likes
comments
collection
share

【机器学习】RandomForest-随机森林中的数学原理

作者站长头像
站长
· 阅读数 5

前言

本文主要进行原理讲解,将很少涉及代码实现或应用。此外,阅读之前请确保已了解决策树模型的相关原理或工作流程和了解基本的随机森林模型。

文章概述

随机森林:随机森林模型属于集成算法中的Bagging算法类型,也就是构建多个相互独立的基评估器(决策树),然后这多个评估器的预测进行平均或多数表决原则来决定集成评估器(随机森林)的结果。

本文主要讲解随机森林模型,但其实是针对于集成学习中的Bagging算法模型的,随机森林正是Bagging算法的一个典型模型。

本文将针对随机森林模型的原理提出几个核心问题,并主要围绕着对这些问题的解答进行讲解:

  1. 随机森林是如何保证基分类器各不相同的?
  2. 随机森林用了什么方法,来保证集成的效果好于单个分类器?
  3. 随机森林除了进行建模预测,还经常用来填补缺失值,为什么常用随机森林模型来填补缺失值?
  4. 有了随机森林模型,既然随机森林在大多数情况性能更好,还用得到决策树模型吗?

本文主要基于分类任务来进行讲解,以下案例或其它内容若不进行说明,则默认为分类任务。对于回归任务,会在文末就不同的地方进行相应说明。

本文提供的代码仅供参考,不用深究于代码,感兴趣可以看一下,不影响本文主要内容。

随机森林中的 "随机"

随机森林是集成了众多树模型的集成模型,集成发挥效果的前提就是,其中每棵树模型都是"随机"的,也就是各不相同的。并且我们可以证明,在一定程度上,这种随机性越大的时候,装袋法 (Bagging) 的效果一般会越来越好。举个例子,当所有树都相同时,它们对于同一个样本都只会产生同一个预测结果,这样的树模型我们集成太多也没有用,正如对于某个政策的实施,我们需要各行各业、各不相同的人来进行投票和反应,随机森林也需要集成各不相同的决策树,来让这些决策树对结果进行投票表决才可能产生更好的效果。

决策树模型的局限性

对于决策树,当单个决策树遇到特征很多、样本很多的数据集时,常常很容易产生过拟合,因此决策树很难适用于这样的数据集,正如一个人(决策树),它脑容量有限(模型性能局限),当你一股脑把大量的知识全塞给他(拟合高维度多样本数据集),他就很难学好(拟合效果不佳),倾向于通过背答案仅仅把这些知识记下来了(过拟合,学习样本噪声),既然如此,我们就不要求这个人什么都会(不要求决策树模型拟合好整个数据集),我们找一群人(随机森林),这群人中每个人都学一部分(每个决策树学一部分数据集的特征信息),最后对预测结果进行投票表决来确定。

随机森林中的 "随机" 正是指集成的决策树的随机,也就是各不相同,我们使用各种方法增加随机性的最终目的就在于让每一棵树成为某一领域的"专家",而不是整个领域的"专家",然后将这些模型集成得到随机森林。

保证树模型的随机性

随机森林主要采用不同的训练特征和不同的训练样本来保证单个决策树的随机性。

不同的训练特征:假设对于某数据集共有 nnn 个特征,决策树在训练时会从这 nnn 个特征中随机抽取一部分特征来进行模型训练,因而会对每棵树的训练增加一定程度的随机性,使得每棵树只关注一部分特征。

但这种做法的局限性是很强的,当我们需要成千上万棵树的时候,数据不一定能够提供成千上万的特征来让我们构筑尽量多尽量不同的树,我们还需要其它方法来增加随机性。

不同的训练样本:每一棵树的训练样本并不是所有的原始样本,而是从原始样本中随机抽样得到的一部分样本,每棵树只关注一部分样本的特征信息,以此增加随机性,让各个树模型不同。

bootstrap & oob_score

在一个含有n个样本的原始训练集中,我们进行随机采样,每次采样一个样本,并在抽取下一个样本之前将该样本放回原始训练集,也就是说下次采样时这个样本依然可能被采集到,这样采集 nnn 次,最终得到一个和原始训练集一样大的,nnn 个样本组成的自助集。由于是随机采样,这样每次的自助集和原始数据集不同,和其他的自助集也是不同的。这样我们就可以自由创造取之不尽用之不竭,并且互不相同的自助集,用这些自助集来训练我们的基分类器,我们的基分类器自然也就各不相同了。

自助集:对原始数据集有放回随机抽样,且与原始数据集样本数目相同。

然而有放回抽样也会有自己的问题,由于是有放回抽样,一些样本可能在同一个自助集中出现多次,而其他一些却可能被忽略,也就是说若自助集中去除重复的样本,剩下的则是原始数据集中的部分样本。

在这 nnn 次随机抽样中,某样本被抽到某个自助集中的概率为:

pi=1−(1−1n)np_i = 1-(1-\frac{1}{n})^npi=1(1n1)n

原始数据集一共包含 nnn 个样本,从中随机抽取得到某个样本的概率为 1n\frac{1}{n}n1,"nnn 次随机抽样中某样本被抽到某个自助集" 的对立事件是 "该样本在这 nnn 次随机抽样都没有被该自助集抽到",后者的概率为 (1−1n)n(1-\frac{1}{n})^n(1n1)n,则很容易得到 pi=1−(1−1n)np_i=1-(1-\frac{1}{n})^npi=1(1n1)n

该函数最终收敛于 1−1e1-\frac{1}{e}1e1,推导如下:

limn→∞pi=1−limn→∞(1+−1n)n=1−1e≈0.632\begin{align} lim_{n \to \infty} p_i&= 1-lim_{n \to \infty} (1+\frac{-1}{n})^n \\ &=1-\frac{1}{e} \\ &\approx0.632 \end{align}limnpi=1limn(1+n1)n=1e10.632
  • 极限公式:lim⁡n→∞(1+an)n=ea\lim_{n \to \infty} (1 + \frac{a}{n})^n = e^alimn(1+na)n=ea

每个样本被自助集抽到的概率约为 63%,也就是说每个自助集中的不重复样本数约占原始样本数的 63%,因此,对于每棵树而言会有约 37% 的训练数据被浪费掉,这部分数据被称为袋外数据(out of bag data,简写为oob)。

为了增强树的随机性,对于每棵树而言,会有约 37% 的训练数据被浪费掉,但对于整体而言这部分数据并没有浪费掉,与其说是约 37% 的训练数据被浪费掉,不如说是每棵树只关注训练集中越 63% 的样本,每棵树都不关注所有的样本,但每棵树却都是其中 63% 样本特征的 "专家"。

在使用随机森林时,由于对于每一棵树而言都含有约 37% 的oob数据,因此我们可以不划分测试集和训练集,只需要用每一棵树的oob数据来测试每一棵树的性能(一般是准确度),最终将测试结果取平均作为整体树模型模型的性能。

这里所说的整体树模型是指所有树模型的平均性能,并不是指利用Bagging算法集成所有树得到的随机森林模型的性能,只是在一定程度上这两个指标相关。

多数表决

多数表决:多个模型对结果进行投票,票数最多的作为整体的结果。

随机森林的本质是一种装袋集成算法(bagging),装袋集成算法是对基评估器的预测结果进行平均或用多数表决原则来决定集成评估器的结果。对于随机森林,也就是其中各个决策树对结果类别进行投票表决,然后对结果采少数服从多数策略输出票数最多的结果。随机森林通过多数表决的方法集成多个独立的决策树模型。

例:一个随机森林模型中含有100个决策树,对于一个样本的预测,其中80个决策树预测该样本的label为"A",10个决策树预测该样本的label为"B",10个决策树预测该样本的label为"C",则随机森林模型的输出结果为label"A"。

当然也存在票数相同的情况,对于这种情况,通常模型会随机选取其中一个作为预测结果。

对于不平衡数据集,一种常见的策略则是选择样本少的类。因为在不平衡数据集下,样本少的类提供的信息也相对更少,模型会更倾向于输出样本多的类(模型会变得不那么公平),这种情况下出现票数相同的话,当然应该选择样本相对更少的类。

这种多数表决的方法来集成多个单独的树模型,随机森林是如何保证集成后的性能一定优于集成前的单个模型的,也就是随机森林的性能是如何保证的呢?

随机森林的性能保证

假设在某二分类任务下,已知某随机森林模型中共含有 nnn 个决策树,对于某样本,其中有 mmm 棵树对该样本的类别预测错误,假设每棵树预测错误的概率相同,为 ϵ\epsilonϵ,设 prp_rpr 为该随机森林模型预测错误的概率,则有:

  1. 我们要计算的是整体模型预测错误的概率,当一共有 nnn 棵树的话,至少有一半以上的树预测错误,整体才会预测错误,考虑到 nnn 可能是奇数或偶数,且我们不考虑正好一半对一半错的平局情况,因此 m=floor(n2)+1m=floor(\frac{n}{2})+1m=floor(2n)+1,也就是至少 mmm 课树判断错误才满足条件。
  2. 每棵树预测错误的概率是 ϵ\epsilonϵ,则一共 nnn 棵树下 mmm 棵树预测错误的概率为 C25mϵm(1−ϵ)n−mC^m_{25} \epsilon^m(1-\epsilon)^{n-m}C25mϵm(1ϵ)nm,也就是从一共 nnn 棵树中随机抽 mmm 棵树,这 mmm 棵树预测错误,另外 n−mn-mnm 棵树预测正确。
  3. 考虑有 mmm 以上棵树预测错误这些所有情况(m,m+1,...,nm,m+1,...,nm,m+1,...,n),然后再把这些所有情况累加求和则就是整体模型预测错误的概率了。

公式如下:

m=floor(n2)+1pr=∑mnCnmϵm(1−ϵ)n−m \begin{align} m &= floor(\frac{n}{2})+1 \\ p_r&=\sum^{n}_{m} C^m_{n}\epsilon^m(1-\epsilon)^{n-m} \end{align}mpr=floor(2n)+1=mnCnmϵm(1ϵ)nm
  • floor(n2)floor(\frac{n}{2})floor(2n):对 n2\frac{n}{2}2n 向下取整。

多分类任务下的结果:在二分类任务下,要使得随机森林预测正确,则其中至少有一半决策树预测正确;而在多分类任务下要使得随机森林预测正确,则需要更少数量的决策树预测正确即可,因此在多分类任务下差距可能相对会更大。

这里假设每棵单独的决策树预测错误的概率相同,实际肯定不总是相同的,但相差不大。

这里是以随机森林模型为例,但并不是只针对该模型,而是针对Bagging算法而言的

按照我们的想法,以该函数绘制两个图像来观察:

  • 图一:每棵树预测错误的概率不变,随着集成的树模型的数量的不断增大,随机森林预测错误的概率变化
  • 图二:集成的树模型的数量不变,随着每棵树预测正确的概率的逐渐增大,随机森林预测正确的概率变化
【机器学习】RandomForest-随机森林中的数学原理

相关代码如下以供参考(可忽略并折叠):

import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
from scipy.special import comb

def get_rfc_error(n, p):
    m = n // 2 + 1
    p_r = np.array([comb(n, i) * (p ** i) * ((1 - p) ** (n-i)) for i in range(m, n+1)]).sum()
    
    return p_r

# 配置mpl全局环境
rc = {
    'font.family': 'Microsoft YaHei',    # 指定字体
    'mathtext.fontset': 'stix',    # 数学公式字符集
}
mpl.rcParams.update(rc)
plt.style.use('ggplot')

# 绘图1:基分类器预测错误的概率不变(0.2)
# 随着基分类器的数量增大, 整体模型预测错误的概率变化
estimators_list = range(1, 101)  # 基分类器数量列表
# 对应的整体模型预测错误率列表
error_list = [get_rfc_error(n, 0.2) for n in estimators_list]  

# 绘制折线图
fig = plt.figure(figsize=(10, 4))
plt.subplot(121)
plt.plot(estimators_list, error_list, color='red', alpha=0.6, label='随机森林')
plt.plot(estimators_list, [0.2]*100, color='blue', alpha=0.6, label='决策树')
plt.text(97, 0.19, '0.2', color='blue', alpha=0.6, fontsize=10)
plt.text(87, 0.007, r'$5.18*10^{-12}$', color='red', alpha=0.6, fontsize=10)
plt.xticks([1, *range(10, 101, 10)])
plt.xlabel('基分类器数量')
plt.ylabel('预测错误的概率')
plt.legend()

# 绘图2:基分类器的数量不变(100棵树)
# 随着基分类器的正确率的增大, 整体模型的正确率变化
clf_list = [i/100 for i in range(0, 101, 1)]    # 基分类器预测正确率列表
# 对应的整体模型预测正确率列表
rfc_list = [1-get_rfc_error(100, 1-p) for p in clf_list] 
plt.legend()

# 绘制折线图
plt.subplot(122)
plt.plot(clf_list, rfc_list, color='red', alpha=0.6, label='随机森林')
plt.plot(clf_list, clf_list, color='blue', alpha=0.6, label='决策树')
plt.xticks(np.arange(0, 1.1, 0.2))
plt.xlabel('基分类器预测正确率')
plt.ylabel('预测正确率')

fig.suptitle('Bagging-随机森林与决策树')
plt.legend()
plt.show()

对于图一:明显随着决策树数量的不断增加,随机森林的错误率降低地很明显并收敛于一个很小的值。需要提及的是,从图中看决策树数量的奇偶数也会影响整体模型的性能,这是错误的,这是由于我们假设所有树模型的错误率都是0.2且所有树模型对于任何数据集本身的性能都是相同的,实际上是无关的。理论上随机森林会随着继承的树模型的数量的增多,错误率逐渐下降最终收敛,且效果应比单独一棵树要好得多,随机森林以此来保证效果优于单个模型。

对于图二:当每棵树的正确率为0.5时,随机森林的性能和单独一棵树的性能是相同的。这是因为,对于二分类任务而言,正确率0.5说明完全随机,一共两个label,我们从中随机选取一个,结果正确的概率就是0.5。从图中我们也可以看出,如果单独一棵树的正确率甚至要差于随机模型,那集成后的随机森林甚至要比单个模型更差。从这里我们也可以了解到,当树模型性能差于完全随机的模型,也就是准确率小于0.5时,集成后的随机森林模型性能一般只会更差。此外,由于我们的假设与实际存在误差,因此并不一定像图中一样当单个树模型准确度在0.65左右时随机森林就收敛了,但差别在于不同的数值,整体图像与实际差别不大。

一般要求的单个准确度更大,整体模型才会逼近性能极限,而不是像图中的0.65左右。具体为什么我们下面会介绍。

实验观察

对于上述的两张图片,图一我们可以很容易地进行实验验证,但图二我们难以设计实验,因为我们难以控制各不相同的每个决策树模型的准确度,因此本文仅对于图一进行实验设计。

我们使用常用的红酒数据集,在决策树参数设置相同的情况下,一定程度上保证单个决策树的准确率,然后观察随着随机森林中树模型的数量的不断增加,其准确率如何变化。

【机器学习】RandomForest-随机森林中的数学原理

相关代码如下以供参考(可忽略并折叠):

import matplotlib.pyplot as plt
import matplotlib as mpl
from sklearn.datasets import load_wine
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score

wine = load_wine()
rfc_list = []

for i in range(0, 100):
    clf = DecisionTreeClassifier(random_state=42)
    rfc = RandomForestClassifier(n_estimators=i+1, random_state=42)
    
    rfc_list.append(cross_val_score(rfc, wine.data, wine.target, cv=10).mean())
    
clf_list = [cross_val_score(clf, wine.data, wine.target, cv=10).mean()] * 100

# 环境
plt.style.use('ggplot')
rc = {
    'font.family': 'Microsoft YaHei'
}
mpl.rcParams.update(rc)

# 绘图
plt.figure()
plt.plot([*range(1, 101)], clf_list, color='blue', alpha=0.6, label='决策树')
plt.plot([*range(1, 101)], rfc_list, color='red', alpha=0.6, label='随机森林')
plt.xticks([1, *range(10, 101, 10)])
plt.text(90, 0.991, f'{max(rfc_list):.2}', color='red', alpha=0.6)
plt.text(95, 0.87, f'{clf_list[0]:.2}', color='blue', alpha=0.6)
plt.xlabel('基分类器数量')
plt.ylabel('准确率')
plt.legend()
plt.show()

从图中我们可以看出,随着基分类器数量的增加,随机森林的准确率总体也相应提升,并最终趋于稳定,并且相对于单个决策树,随机森林在性能上有了很大程度的提升,在绝大多数情况下集成后的随机森林的性能都要高于单个决策树。

在上面我们假设的情况中,决策树的准确率高于一定程度后,随机森林模型的准确率应该高得多,也就是预测错误的概率应该非常非常小才对,为什么实际情况中单个决策树准确率0.87,整体模型的准确率却在0.98、0.99周围呢?

这是因为随机森林是由许多决策树组成的,在我们假设的情况中,我们假设每棵树的准确率是0.8,其实是每棵树对于数据集中的任何数据都有0.8的概率预测正确,但事实情况却是对于特征信息明显的数据,决策树更容易预测正确,对于特征信息不明显的数据,决策树则都更难去预测正确。举个例子说明:把待遇测数据集想象成一个考试,随机森林中集成的100棵树想象成水平各不同,但总体差不多的学生,这些学生遇到简单的题,则都容易做对,遇到难的题,这些学生则全都更倾向于做错了,这是实际情况,但对于我们上方的假设,则是假设所有的学生 (树模型) 对无论难易所有的题 (样本) 的正确率都是0.8,同时这也是上方图二中我们谈到随机森林不一定像图中一样当单个树模型准确度在0.65左右时就收敛的原因。

补充说明:为什么图中随机森林在集成的树模型很少时准确率小于单棵决策树?

  • 该实验中我们使用的是sklearn库中的模型,这里面的决策树模型本身在训练时就带有随机性,也就是选择一部分特征进行训练,随机森林集成的树模型也是如此,但随机性要比单棵决策树高得多。例如共100个特征,sklearn中的单个决策树可能会随机选择80个特征进行拟合,而随机森林集成的树模型则可能只选择20个,因此当随机森林集成的树模型过少时其性能当然可能劣于单棵决策树。

利用随机森林填补缺失值

我们拿到的数据样本一般都不太可能是完美的,缺失值是其中一个方面,对于缺失值的处理,我们有很多种策略,包括直接丢弃样本、均值填充、众数填充、0填充等等,但大多数情况下,每个样本中只是少部分特征数据缺失,我们则可以使用正常数据来训练模型,然后让模型来预测这些缺失数据,将预测结果对缺失值进行填充。

分类模型、回归模型有很多,但却常用随机森林模型来进行缺失值填充的原因是:

  1. 相对决策树等模型,随机森林可以处理高维数据,在各种数据下都有不错的表现
  2. 随机森林能用于回归也能分类,适用于各种类型的数据
  3. 相对Boosting算法,随机森林对异常值、离群值更不敏感,且占用运算资源也更少,调参也相对更容易

使用其它模型填充缺失值可能有着更好的效果,这里只是说明随机森林的优点。

填充缺失值的逻辑

对于一个有 nnn 个特征的数据来说,其中特征 TTT 有缺失值,我们就把特征 TTT 作为待预测标签,其他的 n−1n-1n1 个特征和原本的标签组成新的特征矩阵。那对于特征 TTT 来说,它也存在没有缺失的部分,这部分数据既有标签也有特征,我们把这部分数据用来训练模型,而它缺失的部分,只有特征没有标签,就是我们需要预测的部分。

  • X_train:特征 TTT 不缺失的样本的其它 n−1n-1n1 个特征 + 原本的标签
  • y_train:样本对应的特征 TTT 不缺失的值
  • X_test:特征 TTT 缺失的样本对应的其他 n−1n-1n1 个特征 + 原本的标签
  • y_test:待预测的特征T缺失的值

这种做法非常适用于某一个特征大量缺失,其他特征却很完整的情况

对于数据中除了特征 TTT 之外,其他特征也有缺失值的情况,我们的策略则是:

  • 遍历所有的特征,从缺失最少的开始进行填补(因为填补缺失最少的特征所需要的准确信息最少)
  • 填补一个特征时,先对其他特征的缺失值进行0填充,每完成一轮预测,就将预测值放到原本的特征矩阵中,再继续填补下一个特征。
  • 每一轮填补完毕,有缺失值的特征会减少一个,所以每次循环后,需要用0来填补的特征就越来越少。当进行到最后一个特征时(这个特征应该是所有特征中缺失值最多的),已经没有任何的其他特征需要进行0填充了, 而我们已经使用模型预测为其他特征填补了大量有效信息,可以用来填补缺失最多的特征。
  • 遍历所有的特征后,数据就完整,不再有缺失值了。

注意:在填充缺失值时,不同特征作为标签时回归任务和分类任务要使用不同的随机森林模型。

案例说明和效果对比

我们使用Python中的sklearn包中自带的波士顿房价数据集,由于该数据集不含有缺失值,因此我们将手动将一些数据置为缺失值,然后分别使用0填充、均值填充、众数填充和随机森林填充法,然后将这些数据和原始不含缺失值的数据来训练随机森林模型,并可视化进行效果对比。

添加缺失数据:boston房价数据集是没有缺失值的,形状为 506 * 13,也就是506个样本,12个特征,1个label,整个数据集中共含有506 * 13 个数据(1个样本和1个特征对应一个数据),共首先确定我们希望放入的缺失数据的比例 nnn,则缺失数据个数为 506∗13∗n506 * 13 * n50613n,所有数据要随机遍布在数据集的各行各列当中,而一个缺失的数据会需要一个行索引和一个列索引,因此我们创建两个大小为 506∗13∗n506 * 13 * n50613n 的数组用来存放需置为缺失数据行索引和列索引,分别存放 0 ~ 506 和 0 ~ 13 之间的随机数(可重复),然后将这些数据置为缺失值即可。

由于为可重复随机数,因此实际放入的缺失数据的比例可能要小于 nnn

0填充、均值填充、众数填充、随机森林填充效果对比:

【机器学习】RandomForest-随机森林中的数学原理

相关代码如下以供参考(可忽略并折叠)

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib as mpl
from sklearn.datasets import load_boston
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import cross_val_score
from sklearn.impute import SimpleImputer

boston = load_boston()

X_full, y_full = boston.data, boston.target
n_samples, n_features = X_full.shape[0], X_full.shape[1]

rng = np.random.RandomState(42)
missing_rate = 0.5
n_missing_samples = int(np.floor(n_samples * n_features * missing_rate))

missing_features = rng.randint(0, n_features, n_missing_samples)
missing_samples = rng.randint(0, n_samples, n_missing_samples)

X_missing = X_full.copy()

X_missing[missing_samples, missing_features] = np.nan

X_missing = pd.DataFrame(X_missing)

# 均值填充
imp_mean = SimpleImputer(missing_values=np.nan, strategy='mean')
X_missing_mean = imp_mean.fit_transform(X_missing)

# 0填充
imp_0 = SimpleImputer(missing_values=np.nan, strategy='constant', fill_value=0)
X_missing_0 = imp_0.fit_transform(X_missing)

# 众数填充
imp_mode = SimpleImputer(missing_values=np.nan, strategy = 'most_frequent')
X_missing_mode = imp_mode.fit_transform(X_missing)

# 随机森林填充法
X_missing_rfc = X_missing.copy()
columns_sorted = np.argsort(X_missing_rfc.isnull().sum(axis=0))  # 从小到大排序,返回对应索引顺序

for col_index in columns:
    # 构建新特征矩阵和新标签 (新的数据集)
    X = pd.concat([X_missing_rfc.iloc[:, X_missing_rfc.columns != col_index], pd.DataFrame(y_full)], axis=1) # X是DataFrame
    y = X_missing_rfc.iloc[:, col_index]  # y是Series
    
    # 新的特征矩阵中进行0填充
    X_filled = SimpleImputer(missing_values=np.nan, strategy='constant', fill_value=0).fit_transform(X)
    
    # 构建训练集和 测试集(待遇测的数据)
    y_train = y[y.notnull()]
    y_test = y[y.isnull()]
    X_train = X_filled[y_train.index, :]
    X_test = X_filled[y_test.index, :]
    
    # 用随机森林来填充特征col的缺失值
    rfc = RandomForestRegressor(n_estimators=100, random_state=42)
    rfc.fit(X_train, y_train)
    y_pre = rfc.predict(X_test)
    
    # 更新特征矩阵
    X_missing_rfc.loc[y_test.index, col_index] = y_pre
    # X_missing_rfc.loc[X_missing_rfc.iloc[:, col_index].isnull(), col_index] = y_pre

# 建模预测和效果对比
X_list = [X_missing_mean, X_missing_0, X_missing_mode, X_full, X_missing_rfc]
mse_list = []

for x in X_list:
    rfc = RandomForestRegressor(n_estimators=100, random_state=42)
    mse_list.append(-cross_val_score(rfc, x, y_full, scoring='neg_mean_squared_error', cv=7).mean())

# 绘制直方图
rc = {'font.family': 'Microsoft YaHei'}
mpl.rcParams.update(rc)
plt.style.use('ggplot')

plt.figure()
plt.barh(['0填充', '均值填充', '众数填充', '完整数据', '随机森林填充'], mse_list, color='red', alpha=0.6)
plt.text(18, 3.93, f'{mse_list[4]:.3f}', color='red', alpha=0.6)
plt.text(22, 2.93, f'{mse_list[3]:.3f}', color='red', alpha=0.6)
plt.text(38.5, 1.93, f'{mse_list[2]:.3f}', color='red', alpha=0.6)
plt.text(42, 0.93, f'{mse_list[1]:.3f}', color='red', alpha=0.6)
plt.text(36.5, -0.1, f'{mse_list[0]:.3f}', color='red', alpha=0.6)
plt.xlabel('MSE')
plt.ylabel('填充方案')
plt.title('填充方案效果对比')
plt.show()

ps:本例在填充时没有根据缺失数据特征是离散型变量还是连续型变量分别使用分类模型和回归模型,而是统一使用RandomForestRegressor进行预测填充,实际情况若分开考虑可能会有相对更好的效果。

从图中也可以看出,使用随机森林填充有着最好的效果,甚至超过了使用原来完整数据来训练的模型;这里需要说明一下,虽然效果很好,但是这里是使用随机森林进行填充且同时也使用相同的模型来预测,因此模型进行填充的值也更符合模型本身对该数据集的 "理解",换句话说,模型在填充的时候是把缺失数据预测为它 "认为" 的值,因而同一模型用这些数据进行预测的时候往往出现的效果会更好,但并不一定它的泛化效果一定好于原始的真实数据,本实验中模型在预测这些缺失值的时候可能减少了原本数据集中的噪声,因而得到了更好的效果,但是在其它数据集中不一定出现这种情况。

总结

  1. 随机森林是如何保证基分类器各不相同的?
    • 每棵树都使用从原始特征中随机采样的部分特征
    • 用于每棵树训练的自助集是从原始样本中随机抽样得到的,仅包含原始样本数据的一部分
  2. 随机森林用了什么方法,来保证集成的效果好于单个分类器?
    • Bagging算法,然后再对样本预测结果进行投票,集成各个独立树模型的结果来得到更好的性能
  3. 随机森林除了进行建模预测,还经常用来填补缺失值,为什么常用随机森林模型来填补缺失值?
    • 效果相对很不错
    • 相对决策树等模型,随机森林可以处理高维数据,在各种数据下都有不错的表现
    • 随机森林能用于回归也能分类,适用于各种类型的数据
    • 相对Boosting算法,随机森林对异常值、离群值更不敏感,且占用运算资源也更少,调参也相对更容易
  4. 有了随机森林模型,既然随机森林在大多数情况性能更好,还用得到决策树模型吗?
    • 单模型决策树与随机森林相比,确实通常情况下性能较差。但决策树作为单模型,仍然有其独特的优势与用途:
      • 决策树更容易理解,可解释性更强
      • 决策树训练速度非常快,适合小型数据集,相反,随机森林则更适用于大型数据集
      • 随机森林比起单一决策树需要更长的训练时间和运算资源
    • 我们考虑不同情况来使用不同模型,不存在某个模型在所有场景下都优于另一个模型

Reference