在学习adaboost时遇到的一些问题,和对源代码功能的一些记录,程序来源于菊安酱的机器学习的ppt,虽然作者也对其做了很详细的注解但是由于本人水平的限制很多地方没有看懂,所以就又加了一些注解,可以增加自己的记忆。
import numpy as np
import pandas as pd
#获得特征矩阵和标签矩阵
def get_Mat(path):
dataSet = pd.read_table(path,header = None)
xMat = np.mat(dataSet.iloc[:,:-1].values)
yMat = np.mat(dataSet.iloc[:,-1].values).T
return xMat,yMat
xMat,yMat = get_Mat('C:/Users/ASUS/Desktop/data mining/data/adaboost/test.txt')
获得文本内容,这个没有什么好说的,其xMat,yMat如下图所示。
下面的代码寻找数据集上的最佳单层决策树,看了好久才看懂是什么意思,主要就是遍历各个特征,和每个特征的各种分类情况,来找到一个误差率最小的分类情况。并将其作为最佳的的分类结果输出。源代码写的注解很详细,但是很多地方我并没有看懂,主要感觉是没有联系上下文去解释其作用。
""" 函数功能:单层决策树分类函数 参数说明:
xMat: 数据矩阵
i: 第i列,也就是第几个特征
Q: 阈值
S: 标志 返回:
re: 分类结果 """
def Classify0(xMat,i,Q,S):
re = np.ones((xMat.shape[0],1)) #初始化re为1
if S == 'lt':
re[xMat[:,i] <= Q] = -1 #如果小于阈值,则赋值为-1
else:
re[xMat[:,i] > Q] = -1 #如果大于阈值,则赋值为-1
return re
""" 函数功能:找到数据集上最佳的单层决策树 参数说明:
xMat:特征矩阵
yMat:标签矩阵
D:样本权重 返回:
bestStump:最佳单层决策树信息
minE:最小误差
bestClas:最佳的分类结果 """
def get_Stump(xMat,yMat,D):
m,n = xMat.shape
Steps = 10 #初始化一个步数
bestStump = {} ####用字典形式来储存树桩信息
bestClas = np.mat(np.zeros((m,1))) ####初始化分类结果为1
minE = np.inf ####最小误差初始化为正无穷大
for i in range(n):####遍历所有特征,找出最佳分类器所在的特征列。
Min = xMat[:,i].min() ####找到特征中最小值
Max = xMat[:,i].max() #####找到特征中最大值
stepSize = (Max - Min) / Steps ####计算步长
for j in range(-1, int(Steps)+1): ####遍历每个步长,找出最佳分类器的阈值。
for S in ['lt', 'gt']:####看看在这个阈值下面,大于赋值为1,误差更小,还是小于更好。
Q = (Min + j * stepSize)#计算阈值
re = Classify0(xMat, i, Q, S) ###计算分类结果
err = np.mat(np.ones((m,1))) ####初始化误差矩阵
err[re == yMat] = 0 ####分类正确的,赋值为0
eca = D.T * err ####计算误差
#print(f'切分特征: {i}, 阈值:{np.round(Q,2)}, 标志:{S}, 权重误差: {np.round(eca,3)}')
if eca < minE:####找到误差最小的分类方式
minE = eca
bestClas = re.copy()
bestStump['特征列'] = i
bestStump['阈值'] = Q
bestStump['标志'] = S
return bestStump,minE,bestClas
我在看这个代码得时候,主要是3个循环没有看明白。
for i in range(n):
xxxxxxxxxxxxxx
for j in range(-1, int(Steps)+1):
xxxxxxxxxxxxx
for S in ['lt', 'gt']:
xxxxxxxxxxxx
这三个循环主要就适用于寻找最佳的分类器,最外面的for循环,从每个特征遍历,用于寻找最佳切分的特征列,中间的循环用于在第i个特征列中,按照步长去寻找最佳的切分阈值。最后一个循环用于判断是将大于/小于阈值的分为哪一类。最后得出的最佳分类器分类的结果应该是这样的,第x列,大于m的我们将其划分为xx类。
这里还有个问题没有看明白先记录下来。
Q = (Min + j * stepSize) #计算阈值
re = Classify0(xMat, i, Q, S) ###计算分类结果
err = np.mat(np.ones((m,1))) ####初始化误差矩阵
err[re == yMat] = 0 ####分类正确的,赋值为0
eca = D.T * err ####计算误差
后面的代码就是完全按照adaboost的过程来的,没有什么好所得地方,看看注释应该能弄明白。
""" 函数功能:基于单层决策树的AdaBoost训练过程
参数说明:
xMat:特征矩阵
yMat:标签矩阵
maxC:最大迭代次数
返回:
weakClass:弱分类器信息
aggClass:类别估计值(其实就是更改了标签的估计值)
"""
def Ada_train(xMat, yMat, maxC = 40):
weakClass = []
m = xMat.shape[0]
D = np.mat(np.ones((m, 1)) / m) ####初始化权重
aggClass = np.mat(np.zeros((m,1)))
for i in range(maxC):
Stump, error, bestClas = get_Stump(xMat, yMat,D) ###构建单层决策树
#print(f"D:{D.T}")
alpha=float(0.5 * np.log((1 - error) / max(error, 1e-16))) #####计算弱分类器权重alpha
Stump['alpha'] = np.round(alpha,2)
#存储弱学习算法权重,保留两位小数
weakClass.append(Stump) ####存储单层决策树
#print("bestClas: ", bestClas.T)
expon = np.multiply(-1 * alpha *yMat, bestClas) #####计算e的指数项
D = np.multiply(D, np.exp(expon))
D = D / D.sum() ####根据样本权重公式,更新样本权重
aggClass += alpha * bestClas
#更新累计类别估计值
#print(f"aggClass: {aggClass.T}" )
aggErr = np.multiply(np.sign(aggClass) != yMat, np.ones((m,1))) ###计算误差
errRate = aggErr.sum() / m
if errRate == 0:
break ####误差为0,退出循环
return weakClass, aggClass