第5章AdaBoost 5.1AdaBoost算法原理 对于分类问题,如果任意一个分类器解决不了的问题,那就多个分类器都试试。分类器一多,怎么整理结果?走类似参数拟合的路线,用权重来平衡不同分类器,用误差函数来帮助回归,简单明了——AdaBoost。 5.1.1算法引入 在进行数据分析时,如何探索出性能优良的分类器一直是科学家执着的追求。通常情况下想要找到一个强分类器总是比较困难的,但想要获得一个弱分类器却相对容易。因此,我们提出这样一个问题,能否通过这些容易实现的弱分类器,经过一定组合后构建一个强分类器?AdaBoost算法可解决该问题。AdaBoost属于Boosting的一种算法,具有“自适应”功能。该算法通过不断迭代并根据每次迭代的误差率赋予当前基础分类器权重,同时,自动调整样本权重,最后把所有的分类器通过线性整合,形成一个性能优良的强分类器。AdaBoost提供一种框架,该框架可以将各种弱分类器组合起来,并且该算法相对简单,不需要进行特征的筛选,难出现过拟合。AdaBoost框架如图51所示。 图51AdaBoost框架图 5.1.2科学问题 1. 相关定义 我们首先定义以下几个基本概念。 定义1: 基础分类器H(x) AdaBoost只是提供了一种用于将弱分类器通过线性整合,以获得强分类器的框架。具体的基础分类器可以是任意分类器,在分类时其误差率应满足0<εt<12。 定义2: 误差率εt 误差率主要是用来衡量基础分类器在此次分类中的加权错误率,每次选择误差率最低的分类器加入到最终线性分类器中。 定义3: 分类器权重αt AdaBoost通过线性加和的方式得到最终分类器,αt表示每一个基础分类器在最终分类器中的权值,数字t表示第t轮迭代次数。 定义4: 样本权重ut=(ut,1,ut,2,…,ut,N),N表示样本总数 AdaBoost每一次迭代都会更新样本集合D的权重,ut代表的是第t-1迭代后所计算出的样本权重。 2. 问题定义 在给定数据集D={(x1,y1),(x2,y2),…,(xN,yN)}以及基础分类器H(x)的情况下,AdaBoost算法拟解决以下问题: (1) 如何衡量分类器在Ht(x)每一轮迭代中的权重αt?其中t={1,2,…,T},表示迭代次数。AdaBoost算法通过计算误差率εt,并通过公式αt=12log1-εtεt计算每一轮的权重。 (2) 如何在迭代过程中更新样本集的权重?为了使得每次迭代后的弱分类器更精确,并且使之前误分类样本在下轮迭代中所占比重更大,因此AdaBoost算法根据分类器分类结果对样本的权重进行更新,权重更新公式为 ut+1,i=ut,iZtexp(-αtyiHt(xi)) 5.1.3算法流程 1. 算法步骤 该部分给出AdaBoost算法的步骤: (1) 对给定的二分类数据集D={(x1,y1),(x2,y2),…,(xN,yN)}(x∈X,y∈Y),首先初始化每个样本的权重u1,i=1N,1N,…,1N(i=1,2,…,N),N是样本数量,所有样本在初始阶段同等重要。 (2) AdaBoost算法通过多次迭代生成多个基础分类器,在每次迭代过程中以最小误差率εt选择当前最优的基础分类器,误差率越小表示基础分类器对数据样本预测结果越准确,计算误差率εt的公式为: εt=∑Ni=1ut,iI(Ht(xi)≠yi) (51) (3) AdaBoost算法采用“加权多数表决”方法组合多个基础分类器。算法根据误差率εt为基础分类器Ht(x)赋予权重αt,误差率越小,则分类器权重越大,即该基础分类器在最终分类器中作用越大,计算分类器的权重αt的公式为: αt=12log1-εtεt (52) (4) AdaBoost算法在生成下一个基础分类器之前将放大误分类样本权重并缩小正确分类样本权重,这使得下一轮迭代更关注误分类样本,以期待在下次生成基础分类器过程中正确分类误分类样本,样本权重更新公式为: ut+1,i=ut,iZtexp(-αtyiHt(xi))(i=1,2,…,N) (53) 式(53)中t=1,2,…,T,Zt是归一化因子,Zt计算公式如下: Zt=∑Ni=1ut,iexp(-αtyiHt(xi)) (54) (5) 若多个基础分类器经线性组合后能全部准确预测数据样本类别或者迭代次数超过预定义次数,则迭代终止并形成最终线性分类器: f(x)=∑Tt=1αtHt(x) (55) 把该分类器二值化,得到最终的分类器。 (6) 最开始提出的AdaBoost算法用于解决二分类问题,f(x)经二值化后得到最终分类器,形式化如下: H(x)=sign(f(x))=sign∑Tt=1αtHt(x) (56) 2. AdaBoost理论推导 AdaBoost是一个线性整合模型,假设前t-1轮迭代所产生的分类器已知,即: ft-1(x)=ft-2(x)+αt-1Ht-1(x)=α1H1(x)+…+αt-1Ht-1(x) (57) 下一轮迭代目标是生成在当前数据样本权重下损失函数最小的基础分类器,AdaBoost损失函数为指数函数,即: (αt,Ht(x))=argminα,H∑Ni=1exp[-yi(ft-1(xi)+αHt(xi))] (58) 式(58)又可以表示为: (αt,Ht(x))=argminα,H∑Ni=1u-t,iexp[-yiαHt(xi)] (59) 其中,通过式(53)可得到u-ti=exp[-yift-1(xi)],此时可以看出u-ti与所要求的损失函数没有依赖,与上式的最小化过程没有关系。因此上式求解分为两步: (1) 求解最优的分类器Ht(xi),对任意的α>0,对式(59)求解最小值,得到: Ht(xi)=argminH∑Ni=1u-t,iI(yi≠H(xi)) (510) 损失函数(59)可表示为: ∑Ni=1u-t,iexp[-yiαHt(xi)]=∑Nyi=Ht(xi)u-t,iexp[-α]+∑Nyi≠Ht(xi)u-t,iexp[α] =(eα-e-α)∑Ni=1u-t,iI(yi≠Ht(xi))+e-α∑Ni=1u-t,i (511) 将之前所求的Ht(xi)代入式(511)中,对α求偏导并使偏导为0,可得到此时αt的求解公式(52)。其中,εt是分类器的误差率,此时εt的表示形式如下: εt=∑Ni=1u-t,iI(yi≠Ht(xi))∑Ni=1u-t,i=∑Ni=1u-t,iI(yi≠Ht(xi)) (512) (2) 对于每一轮的权值更新,由ft(x)=ft-1(x)+αtHt(x)且u-ti=exp[-yift-1(xi)]可得权值更新公式: ut+1,i=u-t,iexp(-αtyiHt(xi))(i=1,2,…,N) (513) 5.1.4算法描述 下面给出AdaBoost算法的伪代码: 算法51AdaBoost算法 输入: 训练数据集D={(x1,y1),(x2,y2),…,(xN,yN)},(x∈X,y∈Y),以及基础分类器Ht(xi),最低错误率ξ; 1:初始化样本数据集权值u1,i=1N,1N,…,1N(i=1,2,…,N); 2:for t=1,2,…,T do 3:寻找当前权重下误差率最小的分类器,误差率εt通过式(51)计算得出; 4:if εt>0.5 5:continue; 6:else 7:通过误差率εt计算出该分类器的权重αt,权重通过式(52)计算得出; 8:通过式(53)和上一轮样本的权重ut-1n更新当前样本集权重; 9:计算当前分类器f(x)=∑Tt=1αtHt(x)的错误率ε; 10:if ε≤ξ then 11:返回当前分类器f(x); 12:break; 13:end if 14:end if 15:end for 16:输出最终的分类f(x)=∑Tt=1αtHt (x); 5.1.5补充说明 1. 基础分类器的误差率应该小于0.5 AdaBoost在选择基础分类器时,需要注意到基础分类器的误差率应小于0.5。这一点通过权重迭代式(52)可以看出,当分类器的误差率为0.5时,分类器的权重为0。这与我们的日常感知也是趋于一致的,因为当一个分类器的误差率为0.5时,相当于随机猜测的效果,也就是该分类器在此次分类过程中不起作用。因此在最终得到的线性分类器ft(x)里,该分类器权重为0。同样,当基础分类器的误差率接近零时,该分类器经过ft(x)计算权重变得很大,即该分类器在全部分类器中所占比重大。 2. AdaBoost算法误差分析 本节将对AdaBoost算法误差进行分析,首先给出该算法误差分析的结论: 1N∑Ni{Ht(x)≠yi}≤1N∑Ni=1{exp(-yi*f(xi))}=∏tZt 权值更新的式(53)可以转换为如下形式: ut,iexp(-αtyiHt(xi))=Ztut+1,i (514) 又因为当Ht(x)≠yi时,yif(xi)<0,因此得出exp(-yif(xi))≥1,此时1N∑Ni=1{Ht(x)≠yi}≤1N∑Ni=1{exp(-yif(xi))}成立。 另外,对于1N∑Ni=1{exp(-yi*f(xi))}=∏tZt,由式(54)可得出: 1N∑Ni=1{exp(-yi*f(xi))}=1N∑Ni=1exp-∑Tt=1αtyiHt(xi) =∑Ni=1u1,i∏Tt=1exp(-αtyiHt(xi)) =Z1∑Ni=1u2,i∏Tt=2exp(-αtyiHt(xi)) =Z1Z2∑Ni=1u3,i∏Tt=3exp(-αtyiHt(xi)) … =∏Tt=1Zt (515) 由此,通过式(515)可以看出在每轮迭代过程中,只需要满足训练得到的分类器使得归一化因子Zt最小,便可以进一步降低训练误差。由式(53)可得到: Zt=∑Ni=1ut,iexp(-αtyiHt(xi)) =∑Nyi=Ht(xi)ut,iexp(-αm)+∑Nyi≠Ht(xi)ut,iexp(αt) =(1-εt)exp(-αm)+εtexp(αm) 把式(52)αt=12log1-εtεt代入上式中可以得到2εt(1-εt)=1-4r2m。在这里我们令rm=12-εt,通过泰勒公式分别对ex和1-x展开,所展开的泰勒公式如下所示: f(x)=f(x0)0!+f′(x0)1!(x-x0)+f″(x0)1!(x-x0)2+…+ f(n)(x0)n!(x-x0)n+Rn(x) (516) 这里只展示三阶的泰勒展开式,分别得到ex和1-x: ex=1+x+12x2+16x3+…+Rn(x) (517) 1-x=1-12x-14x2-38x3+…+Rn(x) (518) 可以看出当1-x≤ex,同理可得1-4r2t≤exp(-2r2t),如果存在r>0,对于所有的t有rt≥r成立,因此可以得到: 1N∑Ni=1I(Gxi≠yi)≤exp(-2Tr2) (519) 综上,AdaBoost算法在迭代的过程中,其误差率以指数速率下降。 5.2AdaBoost算法实现 本章展示了算法实现的流程图和核心类。算法实现的流程如图52所示,包含了算法实现的类和函数。 图52算法设计流程图 5.2.1简介 AdaBoost算法实现采用Python语言,主要类如表51所示。 表 51类和文件的描述 类和文件描述 Iris(具体数据Model类Iris) 成员变量: sepal_length# 花萼长度 sepal_width# 花萼宽度 petal_length# 花瓣长度 petal_width# 花瓣宽度 label# 标签 //特征键集 features = ['sepalLength', 'sepalWidth', 'petalLength', 'petalWidth'] 续表 类和文件描述 SimpleDataSet (具体数据Model类SimpleDataSet) 成员变量: x_ais # x轴 y_ais # y轴 label # 标签 //特征键集 features = ['xAis', 'yAis'] AdaBoost (AdaBoost算法核心类,实现分类器构建、误差率计算、数据样本更新以及预测结果获取) 成员变量: train_data_array # 训练数据集 test_data_array # 测试数据集 train_label_array # 训练标签集 weight_array # 样本权重集 函数: def construct_classifier(self): """ 构建多个基础分类器的组合 :return: DecisionStump 列表 """ def get_error(self, temp_array, threshold, lt_label, gt_label): """ 计算当前所选特征下的误差率 :param temp_array: 当前特征数组 :param threshold: 阈值 :param lt_label: 小于阈值的标签 :param gt_label: 大于阈值的标签 :return: 误差率 """ 续表 类和文件描述 AdaBoost def update_data_weight(self, ds: DecisionStump): """ 更新每个数据样本的权重 :param ds: 当前构造的分类器 :return: weightArray 更新后的数据样本权重 """ def get_frst_lable_array(self, ds_list, data_flag: int): """ 综合每次所得到的分类器,代入原始数据计算最终预测结果 :param ds_list: 分类器集 :param data_flag: 分类器集 :return: 预测标签集 """ def test(self, ds_list, test_label_array): """ 测试分类器 :param ds_list: 分类器集 :param test_label_array: 测试标签集 :return: """ DecisionStump Classifier (实现基础分类器之一——决策树桩的构建,打印全部的决策树桩) 函数: def construct_decision_stump(ada_boost, train_data_array): """ 构建当次决策树桩 :param ada_boost: 算法对象 :param train_data_array: 训练数据 :return: DesicionStumpList 当次的决策树桩(为空时表示当前最低错误率大于0.5,使得无法构建决策树桩) 续表 类和文件描述 DecisionStump Classifier """ def print_decision_stump_classifier(self, ds_list, cur_node_model): """ 打印决策树桩分类器 :param ds_list: 决策树集 :param cur_node_model: 当前数据实体 :return: """ DecisionStump (决策树桩的Model实体类) 成员变量: feature_index# 所选特征 threshold# 阈值 lt_label# 小于阈值的分类 gt_label# 大于阈值的分类 alpha_weight# 当前分类器权重 data_util.py (有关数据的工具类,在这里我们实现了: ①从全部数据中取出不含类别标签的数据集和从类别标签集; ②根据随机数从原始数据中划分训练数据、测试数据、训练标签和测试标签; ③初始化数据样本权重) 函数: def divide_train_and_test_data(datas, percent, begin, end): """ 把原始数据划分为训练数据、测试数据、训练标签和测试标签 :param datas: 原始数据 :param begin: 随机数的开始数 :param end: 随机数的结束数 :param percent: 随机数所占比例 :return: 训练数据、测试数据、训练标签和测试标签 """ 续表 类和文件描述 data_util.py def get_data_array(datas, feature_count): """ 从全部数据样本中取出不含标签的数据集 :param datas: 原始数据 :param feature_count: 标签总数 :return: 不含标签的数据集 """ def get_label_array(datas: list, label_col_index: int): """ 从全部数据样本中取出标签集 :param datas: :param label_col_index: :return: """ def init_weight_array(train_data_count): """ 初始化训练数据样本权重 :param train_data_count: 训练数据样本数量 :return: """ Configuration (配置类,包括读写数据路径和算法所需参数的配置) 属性: class Configuration: # 读数据的路径 DATA_PATH = "../data/wine.txt" # 写数据的路径 RESULT_PATH = "../data/wine_result.txt" # 数据样本类别标签所在的列号 LABEL_INDEX = 0 续表 类和文件描述 Configuration # 所选数据样本开始的编号 BEGIN = 1 # 所选数据样本结束的编号 END = 100 # 算法最大迭代次数 MAX_ITER = 500 # 训练数据样本占全部样本的比例,范围:0.5﹤=percent﹤=0.9 PERCENT = 0.8 # 实例化数据实体类 NODE_MODEL = model.Wine main.py (AdaBoost算法入口文件) 函数: def main(): 5.2.2核心代码 根据算法的思想与流程给出其核心代码,包括Main类、AdaBoost类和DecisionStumpClassifier类的详细代码和解释说明,在这里我们选的基本分类器为决策树桩。 1. main() 方法 (1) 首先获取原始数据并划分训练集和测试集。 1start_time = time.clock() 2 3# step1 获取原始数据并划分训练集和测试集 4train_data_array, train_label_array, test_data_array, test_label_array =\ 5divide_train_and_test_data(load_data(Config.DATA_PATH, delim=None), 6Config.PERCENT, Config.BEGIN, Config.END) (2) 初始化训练数据样本权重。 1# step2 初始化训练数据样本权重 2weight_array = init_weight_array(len(train_label_array) (3) 核心步骤,构建多个分类器。 1# step3 核心步骤,构建多个基础分类器的组合 2ada_boost = AdaBoost(train_data_array, 3test_data_array, 4train_label_array, 5weight_array) 6ds_list = ada_boost.construct_classifier() (4) 把测试数据代入最终分类器,得到的预测标签集并统计正确与错误数。 1# step4 测试 2right_count, error_count = ada_boost.test(ds_list, test_label_array) 3 4end_time = time.clock() (5) 把结果输出到文件。 1# step5 打印输出最终分类器和测试结果到文件 2write_data(Config.RESULT_PATH, ds_list, Config.NODE_MODEL, right_count, 3error_count, start_time, end_time) 2. AdaBoost类 (1) 构建多个基础分类器的组合。 1def construct_classifier(self) -﹥ list: 2""" 3构建多个基础分类器的组合 4:return: DecisionStump 列表 5""" 6ds_list = [] 7dsc = DecisionStumpClassifier() 8for i in range(1, Config.MAX_ITER): 9ds = dsc.construct_decision_stump(self, self.__train_data_array) 10if ds is None: 11continue 12else: 13ds_list.append(ds) 14# 获取多个分类器boost后得到的预测标签集,1表示采用训练数据集 15frst_lable_array = self.get_frst_lable_array(ds_list, 1) 16# 比较预测标签集和训练数据真实标签集 17# 如果都预测正确,结束迭代 18if operator.eq(frst_lable_array, self._train_label_array): 19break 20# 否则更新数据样本权重并继续获取下一个分类器 21else: 22self.__weight_array = self.update_data_weight(ds) 23 24return ds_list (2) 在构建决策树桩分类器时计算该分类器的误差率。 1def get_error(self, temp_array: list, threshold: float, 2lt_label: int, gt_label: int) -﹥ float: 3""" 4计算当前所选特征下的误差率 5:param temp_array: 当前特征数组 6:param threshold: 阈值 7:param lt_label: 小于阈值的标签 8:param gt_label: 大于阈值的标签 9:return: 误差率 10""" 11error = .0 12for i in range(len(temp_array)): 13if temp_array[i] ﹤ threshold: 14if self._train_label_array[i] != lt_label: 15error += self._weight_array[i] 16else: 17if self._train_label_array[i] != gt_label: 18error += self._weight_array[i] 19return error (3) 如果现有分类器不能完全准确分类训练数据集,则本次需要更新每个数据样本的权重。 1def update_data_weight(self, ds: DecisionStump): 2""" 3更新每个数据样本的权重 4:param ds: 当前构造的分类器 5:return: weightArray 更新后的数据样本权重 6""" 7feature_index = ds.feature_index# 所选特征的索引号 8threshold = ds.threshold# 阈值 9lt_label = ds.lt_label# 小于阈值的类别 10gt_label = ds.gt_label# 大于阈值的类别 11alpha_weight = ds.alpha_weight# 当前分类器的权重 12 13temp_array = self.__train_data_array[feature_index] 14 15# 计算规范化因子 16def z_cal(tmp_ele, train_label_ele, weight_ele): 17# 小于阈值 18if tmp_ele ﹤ threshold: 19# 预测标签不等于真实标签 20if train_label_ele != lt_label: 21return weight_ele * math.pow(math.e, alpha_weight) 22else: 23return weight_ele * math.pow(math.e, -alpha_weight) 24else: 25if train_label_ele != gt_label: 26return weight_ele * math.pow(math.e, alpha_weight) 27else: 28return weight_ele * math.pow(math.e, -alpha_weight) 29 30self._weight_array = [z_cal(temp_array[i], 31self._train_label_array[i], 32self._weight_array[i]) 33for i in range(len(temp_array))] 34z = sum(self._weight_array) 35self._weight_array = [w / z for w in self._weight_array] 36return self._weight_array (4) 综合每次所得到的分类器,代入数据计算每个数据样本最终预测结果。 1def get_frst_lable_array(self, ds_list, data_flag: int): 2""" 3综合每次所得到的分类器,代入原始数据计算最终预测结果 4:param ds_list: 分类器集 5:param data_flag: 分类器集 6:return: 预测标签集 7""" 8tmp_labels = [.0] * len(self._train_data_array[0]) 9cur_data_array = self._train_data_array \ 10if 1 == data_flag else self._test_data_array 11 12for ds in ds_list: 13feature_index = ds.feature_index # 所选特征的索引号 14threshold = ds.threshold # 阈值 15lt_label = ds.lt_label # 小于阈值的类别 16gt_label = ds.gt_label # 大于阈值的类别 17alpha_weight = ds.alpha_weight # 当前分类器的权重 18 19tmp_array = cur_data_array[feature_index] 20 21tmp_labels = [alpha_weight * lt_label + tmp_labels[j] 22if tmp_array[j] ﹤ threshold 23else alpha_weight * gt_label + tmp_labels[j] 24for j in range(len(tmp_array))] 25 26return [math.copysign(1.0, tmp_ele) for tmp_ele in tmp_labels] (5) 测试分类器。 1def test(self, ds_list, test_label_array): 2""" 3测试分类器 4:param ds_list: 分类器集 5:param test_label_array: 测试标签集 6:return: 7""" 8right_count = 0 9error_count = 0 10if Config.PERCENT ﹤= 0.9: 11frst_label_array = self.get_frst_lable_array(ds_list, 2) 12for i in range(len(test_label_array)): 13if frst_label_array[i] == test_label_array[i]: 14right_count += 1 15else: 16error_count += 1 17return right_count, error_count 3. DecisionStumpClassifier类 (1) 构建当次的决策树桩。 1def construct_decision_stump(ada_boost, train_data_array): 2""" 3构建当次决策树桩 4:param ada_boost: 算法对象 5:param train_data_array: 训练数据 6:return: DesicionStumpList 当次的决策树桩 7(为空表示当前最低错误率大于0.5,使得无法构建决策树桩) 8""" 9feature_index = 1# 所选特征的索引号 10threshold = .0 # 阈值 11lt_label = 0 # 小于阈值的类别 12gt_label = 0 # 大于阈值的类别 13min_error = float('inf') # 误差率 14 15# 选取误差率最小的特征和阈值 16feature_count = len(train_data_array) 17data_count = len(train_data_array[0]) 18 19i = 1 20while i ﹤= feature_count: 21tmp_array = train_data_array[i-1] # 数据样本的当前所选特征集合 22max_value = max(tmp_array) 23min_value = min(tmp_array) 24step = (max_value - min_value) / data_count 25tmp_threshold = min_value 26 27# 从min到max不断增加步长,计算当前阈值下的失误率 28while tmp_threshold ﹤= max_value: 29tmp_array = train_data_array[i-1] 30tmp_error1 = ada_boost\ 31.get_error(tmp_array, tmp_threshold, -1, 1) 32tmp_error2 = ada_boost\ 33.get_error(tmp_array, tmp_threshold, 1, -1) 34 35if tmp_error1 ﹤ tmp_error2: 36if tmp_error1 ﹤ min_error: 37feature_index, threshold, = i-1, tmp_threshold 38lt_label, gt_label, min_error = -1, 1, tmp_error1 39else: 40if tmp_error2 ﹤ min_error: 41feature_index, threshold = i-1, tmp_threshold 42lt_label, gt_label, min_error = 1, -1, tmp_error2 43tmp_threshold += step 44i += 1 45 46# 最低错误率应满足0﹤min_error﹤0.5 47if 0 ﹤ min_error ﹤ 0.5: 48alpha_weight = 0.5 * math.log((1-min_error) / min_error) 49return DecisionStump(feature_index, threshold, lt_label, 50gt_label, alpha_weight) 51 (2) 打印决策树桩分类器。 1def print_decision_stump_classifier(self, ds_list, cur_node_model): 2""" 3打印决策树桩分类器 4:param ds_list: 决策树集 5:param cur_node_model: 当前数据实体 6:return: 7""" 8print('共{:d}个决策树桩'.format(len(ds_list))) 9for index, ds in enumerate(ds_list): 10print(self 11.info 12.format(ds_num=index+1, 13threshold=ds.threshold, 14feature=cur_node_model.features[ds.feature_index], 15lt_label=ds.lt_label, 16gt_label=ds.gt_label, 17alpha_weight=ds.alpha_weight)) 5.3实验数据 本书选择在公开数据集UCI上的Iris数据集(http://archive.ics.uci.edu/ml/datasets/Iris)以及一份简单的数据集。 Iris数据集中每个数据样本均有四个属性和一个标签,表52展示了部分数据样本。 表52Iris数据集部分数据样本 sepal_lengthsepal_widthpetal_lengthpetal_widthclass 5.13.51.40.21 4.93.01.40.21 4.73.21.30.21 4.63.11.50.21 续表 sepal_lengthsepal_widthpetal_lengthpetal_widthclass 7.03.24.71.42 6.43.24.51.52 6.93.14.91.52 5.52.34.01.32 另一份数据集来自《机器学习实战》第5章,该书作者把它用于Logistic回归。本书把该数据集取名为SimpleDataSet,用于AdaBoost。表53是该数据集的部分数据样本。 表53SimpleDataSet数据集部分数据样本 x_axisy_axisclass -0.01761214.053064-1 -1.3956344.6625411 -0.7521576.53862-1 -1.3223717.152853-1 0.42336311.054677-1 0.4067047.0673351 0.66739412.741452-1 -2.460156.8668051 5.4实验结果 5.4.1结果展示 对于Iris数据集,由于其中有四个属性不便于可视化展示,因此我们给出经AdaBoost后所生成的分类器集。对于SimpleDataSet,我们则绘制实验效果图以展示结果。 首先,我们把AdaBoost用于Iris数据集,表54展示了所生成的分类器集,同时展示了把测试数据用于最终分类器所预测得到的正确分类和错误分类数。 表54AdaBoost用于Iris数据集后生成的分类器集与测试结果 总共用时: 414ms 共14个分类器 第1个分类器为: 取的特征为: petalWidth,阈值为: 1.60 1,petalWidth<1.60 -1,petalWidth≥1.60 权重: 1.354025100551105 第2个分类器为: 取的特征为: petalLength,阈值为: 4.92 1,petalLength<4.92 -1,petalLength≥4.92 权重: 0.9076449833191255 第3个分类器为: 取的特征为: petalLength,阈值为: 5.14 1,petalLength<5.14 -1,petalLength≥5.14 权重: 0.703945231704386 第4个分类器为: 取的特征为: sepalLength,阈值为: 6.51 -1,sepalLength<6.51 1,sepalLength≥6.51 权重: 0.6839372392899267 第5个分类器为: 取的特征为: petalLength,阈值为: 4.83 1,petalLength<4.83 -1,petalLength≥4.83 权重: 0.429936514537254 第6个分类器为: 取的特征为: petalWidth,阈值为: 1.71 1,petalWidth<1.71 -1,petalWidth≥1.71 权重: 0.4899552768715438 第7个分类器为: 取的特征为: sepalWidth,阈值为: 2.81 -1,sepalWidth<2.81 1,sepalWidth≥2.81 权重: 0.39721503595826485 第8个分类器为: 取的特征为: petalWidth,阈值为: 1.30 1,petalWidth<1.30 -1,petalWidth≥1.30 权重: 0.3975433595017072 第9个分类器为: 取的特征为: petalLength,阈值为: 5.14 1,petalLength<5.14 -1,petalLength≥5.14 权重: 0.6062702960358095 第10个分类器为: 取的特征为: sepalWidth,阈值为: 3.10 -1,sepalWidth<3.10 1,sepalWidth≥3.10 权重: 0.4433451016170988 续表 第11个分类器为: 取的特征为: petalWidth,阈值为: 1.71 1,petalWidth<1.71 -1,petalWidth≥1.71 权重: 0.3668771900466325 第12个分类器为: 取的特征为: sepalWidth,阈值为: 2.61 -1,sepalWidth<2.61 1,sepalWidth≥2.61 权重: 0.4294725865428733 第13个分类器为: 取的特征为: petalLength,阈值为: 4.42 1,petalLength<4.42 -1,petalLength≥4.42 权重: 0.4364675253599789 第14个分类器为: 取的特征为: petalLength,阈值为: 5.14 1,petalLength<5.14 -1,petalLength≥5.14 权重: 0.43145215977489 测试数据共20个,正确19个,错误1个 其次,我们把AdaBoost用于SimpleDataSet数据集,因为该数据集只有两个属性和一个标签,便于可视化,因此我们绘制该数据集及所生成的分类器,实验效果如图53~图56所示。 图53原始数据集 图543个决策树桩分类器 图555个决策树桩分类器 图5610个决策树桩分类器 5.4.2结果分析 通过所展示的效果图,我们可以看到随着基础分类器的逐渐增加,所生成的综合分类器在划分数据集中的Class 1和Class 2时逐渐准确,这也进一步证实AdaBoost算法综合多个基础分类器后所取得的效果远优于一个基础分类器。 AdaBoost算法最开始仅用于解决二分类问题,后扩展到解决多分类问题。当数据样本具有多个类别时,可采用两种方式将其转换为二分类问题。一种方式是把训练样本某一类当成一类,剩下的几类归为同一类; 另一种方式是选择训练样本某一类当成一类,再选择另外的某一类自成一类。不管哪种方式,AdaBoost均会根据类别两两组合情况生成多个最终分类器,并基于“多数表决”的规则把全部分类器所预测的最多类别作为当前数据样本类别。