随机森林(Random Forest)原理小结
随机森林(Random Forest)原理小结
接着上一章节的决策树模型,接下来会介绍一些基于决策树的,具有代表性的集成模型
,如随机森林(RF),GBDT,XGBoost以及lightGBM。
本章主要介绍随机森林(Random Forest,简写RF)
,RF是bagging
集成模型的扩展变体,所以前面会简要介绍一些关于bagging的内容,后面会有专门的“集成学习”的章节来重点介绍bagging相关内容。
1. bagging
bagging是并行式集成
学习方法的著名代表。
bagging希望集成中的个体学习器尽可能相互独立,而这在现实任务中几乎无法做到,更简单方便的实现是 使基学习器具有较大差异
,或者说使得基学习器更具有 “多样性”
。
bagging基学习器的多样性通过“样本扰动”
达到,而这是通过”自主采样法“(bootstrap sampling)
实现
bootstrap sampling:
给定包含m个样本的数据集D,每次随机、有放回的挑选一个样本,执行m次,最后得到一个包含m个样本的数据集D’。一个样本在m次采样中始终不被抽取到的概率是 ( 1 − 1 m ) m (1-\frac{1}{m})^m (1−m1)m,而 l i m m → ∞ ( 1 − 1 m ) m → 1 e ≈ 0.368 lim_{m\rightarrow\infty}(1-\frac{1}{m})^m \rightarrow\frac{1}{e}\approx0.368 limm→∞(1−m1)m→e1≈0.368,即初始训练集中有63.2%的样本会出现在采样集中。
bagging对分类
任务使用简单投票法
,对回归
任务使用简单平均法
。
从偏差-方差分解角度看,bagging主要关注降低方差
。
2. 随机森林(RF)
random forest:an ensemble
of decision trees.
The idea behind a random forest is to average multiple (deep) decision trees that individually suffer from high variance, to build a more robust model
that has a better generalization performance and is less susceptible to overfitting
.(塑料英翻:随机森林背后的思想是平均多个(深)决策树,这些树各自遭受高方差,建立一个更稳健的模型,具有更好的泛化性能,并且不太容易被过度拟合。)
random forest是bagging的扩展变体。在以决策树为基学习器构建bagging集成的基础上,进一步在决策树训练过程中引入“随机属性选择”
。
传统决策树在选择划分属性时,是在当前节点的属性集合k中选择一个最优属性;而在RF中,是先从该节点的属性集合中随机选择含有d个属性的子集
,然后从该子集
中选择一个最优属性用于划分。一般地,推荐
d
=
l
o
g
2
k
d=log_2k
d=log2k或者
d
=
k
d=\sqrt{k}
d=k
。
简单来说,bagging基学习器的多样性来自于“样本扰动”
,而RF除了“样本扰动”
,还增加了“属性扰动”
,而这使得最终基学习器之间的差异度进一步增加,从而使得最终的集成模型的泛化性能进一步提升。
RF 与 bagging的比较:
- 随机森林的**
收敛性
**与bagging相似,其实性能相对较差,因为引入了属性扰动,个体的性能往往有所降低,但随着基学习器的个数增加,随机森林能收敛到更低的泛化误差。 - 因为是考察的属性的子集,所以RF的**
训练效率
**常优于bagging。
RF中决策树采用的是CART决策树
作为基学习器
。
2.1 RF 分类
RF在可解释性上对不能同传统决策树相提并论,但是它一大优势
是不用太关注选好好的超参数。RF相比单个决策树,对噪声或者异常值十分具有鲁棒性,因而RF通常亦不需要剪枝。在实践中唯一需要关心的参数就是the number of trees: n
。n越大,RF分类器的性能越好,同时其计算代价也越大。
当然,如果需要,像bootstrap sample的采样数m
,以及再每次split时随机选取的属性子集的数目d
,这些超参数也是可以优化的。可以通过这些参数来控制RF的bias-variance tradeoff。
属性子集d,越小,个体树的性能有所降低,但整个RF模型会越健壮。d越大,属性随机性会降低,容易过拟合。
减小采样数m,能增加个体树之间的多样性,因为一个样本被包含在bootstrap sample里的概率变小了。所以减小m可以增加RF的随机性,这将有助于降低过拟合风险。然而,更小的m将会使得RF的整体性能降低,使得training和test 性能之间的有小的gap,有更低的test performance。相反的,增大m会引起一定程度的过拟合,因为bootstrap sample以及后续的个体决策树会变得彼此更相似,他们会更加接近的学习拟合原始训练数据集。
一般的,the size of the bootstrap sample m 就选择为与原始训练数据集相等的值(sklearn中也是这么实现的)。这往往就能提供一个较好的bias-variance tradeoff。而关于每次split时的属性子集数 d ,一般取的比属性总数小,sklearn中的实现是默认取值为 d = m d=\sqrt{m} d=m ,m表示训练集中特征总数。
demo示例:
数据集:鸢尾花,使用RandomForestClassifier
实现分类:
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
import numpy as np
from matplotlib.colors import ListedColormap
import matplotlib.pyplot as plt
iris = datasets.load_iris()
print(iris['data'].shape, iris['target'].shape) # (150, 4) (150,)
X = iris.data[:,[2,3]]
y = iris.target
print('Class labels:', np.unique(y))
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=1, stratify=y)
print(X_train.shape, y_train.shape)
输出:
(150, 4) (150,)
Class labels: [0 1 2]
(105, 2) (105,)
训练,并绘制分类决策边界:
forest = RandomForestClassifier(criterion='gini', # 划分准则
n_estimators=25, # 25个基学习器(决策树)
random_state=1,
n_jobs=2) # 并行执行
forest.fit(X_train, y_train)
X_combined = np.vstack((X_train, X_test))
y_combined = np.hstack((y_train, y_test))
plot_decision_regions(X_combined, y_combined, classifier=forest, test_idx=range(105,150))
plt.xlabel('petal length')
plt.ylabel('petal width')
plt.legend(loc='upper left')
plt.show()
2.2 特征重要性
Using a random forest, we can measure the feature importance
as the averaged impurity decrease
computed from all decision trees in the forest, without making any assumptions about whether our data is linearly separable or not.(可通过计算所有树的**平均不纯度减少值
**来衡量特征重要性)。
此功能在sklearn中已实现好,使用时,先在sklearn中训练好一个RandomForestClassifier后,然后就可以调用feature_importances_ 属性来获得特征重要性。
使用RandomForestClassifier作为特征选择器,获得重要的特征后,只拿那些重要的特征去训练接下来的模型,RF作为data pipeline中的特征抽取步骤。
demo示例:
数据集:白酒数据,共有13个特征,使用RandomForestClassifier
训练,然后调用feature_importances_属性获得特征重要性:
# Wine dataset and rank the 13 features by their respective importance measures
df_wine = pd.read_csv(data_dir+'wine.data',
header=None,
names=['Class label', 'Alcohol', 'Malic acid', 'Ash', 'Alcalinity of ash', 'Magnesium',
'Total phenols', 'Flavanoids', 'Nonflavanoid phenols', 'Proanthocyanins', 'Color intensity',
'Hue', 'OD280/OD315 of diluted wines', 'Proline'])
print('Class labels', np.unique(df_wine['Class label']))
print('numbers of features:', len(df_wine.keys())-1)
df_wine.head()
输出:
Class labels [1 2 3]
numbers of features: 13
X, y = df_wine.iloc[:, 1:].values, df_wine.iloc[:, 0].values
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0, stratify=y)
X_train.shape
输出:
(124, 13)
feat_labels = df_wine.columns[1:]
forest = RandomForestClassifier(n_estimators=200, random_state=1)
forest.fit(X_train, y_train)
importances = forest.feature_importances_
print(len(importances))
importances
输出:
13
array([0.11535953, 0.02485979, 0.01367936, 0.0206145 , 0.03254997,
0.04019533, 0.18793917, 0.01096301, 0.02611948, 0.13488692,
0.06345939, 0.13440744, 0.19496612])
"""
numpy.argsort(a, axis=-1, kind=’quicksort’, order=None)
功能: 将矩阵a在指定轴axis上排序,并返回排序后的下标
参数: a:输入矩阵, axis:需要排序的维度
返回值: 输出排序后的下标
"""
indices = np.argsort(importances)[::-1] # 取反后是从大到小
indices
输出:
array([12, 6, 9, 11, 0, 10, 5, 4, 8, 1, 3, 2, 7])
for i in range(X_train.shape[1]):
print("%2d) %-*s %f" % (i + 1, 30, feat_labels[indices[i]], importances[indices[i]]))
输出:
1) Proline 0.194966
2) Flavanoids 0.187939
3) Color intensity 0.134887
4) OD280/OD315 of diluted wines 0.134407
5) Alcohol 0.115360
6) Hue 0.063459
7) Total phenols 0.040195
8) Magnesium 0.032550
9) Proanthocyanins 0.026119
10) Malic acid 0.024860
11) Alcalinity of ash 0.020615
12) Ash 0.013679
13) Nonflavanoid phenols 0.010963
绘图:
plt.title('Feature Importance')
plt.bar(range(X_train.shape[1]), importances[indices], align='center')
plt.xticks(range(X_train.shape[1]), feat_labels[indices], rotation=90)
plt.xlim([-1, X_train.shape[1]])
plt.tight_layout()
plt.show()
2.3 RF 回归
The basic random forest algorithm for regression is almost identical to the random forest algorithm for classification, the only difference is that we use the MSE criterion
to grow the individual decision trees, and the predicted target variable is calculated as the average prediction
over all decision trees.(几乎很RF分类器一样,只是使用**MSE
**作为生成个体决策树的标准,预测时将所有树的结果进行求平均
来获得最终的预测结果值。)
demo示例:
数据集:房价预测,使用RandomForestRegressor
实现回归:
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_squared_error
df = pd.read_csv(data_dir+'housing.data.txt',
header=None,
sep='\s+',
names= ['CRIM', 'ZN', 'INDUS', 'CHAS', 'NOX', 'RM', 'AGE', 'DIS', 'RAD', 'TAX', 'PTRATIO', 'B', 'LSTAT', 'MEDV'])
df.head()
输出:
X = df.iloc[:, :-1].values
y = df['MEDV'].values
X_train, X_test, y_train, y_test =train_test_split(X, y, test_size=0.2, random_state=1)
print(X_train.shape, y_train.shape)
输出:
(404, 13) (404,)
训练:
forest = RandomForestRegressor(n_estimators=100, 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)))
输出:
MSE train: 1.237, test: 8.916
3. 模型评价
优点:
- 训练可以并行化,训练速度快
- 训练后,可以给出各个特征对于输出的重要性/贡献程度
- 决策树和RF具有尺度不变性,不需要做feature scaling
- RF中的基学习器(决策树)不需要剪枝
- 不需要太多的调参,唯一需要关注的就是RF中基学习器的个数n_estimator
- 样本扰动+属性扰动增加了随机性,使得RF不像决策树那样容易过拟合,模型方差小,泛化能力强
- RF对异常值/缺失值不敏感,十分具有鲁棒性
缺点:
- 在某些噪音比较大的样本集上,RF模型容易陷入过拟合。
- 值划分比较多的特征容易对RF的决策产生更大的影响,从而影响拟合的模型的效果。
4. 参考
- [1] 机器学习(西瓜书) 周志华
- [2] Python_Machine_Learning_2nd_Edition