第四篇 深入监督学习:随机森林

深入监督学习:随机森林

前面我们见过了一种强大的分类器:支持向量机
这里我们将看到另外一种强大的算法,这是一种non-parametric算法叫做随机森林

1
2
3
4
5
6
7
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
# use seaborn plotting defaults
import seaborn as sns; sns.set()

随机森林:决策树

随机森林基于决策树,它是集成学习的一个例子。基于这个原因,我们用决策树开始介绍。
决策树用一种非常直观的方式来分类对象:你可以问一系列问题来做出yes或no的判断。

1
2
import fig_code
fig_code.plot_example_decision_tree()

png

上面的二叉树分类很有效率。这个技巧的关键是对特征提问
这个过程是这样的:在训练决策树分类器过程中,算法查找每一个特征,然后决定哪一个是正确的答案。

创建决策树

这里是sklearn中的决策树分类器的一个例子。我们开始定义一些二维的打过标签的数据。

1
2
3
4
5
from sklearn.datasets import make_blobs
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');

png

我们引入一些可视化的函数

1
from fig_code import visualize_tree, plot_tree_interactive

现在我们使用IPython的interact,来看一下决策树的分割过程:

1
plot_tree_interactive(X, y);

png

注意随着深度的增加,这些节点一分为二,这些节点只包含一个分类。它是非常快速的非参数分类,在实践中很有用。

问题:你能发现上面的有什么问题?

决策树和过拟合

决策树有个问题,就是它很容易产生过拟合。因为他们在分类的过程很容易学习到noise而不是signal。我们看下两棵决策树根据同一个数据集中两个不同子类构造的过程:

1
2
3
4
5
6
7
from sklearn.tree import DecisionTreeClassifier
clf = DecisionTreeClassifier()
plt.figure()
visualize_tree(clf, X[:200], y[:200], boundaries=False)
plt.figure()
visualize_tree(clf, X[-200:], y[-200:], boundaries=False)
<matplotlib.figure.Figure at 0x118962a50>

png

<matplotlib.figure.Figure at 0x118f4de50>

png

<matplotlib.figure.Figure at 0x11858d790>

png

分类完全不同!很明显这产生了过拟合:当预测新的样本时,结果反映了模型中的noise而不是signal

Estimators的集成:随机森林

一个解决过拟合的可以方式是使用集成方法:这是一个meta-estimator,它由多个独立的estimator组成,能够平衡每个estimator过拟合结果。和它包含的单独的estimator相比,评估的结果看上去更加健壮和精确。

一个最常见的集成方法叫做随机森林,它由许多决策树组成。

让我先看看下面的决策树例子获得一个感性的认识:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def fit_randomized_tree(random_state=0):
X, y = make_blobs(n_samples=300, centers=4,
random_state=0, cluster_std=2.0)
clf = DecisionTreeClassifier(max_depth=15)
rng = np.random.RandomState(random_state)
i = np.arange(len(y))
rng.shuffle(i)
visualize_tree(clf, X[i[:250]], y[i[:250]], boundaries=False,
xlim=(X[:, 0].min(), X[:, 0].max()),
ylim=(X[:, 1].min(), X[:, 1].max()))
from IPython.html.widgets import interact
interact(fit_randomized_tree, random_state=[0, 100]);

png

虽然样本发生变化时模型对样本的分类也变化了,但是大的特性保持不变。随机森林分类器做的事情类似,它综合考虑了所有决策树的判断:

1
2
3
from sklearn.ensemble import RandomForestClassifier
clf = RandomForestClassifier(n_estimators=100, random_state=0)
visualize_tree(clf, X, y, boundaries=False);

png

通过平衡100个随机模型,随机森林最后得出的模型更好拟合了数据。

例子:移步到回归

上面我们在分类问题上使用了随机森林。它在回归问题上也能很好地工作。使用的estimator叫做sklearn.ensemble.RandomForestRegressor

让我们来看下怎么使用它:

1
2
3
4
5
6
7
8
9
10
11
12
13
from sklearn.ensemble import RandomForestRegressor
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)
noise = sigma * np.random.randn(len(x))
return slow_oscillation + fast_oscillation + noise
y = model(x)
plt.errorbar(x, y, 0.3, fmt='o');

png

1
2
3
4
5
6
7
xfit = np.linspace(0, 10, 1000)
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);

png

我们可以看到,我们甚至没有指定一个multi-period模型,随机森林就能够足够灵活地适应了multi-period数据。

例子:应用随机森林分类数字

我们之前见过了手写数字数据集。我们这里也用它来测试随机森林的准确率。

1
2
3
from sklearn.datasets import load_digits
digits = load_digits()
digits.keys()
['images', 'data', 'target_names', 'DESCR', 'target']
1
2
3
4
X = digits.data
y = digits.target
print(X.shape)
print(y.shape)
(1797, 64)
(1797,)

首先我们看一下这些手写数字:

1
2
3
4
5
6
7
8
9
10
11
# set up the figure
fig = plt.figure(figsize=(6, 6)) # figure size in inches
fig.subplots_adjust(left=0, right=1, bottom=0, top=1, hspace=0.05, wspace=0.05)
# plot the digits: each image is 8x8 pixels
for i in range(64):
ax = fig.add_subplot(8, 8, i + 1, xticks=[], yticks=[])
ax.imshow(digits.images[i], cmap=plt.cm.binary)
# label the image with the target value
ax.text(0, 7, str(digits.target[i]))

png

我们能快速使用决策树来对这些数字进行分类:

1
2
3
4
5
6
7
from sklearn.model_selection import train_test_split
from sklearn import metrics
Xtrain, Xtest, ytrain, ytest = train_test_split(X, y, random_state=0)
clf = DecisionTreeClassifier(max_depth=11)
clf.fit(Xtrain, ytrain)
ypred = clf.predict(Xtest)

我们检查一下分类器准确率:

1
metrics.accuracy_score(ypred, ytest)
0.84222222222222221

用混淆矩阵更直观:

1
2
3
4
5
6
plt.imshow(metrics.confusion_matrix(ypred, ytest),
interpolation='nearest', cmap=plt.cm.binary)
plt.grid(False)
plt.colorbar()
plt.xlabel("predicted label")
plt.ylabel("true label");

png

练习

  1. sklearn.ensemble.RandomForestClassifier来实现上面的任务。调节`max_depthmax_featuresn_estimators``参数
    会对结果造成什么影响?
  2. 再用sklearn.svm.SVC分类器,调节kernel, Cgamma试试看?
  3. 对每一个模型用一些参数集,然后检查一下F1分数(sklearn.metrics.f1_score)。

Jupyter实现


知识共享许可协议
本作品采用知识共享署名-非商业性使用-禁止演绎 3.0 未本地化版本许可协议进行许可。