随机森林分类与回归
决策树
随机森林是通过集成学习的思想将多棵树集成的一种算法,它的基本单元是决策树。因此,我们将从讨论决策树本身开始。
决策树也是一种十分常见的监督学习方法。它是一种特殊的树形结构,一般由节点和有向边组成。其中,节点表示特征、属性或者一个类。而有向边包含有判断条件。

如图所示,决策树从根节点开始延伸,经过不同的判断条件后,到达不同的子节点。而上层子节点又可以作为父节点被进一步划分为下层子节点。一般情况下,我们从根节点输入数据,经过多次判断后,这些数据就会被分为不同的类别。这就构成了一颗简单的分类决策树。
用 scikit-learn 构建一个决策树模型举一个例子,我们先用 scikit-learn 的数据生成器 make_blobs 生成环形形状的数据:
from sklearn.datasets import make_blobs
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
%matplotlib inline
# 导入环形形状数据生成器
plt.style.use('seaborn') # 样式美化
# 生成 300 个并分成 4 类的数据
X, y = make_blobs(n_samples=300, centers=4,
random_state=0, cluster_std=1.0)
plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='rainbow') # 绘制散点图
为了减少后面代码的重复,这里我们先定义一个决策树可视化的函数 visualize_tree,调用此函数可实现分类结果可视化。其参数 estimator 代表 scikit-learn 估计器,boundaries 决定是否绘制分类边界。函数代码如下:
def visualize_tree(estimator, X, y, boundaries=True,
xlim=None, ylim=None):
estimator.fit(X, y) # 拟合模型
# 保证所有点都在画布内
if xlim is None:
xlim = (X[:, 0].min() - 0.1, X[:, 0].max() + 0.1)
if ylim is None:
ylim = (X[:, 1].min() - 0.1, X[:, 1].max() + 0.1)
x_min, x_max = xlim
y_min, y_max = ylim
# np.linspace(x_min, x_max, 100)在[x_min,x_max]中产生100个均匀间隔的数字(包括尾部)
xx, yy = np.meshgrid(np.linspace(x_min, x_max, 100),
np.linspace(y_min, y_max, 100)) # 生成网格点坐标矩阵
# ravel()将xx、yy变成一行矩阵(扁平化操作),np.c_将两个矩阵按列叠加成一个矩阵,即变成一个二维矩阵
Z = estimator.predict(np.c_[xx.ravel(), yy.ravel()])
# 绘制分类图
Z = Z.reshape(xx.shape)
plt.figure() # 创建画布
plt.pcolormesh(xx, yy, Z, alpha=0.2, cmap='rainbow')
# plt.pcolormesh()会根据 z 的结果自动在cmap中选择颜色
plt.clim(y.min(), y.max())
# 绘制训练数据点
plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='rainbow')
plt.axis('off') # 不显示坐标及标签
plt.xlim(x_min, x_max)
plt.ylim(y_min, y_max)
plt.clim(y.min(), y.max())
# 绘制决策边界(黑色)
def plot_boundaries(i, xlim, ylim):
if i < 0:
return
tree = estimator.tree_ # 访问决策树整个结构
# tree.feature 为节点分割特征数组
if tree.feature[i] == 0:
# tree.threshold 为节点分裂点数组
plt.plot([tree.threshold[i], tree.threshold[i]], ylim, '-k')
plot_boundaries(tree.children_left[i],
[xlim[0], tree.threshold[i]], ylim) # tree.children_left 左分类数组
plot_boundaries(tree.children_right[i],
[tree.threshold[i], xlim[1]], ylim) # tree.children_right 右分类数组
elif tree.feature[i] == 1:
plt.plot(xlim, [tree.threshold[i], tree.threshold[i]], '-k')
plot_boundaries(tree.children_left[i], xlim,
[ylim[0], tree.threshold[i]])
plot_boundaries(tree.children_right[i], xlim,
[tree.threshold[i], ylim[1]])
if boundaries:
plot_boundaries(0, plt.xlim(), plt.ylim())
让我们使用 Jupyter notebook 的交互功能 ipywidgets 来查看决策树具体如何分类的,这里我们使用 scikit-learn 的 DecisionTreeClassifier 估计器构建决策树模型,其参数 max_depth 设置决策树的最大深度,深度越大,越容易过拟合,需要注意的是还有个重要参数 criterion:特征选择标准,默认值为 'gini',基尼系数,即 CART 算法,另一个特征选择标准为 'entropy',信息增益,即 ID3 算法,关于决策树算法你可以参考 简单理论,这里就不详细说明了,关于 DecisionTreeClassifier 估计器的详细其他参数说明可以查看 官方文档。
# 导入估计器
from sklearn.tree import DecisionTreeClassifier
from ipywidgets import interact
def interactive_tree(depth=1):
clf = DecisionTreeClassifier(max_depth=depth, random_state=0)
visualize_tree(clf, X, y) # 调用自定义函数,注意此函数完成了拟合模型的过程
# 选择深度为 1 到 5
interact(interactive_tree, depth=(1, 5))
移动上图的滚动条,我们可以看到深度即分类数从 1 到 5 的分类结果,注意每次深度增加时,每个节点都被分为两个部分,但那些节点仅包含一个类,而且当深度达到 5 时,很明显模型已经过拟合了。
决策树的一个问题是,很容易创建出过拟合的树。例如,让我们看一下基于此数据集的两个子集构建的两棵树:
clf = DecisionTreeClassifier() # 构建模型
plt.figure() # 创建画布
visualize_tree(clf, X[:200], y[:200], boundaries=False) # 选择前 200 个数据,不绘制决策边界
plt.figure()
visualize_tree(clf, X[-200:], y[-200:], boundaries=False) # 选择后 200 个数据
从上面两个图看出,分类的细节完全不同。这表明模型已经过拟合了,当我们预测新数据点的值时,结果更能反映模型中的噪声而不是整体数据。
随机森林
解决过拟合的一种可能方法是使用集成学习:通过建立几个模型组合的来解决单一预测问题。它的工作原理是生成多个分类器或者模型,各自独立地学习和作出预测。这些预测最后结合成单预测,因此优于任何一个单分类的做出预测。
最常见的集成学习之一是随机森林,随机森林对于一个训练集随机建立多颗决策树,而建立这些决策树时,会采取一种叫 Bootstrap 的取样方式,即每一次从数据集中又放回的取出一部分数据,再用这部分数据去建立小决策树。对于随机森林而言,最终的分类结果由众多小决策树输出类别的众数确定。下图展示了一个由 3 颗决策树构成的随机森林过程。

