《机器学习实战》Adaboost算法中buildstump函数的最直观详解
学习《机器学习实战》Adaboost算法时,发现书中对build_stump函数的讲解非常笼统,理解起来有难度,而这个函数又是理解后面完整Adaboost算法的重点与基础,因此写此文详细阐述我对buildstump函数的理解
相关代码详解
单层决策树分类函数stump_classify
def stump_classify(data_mat, dimen, thresh_val, thresh_ineq):
"""
:param data_mat: Mat据集
:param dimen: 特征的哪一个列
:param thresh_val: 特征列要比较的值
:param thresh_ineq: 分类标志
:return: np.array
"""
ret_array = np.ones((np.shape(data_mat)[0], 1)) #将分类向量的值先全部初始化为1
if thresh_ineq == 'lt': #若标志为"lt"则把小于阈值的点判定为"-1"类,否则把大于阈值的点判定为"-1"类
ret_array[data_mat[:, dimen] <= thresh_val] = -1.0 # data_mat[:, dimen] 表示数据集中第dimen列的所有值
else:
ret_array[data_mat[:, dimen] > thresh_val] = -1.0
return ret_array
建立单层决策树的弱分类器函数buildstump
def buildstump(data_arr, class_labels, D):
"""
:param data_arr: 特征标签集合
:param class_labels: 分类标签集合
:param D: 最初的特征权重值
:return: bestStump 最优的分类器模型
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))) #建立一个初始值全为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): #第二层循环,即对每一步的跨度,下面的图可以很好的说明,也就是垂直于X轴的一条条直线
for inequal in ['lt', 'gt']: #这里太长,见下<1>
thresh_val = (range_min + float(j) * step_size) #计算阈值,即为划分轴在x轴上的值
predicted_vals = stump_classify(data_mat, i, thresh_val, inequal) #带入stump_classify函数 得到返回的分类列表
err_arr = np.mat(np.ones((m, 1))) #先将错误向量的值全部初始化为1
err_arr[predicted_vals == label_mat] = 0 #若得到的分类列表的值与训练集的类标签值一直,则设置为0
weighted_err = D.T * err_arr #这里太长,见下<2>
print("split:第%d列,阈值:%.2f,标志:%s,错误率:%.3f"%(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
best_stump['thresh'] = thresh_val
best_stump['ineq'] = inequal
# best_stump 表示分类器的结果,在第几个列上,用大于/小于比较,阈值是多少 (单个弱分类器)
print("最优分类器为%s\n最小错误率为:%.6f\n最优分类结果为\n%s"%(best_stump, min_err, best_class_est))
return best_stump, min_err, best_class_est
<1>第三层循环,这里书上说是对每个不等号,太难懂了,其实这里lt的意思是less tan少于,gt的意思是great than 多于,也就是在当前步长的划分下,分别对数据进行"lt"和"gt"的标示,然后将数据集带入stum_classify进行错误测试,lt表示把小于阈值的点判成是"-1"类,gt标示把大于阈值的点判成"-1"类,逐一对比在同一个步长的划分下,哪种标示方法的错误率低
<2>这里是矩阵乘法,要重点说明一下,这个乘法得到的是一个具体的数值,即错误率,例如,当数据集有4行时,因为D=[1/4,1/4,1/4,1/4],若在当前的步长与标志下,err_arr中前3个判定值是正确的,即err_arr=[0,0,0,1],计算得weighted_err=1/4,正好是:判错数目/总样本数,现在能体会到上面设置为1或0的巧妙了吧?另外,weighted_err也是与Adaboost分类器交互的地方,利用它计算每个弱分类器的α值,从而进一步更新特征权重矩阵D,在原有的基础上得到更准确的D,该部分内容可以参考书P117页的讲解,还是比较好理解的
理解了上面的注解后,接下来我们跑一下函数
1.导入数据
import adaboost
datMat,classLabels=adaboost.load_sim_data()
2.给特征权重D赋初始值
D = mat(ones( (5,1) / ) 5 )
3.加载buidstump函数
adaboost.buidstump(datMat,classLebels,D)
利用图讲接树桩分类器的构建过程,再加深一下大家的理解.
如上图,biuldstump其实就是在构建一个单决策树的弱分类器,之所以弱, 是因为你找不到任何一种单层决策树(其实就是与坐标轴平行的分割线)能将数据完全正确的分割开来,在构建过程中,通过对每个特征下的数值,以一定的步长进行遍历(取决于你设置的步数),其实就相当于上图黄线逐渐向右移动,对比移动到哪个位置的时候错误率最低,于是就能得到该数据集下的最优单层决策树,理解了这些,在此基础上,利用错误率计算出→α,再用α计算出新的特征权重矩阵D→D,最后,利用这几次得到几个弱分类器集合,就能得到不错的分类结果了。
下一篇: 著名的C/C++框架和第三方库总结