机器学习_2_决策树与随机森林

机器学习_2_决策树与随机森林

写在前面:

  • 新手入门,有理解不到位的地方欢迎批评指正。

1. 理论

1. 决策树

  • 决策树是一种树型结构,其中每个内部结点表示在一个属性上的测试,每个分支代表一个测试输出,每个叶结点代表一种类别。

  • 决策树学习采用的是自顶向下的递归方法,其基本思想是以信息熵为度量构造一棵熵值下降最快的树,到叶子节点处的熵值为零,此时每个叶节点中的实例都属于同一类 。

  • 我们希望决策树的分支结点所包含的样本尽可能属于同一类别,即结点的"纯度" (purity)越来越高.

建立决策树的关键,即在当前状态下选择哪个属性作为分类依据。根据不同的目标函数,建立决策树主要有一下三种算法。

  • ID3:(信息增益/互信息 g ( D , A ) \mathrm{g}(\mathrm{D}, \mathrm{A}) g(D,A)
    g ( D , A ) = H ( D ) − H ( D ∣ A ) \mathrm{g}(\mathrm{D}, \mathrm{A})=\mathrm{H}(\mathrm{D})-\mathrm{H}(\mathrm{D} \mid \mathrm{A}) g(D,A)=H(D)H(DA)

  • C4.5:(信息增益率 g r ( D , A ) \mathrm{g}_{\mathrm{r}}(\mathrm{D}, \mathrm{A}) gr(D,A)
    g r ( D , A ) = g ( D , A ) / H ( A ) \mathrm{g}_{\mathrm{r}}(\mathrm{D}, \mathrm{A})=\mathrm{g}(\mathrm{D}, \mathrm{A}) / \mathrm{H}(\mathrm{A}) gr(D,A)=g(D,A)/H(A)

  • CART:(基尼指数 Gini ⁡ ( p ) \operatorname{Gini}(p) Gini(p)
    Gini ⁡ ( p ) = ∑ k = 1 K p k ( 1 − p k ) = 1 − ∑ k = 1 K p k 2 = 1 − ∑ k = 1 K ( ∣ C k ∣ ∣ D ∣ ) 2 \begin{array}{l} \operatorname{Gini}(p)=\sum_{k=1}^{K} p_{k}\left(1-p_{k}\right)=1-\sum_{k=1}^{K} p_{k}^{2} \\ =1-\sum_{k=1}^{K}\left(\frac{\left|C_{k}\right|}{|D|}\right)^{2} \end{array} Gini(p)=k=1Kpk(1pk)=1k=1Kpk2=1k=1K(DCk)2
    说明:

    一个属性的信息增益(率)/gini指数越大,表明属性对样本的熵减少的能力更强,这个属性使得数据由不确定性变成确定性的能力越强。

    其中:

    • 训练数据集为 D D D ∣ D ∣ |D| D表示样本个数。
    • 设有 K K K个类 C k , k = 1 , 2 ⋯ K C_{k}, k=1,2 \cdots K Ck,k=1,2K ∣ C k ∣ \left|C_{k}\right| Ck为属于类 C k C_{k} Ck的样本个数,有 ∑ k ∣ C k ∣ = ∣ D ∣ \sum_{k}\left|C_{k}\right|=|D| kCk=D
    • 设特征 A A A n n n个不同的取值 { a 1 , a 2 ⋯ a n } \left\{a_{1}, a_{2} \cdots a_{n}\right\} {a1,a2an},根据特征 A A A的取值将 D D D划分为 n n n个子集 D 1 , D 2 ⋯ D n D_{1}, D_{2} \cdots D_{n} D1,D2Dn ∣ D i ∣ \left|D_{i}\right| Di D i D_{i} Di的样本个数,有 ∑ i ∣ D i ∣ = ∣ D ∣ \sum_{i}\left|D_{i}\right|=|D| iDi=D
    • 记子集 D i D_{i} Di中属于类 C k C_k Ck的样本的集合为 D i k D_{ik} Dik ∣ D i k ∣ |D_{ik}| Dik D i k D_{ik} Dik的样本个数
    • 数据集 D D D的经验熵 H ( D ) = − ∑ k = 1 K ∣ C k ∣ ∣ D ∣ log ⁡ ∣ C k ∣ ∣ D ∣ H(D)=-\sum_{k=1}^{K} \frac{\left|C_{k}\right|}{|D|} \log \frac{\left|C_{k}\right|}{|D|} H(D)=k=1KDCklogDCk
    • 特征 A A A对数据集 D D D的经验条件熵 H ( D ∣ A ) = H ( A , D ) − H ( A ) \mathrm{H}(\mathrm{D} \mid \mathrm{A})=\mathrm{H}(\mathrm{A}, \mathrm{D})-\mathrm{H}(\mathrm{A}) H(DA)=H(A,D)H(A)
    • 联合熵 H ( A , D ) = − ∑ A , D p ( A , D ) log ⁡ p ( A , D ) \mathrm{H}(\mathrm{A}, \mathrm{D})=-\sum_{A, D} p(A, D) \log p(A, D) H(A,D)=A,Dp(A,D)logp(A,D)
    • H ( A ) = − ∑ A p ( A ) log ⁡ p ( A ) \mathrm{H}(\mathrm{A})=-\sum_{A} p(A) \log p(A) H(A)=Ap(A)logp(A)

损失函数/评价函数
C ( T ) = ∑ t ∈ l e a f N t ⋅ H ( t ) C(T)=\sum_{t \in l e a f} N_{t} \cdot H(t) C(T)=tleafNtH(t)

其中:

  • 假定样本的总类别为 K K K
  • 对于决策树的某叶结点 t t t,假定该叶结点含有样本数目为 N t N_{t} Nt,其中第 k k k类的样本点数目为 n k n_k nk k = 1 , 2 , … , K k=1,2,…,K k=1,2,,K

过拟合问题

  • 剪枝

    由完全树 T 0 T_0 T0开始,剪枝部分结点得到 T 1 T_1 T1,再次剪枝部分结点得到 T 2 T_2 T2…直到仅剩树根的树$T_k $ ,然后在验证数据集上对这 k k k个树分别评价,选择损失函数最小的树 T α T_α Tα

    • 预剪枝
    • 后剪枝
  • 随机森林

2. 随机森林

Bagging(bootstrap aggregation )策略:

  • 从样本集中重采样(bootstrap)(有重复的)选出 n n n个样本

  • 在所有属性上,对这n个样本建立分类器(ID3、 C4.5、 CART、 SVM、 Logistic回归等)

  • 重复以上两步 m m m次,即获得了 m m m个分类器

  • 将数据放在这 m m m个分类器上,最后根据这 m m m个分类器的投票结果,决定数据属于哪一类

  • bootstrap每次约有36.79%的样本不会出现在bootstrap所采集的样本集合中,将未参与模型训练的数据称为袋外数据OOB(Out Of Bag)。它可以用于取代测试集用于误差估计。袋外数据误差估计与同训练集一样大小的测试集精度相同 。

随机森林在bagging基础上做了修改:

  • 从样本中用bootstrap采样选出 n n n个样本;

  • 从所有属性中随机选择 k k k个属性,选择最佳分割属性作为节点建立CART决策树;

  • 重复以上两步 m m m次,即建立了 m m m棵CART决策树;

  • m m m个CART形成随机森林,通过投票表决结果,决定数据属于哪一类 。

投票机制

  • 简单投票机制
    • 一票否决(一致表决)
    • 少数服从多数(最多)
      • 有效多数(加权)
    • 阈值表决
  • 贝叶斯投票机制(加入一些先验知识)

随机森林RF应用

  • 计算样本间相似度:

    两样本同时出现在相同叶结点的次数越多,则二者越相似 。

  • 计算特征重要度:

    计算正例经过的结点,使用经过结点的数目、经过结点的gini系数和等指标。或者,随机替换一列数据,重新建立决策树,计算新模型的正确率变化,从而考虑这一列特征的重要性。

  • 异常值检测 :

    随机选择特征、随机选择分割点,生成一定深度的决策树iTree,若干颗iTree组成iForest ;计算iTree中样本 x x x从根到叶子的长度 f ( x ) f(x) f(x);计算iForest中 f ( x ) f(x) f(x)的总和 F ( x ) F(x) F(x)若样本x为异常值,它应在大多数iTree中很快从根到达叶子,即 F ( x ) F(x) F(x)较小。

样本不均衡的处理办法

​ 假定样本数目A类比B类多,且严重不平衡:

  • A类下采样Undersampling
    • 随机欠采样
    • A类分成若干子类,分别与B类进入ML模型
    • 基于聚类的A类分割
  • B类上采样Oversampling
    • 避免欠采样造成的信息丢失
  • B类数据合成Synthetic Data Generation
    • 随机插值得到新样本
    • SMOTE(Synthetic Minority Over-sampling Technique)
  • 代价敏感学习Cost Sensitive Learning
    • 降低A类权值,提高B类权值

2. 代码实践

代码环境:

  • win7+PyCharm 2018.3.1 Professional+python3.7

决策树:

# -*- coding:utf-8 -*-

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib as mpl
from sklearn import tree
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
import pydotplus


# 花萼长度、花萼宽度,花瓣长度,花瓣宽度
iris_feature_E = 'sepal length', 'sepal width', 'petal length', 'petal width'
iris_feature = u'花萼长度', u'花萼宽度', u'花瓣长度', u'花瓣宽度'
iris_class = 'Iris-setosa', 'Iris-versicolor', 'Iris-virginica'

if __name__ == "__main__":
    mpl.rcParams['font.sans-serif'] = [u'SimHei']
    mpl.rcParams['axes.unicode_minus'] = False

    path = 'iris.data'  # 数据文件路径
    data = pd.read_csv(path, header=None)
    x = data[range(4)]
    y = pd.Categorical(data[4]).codes
    # 为了可视化,仅使用前两列特征
    x = x.iloc[:, :2]
    x_train, x_test, y_train, y_test = train_test_split(x, y, train_size=0.7, random_state=1)
    print y_test.shape
    
     # 决策树参数估计
    # min_samples_split = 10:如果该结点包含的样本数目大于10,则(有可能)对其分支
    # min_samples_leaf = 10:若将某结点分支后,得到的每个子结点样本数目都大于10,则完成分支;否则,不进行分支
    model = DecisionTreeClassifier(criterion='entropy')
    model.fit(x_train, y_train)
    y_test_hat = model.predict(x_test)      # 测试数据
    
    # 保存
    # dot -Tpng my.dot -o my.png
    # 1、输出
    # with open('iris.dot', 'w') as f:
    #     tree.export_graphviz(model, out_file=f)
    # 2、给定文件名
    # tree.export_graphviz(model, out_file='iris1.dot')
    # 3、输出为pdf格式
    dot_data = tree.export_graphviz(model, out_file=None, feature_names=iris_feature_E[:2], class_names=iris_class,
                                    filled=True, rounded=True, special_characters=True)
    graph = pydotplus.graph_from_dot_data(dot_data)
    graph.write_pdf('iris.pdf')
    f = open('iris.png', 'wb')
    f.write(graph.create_png())
    f.close()

    # 画图
    N, M = 50, 50  # 横纵各采样多少个值
    x1_min, x2_min = x.min()
    x1_max, x2_max = x.max()
    t1 = np.linspace(x1_min, x1_max, N)
    t2 = np.linspace(x2_min, x2_max, M)
    x1, x2 = np.meshgrid(t1, t2)  # 生成网格采样点
    x_show = np.stack((x1.flat, x2.flat), axis=1)  # 测试点
    print x_show.shape
    
    cm_light = mpl.colors.ListedColormap(['#A0FFA0', '#FFA0A0', '#A0A0FF'])
    cm_dark = mpl.colors.ListedColormap(['g', 'r', 'b'])
    y_show_hat = model.predict(x_show)  # 预测值
    print y_show_hat.shape
    print y_show_hat
    y_show_hat = y_show_hat.reshape(x1.shape)  # 使之与输入的形状相同
    print y_show_hat
    plt.figure(facecolor='w')
    plt.pcolormesh(x1, x2, y_show_hat, cmap=cm_light)  # 预测值的显示
    plt.scatter(x_test[0], x_test[1], c=y_test.ravel(), edgecolors='k', s=150, zorder=10, cmap=cm_dark, marker='*')  # 测试数据
    plt.scatter(x[0], x[1], c=y.ravel(), edgecolors='k', s=40, cmap=cm_dark)  # 全部数据
    plt.xlabel(iris_feature[0], fontsize=15)
    plt.ylabel(iris_feature[1], fontsize=15)
    plt.xlim(x1_min, x1_max)
    plt.ylim(x2_min, x2_max)
    plt.grid(True)
    plt.title(u'鸢尾花数据的决策树分类', fontsize=17)
    plt.show()

    # 训练集上的预测结果
    y_test = y_test.reshape(-1)
    print y_test_hat
    print y_test
    result = (y_test_hat == y_test)   # True则预测正确,False则预测错误
    acc = np.mean(result)
    print '准确度: %.2f%%' % (100 * acc)

    # 过拟合:错误率
    depth = np.arange(1, 15)
    err_list = []
    for d in depth:
        clf = DecisionTreeClassifier(criterion='entropy', max_depth=d)
        clf.fit(x_train, y_train)
        y_test_hat = clf.predict(x_test)  # 测试数据
        result = (y_test_hat == y_test)  # True则预测正确,False则预测错误
        if d == 1:
            print result
        err = 1 - np.mean(result)
        err_list.append(err)
        # print d, ' 准确度: %.2f%%' % (100 * err)
        print d, ' 错误率: %.2f%%' % (100 * err)
    plt.figure(facecolor='w')
    plt.plot(depth, err_list, 'ro-', lw=2)
    plt.xlabel(u'决策树深度', fontsize=15)
    plt.ylabel(u'错误率', fontsize=15)
    plt.title(u'决策树深度与过拟合', fontsize=17)
    plt.grid(True)
    plt.show()

随机森林:

# -*- coding:utf-8 -*-

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib as mpl
from sklearn.ensemble import RandomForestClassifier


def iris_type(s):
    it = {'Iris-setosa': 0, 'Iris-versicolor': 1, 'Iris-virginica': 2}
    return it[s]

# 'sepal length', 'sepal width', 'petal length', 'petal width'
iris_feature = u'花萼长度', u'花萼宽度', u'花瓣长度', u'花瓣宽度'

if __name__ == "__main__":
    mpl.rcParams['font.sans-serif'] = [u'SimHei']  # 黑体 FangSong/KaiTi
    mpl.rcParams['axes.unicode_minus'] = False

    path = '..\\8.Regression\\iris.data'  # 数据文件路径
    data = pd.read_csv(path, header=None)
    x_prime = data[range(4)]
    y = pd.Categorical(data[4]).codes

    feature_pairs = [[0, 1], [0, 2], [0, 3], [1, 2], [1, 3], [2, 3]]
    plt.figure(figsize=(10, 9), facecolor='#FFFFFF')
    for i, pair in enumerate(feature_pairs):
        # 准备数据
        x = x_prime[pair]

        # 随机森林
        clf = RandomForestClassifier(n_estimators=200, criterion='entropy', max_depth=3)
        clf.fit(x, y.ravel())

        # 画图
        N, M = 50, 50  # 横纵各采样多少个值
        x1_min, x2_min = x.min()
        x1_max, x2_max = x.max()
        t1 = np.linspace(x1_min, x1_max, N)
        t2 = np.linspace(x2_min, x2_max, M)
        x1, x2 = np.meshgrid(t1, t2)  # 生成网格采样点
        x_test = np.stack((x1.flat, x2.flat), axis=1)  # 测试点

        # 训练集上的预测结果
        y_hat = clf.predict(x)
        y = y.reshape(-1)
        c = np.count_nonzero(y_hat == y)    # 统计预测正确的个数
        print '特征:  ', iris_feature[pair[0]], ' + ', iris_feature[pair[1]],
        print '\t预测正确数目:', c,
        print '\t准确率: %.2f%%' % (100 * float(c) / float(len(y)))

        # 显示
        cm_light = mpl.colors.ListedColormap(['#A0FFA0', '#FFA0A0', '#A0A0FF'])
        cm_dark = mpl.colors.ListedColormap(['g', 'r', 'b'])
        y_hat = clf.predict(x_test)  # 预测值
        y_hat = y_hat.reshape(x1.shape)  # 使之与输入的形状相同
        plt.subplot(2, 3, i+1)
        plt.pcolormesh(x1, x2, y_hat, cmap=cm_light)  # 预测值
        plt.scatter(x[pair[0]], x[pair[1]], c=y, edgecolors='k', cmap=cm_dark)  # 样本
        plt.xlabel(iris_feature[pair[0]], fontsize=14)
        plt.ylabel(iris_feature[pair[1]], fontsize=14)
        plt.xlim(x1_min, x1_max)
        plt.ylim(x2_min, x2_max)
        plt.grid()
    plt.tight_layout(2.5)
    plt.subplots_adjust(top=0.92)
    plt.suptitle(u'随机森林对鸢尾花数据的两特征组合的分类结果', fontsize=18)
    plt.show()


参考资料:

  • https://www.bilibili.com/video/BV1Tb411H7uC
  • 统计学习方法第二版_李航
  • 西瓜书
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值