1. 随机森林的介绍

随机森林就是通过集成学习的思想将多棵树集成的一种算法,它的基本单元是决策树,而它的本质属于机器学习的一大分支——集成学习(Ensemble Learning)方法。随机森林的名称中有两个关键词,一个是“随机”,一个就是“森林”。“森林”我们很好理解,一棵叫做树,那么成百上千棵就可以叫做森林了,这样的比喻还是很贴切的,其实这也是随机森林的主要思想–集成思想的体现。“随机”的含义我们会在下边部分讲到。

其实从直观角度来解释,每棵决策树都是一个分类器(假设现在针对的是分类问题),那么对于一个输入样本,N棵树会有N个分类结果。而随机森林集成了所有的分类投票结果,将投票次数最多的类别指定为最终的输出,这就是一种最简单的 Bagging 思想。

随机森林中有许多的分类树。如果要将一个输入样本进行分类,需要将输入样本输入到每棵树中进行分类。打个形象的比喻:森林中召开会议,讨论某个动物到底是老鼠还是松鼠,每棵树都要独立地发表自己对这个问题的看法,也就是每棵树都要投票。该动物到底是老鼠还是松鼠,要依据投票情况来确定,获得票数最多的类别就是森林的分类结果。森林中的每棵树都是独立的,99.9%不相关的树做出的预测结果涵盖所有的情况,这些预测结果将会彼此抵消。少数优秀的树的预测结果将会超脱于芸芸“噪音”,做出一个好的预测。将若干个弱分类器的分类结果进行投票选择,从而组成一个强分类器,这就是随机森林bagging的思想(关于bagging的一个有必要提及的问题:bagging的代价是不用单棵决策树来做预测,具体哪个变量起到重要作用变得未知,所以bagging改进了预测准确率但损失了解释性。)

有了树我们就可以分类了,那么问题来了,森林中的每棵树是怎么生成的呢

1.1 随机森林中“树”的生成

每棵树的按照如下规则生成:

  1. 如果训练集大小为N,对于每棵树而言,随机且有放回地从训练集中的抽取N个训练样本(这种采样方式称为bootstrap sample方法),作为该树的训练集;从这里我们可以知道:每棵树的训练集都是不同的,而且里面包含重复的训练样本(理解这点很重要)。

    这里有两个问题:

    Problem1 : 为什么要随机抽样训练集?
    如果不进行随机抽样,每棵树的训练集都一样,那么最终训练出的树分类结果也是完全一样的,这样的话完全没有bagging的必要;

    Problem2: 为什么要有放回地抽样?

    • 如果不放回抽样,每棵树用的样本完全不同,结果是有偏的,基学习器之间的相似性小,投票结果差,模型偏差大
    • 如果不抽样,基学习器用所有样本,那么模型的泛化能力弱,基学习器之前相似性太大差异性太小,模型的偏差大
  2. 如果每个样本的特征维度为M,指定一个常数m<<M,随机地从M个特征中选取m个特征子集,每次树进行分裂时,从这m个特征中选择最优的;

  3. 每棵树都尽最大程度的生长,并且没有剪枝过程。

一开始我们提到的随机森林中的“随机”就是指的这里的两个随机性。两个随机性的引入对随机森林的分类性能至关重要。由于它们的引入,使得随机森林不容易陷入过拟合,并且具有很好得抗噪能力(比如:对缺省值不敏感)。

1.2 随机森林分类效果(错误率)的影响因素:

随机森林分类效果(错误率)与两个因素有关:

  • 森林中任意两棵树的相关性:相关性越大,错误率越大;
  • 森林中每棵树的分类能力:每棵树的分类能力越强,整个森林的错误率越低。

减小特征选择个数m,树的相关性和分类能力也会相应的降低;增大m,两者也会随之增大。所以关键问题是如何选择最优的m(或者是范围),这也是随机森林唯一的一个参数。

1.3 袋外错误率(oob error)

上面我们提到,构建随机森林的关键问题就是如何选择最优的m,要解决这个问题主要依据计算袋外错误率oob error(out-of-bag error)。

随机森林有一个重要的优点就是,没有必要对它进行交叉验证或者用一个独立的测试集来获得误差的一个无偏估计。它可以在内部进行评估,也就是说在生成的过程中就可以对误差建立一个无偏估计。

在构建每棵树时,我们对训练集使用了不同的bootstrap sample(随机且有放回地抽取)。所以对于每棵树而言(假设对于第k棵树),大约有1/3的训练实例没有参与第k棵树的生成,它们称为第k棵树的oob样本。