随机森林可以有效地降低过拟合程度,具有较好的泛化误差。另外,训练速度也非常快,模型的表现往往都比较好,是十分受欢迎的一种机器学习方法。
随机森林分类
在前面决策树的学习中我们已经了解到了同一组数据不同的子集分类结果的细节也会有很大不同,但观察其分类结果图,分类较大的特征部分是没有变化的,接下来让我们看看随机森林的分类效果,这里使用了 scikit-learn 的随机森林分类估计器
RandomForestClassifier,其中参数 n_estimators 表示估计器中决策树的数量,其他参数还包括决策树估计器的系列参数,完整参数可以参考 官方文档。
# 导入 随机森林估计器
from sklearn.ensemble import RandomForestClassifier
# 构建包含 100 个决策树分类器的随机森林模型
clf = RandomForestClassifier(n_estimators=100, random_state=0)
visualize_tree(clf, X, y, boundaries=False)
我们最终得到了一个更适合我们数据的整体模型。你还可以将参数 n_estimators 的数值改小或者改得更大来看看分类效果。
接下来我们还是使用上个实验用到的 digits 手写数字数据集来测试下随机森林分类器的效果。
步骤和上个实验一样,我们先导入数据并查看数据的前 5 张图片:
from sklearn import datasets # 导入数据集模块
# 载入数据集
digits = datasets.load_digits()
# 绘制数据集前 5 个手写数字的灰度图像
for index, image in enumerate(digits.images[:5]):
plt.subplot(2, 5, index+1) # 子图绘制
plt.imshow(image, cmap=plt.cm.gray_r, interpolation='nearest')
# 图片灰度处理
下面,我们划分训练集和测试集,然后针对测试集进行预测并评估预测精准度。
from sklearn.model_selection import train_test_split # 导入数据集划分模块
from sklearn.metrics import accuracy_score # 导入评估模块
X = digits.data
y = digits.target
# 划分数据集,test_size 默认值为 0.3
Xtrain, Xtest, ytrain, ytest = train_test_split(X, y, random_state=0)
# 构建包含 100 个决策树分类器的模型
clf = RandomForestClassifier(n_estimators=100, random_state=0)
clf.fit(Xtrain, ytrain)
ypred = clf.predict(Xtest) # 模型预测
scores = accuracy_score(ypred, ytest) # 评估预测精准度
scores
我们可以看到最后预测精准度达到了 97.5%,上个实验中 SVM 的 gamma 为 0.001 时预测准确度达到了 98%,你可以尝试改改随机森林估计器的参数,n_estimators、max_depth 等,看看精准度是否有提高。
随机森林回归
前面我们在分类的背景下考虑了随机森林,另外随机森林也可以用于回归。用于随机森林回归的估计器是 sklearn.ensemble.RandomForestRegressor。让我们看看如何构建回归模型。
第一步,先生成具有一定规律的并带有误差的数据:
# 导入随机森林回归估计器
from sklearn.ensemble import RandomForestRegressor
# 随机生成 100 个 0-10 的浮点数
x = 10 * np.random.rand(100)
def model(x, sigma=0.3):
fast_oscillation = np.sin(5 * x)
slow_oscillation = np.sin(0.5 * x)
# 生成100个 0.3倍的标准正态分布噪声
noise = sigma * np.random.randn(len(x)) # len(x)=100
return slow_oscillation + fast_oscillation + noise
y = model(x)
# plt.errorbar()函数用于表现有一定置信区间的带误差数据,误差范围为 0.3
plt.errorbar(x, y, 0.3, fmt='o')
对前面生成的带有噪声的数据进行训练,并用 np.linspace 等距得生成 1000 个数据作为测试集,并将训练集数据、随机森林模型预测结果(红色曲线)和真实结果图(黑色曲线)绘制在一张图上。
xfit = np.linspace(0, 10, 1000) # 生成 0-10 等距的1000个数据作为测试集
# 构建包含 100 个决策树分类器的模型、拟合并进行预测,得到随机森林预测结果
yfit = RandomForestRegressor(100).fit(x[:, None], y).predict(xfit[:, None])
ytrue = model(xfit, 0) # 不含噪声的测试集的真实结果
plt.errorbar(x, y, 0.3, fmt='o') # 训练集绘制
plt.plot(xfit, yfit, '-r') # 绘制随机森林预测结果(红线图)
plt.plot(xfit, ytrue, '-k', alpha=0.5) # 绘制真实结果(黑线图)
从上图看出,随机森林的回归效果还是很不错的。