Boosting的原理的简单理解
通过构建几个弱分类器,将几个弱分类器进行集成,从而得到一个分类性能较好的强分类器。分类器的优化也就是训练过程是一个串行的过程,后一个分类器的优化是建立在前一个分类器的基础上的。
Boosting算法一个常见的实例就是提升树,这个树是一个二叉树,后一个树的构建在前几个树的基础上。树的参数的确定过程实际上就相当于神经网络的训练过程。树的参数包括:所选择的用来分类的特征是什么,按照这个特征进行分类的话,分类的阈值是什么。策略就是:大于这个分类阈值的话,把样本分为-1类还是小于分类阈值将样本划分为-1类,对应着右子树和左子树的概念。
具体代码实现
## adaptive boost 自适应的提升算法
## 具体的算法步骤是:
## 创建一个基本的分类树
## 获得当前的分类树的预测结果
## 根据规则进行样本权重的更新
## 'NoneType' object has no attribute 'append'
"初始化模型的参数"
import numpy as np
class AdaBoost():
def __init__(self,X):
self.x_train = X[:,:-1]
self.x_label = X[:,-1]
self.w = np.array([1/self.x_train.shape[0]]*np.shape(self.x_train)[0])
self.G = []
self.max_iter =10
self.high = 0.1
## M个分类器这个变量怎么进行初始化呢??
def one_classifier(self):
_, n = np.shape(self.x_train)
value = None
sign = None
best_stump = {}
err_min = np.inf # 先设置为无穷大
for i in range(n):
value = max(self.x_train[:,i]) - min(self.x_train[:,i])
step_size = value/n
for j in range(int(n)+1):
threshold = min(self.x_train[:,i]) + j*step_size
# 左子树和右子树的分类的错误率都要进行计算
# 目的是比较哪两个子树的分类的误差率较小
for strategy in ['left','right']:
predict_label = self.predict_one(self.x_train,i,threshold,strategy)
# 接下来统计分类错误的个数
# 接下来统计加权的分类错误的权重
# 目的是为了比较左边子树和右边子树到底是哪一个的分类率较好
err_ = np.array(np.ones(np.shape(self.x_train)[0]))
err_[predict_label == self.x_label] = 0
err_1 = np.dot(self.w, err_) # 计算分类错误的样本的加权的权重
#err_num = int(m) - sum(predict_label == self.x_label)
#err_rate = err_num/m
if err_1 < err_min:
err_min = err_1
sign = predict_label
# 获得而一颗分类的子树
best_stump['dim'] = n
best_stump['threshold'] = j
best_stump['strategy'] = strategy
return best_stump, sign, err_min
@staticmethod
def predict_one(data,dimension,thre,strategy):
arr_base = np.array(np.ones(np.shape(X)[0]))
if strategy == 'left':
arr_base[data[:,dimension] <= thre] = -1
else:
arr_base[data[:,dimension] > thre] = -1
return arr_base
# 接下来是fit函数以及evaluate函数了
def fit(self):
G = 0
# 先要设置一个基本的分类器,这个分类器表示的是各个弱分类器的和
# 遍历1,到最大的迭代次数
# 创建一个当前最佳的分类器树桩,获得弱分类器的加权系数
# 在表示一个若分类器的字典里面增加一个属性,这个属性表示这个分类器的加权系数
# 把这个弱分类器保存到大的分类器中
# G进行更新,表示的是最后的分类器,也就是继承好以后的分类器
# G进行输出,得到每一个样本的预测标签值
# 计算集成后的分类器的误差
# 判断误差是否小于阈值,如果是的话,说明此时这个集成以后的分类器的效果是比较好的,直接跳出循环
# 如果误差不满足要求,则将更新训练集中每一个样本的权重
# 样本的权重关系到重新构造的数据集的可能性
# 其实这个训练过程iterations迭代就是相当于epoch,在这里一个batch就是全部分样本,也就是full_batch
# 进行loss计算,根据计算的loss对参数权重进行更新,在这里说的参数权重就是指的模型的参数权重
for i in range(self.max_iter):# 每一次迭代都会返回一个分类树的树桩
best_stump, sign, err_min = self.one_classifier()
alpha = 1/2*np.log((1-err_min)/err_min)
best_stump['alpha'] = alpha
self.G.append(best_stump)
G += sign * alpha
predict_end = np.sign(G)
err_rate_ = sum(predict_end==self.x_label)/len(self.x_label)
if err_rate_ <= self.high:
print('iterations:')
print(i)
break
else:
self.weight_update(alpha,predict_end)
return self.w
def weight_update(self,alpha,predict):
self.w = self.w * np.exp(-self.x_label*alpha*predict)# 这是一个向量
self.w = self.w/(sum(self.w)) #被除数部分是一个求和以后的数值
# 测试训练好的提升树对新样本的分类能力
# 输入一个新的样本
# 创建一个空的分类器
# 把已经训练好的存储在self.G的列表中的每一个基本的子树复制过来
# 遍历每一个基本的子分类器
# 把每一个基本的子分类器的属性与值拿到,输入到一个分类器函数中,获得预测结果
def test(self,x_test):
G_total = 0
m = np.shape(x_test)[0]
for i in range(len(self.G)):
G_test = self.G[i] #
G_test_ = self.predict_one(x_test,G_test['dim'],G_test['threshold'],G_test['strategy'])
alpha = G_test['alpha']
G_total += alpha*G_test_
test_predict_ = np.sign(G_total)
return test_predict_
if __name__ =='__main__':
# 加载训练数据
X = np.array([[0, 1, 3], [0, 3, 1], [1, 2, 2], [1, 1, 3], [1, 2, 3], [0, 1, 2],
[1, 1, 2], [1, 1, 1], [1, 3, 1], [0, 2, 1]])
y = np.array([-1, -1, -1, -1, -1, -1, 1, 1, -1, -1])
X = np.c_[X,y.T] #在最后面插入一列,表示的是标签
clf = AdaBoost(X)
weight = clf.fit()
test_ = clf.test(X)
test_ = np.array(test_,dtype=int)
print(weight)
print('Test label:')
print(test_)
print('---------------------------------')
print('The true labels:')
print(y)
迭代十次以后,训练集的样本的权重如下