第5章 随机森林(算法原理+手编实现+库调用)


俯视机器学习

第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=1mpi(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=1mpi(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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值