而这样的采样特点就允许我们进行oob估计,它的计算方式如下:

(note:以样本为单位)

1)对每个样本,计算它作为oob样本的树对它的分类情况(约1/3的树);

2)然后以简单多数投票作为该样本的分类结果;

3)最后用误分个数占样本总数的比率作为随机森林的oob误分率。

2. 随机森林工作原理解释的一个例子

2.1 问题描述

描述:根据已有的训练集已经生成了对应的随机森林,随机森林如何利用某一个人的年龄(Age)、性别(Gender)、教育情况(Highest Educational Qualification)、工作领域(Industry)以及住宅地(Residence)共5个字段来预测他的收入层次。
收入层次 :
Band 1 : Below $40,000
Band 2: $40,000 – 150,000
Band 3: More than $150,000

随机森林中每一棵树都可以看做是一棵CART(分类回归树),这里假设森林中有5棵CART树,总特征个数N=5,我们取m=1(这里假设每个CART树对应一个不同的特征)。



2.2 具体问题

我们要预测的某个人的信息如下:

  1. Age : 35 years ;
  2. Gender : Male ;
  3. Highest Educational Qualification : Diploma holder;
  4. Industry : Manufacturing;
  5. Residence : Metro.

根据这个人的5个相关信息,可判断其收入层次为?

2.3 得出结论

根据这五棵CART树的分类结果,我们可以针对这个人的信息建立收入层次的分布情况:

最后,我们得出结论,这个人的收入层次70%是一等,大约24%为二等,6%为三等,所以最终认定该人属于一等收入层次(小于$40,000)。

3. 随机森林实现(用python)

问题:如何衡量住房指标的主要影响因素所占的比重?

3.1 数据集来源

住房数据集

3.2 代码

3.2.1一些可以了解的参数

要了解sklearn.ensemble.RandomForestRegressor每个参数的意义,我们需要从函数定义入手,具体介绍还得看官网介绍:

