Skip to main content

随机森林

随机森林

随机森林是对决策树集合的特有名称。

随机森林里我们有多个决策树(所以叫“森林”)。

为了给一个新的观察值分类,根据它的特征,每一个决策树都会给出一个分类。

随机森林算法选出投票最多的分类作为分类结果。

怎样生成决策树:

  1. 如果训练集中有 N 种类别,则有重复地随机选取 N 个样本。这些样本将组成培养决策树的训练集。

  2. 如果有 M 个特征变量,那么选取数m << M,从而在每个节点上随机选取 m 个特征变量来分割该节点。m 在整个森林养成中保持不变。

  3. 每个决策树都最大程度上进行分割,没有剪枝。


from sklearn.ensemble import RandomForestClassifier
import numpy as np

# 创建一些示例数据
X = np.array([[1, 2], [2, 3], [2, 5], [3, 2], [3, 3], [4, 5]]) # 特征
y = np.array([0, 0, 1, 0, 1, 1]) # 目标标签

# 创建随机森林分类器
'''
一般来说,深度越大,拟合效果越好,速度越慢,常用的可以取值10到100之间。
'''
n_estimators = 100 # 设置随机森林中的树的数量
model = RandomForestClassifier(n_estimators=n_estimators)

# 拟合模型
model.fit(X, y)

# 预测新数据点
new_data_point = np.array([[3, 4]]) # 要预测的新数据点
predicted_class = model.predict(new_data_point)

print("预测类别:", predicted_class)

简单示例

import matplotlib.pyplot as plt
import numpy as np
from sklearn.datasets import make_blobs
from sklearn.ensemble import RandomForestClassifier
from sklearn.calibration import CalibratedClassifierCV
from sklearn.metrics import log_loss
# 随机数种子设为0,前面有讲过,这样可以保证每次运行结果都一样
np.random.seed(0)

# 随机生成1000个样本,每个样本包含2个特征,共4个簇
X, y = make_blobs(n_samples=1000, random_state=42, cluster_std=5.0)

# 用前800个样本作为训练集,并且这800个样本中,前600个样本作为训练集,后200个样本用来矫正
X_train, y_train = X[:600], y[:600]
X_valid, y_valid = X[600:800], y[600:800]

# 直接选取前800个样本作为训练集
X_train_valid, y_train_valid = X[:800], y[:800]

# 用前800个样本作为训练+验证集,剩下的200个样本作为测试集
X_test, y_test = X[800:], y[800:]

# 树的数量为25
clf = RandomForestClassifier(n_estimators=25)
# 直接使用前80%的数据训练模型
clf.fit(X_train_valid, y_train_valid)
# 后20%的数据测试,
clf_probs = clf.predict_proba(X_test)

效果评估

# 用log_loss来评估模型
score = log_loss(y_test, clf_probs)
print(" %.3f " % score)
# 这个分类器对所有800个训练数据点都进行了训练,那么它对它的预测过于自信,导致了过拟合

# 接下来需要矫正这个分类器,让它对自己的预测不那么自信

# 修正前的误差达到了1.3 修正后的数据误差降到了 0.534

使用模型校准器

# Train random forest classifier, calibrate on validation data and evaluate
# on test data
# 使用前60%的数据训练,中20%的数据校准,后20%的数据测试
clf = RandomForestClassifier(n_estimators=25)
clf.fit(X_train, y_train)
clf_probs = clf.predict_proba(X_test)
# 获取原本的模型
# method="sigmoid" 代表使用sigmoid函数来进行校准
# cv="prefit" 代表使用预先训练好的模型来进行校准
sig_clf = CalibratedClassifierCV(clf, method="sigmoid", cv="prefit")
# 对中间的20%预测后进行概率调试(模型优化)
sig_clf.fit(X_valid, y_valid)
# .predict_proba(X_test) 进行预测(优化后的模型使用方法不变)
sig_clf_probs = sig_clf.predict_proba(X_test)
# 用log_loss来评估模型
sig_score = log_loss(y_test, sig_clf_probs)

print("%.3f" % sig_score)
# 修正前的误差达到了1.3 修正后的数据误差降到了 0.534


模型校准器工作原理

# Plot changes in predicted probabilities via arrows
plt.figure()
# 颜色表示实例的真正类(红色:1类,绿色:2类,蓝色:3类)
colors = ["r", "g", "b"]
for i in range(clf_probs.shape[0]):
plt.arrow(clf_probs[i, 0], clf_probs[i, 1],
sig_clf_probs[i, 0] - clf_probs[i, 0],
sig_clf_probs[i, 1] - clf_probs[i, 1],
color=colors[y_test[i]], head_width=1e-2)

