Adaboost算法
集成学习概述
集成学习算法定义
- 集成学习(Ensemble learning)就是讲若干个弱分类器通过一定策略组合后产生一个强分类器。弱分类器(weak Classifier)指的就是那些分类准确率只比随机猜测好一点的分类器。而强分类器(strong Classifier)的分类准确率会高很多,这里的弱和强是相对的,弱分类器也叫做基分类器
-
分类:
bagging
boosting
bagging(装袋)
-
bagging方法又叫做自举汇聚法(boostrap aggregating),是一种根据均匀概率分布从数据集中重复抽样(有放回)的技术,每个数据集和原始数据集大小相等,由于新数据集的每一个样本都是从原数据集合中有放回随机抽样出来的,所以每个数据集中可能有重复的值,而原始数据集中的某些样本可能根本就没有出现在新数据集中
-
有放回的随机抽样:自主采样法(Bootstap sampling),也就是说对于m个样本的原始数据集,每次随机选取一个样本放回采样集合中,然后这个样本重新放回,再进行下一次随机抽样,直到采样集合中样本数量达到m,这样一个采样集合就构建好了,重复过程,生成n个采样集合
-
将n个采样集合,分别进行训练,得到n个弱分类器,根据每个结果进行组合,得到强分类器
-
降低弱分类器的方差
boosting(提升)
-
迭代过程,用来自适应的改变训练样本的分布,使得弱分类器聚焦到那些很难分类的样本上,它的做法是给每一个训练样本赋予一个权重,在每一轮训练中自动调整权重
-
组合策略
-
平均法:
-
投票法:
-
学习法:
-
Adaboost算法(自适应提升算法)
-
收集数据:可以使用任意方法
准备数据:依赖于所使用的弱分类器类型,本章使用的是单层决策树,这种分类器可以处理任何数据类型。
当然也可以使用任意分类器作为弱分类器,第2章到第6章中的任一分类器都可以充当弱分类器。
作为弱分类器,简单分类器的效果更好。
分析数据:可以使用任意方法。
训练算法:AdaBoost 的大部分时间都用在训练上,分类器将多次在同一数据集上训练弱分类器。
测试算法:计算分类的错误率。
使用算法:通SVM一样,AdaBoost 预测两个类别中的一个。如果想把它应用到多个类别的场景,那么就要像多类 SVM 中的做法一样对 AdaBoost -
优点:泛化(由具体的、个别的扩大为一般的)错误率低,易编码,可以应用在大部分分类器上,无参数调节。
缺点:对离群点敏感。
适用数据类型:数值型和标称型数据
案例一:自适应算法实现
-
导入数据:
def load_sim_data(): """ 测试数据, :return: data_arr feature对应的数据集 label_arr feature对应的分类标签 """ data_mat = np.matrix([[1.0, 2.1], [2.0, 1.1], [1.3, 1.0], [1.0, 1.0], [2.0, 1.0]]) class_labels = [1.0, 1.0, -1.0, -1.0, 1.0] return data_mat, class_labels
-
算法测试:将符合条件的数据转化,测试是否有某个值小于或者大于我们正在测试的阈值,如果大于某个阈值就是错误的
def stump_classify(data_mat, dimen, thresh_val, thresh_ineq): """ (将数据集,按照feature列的value进行 二分法切分比较来赋值分类) :param data_mat: Matrix数据集 :param dimen: 特征的哪一个列 :param thresh_val: 特征列要比较的值 :param thresh_ineq: :return: np.array """ ret_array = np.ones((np.shape(data_mat)[0], 1)) # data_mat[:, dimen] 表示数据集中第dimen列的所有值 # thresh_ineq == 'lt'表示修改左边的值,gt表示修改右边的值 # (这里其实我建议理解为转换左右边,就是一棵树的左右孩子,可能有点问题。。。待考证) if thresh_ineq == 'lt':# 假设左边比较一下 ret_array[data_mat[:, dimen] <= thresh_val] = -1.0 else:# 假设右边比较一下 ret_array[data_mat[:, dimen] > thresh_val] = -1.0 return ret_array
-
单层决策树的实现:最优化单层决策树
# 这个算法是为了寻找最好的单层决策树 def build_stump(data_arr, class_labels, D): """ 得到决策树的模型 (这个比较重要,需要看懂) :param data_arr: 特征标签集合 :param class_labels: 分类标签集合 :param D: 最初的特征权重值 :return: best_Stump 最优的分类器模型 min_error 错误率 best_class_est 训练后的结果集 """ data_mat = np.mat(data_arr) label_mat = np.mat(class_labels).T m, n = np.shape(data_mat) num_steps = 10.0 best_stump = {} best_class_est = np.mat(np.zeros((m, 1)))#训练后的结果集 # 无穷大 min_err = np.inf for i in range(n): range_min = data_mat[:, i].min() range_max = data_mat[:, i].max() step_size = (range_max - range_min) / num_steps for j in range(-1, int(num_steps) + 1): for inequal in ['lt', 'gt']: thresh_val = (range_min + float(j) * step_size) predicted_vals = stump_classify(data_mat, i, thresh_val, inequal) err_arr = np.mat(np.ones((m, 1))) err_arr[predicted_vals == label_mat] = 0 # 这里是矩阵乘法 weighted_err = D.T * err_arr ''' dim 表示 feature列 thresh_val 表示树的分界值 inequal 表示计算树左右颠倒的错误率的情况 weighted_error 表示整体结果的错误率 best_class_est 预测的最优结果 (与class_labels对应) ''' # print('split: dim {}, thresh {}, thresh inequal: {}, the weighted err is {}'.format( # i, thresh_val, inequal, weighted_err # )) if weighted_err < min_err: min_err = weighted_err best_class_est = predicted_vals.copy()#可以保存的结果集储存 best_stump['dim'] = i# 第i列 best_stump['thresh'] = thresh_val# 阈值 best_stump['ineq'] = inequal#比较范围,是用大于还是用小于 # best_stump 表示分类器的结果,在第几个列上,用大于/小于比较,阈值是多少 (单个弱分类器) # print(best_stump) return best_stump, min_err, best_class_est # print(np.mat(np.ones((5,1))/5))#赋值相同的权重 # [[0.2] # [0.2] # [0.2] # [0.2] # [0.2]] # print(build_stump(datMat,classLables,np.mat(np.ones((5,1))/5)))
-
构建自适应算法
def ada_boost_train_ds(data_arr, class_labels, num_it=40): """ adaBoost训练过程放大 :param data_arr: 特征标签集合 :param class_labels: 分类标签集合 :param num_it: 迭代次数 :return: weak_class_arr 弱分类器的集合 agg_class_est 预测的分类结果值 """ weak_class_arr = [] m = np.shape(data_arr)[0] # 初始化 D,设置每个特征的权重值,平均分为m份 D = np.mat(np.ones((m, 1)) / m) agg_class_est = np.mat(np.zeros((m, 1)))#初始化0矩阵 for i in range(num_it): # 得到决策树的模型 best_stump, error, class_est = build_stump(data_arr, class_labels, D)# 寻找最佳的单层决策树 print('D: {}'.format(D.T)) # alpha 目的主要是计算每一个分类器实例的权重(加和就是分类结果) # 计算每个分类器的 alpha 权重值 alpha = float(0.5 * np.log((1.0 - error) / max(error, 1e-16))) best_stump['alpha'] = alpha # store Stump Params in Array weak_class_arr.append(best_stump) # print('class_est: {}'.format(class_est.T)) # 分类正确:乘积为1,不会影响结果,-1主要是下面求e的-alpha次方 # 分类错误:乘积为 -1,结果会受影响,所以也乘以 -1 expon = np.multiply(-1 * alpha * np.mat(class_labels).T, class_est) # 判断正确的,就乘以-1,否则就乘以1, 为什么? 书上的公式有问题 # print('(-1取反)预测值 expon=', expon.T) # 计算e的expon次方,然后计算得到一个综合的概率的值 # 结果发现: 判断错误的样本,D对于的样本权重值会变大。 # multiply是对应项相乘 D = np.multiply(D, np.exp(expon)) D = D / D.sum() # 预测的分类结果值,在上一轮结果的基础上,进行加和操作 # print('叠加前的分类结果class_est: {}'.format(class_est.T)) agg_class_est += alpha * class_est print('叠加后的分类结果agg_class_est: {}'.format(agg_class_est.T)) # sign 判断正为1, 0为0, 负为-1,通过最终加和的权重值,判断符号。 # 结果为:错误的样本标签集合,因为是 !=,那么结果就是0 正, 1 负,这里1就是表示是错误分辨的 agg_errors = np.multiply(np.sign(agg_class_est) != np.mat(class_labels).T, np.ones((m, 1))) error_rate = agg_errors.sum() / m print('total error: {}\n'.format(error_rate)) if error_rate == 0.0: break print(D) return weak_class_arr
-
测试代码
def ada_classify(data_to_class, classifier_arr): """ 通过刚刚上面那个函数得到的弱分类器的集合进行预测 :param data_to_class: 数据集 :param classifier_arr: 分类器列表 :return: 正负一,也就是表示分类的结果 """ data_mat = np.mat(data_to_class)# 测试9 m = np.shape(data_mat)[0] agg_class_est = np.mat(np.zeros((m, 1))) for i in range(len(classifier_arr)): class_est = stump_classify( data_mat, classifier_arr[i]['dim'], classifier_arr[i]['thresh'], classifier_arr[i]['ineq'] ) agg_class_est += classifier_arr[i]['alpha'] * class_est print(agg_class_est) return np.sign(agg_class_est)
-
完整代码:
# 自适应算法实现实例一 import numpy as np def load_sim_data(): """ 测试数据, :return: data_arr feature对应的数据集 label_arr feature对应的分类标签 """ data_mat = np.matrix([[1.0, 2.1], [2.0, 1.1], [1.3, 1.0], [1.0, 1.0], [2.0, 1.0]]) class_labels = [1.0, 1.0, -1.0, -1.0, 1.0] return data_mat, class_labels datMat,classLables = load_sim_data() def stump_classify(data_mat, dimen, thresh_val, thresh_ineq): """ (将数据集,按照feature列的value进行 二分法切分比较来赋值分类) :param data_mat: Matrix数据集 :param dimen: 特征的哪一个列 :param thresh_val: 特征列要比较的值 :param thresh_ineq: :return: np.array """ ret_array = np.ones((np.shape(data_mat)[0], 1)) # data_mat[:, dimen] 表示数据集中第dimen列的所有值 # thresh_ineq == 'lt'表示修改左边的值,gt表示修改右边的值 # (这里其实我建议理解为转换左右边,就是一棵树的左右孩子,可能有点问题。。。待考证) if thresh_ineq == 'lt':# 假设左边比较一下 ret_array[data_mat[:, dimen] <= thresh_val] = -1.0 else:# 假设右边比较一下 ret_array[data_mat[:, dimen] > thresh_val] = -1.0 return ret_array # 这个算法是为了寻找最好的单层决策树 def build_stump(data_arr, class_labels, D): """ 得到决策树的模型 (这个比较重要,需要看懂) :param data_arr: 特征标签集合 :param class_labels: 分类标签集合 :param D: 最初的特征权重值 :return: best_Stump 最优的分类器模型 min_error 错误率 best_class_est 训练后的结果集 """ data_mat = np.mat(data_arr) label_mat = np.mat(class_labels).T m, n = np.shape(data_mat) num_steps = 10.0 best_stump = {} best_class_est = np.mat(np.zeros((m, 1)))#训练后的结果集 # 无穷大 min_err = np.inf for i in range(n): range_min = data_mat[:, i].min() range_max = data_mat[:, i].max() step_size = (range_max - range_min) / num_steps for j in range(-1, int(num_steps) + 1): for inequal in ['lt', 'gt']: thresh_val = (range_min + float(j) * step_size) predicted_vals = stump_classify(data_mat, i, thresh_val, inequal) err_arr = np.mat(np.ones((m, 1))) err_arr[predicted_vals == label_mat] = 0 # 这里是矩阵乘法 weighted_err = D.T * err_arr ''' dim 表示 feature列 thresh_val 表示树的分界值 inequal 表示计算树左右颠倒的错误率的情况 weighted_error 表示整体结果的错误率 best_class_est 预测的最优结果 (与class_labels对应) ''' # print('split: dim {}, thresh {}, thresh inequal: {}, the weighted err is {}'.format( # i, thresh_val, inequal, weighted_err # )) if weighted_err < min_err: min_err = weighted_err best_class_est = predicted_vals.copy()#可以保存的结果集储存 best_stump['dim'] = i# 第i列 best_stump['thresh'] = thresh_val# 阈值 best_stump['ineq'] = inequal#比较范围,是用大于还是用小于 # best_stump 表示分类器的结果,在第几个列上,用大于/小于比较,阈值是多少 (单个弱分类器) # print(best_stump) return best_stump, min_err, best_class_est # print(np.mat(np.ones((5,1))/5))#赋值相同的权重 # [[0.2] # [0.2] # [0.2] # [0.2] # [0.2]] # print(build_stump(datMat,classLables,np.mat(np.ones((5,1))/5))) def ada_boost_train_ds(data_arr, class_labels, num_it=40): """ adaBoost训练过程放大 :param data_arr: 特征标签集合 :param class_labels: 分类标签集合 :param num_it: 迭代次数 :return: weak_class_arr 弱分类器的集合 agg_class_est 预测的分类结果值 """ weak_class_arr = [] m = np.shape(data_arr)[0] # 初始化 D,设置每个特征的权重值,平均分为m份 D = np.mat(np.ones((m, 1)) / m) agg_class_est = np.mat(np.zeros((m, 1)))#初始化0矩阵 for i in range(num_it): # 得到决策树的模型 best_stump, error, class_est = build_stump(data_arr, class_labels, D)# 寻找最佳的单层决策树 print('D: {}'.format(D.T)) # alpha 目的主要是计算每一个分类器实例的权重(加和就是分类结果) # 计算每个分类器的 alpha 权重值 alpha = float(0.5 * np.log((1.0 - error) / max(error, 1e-16))) best_stump['alpha'] = alpha # store Stump Params in Array weak_class_arr.append(best_stump) # print('class_est: {}'.format(class_est.T)) # 分类正确:乘积为1,不会影响结果,-1主要是下面求e的-alpha次方 # 分类错误:乘积为 -1,结果会受影响,所以也乘以 -1 expon = np.multiply(-1 * alpha * np.mat(class_labels).T, class_est) # 判断正确的,就乘以-1,否则就乘以1, 为什么? 书上的公式有问题 # print('(-1取反)预测值 expon=', expon.T) # 计算e的expon次方,然后计算得到一个综合的概率的值 # 结果发现: 判断错误的样本,D对于的样本权重值会变大。 # multiply是对应项相乘 D = np.multiply(D, np.exp(expon)) D = D / D.sum() # 预测的分类结果值,在上一轮结果的基础上,进行加和操作 # print('叠加前的分类结果class_est: {}'.format(class_est.T)) agg_class_est += alpha * class_est print('叠加后的分类结果agg_class_est: {}'.format(agg_class_est.T)) # sign 判断正为1, 0为0, 负为-1,通过最终加和的权重值,判断符号。 # 结果为:错误的样本标签集合,因为是 !=,那么结果就是0 正, 1 负,这里1就是表示是错误分辨的 agg_errors = np.multiply(np.sign(agg_class_est) != np.mat(class_labels).T, np.ones((m, 1))) error_rate = agg_errors.sum() / m print('total error: {}\n'.format(error_rate)) if error_rate == 0.0: break print(D) return weak_class_arr # print(ada_boost_train_ds(datMat,classLables,9)) classLables_arr = ada_boost_train_ds(datMat,classLables,9) def ada_classify(data_to_class, classifier_arr): """ 通过刚刚上面那个函数得到的弱分类器的集合进行预测 :param data_to_class: 数据集 :param classifier_arr: 分类器列表 :return: 正负一,也就是表示分类的结果 """ data_mat = np.mat(data_to_class)# 测试9 m = np.shape(data_mat)[0] agg_class_est = np.mat(np.zeros((m, 1))) for i in range(len(classifier_arr)): class_est = stump_classify( data_mat, classifier_arr[i]['dim'], classifier_arr[i]['thresh'], classifier_arr[i]['ineq'] ) agg_class_est += classifier_arr[i]['alpha'] * class_est print(agg_class_est) return np.sign(agg_class_est) # print(ada_classify([0,0],classLables_arr))
疝病马数据使用自适应算法实现
-
和上边代码差不多,就是加了一个rank测试
import numpy as np def stump_classify(data_mat, dimen, thresh_val, thresh_ineq): """ (将数据集,按照feature列的value进行 二分法切分比较来赋值分类) :param data_mat: Matrix数据集 :param dimen: 特征的哪一个列 :param thresh_val: 特征列要比较的值 :param thresh_ineq: :return: np.array """ ret_array = np.ones((np.shape(data_mat)[0], 1)) # data_mat[:, dimen] 表示数据集中第dimen列的所有值 # thresh_ineq == 'lt'表示修改左边的值,gt表示修改右边的值 # (这里其实我建议理解为转换左右边,就是一棵树的左右孩子,可能有点问题。。。待考证) if thresh_ineq == 'lt':# 假设左边比较一下 ret_array[data_mat[:, dimen] <= thresh_val] = -1.0 else:# 假设右边比较一下 ret_array[data_mat[:, dimen] > thresh_val] = -1.0 return ret_array # 这个算法是为了寻找最好的单层决策树 def build_stump(data_arr, class_labels, D): """ 得到决策树的模型 (这个比较重要,需要看懂) :param data_arr: 特征标签集合 :param class_labels: 分类标签集合 :param D: 最初的特征权重值 :return: best_Stump 最优的分类器模型 min_error 错误率 best_class_est 训练后的结果集 """ data_mat = np.mat(data_arr) label_mat = np.mat(class_labels).T m, n = np.shape(data_mat) num_steps = 10.0 best_stump = {} best_class_est = np.mat(np.zeros((m, 1)))#训练后的结果集 # 无穷大 min_err = np.inf for i in range(n): range_min = data_mat[:, i].min() range_max = data_mat[:, i].max() step_size = (range_max - range_min) / num_steps for j in range(-1, int(num_steps) + 1): for inequal in ['lt', 'gt']: thresh_val = (range_min + float(j) * step_size) predicted_vals = stump_classify(data_mat, i, thresh_val, inequal) err_arr = np.mat(np.ones((m, 1))) err_arr[predicted_vals == label_mat] = 0 # 这里是矩阵乘法 weighted_err = D.T * err_arr ''' dim 表示 feature列 thresh_val 表示树的分界值 inequal 表示计算树左右颠倒的错误率的情况 weighted_error 表示整体结果的错误率 best_class_est 预测的最优结果 (与class_labels对应) ''' # print('split: dim {}, thresh {}, thresh inequal: {}, the weighted err is {}'.format( # i, thresh_val, inequal, weighted_err # )) if weighted_err < min_err: min_err = weighted_err best_class_est = predicted_vals.copy()#可以保存的结果集储存 best_stump['dim'] = i# 第i列 best_stump['thresh'] = thresh_val# 阈值 best_stump['ineq'] = inequal#比较范围,是用大于还是用小于 # best_stump 表示分类器的结果,在第几个列上,用大于/小于比较,阈值是多少 (单个弱分类器) # print(best_stump) return best_stump, min_err, best_class_est # print(np.mat(np.ones((5,1))/5))#赋值相同的权重 # [[0.2] # [0.2] # [0.2] # [0.2] # [0.2]] # print(build_stump(datMat,classLables,np.mat(np.ones((5,1))/5))) def ada_boost_train_ds(data_arr, class_labels, num_it=40): """ adaBoost训练过程放大 :param data_arr: 特征标签集合 :param class_labels: 分类标签集合 :param num_it: 迭代次数 :return: weak_class_arr 弱分类器的集合 agg_class_est 预测的分类结果值 """ weak_class_arr = [] m = np.shape(data_arr)[0] # 初始化 D,设置每个特征的权重值,平均分为m份 D = np.mat(np.ones((m, 1)) / m) agg_class_est = np.mat(np.zeros((m, 1)))#初始化0矩阵 for i in range(num_it): # 得到决策树的模型 best_stump, error, class_est = build_stump(data_arr, class_labels, D)# 寻找最佳的单层决策树 # print('D: {}'.format(D.T)) # alpha 目的主要是计算每一个分类器实例的权重(加和就是分类结果) # 计算每个分类器的 alpha 权重值 alpha = float(0.5 * np.log((1.0 - error) / max(error, 1e-16))) best_stump['alpha'] = alpha # store Stump Params in Array weak_class_arr.append(best_stump) # print('class_est: {}'.format(class_est.T)) # 分类正确:乘积为1,不会影响结果,-1主要是下面求e的-alpha次方 # 分类错误:乘积为 -1,结果会受影响,所以也乘以 -1 expon = np.multiply(-1 * alpha * np.mat(class_labels).T, class_est) # 判断正确的,就乘以-1,否则就乘以1, 为什么? 书上的公式有问题 # print('(-1取反)预测值 expon=', expon.T) # 计算e的expon次方,然后计算得到一个综合的概率的值 # 结果发现: 判断错误的样本,D对于的样本权重值会变大。 # multiply是对应项相乘 D = np.multiply(D, np.exp(expon)) D = D / D.sum() # 预测的分类结果值,在上一轮结果的基础上,进行加和操作 # print('叠加前的分类结果class_est: {}'.format(class_est.T)) agg_class_est += alpha * class_est # print('叠加后的分类结果agg_class_est: {}'.format(agg_class_est.T)) # sign 判断正为1, 0为0, 负为-1,通过最终加和的权重值,判断符号。 # 结果为:错误的样本标签集合,因为是 !=,那么结果就是0 正, 1 负,这里1就是表示是错误分辨的 agg_errors = np.multiply(np.sign(agg_class_est) != np.mat(class_labels).T, np.ones((m, 1))) error_rate = agg_errors.sum() / m print('total error: {}\n'.format(error_rate)) if error_rate == 0.0: break # print(D) return weak_class_arr def loadDataSet(fileName): numFeat = len(open(fileName).readline().split('\t'))# 22个列 21个特征 1个标签 dataMat = [];labelMat = [] fr = open(fileName) for line in fr.readlines(): lineArr = [] curLine = line.strip().split('\t') # 将21个特征保存起来 for i in range(numFeat-1): lineArr.append((float(curLine[i]))) # print(len(lineArr)) dataMat.append(lineArr) labelMat.append(float(curLine[-1])) return dataMat,labelMat datArr,labelArr = loadDataSet(r'/home/ach/桌面/machine-learning/ai/Adaboost/7.AdaBoost/horseColicTraining2.txt') # print(ada_boost_train_ds(datArr,labelArr,10)) classifiterArray = ada_boost_train_ds(datArr,labelArr,10000) def ada_classify(data_to_class, classifier_arr): """ 通过刚刚上面那个函数得到的弱分类器的集合进行预测 :param data_to_class: 数据集 :param classifier_arr: 分类器列表 :return: 正负一,也就是表示分类的结果 """ data_mat = np.mat(data_to_class)# 测试9 m = np.shape(data_mat)[0] agg_class_est = np.mat(np.zeros((m, 1))) for i in range(len(classifier_arr)): class_est = stump_classify( data_mat, classifier_arr[i]['dim'], classifier_arr[i]['thresh'], classifier_arr[i]['ineq'] ) agg_class_est += classifier_arr[i]['alpha'] * class_est # print(agg_class_est) return np.sign(agg_class_est) def test(): testArr,testLabelArr = loadDataSet(r'/home/ach/桌面/machine-learning/ai/Adaboost/7.AdaBoost/horseColicTest2.txt') pridict = ada_classify(testArr,classifiterArray) errArr = np.mat(np.ones((67,1))) error = errArr[pridict!=np.mat(testLabelArr).T].sum() print('error = {}'.format(error/len(testLabelArr))) test()
-
和书上结果差不多:
处理非均衡问题
-
代码
import numpy as np def stump_classify(data_mat, dimen, thresh_val, thresh_ineq): """ (将数据集,按照feature列的value进行 二分法切分比较来赋值分类) :param data_mat: Matrix数据集 :param dimen: 特征的哪一个列 :param thresh_val: 特征列要比较的值 :param thresh_ineq: :return: np.array """ ret_array = np.ones((np.shape(data_mat)[0], 1)) # data_mat[:, dimen] 表示数据集中第dimen列的所有值 # thresh_ineq == 'lt'表示修改左边的值,gt表示修改右边的值 # (这里其实我建议理解为转换左右边,就是一棵树的左右孩子,可能有点问题。。。待考证) if thresh_ineq == 'lt':# 假设左边比较一下 ret_array[data_mat[:, dimen] <= thresh_val] = -1.0 else:# 假设右边比较一下 ret_array[data_mat[:, dimen] > thresh_val] = -1.0 return ret_array # 这个算法是为了寻找最好的单层决策树 def build_stump(data_arr, class_labels, D): """ 得到决策树的模型 (这个比较重要,需要看懂) :param data_arr: 特征标签集合 :param class_labels: 分类标签集合 :param D: 最初的特征权重值 :return: best_Stump 最优的分类器模型 min_error 错误率 best_class_est 训练后的结果集 """ data_mat = np.mat(data_arr) label_mat = np.mat(class_labels).T m, n = np.shape(data_mat) num_steps = 10.0 best_stump = {} best_class_est = np.mat(np.zeros((m, 1)))#训练后的结果集 # 无穷大 min_err = np.inf for i in range(n): range_min = data_mat[:, i].min() range_max = data_mat[:, i].max() step_size = (range_max - range_min) / num_steps for j in range(-1, int(num_steps) + 1): for inequal in ['lt', 'gt']: thresh_val = (range_min + float(j) * step_size) predicted_vals = stump_classify(data_mat, i, thresh_val, inequal) err_arr = np.mat(np.ones((m, 1))) err_arr[predicted_vals == label_mat] = 0 # 这里是矩阵乘法 weighted_err = D.T * err_arr ''' dim 表示 feature列 thresh_val 表示树的分界值 inequal 表示计算树左右颠倒的错误率的情况 weighted_error 表示整体结果的错误率 best_class_est 预测的最优结果 (与class_labels对应) ''' # print('split: dim {}, thresh {}, thresh inequal: {}, the weighted err is {}'.format( # i, thresh_val, inequal, weighted_err # )) if weighted_err < min_err: min_err = weighted_err best_class_est = predicted_vals.copy()#可以保存的结果集储存 best_stump['dim'] = i# 第i列 best_stump['thresh'] = thresh_val# 阈值 best_stump['ineq'] = inequal#比较范围,是用大于还是用小于 # best_stump 表示分类器的结果,在第几个列上,用大于/小于比较,阈值是多少 (单个弱分类器) # print(best_stump) return best_stump, min_err, best_class_est # print(np.mat(np.ones((5,1))/5))#赋值相同的权重 # [[0.2] # [0.2] # [0.2] # [0.2] # [0.2]] # print(build_stump(datMat,classLables,np.mat(np.ones((5,1))/5))) def ada_boost_train_ds(data_arr, class_labels, num_it=40): """ adaBoost训练过程放大 :param data_arr: 特征标签集合 :param class_labels: 分类标签集合 :param num_it: 迭代次数 :return: weak_class_arr 弱分类器的集合 agg_class_est 预测的分类结果值 """ weak_class_arr = [] m = np.shape(data_arr)[0] # 初始化 D,设置每个特征的权重值,平均分为m份 D = np.mat(np.ones((m, 1)) / m) agg_class_est = np.mat(np.zeros((m, 1)))#初始化0矩阵 for i in range(num_it): # 得到决策树的模型 best_stump, error, class_est = build_stump(data_arr, class_labels, D)# 寻找最佳的单层决策树 # print('D: {}'.format(D.T)) # alpha 目的主要是计算每一个分类器实例的权重(加和就是分类结果) # 计算每个分类器的 alpha 权重值 alpha = float(0.5 * np.log((1.0 - error) / max(error, 1e-16))) best_stump['alpha'] = alpha # store Stump Params in Array weak_class_arr.append(best_stump) # print('class_est: {}'.format(class_est.T)) # 分类正确:乘积为1,不会影响结果,-1主要是下面求e的-alpha次方 # 分类错误:乘积为 -1,结果会受影响,所以也乘以 -1 expon = np.multiply(-1 * alpha * np.mat(class_labels).T, class_est) # 判断正确的,就乘以-1,否则就乘以1, 为什么? 书上的公式有问题 # print('(-1取反)预测值 expon=', expon.T) # 计算e的expon次方,然后计算得到一个综合的概率的值 # 结果发现: 判断错误的样本,D对于的样本权重值会变大。 # multiply是对应项相乘 D = np.multiply(D, np.exp(expon)) D = D / D.sum() # 预测的分类结果值,在上一轮结果的基础上,进行加和操作 # print('叠加前的分类结果class_est: {}'.format(class_est.T)) agg_class_est += alpha * class_est # print('叠加后的分类结果agg_class_est: {}'.format(agg_class_est.T)) # sign 判断正为1, 0为0, 负为-1,通过最终加和的权重值,判断符号。 # 结果为:错误的样本标签集合,因为是 !=,那么结果就是0 正, 1 负,这里1就是表示是错误分辨的 agg_errors = np.multiply(np.sign(agg_class_est) != np.mat(class_labels).T, np.ones((m, 1))) error_rate = agg_errors.sum() / m print('total error: {}\n'.format(error_rate)) if error_rate == 0.0: break # print(D) return weak_class_arr,agg_class_est def loadDataSet(fileName): numFeat = len(open(fileName).readline().split('\t'))# 22个列 21个特征 1个标签 dataMat = [];labelMat = [] fr = open(fileName) for line in fr.readlines(): lineArr = [] curLine = line.strip().split('\t') # 将21个特征保存起来 for i in range(numFeat-1): lineArr.append((float(curLine[i]))) # print(len(lineArr)) dataMat.append(lineArr) labelMat.append(float(curLine[-1])) return dataMat,labelMat datArr,labelArr = loadDataSet(r'/home/ach/桌面/machine-learning/ai/Adaboost/7.AdaBoost/horseColicTraining2.txt') # print(ada_boost_train_ds(datArr,labelArr,10)) classifiterArray,aggClassEst = ada_boost_train_ds(datArr,labelArr,100) def plot_roc(pred_strengths, class_labels): """ (打印ROC曲线,并计算AUC的面积大小) :param pred_strengths: 最终预测结果的权重值 :param class_labels: 原始数据的分类结果集 :return: """ import matplotlib.pyplot as plt # variable to calculate AUC y_sum = 0.0 # 对正样本的进行求和 num_pos_class = np.sum(np.array(class_labels) == 1.0) # 正样本的概率 y_step = 1 / float(num_pos_class) # 负样本的概率 x_step = 1 / float(len(class_labels) - num_pos_class) # np.argsort函数返回的是数组值从小到大的索引值 # get sorted index, it's reverse sorted_indicies = pred_strengths.argsort() # 测试结果是否是从小到大排列 # 可以选择打印看一下 # 开始创建模版对象 fig = plt.figure() fig.clf() ax = plt.subplot(111) # cursor光标值 cur = (1.0, 1.0) # loop through all the values, drawing a line segment at each point for index in sorted_indicies.tolist()[0]: if class_labels[index] == 1.0: del_x = 0 del_y = y_step else: del_x = x_step del_y = 0 y_sum += cur[1] # draw line from cur to (cur[0]-delX, cur[1]-delY) # 画点连线 (x1, x2, y1, y2) # print cur[0], cur[0]-delX, cur[1], cur[1]-delY ax.plot([cur[0], cur[0] - del_x], [cur[1], cur[1] - del_y], c='b') cur = (cur[0] - del_x, cur[1] - del_y) # 画对角的虚线线 ax.plot([0, 1], [0, 1], 'b--') plt.xlabel('False positive rate') plt.ylabel('True positive rate') plt.title('ROC curve for AdaBoost horse colic detection system') # 设置画图的范围区间 (x1, x2, y1, y2) ax.axis([0, 1, 0, 1]) plt.show() ''' 参考说明:http://blog.csdn.net/wenyusuran/article/details/39056013 为了计算 AUC ,我们需要对多个小矩形的面积进行累加。 这些小矩形的宽度是x_step,因此可以先对所有矩形的高度进行累加,最后再乘以x_step得到其总面积。 所有高度的和(y_sum)随着x轴的每次移动而渐次增加。 ''' print("the Area Under the Curve is: ", y_sum * x_step) plot_roc(aggClassEst.T,labelArr)
-
运行结果:
Skleanring实现adaboost算法
from sklearn.ensemble import AdaBoostClassifier
from sklearn.tree import DecisionTreeClassifier
import numpy as np
def loadDataSet(fileName):
numFeat = len(open(fileName).readline().split('\t'))# 22个列 21个特征 1个标签
dataMat = [];labelMat = []
fr = open(fileName)
for line in fr.readlines():
lineArr = []
curLine = line.strip().split('\t')
# 将21个特征保存起来
for i in range(numFeat-1):
lineArr.append((float(curLine[i])))
# print(len(lineArr))
dataMat.append(lineArr)
labelMat.append(float(curLine[-1]))
return dataMat,labelMat
datArr,labelArr = loadDataSet(r'/home/ach/桌面/machine-learning/ai/Adaboost/7.AdaBoost/horseColicTraining2.txt')
clf = AdaBoostClassifier(DecisionTreeClassifier(max_depth=2,min_samples_split=5,min_samples_leaf=5),n_estimators=40,random_state=10000)
clf.fit(np.mat(datArr),labelArr)
datArr1,labelArr1 = loadDataSet(r'/home/ach/桌面/machine-learning/ai/Adaboost/7.AdaBoost/horseColicTest2.txt')
print(clf.score(np.mat(datArr1),labelArr1))