本文探究一些关于随机森林细节的东西
一:随机森林效果比单个决策树要好的原因是?
百家言胜过一言堂
在bagging中,多个分类器的随机森林为什么要比单个基本分类器要好呢?
我们从数学的角度来简单分析下:
假设我们有单独的一棵树,其正确率是80%(错误率是ε=0.2),我们现在要建立一片森林,假如要建立25棵树,一般在bagging下,表决的结果是根据多棵树的平均(回归问题)或者多数(分类问题)来决定的。
分类问题,根据多数原则来。
那么在随机森林下一个样本倍分错的概率是:
其值约等于0.000369因此,在随机森林的情况下,这个样本倍分错的概率小多了啊。
代码计算如下:
import numpy as np
from scipy.special import comb
res = np.array([(comb(25, i)) * (0.2 ** i) * ((1-0.2) ** (25-i)) for i in range(13, 26)]).sum()
print(res)
二:random_state的使用
上文我们学习了n_estimators的使用。本文我们学习random_state的使用
在决策树中,自带随机属性,为了防止过拟合,碰运气式的尝试,节点特征分裂的时候,会随机选择某个特征进行分裂,因此造成决策树的随机性,是由random_state来控制的。
一个random_state值控制一棵树。
在随机森林中,也是带有类似的随机性,用法和决策树类似,但是随机森林中,random_state值控制的是生成森林的模式,不是森林中所有的树都是一个样子,每棵树的模式可能是不同的。
看看下面的实例代码就可以知道。
1:第一步,加载数据
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_wine
from sklearn.model_selection import train_test_split, cross_val_score
import matplotlib.pyplot as plt
# 分类问题举例,加载自带的红酒数据集
wine = load_wine() # 178个数据,13个属性,三个分类种类。
print(wine.data.shape)
print(wine.target.shape)
print(wine.target_names) # 所有类别标签地名称
print(wine.feature_names) # 所有特征属性的名称
# 把红酒数据进行切分,切分成训练集和测试集合,切分比例一般是7:3
data_train, data_test, target_train, target_test = train_test_split(wine.data, wine.target, test_size=0.3)
print(data_train.shape)
print(target_train.shape)
print(data_test.shape)
print(target_test.shape)
2:第二步,在相同的数据集的情况下多次运行某个具有random_state的随机森林
scores_clf = []
scores_rfc = []
for i in range(1, 11):
clf = DecisionTreeClassifier(criterion='gini', random_state=20)
score_clf = cross_val_score(clf, wine.data, wine.target, cv=10).mean()
scores_clf.append(score_clf)
rfc = RandomForestClassifier(criterion='gini', n_estimators=10, random_state=20)
score_tfc = cross_val_score(rfc, wine.data, wine.target, cv=10).mean()
scores_rfc.append(score_tfc)
# 画图画出来展示下
plt.plot(range(1, 11), scores_clf, label="DecisionTree")
plt.plot(range(1, 11), scores_rfc, label="RandomForest")
plt.legend() # 显示图例
plt.show()
图像如下:
发现在十次循环中(指定了random_state后),决策树和随机森林的值在相同的数据集情况下表现分数都是固定的,不再跟上一篇博文(没有指定random_state后)那样具有很大随机性了。
你可能会问,整个森林中,每棵树到底是不是一样的呢?答案是不一样的。
为了进一步探索,我们看看一颗随机数的一些细节。在指定random_state的情况下。
# 先实例化一个随机森林
rfc = RandomForestClassifier(criterion='gini', random_state=10, n_estimators=10)
rfc = rfc.fit(data_train, target_train)
score = rfc.score(data_test, target_test)
print('Random Forest Score:{}'.format(score)) # 一般性情况下,会比决策树大一些,或者相等.
print(rfc.estimators_) # 打印所有的DecisionTreeClassifier的实例
for i in range(len(rfc.estimators_)):
print("第{}颗树的random_state是{}.".format(i+1, rfc.estimators_[i].random_state))
打印如下:
从这里我们可以发现哈,每个树自己的random_state都是不一样的,其他参数都是一样的。
所以造成了每个树自身的结构是不一样的。
且上述代码反复执行,每棵基分类器也是不变的,可是去掉random_state指定后,就会发生随机地改变。
也就是指定了某个random_state值固定后,森林中每个树的random_state也就是定了,也就是整个森林的模式就是定了的,但是每棵树是不一致的。
用袋装法进行集成时,基分类器应当是相互独立的,是不相同的。且随机性越强,袋装法的效果也是越来越好。
三:bootstarp & oob_score
除了上述的随机性控制参数外, 还有其他可以学习的参数。
要让基分类器进来不一样,首先想到的就是使用不同的训练集来进行训练,袋装法是通过有放回的抽样来形成不同的训练数据。bootstarp参数就是用来控制抽样技术的参数。具体的细节我们再学习随机森林的时候学习过了,这里不展开。
Bootstarp参数就是指的是,有放回的抽样的随机抽样,这个参数不会被我们设置为FALSE。
OOB就是out of bag的意思,袋外的样本,就是在随机抽样过程中会有计算出来大概37%的概率会被遗漏,这部分数据就可以拿来被用作测试集。
如果发生了一些情况下,所有的样本都是被抽到过,没有掉落在袋外的,那么久没有测试集了,这时候需要继续使用袋外的样本做测试集,那么需要实例化时候,就将oob_score这个参数调整为TRUE,训练完毕后,我们可以通过随机森林的另外个属性 oob_score_来查看袋外样本的测试结果。
# 先实例化一个随机森林
rfc = RandomForestClassifier(criterion='gini', n_estimators=10, oob_score=True, bootstrap=True)
rfc = rfc.fit(data_train, target_train) # 直接使用原始的训练借口测试,
print('Random Forest OOB Score:{}'.format(rfc.oob_score_)) # oob_score=True表示使用袋外数据做测试,这里可以直接查看袋外数据的测试结果
四:重要的属性和接口
rfc.estimators_:可以查看所有的基分类器的形式。
oob_socre:利用袋外数据直接做测试结果的分析。
除此之外呢,feature_importances_,查看特征的重要性。还有一些常用的接口。
# 先实例化一个随机森林
rfc = RandomForestClassifier(criterion='gini', n_estimators=10, bootstrap=True)
rfc = rfc.fit(data_train, target_train) # 直接使用原始的训练借口测试,
score = rfc.score(data_test, target_test)
print('Random Forest OOB Score:{}'.format(score)) # 测试集的测试结果
print(*zip(wine.feature_names, rfc.feature_importances_)) # 特征的重要度
res = rfc.apply(data_test)
print(res) # 返回每个样本第几个基分类器的第几个节点编号
res = rfc.predict(data_test)
print(res) # 返回每个样本第几个基分类器的预测结果
res = rfc.predict_proba(data_test)
print(res) # 返回每个样本在每个分类类别上的预测概率