俯视机器学习
第5章 随机森林
1 随机森林简介
随机森林是一种集成算法。集成算法有很多种形式,本章介绍一种常见的算法——随机森林。其实随机森林想法上很简单,它就是将多个学习器(如决策树)进行组合,然后采用投票法进行分类或回归。
例如对一个 3 分类问题,类别记为 y ∈ { 0 , 1 , 2 } y\in \{0,1,2\} y∈{0,1,2},训练 100 棵决策树。要判断样本 x x x 属于哪个类,就让这 100 棵树分别预测,结果预测 x x x 属于 { 0 , 1 , 2 } \{0,1,2\} {0,1,2} 类的树的数目分别为 n 0 = 10 , n 1 = 20 , n 2 = 70 n_0=10, n_1=20, n_2=70 n0=10,n1=20,n2=70 ,则按少数服从多数原则,认为 x x x 的类别为 2 2 2 。如果最高票有多个,那就随机挑一个类。
通常,我们希望每棵树不一样 ,否则用多棵树毫无意义。为了让树不一样,可以设置树的深度等不同,但更多时候是采用不同的样本进行训练树,比如每次随机从训练集中抽出 50% 的数据进行训练,由于数据选择具有随机性,所以训练出来的树也会不同。
2 实战:随机森林编程实现及红酒分类
import random
import numpy as np
import matplotlib.pyplot as plt
from sklearn.tree import DecisionTreeClassifier
from sklearn.datasets import load_wine
from sklearn.model_selection import train_test_split
def randomForest(Xtrain, ytrain, Xtest, ytest=None, rate=0.3, nTree=100):
ypred0 = np.zeros(len(Xtest))
ypred1 = np.zeros(len(Xtest))
ypred2 = np.zeros(len(Xtest))
for _ in range(nTree):
idx = random.sample(range(len(Xtrain)), k=int(rate*len(Xtrain)))
X = Xtrain[idx]
y = ytrain[idx]
dt = DecisionTreeClassifier(max_depth=3)
dt.fit(X, y)
ypred = dt.predict(Xtest)
ypred0 += np.int32(ypred == 0)
ypred1 += np.int32(ypred == 1)
ypred2 += np.int32(ypred == 2)
ypred = np.argmax(np.c_[ypred0, ypred1, ypred2], axis=1)
return ypred
X, y = load_wine(return_X_y=True)
Xtrain, Xtest, ytrain, ytest = train_test_split(X, y, test_size=0.3, shuffle=True)
print(Xtrain.shape, Xtest.shape, ytrain.shape, ytest.shape)
# 决策树得分,进行50次循环,取平均值
score = 0.
for _ in range(50):
dtc = DecisionTreeClassifier(max_depth=3)
dtc.fit(Xtrain, ytrain)
score += dtc.score(Xtest, ytest)
print(f'Decsion Tree Score = {score / 50}')
# 随机森林得分,进行50次循环,取平均值
# 随机森林得分,进行50次循环,取平均值
score = 0.
for _ in range(50):
ypred = randomForest(Xtrain, ytrain, Xtest, ytest, rate=0.5, nTree=100)
acc = (ypred == ytest).sum() / len(ytest)
score += acc
print(f'Random Forest Score = {score / 50}')
3 Sklearn 中的随机森林
class sklearn.ensemble.RandomForestClassifier(n_estimators=100, *, criterion=’gini’,max_depth=None, min_samples_split=2, min_samples_leaf=1,min_weight_fraction_leaf=0.0, max_features=’auto’,max_leaf_nodes=None, min_impurity_decrease=0.0,min_impurity_split=None, bootstrap=True, oob_score=False,n_jobs=None, random_state=None,verbose=0, warm_start=False, class_weight=None, ccp_alpha=0.0,max_samples=None)
-
参数:
n_estimators [int, default=100]
树的数目bootstrap [bool, default=True]
一种有放回的抽样方法- 其他同决策树
-
属性:
base_estimator_ [DecisionTreeClassifier]
基分类器estimators_ [list of DecisionTreeClassifier]
分类器集合- 其他同决策树
-
方法:
fit(X, y[, sample_weight])
拟合predict(X)
score(X, y[, sample_weight])
返回准确率(accurancy)
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import make_classification
X, y = make_classification(n_samples=1000, n_features=10, n_informative=2, n_redundant=0,
random_state=0, shuffle=False)
clf = RandomForestClassifier(max_depth=2, random_state=0)
clf.fit(X, y)
ypred = clf.predict(X)
clf.score(X, y)
4 补充:熵、交叉熵
最短编码角度理解交叉熵。
A A A 地有 4 中天气情况:晴、雨、雪、雾,若预测每种气候的概率分别为 1/2, 1/4, 1/8, 1/8。要记录 80 天的天气,假设每天只能有一种天气,各天的天气之间没有任何关系,问最少要多少位二进制才能表示才能进行编码和解码传输。
方案 1 1 1 :每种情况用 2 位表示,共用 2 × 80 = 160 2\times 80 = 160 2×80=160 位表示。解码时每次读取 2 位,即可依次得出各天天气。
方案 2 2 2 :用无前缀的变编码方式表示,如用 1 1 1 代表晴, 01 01 01 代表雨, 001 001 001 代表雪, 000 000 000 代表雾。则平均下来,平均用 1 × 1 / 2 × 80 + 2 × 1 / 4 × 80 + 3 × 1 / 8 × 80 + 3 × 1 / 8 × 80 = 140 1\times 1/2 \times 80 + 2\times 1/4\times 80+3\times1/8\times 80 + 3\times 1/8\times 80 = 140 1×1/2×80+2×1/4×80+3×1/8×80+3×1/8×80=140 。可见用这种编码所用字节更少,而且由于编码互不为前缀,因此能解码。
方案 2 2 2 的思路也很简单:对于经常出现的情形,用短编码,反之用长编码。
但上面是预测的天气,如果真实天气是 晴、雨、雪、雾 概率分别为 1/8, 1/8, 1/4, 1/2。则出现了经常出现的天气用了长编码,直接导致所用的编码需要的二进制很多。因此,采用方案 2 2 2 的前提在于预测与实际符合,则编码短,否则编码反而更长。引申一下,把编码长度看作损失函数,就是预测与实际符合,则损失小,否则损失大。
其实这个所需的最小编码长度就是信息量,后续可能遇到小数,小数也可以当作扩展的整数。
如果一件事真实发生的概率为 p p p ,则它相当于有 1 / p 1/p 1/p 种情形,那最优编码是需要 log 2 1 / p = − log 2 p \log_2 1/p=-\log_2 p log21/p=−log2p 位来表示。
如果事件
A
A
A 有
n
n
n 种可能取值,各种情况发生的概率分别为
p
1
,
.
.
.
,
p
n
p_1, ..., p_n
p1,...,pn ,则每种取值对应的最优编码长度应为
−
log
2
p
i
,
(
i
=
1
,
.
.
.
,
n
)
-\log_2 p_i, (i=1,...,n)
−log2pi,(i=1,...,n) ,则其平均所需编码长度为
∑
i
=
1
m
p
i
(
−
log
2
p
i
)
(1)
\sum_{i=1}^m p_i (-\log_2 p_i) \tag{1}
i=1∑mpi(−log2pi)(1)
其实这就是熵。
而如果预测
A
A
A 的各种情形概率分别为
q
1
,
.
.
.
,
q
n
q_1, ..., q_n
q1,...,qn 的时候,我们会对第
i
i
i 种取值用
−
log
2
q
i
-\log_2 q_i
−log2qi 的编码长度。结果真实总的编码长度为
∑
i
=
1
m
p
i
(
−
log
2
q
i
)
(2)
\sum_{i=1}^m p_i (-\log_2 q_i) \tag{2}
i=1∑mpi(−log2qi)(2)
这个值就叫做交叉熵这个值在预测和真实相符合的时候取最小值,此时交叉熵等于熵。其他情形下总有交叉熵大于熵,因为你对经常发生的事采取了长编码,而对很少发生的事件采取短编码,这样总体上总不会是最优的方式。
在机器学习中,比如把3分类用
1
,
2
,
3
1, 2, 3
1,2,3 表示。取出一个样本,如果真实类别是
1
1
1 ,因为样本是固定的,因此真实类别概率为
[
p
1
,
p
2
,
p
3
]
=
[
1
,
0
,
0
]
[p_1, p_2, p_3]=[1, 0, 0]
[p1,p2,p3]=[1,0,0], 我们希望把它预测概率也是
[
q
1
,
q
2
,
q
3
]
=
[
1
,
0
,
0
]
[q_1, q_2, q_3] = [1, 0, 0]
[q1,q2,q3]=[1,0,0] ,这样交叉熵
∑
i
=
1
m
p
i
(
−
log
2
q
i
)
\sum_{i=1}^m p_i (-\log_2 q_i)
∑i=1mpi(−log2qi) 才最小。当然,如果用 sigmoid
函数或以后要学习的 softmax
函数,完全等于
0
0
0 是达不到的,但我们还是希望交叉熵尽可能小。
版权申明:本教程版权归创作人所有,未经许可,谢绝转载!
交流讨论QQ群:784117704
部分视频观看地址:b站搜索“火力教育”
课件下载地址:QQ群文件(有最新更新) or 百度网盘PDF课件及代码
链接:https://pan.baidu.com/s/1lc8c7yDc30KY1L_ehJAfDg
提取码:u3ls