西瓜书课后题——第八章(集成学习)

本文详细探讨了集成学习中的AdaBoost、Bagging和GradientBoosting算法,包括它们的一致性替代函数、编程实现及异同分析。通过实例展示了在西瓜数据集上应用这些算法的分类效果,指出Bagging降低方差、AdaBoost降低偏差的特性,以及随机森林为何训练速度快于决策树Bagging。同时讨论了如何利用集成学习提升朴素贝叶斯和k近邻分类器的性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

8.1 证明式(8.3)

公式编辑起来比较麻烦,直接手写拍一个图片给出详细的证明过程。

8.2      证明:

首先,要知道0/1损失函数的一致替代函数的含义。因为0/1损失非凸、非连续,数学性质不好,为了便于计算求解,人们用一些数学性质比较好的函数来替代0/1损失函数。常用的替代函数有指数函数、对数函数、hinge函数。  可参见西瓜书130页的内容。

0/1损失函数原型如下:

l_{0/1}(z)=\left\{\begin{matrix} 1, & z<0;\\0,& otherwise \end{matrix}\right.

所以,对于任意损失函数  l(-f(x)H(x)),   则整体损失  Loss = \small E_{x}(l(-f(x)H(x)))=l(-H(x))P(f(x)=1|x)+l(H(x))P(f(x)= -1|x)

\small P(f(x)=1|x)>P(f(x)=-1|x)时,也就是 x 分类为1的概率更大时,为了保证损失函数较小,则希望\small l(-H(x))<l(H(x)), 又因为题中说明 损失函数 \small l(-f(x)H(x)) 对 H(x) 是单调递减的,则 由 \small l(-H(x))<l(-(-H(x))) 可知,\small H(x)>-H(x),因为H(x)取值为 +1 和 -1, 所以此时 H(x) 只能为 1 ;

同理,当 x 分类为 -1 的概率更大时,H(x) 取值即为 -1。

因此可得,在最小化由 l 损失函数计算得到的整体损失的过程中,已经达到了贝叶斯最优错误率。 可参见西瓜书174页 。 因此即可为0/1损失函数的替代函数。(个人的理解也就是说,最小化这个函数的过程,也就是在使预测的标签和实际真实标签尽最大可能一致的过程。)

 

8.3  AdaBoost集成编程实现。

该算法是序列化的串行的集成学习算法,算法的具体步骤见西瓜书 第174页,相关推导过程见 173~177页。此处不再详述。

基于西瓜数据集3.0alpha,采用决策树为基学习器,训练11轮得到最终结果。由于数据量比较小,所以采用的是决策数桩为基学习器。

采用最大信息增益作为划分属性选择的依据,在计算交叉熵时,相较于之前第四章中的做法,这里要计算加权的交叉熵。

另外需要注意一点就是错误率的计算也是要加权进行。                  权重更新一定切记进行规范化操作!!

完整的代码如下:

import numpy as np
import pandas as pd
import math
import matplotlib.pyplot as plt

class Adaboost:
    # 导入数据
    def loadData(self):
        dataset = pd.read_excel('./WaterMelon_3.0.xlsx',encoding = 'gbk')  # 读取数据
        Attributes = dataset.columns         # 所有属性的名称
        m,n = np.shape(dataset)              # 得到数据集大小
        dataset = np.matrix(dataset)
        for i in range(m):                  # 将标签替换成 好瓜 1 和 坏瓜 -1
            if dataset[i,n-1]=='是': dataset[i,n-1] = 1
            else : dataset[i,n-1] = -1
        self.future = Attributes[1:n-1]      # 特征名称(属性名称)
        self.x = dataset[:,1:n-1]            # 样本
        self.y = dataset[:,n-1].flat         # 实际标签
        self.m = m                           # 样本个数

    def __init__(self,T):
        self.loadData()
        self.T = T                  # 迭代次数
        self.seg_future = list()    # 存贮每一个基学习器用来划分的属性
        self.seg_value = list()     # 存贮每一个基学习器的分割点
        self.flag = list()          # 标志每一个基学习器的判断方向。
                                    # 取0时 <= value 的样本标签为1,取1时 >value 的样本标签为1
        self.w = 1.0/self.m * np.ones((self.m,))     # 初始的权重

    # 计算交叉熵
    def entropyD(self,D):          # D 表示样本的编号,从0到16
        pos = 0.0000000001
        neg = 0.0000000001
        for i in D:
            if self.y[i]==1: pos = pos + self.w[i]      # 标签为1的权重
            else: neg = neg + self.w[i]                 # 标签为-1的权重
        P_pos = pos/(pos+neg)                           # 标签为1占的比例
        P_neg = neg/(pos+neg)                           # 标签为-1占的比例
        ans = - P_pos * math.log2(P_pos) - P_neg * math.log2(P_neg)      # 交叉熵
        return ans

    # 获得在连续属性上的最大信息增益及对应的划分点
    def gainFloat(self,p):            # p为对应属性编号(0表示密度,1表示含糖率)
        a = []
        for i in range(self.m):      # 得到所有属性值
            a.append(self.x[i,p])
        a.sort()                      # 排序
        T = []
        for i in range(len(a)-1):    # 计算每一个划分点
            T.append(round((a[i]+a[i+1])/2,4))
        res = self.entropyD([i for i in range(self.m)])     # 整体交叉熵
        ans = 0
        divideV = T[0]
        for i in range(len(T)):         # 循环根据每一个分割点进行划分
            left = []
            right = []
            for j in range(self.m):     # 根据特定分割点将样本分成两部分
                if(self.x[j,p] <= T[i]):
                    left.append(j)
                else:
                    right.append(j)
            temp = res-self.entropyD(left)-self.entropyD(right)    # 计算特定分割点下的信息增益
            if temp>ans:
                divideV = T[i]     # 始终存贮产生最大信息增益的分割点
                ans = temp         # 存贮最大的信息增益
        return ans,divideV

    # 进行决策,选择合适的属性进行划分
    def decision_tree(self):
        gain_1,devide_1 = self.gainFloat(0)           # 得到对应属性上的信息增益及划分点
        gain_2,devide_2 = self.gainFloat(1)
        if gain_1 >= gain_2:                          # 选择信息增益大的属性作为划分属性
            self.seg_future.append(self.future[0])
            self.seg_value.append(devide_1)
            V = devide_1
            p = 0
        else:
            self.seg_future.append(self.future[1])
            self.seg_value.append(devide_2)
            V = devide_2
            p = 1
        left_total = 0
        right_total = 0
        for i in range(self.m):                    # 计算划分之后每一部分的分类结果
            if self.x[i,p] <= V:
                left_total = left_total + self.y[i]*self.w[i]        # 加权分类得分
            else:
                right_total = right_total + self.y[i]*self.w[i]
        if left_total > right_total:
            flagg = 0
        else:
            flagg = 1
        self.flag.append(flagg)                  # flag表示着分类的情况

    # 得到样本在当前基学习器上的预测
    def pridect(self):
        hlist = np.ones((self.m,))
        if self.seg_future[-1]=='密度': p = 0
        else: p = 1
        if self.flag[-1]==0:                  # 此时小于等于V的样本预测为1
            for i in range(self.m):
                if self.x[i,p] <= self.seg_value[-1]:
                    hlist[i] = 1
                else: hlist[i] = -1
        else:                                # 此时大于V的样本预测是1
            for i in range(self.m):
                if self.x[i,p] <= self.seg_value[-1]:
                    hlist[i] = -1
                else:
                    hlist[i] = 1
        return hlist

    # 计算当前基学习器分类的错误率
    def getError(self,h):
        error = 0
        for i in range(self.m):
            if self.y[i]!=h[i]:
                error = error + self.w[i]
        return error         # 返回错误率

    # 训练过程,进行集成
    def train(self):
        H = np.zeros(self.m)
        self.H_predict = []                        # 存贮每一个集成之后的分类结果
        self.alpha = list()                        # 存贮基学习器的权重
        for t in range(self.T):
            self.decision_tree()                   # 得到基学习器分类结果
            hlist = self.pridect()                 # 计算该基学习器的预测值
            error = self.getError(hlist)           # 计算该基学习器的错误率
            if error >
### 西瓜第八章课后解答与讨论 对于西瓜第八章课后目,重点在于理解集成学习的概念及其不同方法的应用。这里提供几个典型问的回答。 #### 不剪枝决策树构建 针对西瓜数据3.0α,在构建不剪枝决策树时,需要考虑如何选择最优划分属性以及处理连续值属性的方法[^1]。通常情况下,会基于信息增益或其他标准来挑选最佳分割点,并且对于数值型特征,则需将其离散化以便于分类器使用。 ```python from sklearn.tree import DecisionTreeClassifier import numpy as np # 构建简单的决策树模型作为示例 clf = DecisionTreeClassifier(criterion="entropy", max_depth=None, min_samples_split=2) X_train = [[...], [...]] # 输入样本集 y_train = [...] # 对应标签列表 clf.fit(X_train, y_train) ``` #### Bagging算法实现 Bagging是一种重要的集成策略,通过多次采样并训练多个基估计器最后汇总预测结果以提高稳定性。具体到Python代码层面可以借助`sklearn`库中的`BaggingClassifier`类轻松完成: ```python from sklearn.ensemble import BaggingClassifier from sklearn.neighbors import KNeighborsClassifier base_estimator = KNeighborsClassifier(n_neighbors=5) bagging_model = BaggingClassifier(base_estimator=base_estimator, n_estimators=10, bootstrap=True, oob_score=False, random_state=42) bagging_model.fit(X_train, y_train) ``` #### AdaBoost.M1算法解析 AdaBoost即自适应增强法,能够迭代调整权重使得难分错的数据得到更多关注从而逐步提升整体表现。在每次循环过程中更新实例权值分布并将弱分类器组合成强分类器。 ```python from sklearn.ensemble import AdaBoostClassifier ada_boost_clf = AdaBoostClassifier( base_estimator=DecisionTreeClassifier(max_depth=1), algorithm='SAMME', n_estimators=50, learning_rate=1. ) ada_boost_clf.fit(X_train, y_train) ```
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值