sklearn.ensemble.RandomForestRegressor(n_estimators=100, *, # 树的棵树,默认是100criterion='mse', # 默认“ mse”,衡量质量的功能,可选择“mae”。max_depth=None, # 树的最大深度。min_samples_split=2, # 拆分内部节点所需的最少样本数:min_samples_leaf=1, # 在叶节点处需要的最小样本数。min_weight_fraction_leaf=0.0, # 在所有叶节点处的权重总和中的最小加权分数。max_features='auto', # 寻找最佳分割时要考虑的特征数量。max_leaf_nodes=None, # 以最佳优先方式生长具有max_leaf_nodes的树。min_impurity_decrease=0.0, # 如果节点分裂会导致杂质的减少大于或等于该值,则该节点将被分裂。min_impurity_split=None, # 提前停止树木生长的阈值。bootstrap=True, # 建立树木时是否使用bootstrap抽样。 如果为False,则将整个数据集用于构建每棵决策树。oob_score=False, # 是否使用out-of-bag样本估算未过滤的数据的R2。n_jobs=None, # 并行运行的Job数目。random_state=None, # 控制构建树时样本的随机抽样verbose=0, # 在拟合和预测时控制详细程度。warm_start=False, # 设置为True时,重复使用上一个解决方案,否则,只需拟合一个全新的森林。ccp_alpha=0.0,max_samples=None)# 如果bootstrap为True,则从X抽取以训练每个决策树。

3.2.2 核心代码部分

from sklearn.ensemble import RandomForestRegressorimport numpy as npimport pandas as pdfrom sklearn.model_selection import train_test_splitimport matplotlib.pyplot as pltfrom sklearn.metrics import r2_scorefrom sklearn.metrics import mean_squared_error#导入数据集df = pd.read_csv('E:\\随机森林\\test_py住房\\housing.data.txt',sep='\s+') # 输出数据预览print(df.head())#自变量(该数据集的前13项)X = df.iloc[:, :-1].values #因变量(该数据集的最后1项,即第14项)y = df.iloc[:, -1].valuesX_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.6, random_state=1)# 评估回归性能# criterion :# 回归树衡量分枝质量的指标,支持的标准有三种:# 1)输入"mse"使用均方误差mean squared error(MSE),父节点和叶子节点之间的均方误差的差额将被用来作为特征选择的标准,# 这种方法通过使用叶子节点的均值来最小化L2损失# 2)输入“friedman_mse”使用费尔德曼均方误差,这种指标使用弗里德曼针对潜在分枝中的问题改进后的均方误差# 3)输入"mae"使用绝对平均误差MAE(mean absolute error),这种指标使用叶节点的中值来最小化L1损失#此处使用mseforest = RandomForestRegressor(n_estimators=1000, criterion='mse', random_state=1, n_jobs=-1)forest.fit(X_train, y_train)y_train_pred = forest.predict(X_train)y_test_pred = forest.predict(X_test)print('MSE train: %.3f, test: %.3f' % (mean_squared_error(y_train, y_train_pred),mean_squared_error(y_test, y_test_pred)))print('R^2 train: %.3f, test: %.3f' % (r2_score(y_train, y_train_pred),r2_score(y_test, y_test_pred)))

运行结果:

 CRIMZNINDUSCHASNOX RM AGE DISRADTAX\00.0063218.0 2.31 00.5386.57565.24.09001296.0 10.02731 0.0 7.07 00.4696.42178.94.96712242.0 20.02729 0.0 7.07 00.4697.18561.14.96712242.0 30.03237 0.0 2.18 00.4586.99845.86.06223222.0 40.06905 0.0 2.18 00.4587.14754.26.06223222.0PTRATIO BLSTATMEDV0 15.3396.90 4.9824.01 17.8396.90 9.1421.62 17.8392.83 4.0334.73 18.7394.63 2.9433.44 18.7396.90 5.3336.2MSE train: 1.933, test: 13.609R^2 train: 0.977, test: 0.840

3.2.3 变量重要性及其可视化

通过变量重要性评价,可以删除那些不重要的变量,并且性能不会受到影响。另外,如果我们使用不同的机器学习方法(例如支持向量机),则可以将随机森林特征重要性用作一种特征选择方法。

为了量化整个随机森林中所有变量对模型的贡献,我们可以查看变量的相对重要性。 Skicit-learn中返回的重要性表示包含特定变量可以提高预测。 重要性的实际计算超出了本文的范围,这里仅对模型输出重要性数值进行使用。

# Get numerical feature importancesimportances = list(forest.feature_importances_)print(importances)# Saving feature names for later usefeature_list = list(df.columns)[0:13]feature_importances = [(feature, round(importance, 3)) for feature, importance in zip(feature_list, importances)]# Sort the feature importances by most important firstfeature_importances = sorted(feature_importances, key = lambda x: x[1], reverse = True)# Print out the feature and importances print(feature_importances)

运行结果:

[0.040066920510444685, 0.0015561538993528526, 0.01045073058081698, 0.000895935306995048, 0.010459068115914133, 0.259747948545512, 0.019742528892980764, 0.06524483886639866, 0.004290355906371658, 0.019780228992956887, 0.011624227035179726, 0.01186651010741281, 0.5442745532396639][('LSTAT', 0.544), ('RM', 0.26), ('DIS', 0.065), ('CRIM', 0.04), ('AGE', 0.02), ('TAX', 0.02), ('PTRATIO', 0.012), ('B', 0.012), ('INDUS', 0.01), ('NOX', 0.01), ('RAD', 0.004), ('ZN', 0.002), ('CHAS', 0.001)]
import matplotlib.pyplot as plt# Set the style# plt.style.use('fivethirtyeight')# list of x locations for plottingx_values = list(range(len(importances)))print(x_values)# Make a bar chartplt.bar(x_values, importances, orientation = 'vertical')# Tick labels for x axisplt.xticks(x_values, feature_list,rotation=30)# Axis labels and titleplt.ylabel('Importance'); plt.xlabel('Variable'); plt.title('Variable Importances');plt.show()

运行结果:

至此,可以观察得出,特征”LSTAT“所占最终决策的比重最大,其次是”RM“…

3.2.4 随机森林的可视化结构图

运行如下代码, 从结构化可以看到mse逐渐减小。

这里一共是14层树。其中X[0], X[1], X[2], X[3], X[4]…分别为对应的自变量。

from sklearn.pipeline import Pipelinefrom sklearn.decomposition import PCAfrom sklearn.preprocessing import StandardScalerpipe = Pipeline([('scaler', StandardScaler()), ('reduce_dim', PCA()), ('regressor', forest)])pipe.fit(X_train, y_train)ypipe = pipe.predict(X_test)from six import StringIOfrom IPython.display import Imagefrom sklearn.tree import export_graphvizimport pydotplusimport osdot_data = StringIO()export_graphviz(pipe.named_steps['regressor'].estimators_[0],out_file=dot_data)graph = pydotplus.graph_from_dot_data(dot_data.getvalue())graph.write_png('tree.png')Image(graph.create_png())

运行结果:

参考:
【机器学习】 随机森林(Random Forest)
用Python实现随机森林回归