# Plot perfect predictions
plt.plot([1.0], [0.0], 'ro', ms=20, label="Class 1")
plt.plot([0.0], [1.0], 'go', ms=20, label="Class 2")
plt.plot([0.0], [0.0], 'bo', ms=20, label="Class 3")

# Plot boundaries of unit simplex
plt.plot([0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], 'k', label="Simplex")

# Annotate points on the simplex
plt.annotate(r'($\frac{1}{3}$, $\frac{1}{3}$, $\frac{1}{3}$)',
xy=(1.0/3, 1.0/3), xytext=(1.0/3, .23), xycoords='data',
arrowprops=dict(facecolor='black', shrink=0.05),
horizontalalignment='center', verticalalignment='center')
plt.plot([1.0/3], [1.0/3], 'ko', ms=5)
plt.annotate(r'($\frac{1}{2}$, $0$, $\frac{1}{2}$)',
xy=(.5, .0), xytext=(.5, .1), xycoords='data',
arrowprops=dict(facecolor='black', shrink=0.05),
horizontalalignment='center', verticalalignment='center')
plt.annotate(r'($0$, $\frac{1}{2}$, $\frac{1}{2}$)',
xy=(.0, .5), xytext=(.1, .5), xycoords='data',
arrowprops=dict(facecolor='black', shrink=0.05),
horizontalalignment='center', verticalalignment='center')
plt.annotate(r'($\frac{1}{2}$, $\frac{1}{2}$, $0$)',
xy=(.5, .5), xytext=(.6, .6), xycoords='data',
arrowprops=dict(facecolor='black', shrink=0.05),
horizontalalignment='center', verticalalignment='center')
plt.annotate(r'($0$, $0$, $1$)',
xy=(0, 0), xytext=(.1, .1), xycoords='data',
arrowprops=dict(facecolor='black', shrink=0.05),
horizontalalignment='center', verticalalignment='center')
plt.annotate(r'($1$, $0$, $0$)',
xy=(1, 0), xytext=(1, .1), xycoords='data',
arrowprops=dict(facecolor='black', shrink=0.05),
horizontalalignment='center', verticalalignment='center')
plt.annotate(r'($0$, $1$, $0$)',
xy=(0, 1), xytext=(.1, 1), xycoords='data',
arrowprops=dict(facecolor='black', shrink=0.05),
horizontalalignment='center', verticalalignment='center')
# Add grid
plt.grid(False)
for x in [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]:
plt.plot([0, x], [x, 0], 'k', alpha=0.2)
plt.plot([0, 0 + (1-x)/2], [x, x + (1-x)/2], 'k', alpha=0.2)
plt.plot([x, x + (1-x)/2], [0, 0 + (1-x)/2], 'k', alpha=0.2)

plt.title("Change of predicted probabilities after sigmoid calibration")
plt.xlabel("Probability class 1")
plt.ylabel("Probability class 2")
plt.xlim(-0.05, 1.05)
plt.ylim(-0.05, 1.05)
plt.legend(loc="best")

print("Log-loss of")
print(" * uncalibrated classifier trained on 800 datapoints: %.3f "
% score)
print(" * classifier trained on 600 datapoints and calibrated on "
"200 datapoint: %.3f" % sig_score)

# Illustrate calibrator
plt.figure()
# generate grid over 2-simplex
p1d = np.linspace(0, 1, 20)
p0, p1 = np.meshgrid(p1d, p1d)
p2 = 1 - p0 - p1
p = np.c_[p0.ravel(), p1.ravel(), p2.ravel()]
p = p[p[:, 2] >= 0]

calibrated_classifier = sig_clf.calibrated_classifiers_[0]
prediction = np.vstack([calibrator.predict(this_p)
for calibrator, this_p in
zip(calibrated_classifier.calibrators, p.T)]).T
prediction /= prediction.sum(axis=1)[:, None]

# Plot modifications of calibrator
for i in range(prediction.shape[0]):
plt.arrow(p[i, 0], p[i, 1],
prediction[i, 0] - p[i, 0], prediction[i, 1] - p[i, 1],
head_width=1e-2, color=colors[np.argmax(p[i])])
# Plot boundaries of unit simplex
plt.plot([0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], 'k', label="Simplex")

plt.grid(False)
for x in [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]:
plt.plot([0, x], [x, 0], 'k', alpha=0.2)
plt.plot([0, 0 + (1-x)/2], [x, x + (1-x)/2], 'k', alpha=0.2)
plt.plot([x, x + (1-x)/2], [0, 0 + (1-x)/2], 'k', alpha=0.2)

# sigmoid校准器图示
plt.title("sigmoid ")
plt.xlabel("Probability class 1")
plt.ylabel("Probability class 2")
# .xlim是设置x轴的范围
plt.xlim(-0.05, 1.05)
# .ylim是设置y轴的范围
plt.ylim(-0.05, 1.05)

plt.show()