AdaBoost算法是通过改变训练样本权重来学习多个弱分类器并线性组合成强分类器的Boosting算法。
Boosting方法要解答的两个关键问题:
一是在训练过程中如何改变训练样本的权重或者概率分布;
二是如何将多个弱分类器组合成一个强分类器。
AdaBoost的做法是:一是提高前一轮被弱分类器分类错误的样本的权重,而降低分类正确的样本的权重;而是对多个弱分类器进行线性组合,提高分类效果好的弱分类器的权重,降低分类误差率高的弱分类器的权重。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.datasets import make_blobs
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
X, y = make_blobs(n_samples=150, n_features=2, centers=2, cluster_std=1.2, random_state=40)
# 将标签转换为1/-1
y_=y.copy()
# 把数据集中的标签为0的数据转换成标签为-1
y_[y_==0]=-1
y_.astype(float)
# 训练/测试数据集划分
X_train, X_test, y_train, y_test = train_test_split(X, y_, test_size=0.3, random_state=43)
colors={1:'red',-1:'blue'}
plt.scatter(X[:,0],X[:,1],c=pd.Series(y_).map(colors))
plt.show()
单层决策树(decision stump,也称决策树桩)是一种简单的决策树。由于这棵树只有一次分裂过程,因此它实际上就是一个树桩。
class decision_stump:
def __init__(self):
# 基于划分阈值决定样本分类为1还是-1
self.label=1
# 特征索引
self.feature_index=None
# 特征划分阈值
self.threshold=None
# 指示分类准确率
self.alpha=None
这里叙述一下Adaboost算法。
假设给定一个二类分类的训练数据集:
T
=
{
(
x
1
,
y
1
)
,
(
x
2
,
y
2
)
,
⋯
,
(
x
N
,
y
N
)
}
T=\{(x_1,y_1)\;,\;(x_2,y_2)\;,\;\cdots\;,\;(x_N,y_N)\}
T={(x1,y1),(x2,y2),⋯,(xN,yN)}
其中,每个样本点由实例与标记组成。实例
x
i
∈
χ
⊆
R
n
x_i\in \chi \subseteq \R^n
xi∈χ⊆Rn,标记
y
i
∈
Y
=
{
−
1
,
+
1
}
y_i\in Y=\{-1\;,\;+1\}
yi∈Y={−1,+1},
χ
\chi
χ是实例空间,
Y
Y
Y是标记集合。Adaboost利用以下算法,从训练数据中学习一系列弱分类器或基本分类器,并将这些弱分类器线性组合成一个强分类器。
算法步骤如下:
输入:训练数据集 T = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , ⋯ , ( x N , y N ) T=\{(x_1,y_1)\;,\;(x_2,y_2)\;,\;\cdots\;,\;(x_N,y_N) T={(x1,y1),(x2,y2),⋯,(xN,yN),其中 x i ∈ χ ⊆ R n x_i\in \chi \subseteq \R^n xi∈χ⊆Rn, y i ∈ Y = { − 1 , + 1 } y_i\in Y=\{-1,+1\} yi∈Y={−1,+1};弱学习算法;
输出:最终分类器 G ( x ) G(x) G(x)
(1)初始化训练数据的权值分布:
D
1
=
(
ω
11
,
⋯
,
ω
1
i
,
⋯
,
ω
1
n
)
,
ω
1
i
=
1
N
,
i
=
1
,
2
,
⋯
,
N
D_1=(\omega_{11}\;,\;\cdots\;,\;\omega_{1i}\;,\;\cdots\;,\;\omega_{1n})\;,\;\omega_{1i}=\frac{1}{N}\;,\;i=1,2,\cdots,N
D1=(ω11,⋯,ω1i,⋯,ω1n),ω1i=N1,i=1,2,⋯,N
(2)对
m
=
1
,
2
,
⋯
,
M
m=1,2,\cdots,M
m=1,2,⋯,M
使用具有权值分布
D
m
D_m
Dm的训练数据集学习,得到基本分类器:
G
m
(
x
)
:
χ
→
{
−
1
,
+
1
}
G_m(x):\chi \rightarrow\{-1,+1\}
Gm(x):χ→{−1,+1}
计算
G
m
(
x
)
G_m(x)
Gm(x)在训练数据集上的分类误差率:
e
m
=
∑
i
=
1
N
P
(
G
m
(
x
i
)
≠
y
i
)
=
∑
i
=
1
N
ω
m
i
I
(
G
m
(
x
i
)
≠
y
i
)
e_m=\sum_{i=1}^NP(G_m(x_i)\neq y_i)=\sum_{i=1}^N\omega_{mi}I(G_m(x_i)\neq y_i)
em=i=1∑NP(Gm(xi)=yi)=i=1∑NωmiI(Gm(xi)=yi)
计算
G
m
(
x
)
G_m(x)
Gm(x)的系数:
α
m
=
1
2
log
1
−
e
m
e
m
\alpha_m=\frac{1}{2}\log\frac{1-e_m}{e_m}
αm=21logem1−em
更新训练数据集的权值分布:
D
m
+
1
=
(
ω
m
+
1
,
1
,
⋯
,
ω
m
+
1
,
i
,
⋯
,
ω
m
+
1
,
N
D_{m+1}=(\omega_{m+1,1}\;,\;\cdots\;,\;\omega_{m+1,i}\;,\;\cdots\;,\;\omega_{m+1,N}
Dm+1=(ωm+1,1,⋯,ωm+1,i,⋯,ωm+1,N
ω
m
+
1
,
i
=
ω
m
i
Z
m
exp
(
−
α
m
y
i
G
m
(
x
i
)
)
,
i
=
1
,
2
,
⋯
,
N
\omega_{m+1,i}=\frac{\omega_{mi}}{Z_m}\exp(-\alpha_my_iG_m(x_i))\;,\;i=1,2,\cdots,N
ωm+1,i=Zmωmiexp(−αmyiGm(xi)),i=1,2,⋯,N
这里,
Z
m
Z_m
Zm是规范化因子:
Z
m
=
∑
i
=
1
N
ω
m
i
exp
(
−
α
m
y
i
G
m
(
x
)
)
Z_m=\sum_{i=1}^N\omega_{mi}\exp(-\alpha_my_iG_m(x))
Zm=i=1∑Nωmiexp(−αmyiGm(x))
它使得
D
m
+
1
D_{m+1}
Dm+1成为一个概率分布。
(3)构建基本分类器的线性组合:
f
(
x
)
=
∑
m
=
1
M
α
m
G
m
(
x
)
f(x)=\sum_{m=1}^M\alpha_mG_m(x)
f(x)=m=1∑MαmGm(x)
得到最终分类器:
G
(
x
)
=
s
i
g
n
(
f
(
x
)
)
=
s
i
g
n
(
∑
m
=
1
M
α
m
G
m
(
x
)
)
G(x)=sign(f(x))=sign(\sum_{m=1}^M\alpha_mG_m(x))
G(x)=sign(f(x))=sign(m=1∑MαmGm(x))
弱分类器的个数
def __init__(self,n_estimators=10):
self.n_estimators=n_estimators
Adaboost拟合算法
def fit(self,X,y):
m,n=X.shape
# (1) 初始化权重分布为均匀分布 1/N
w=np.full(m,(1/m))
# 初始化基分类器列表
self.estimators = []
# (2) for m in (1,2,...,M)
for _ in range(self.n_estimators):
# (2.a) 训练一个弱分类器:决策树桩
estimator = decision_stump()
# 设定一个最小化误差
min_error = float('inf')
# 遍历数据集特征,根据最小分类误差率选择最优划分特征
for i in range(n):
# 获取特征值
values=np.expand_dims(X[:,i],axis=1)
# 特征值去重
unique_values = np.unique(values)
# 尝试将每一个特征值作为分类阈值
for threshold in unique_values:
p=1
# 初始化所有预测值为1
pred=np.ones(np.shape(y))
# 小于分类阈值的预测值为-1
pred[X[:,i]<threshold]=-1
# 2.b 计算误差率
error=sum(w[y!=pred])
# 如果分类误差大于0.5,则进行正负预测翻转
# 例如 error = 0.6 => (1 - error) = 0.4
if error>0.5:
error=1-error
p=-1
# 一旦获得最小误差则保存相关参数配置
if error<min_error:
estimator.lable=p
estimator.threshold=threshold
estimator.feature_index=i
min_error=error
计算基分类器的权重
estimator.alpha=0.5*np.log((1-min_error)/(min_error+1e-9))
# 初始化所有预测值为1
preds=np.ones(np.shape(y))
# 获取所有小于阈值的负类索引
negative_idx=(estimator.lable*X[:,estimator.feature_index]<estimator.lable*estimator.threshold)
# 将负类设为 '-1'
preds[negative_idx]=-1
更新样本权重
w*=np.exp(-estimator.alpha*y*preds)
w/=np.sum(w)
# 保存该弱分类器
self.estimators.append(estimator)
定义预测函数
def predict(self,X):
m=len(X)
y_pred=np.zeros((m,1))
# 计算每个弱分类器的预测值
for estimator in self.estimators:
# 初始化所有的预测值为1
predictions=np.ones(np.shape(y_pred))
# 获取所有小于阈值的负类索引
negative_idx = (estimator.label * X[:, estimator.feature_index] < estimator.label * estimator.threshold)
# 将负类设为 '-1'
predictions[negative_idx]=-1
# 2.e 对每个弱分类器的预测结果进行加权
y_pred+=estimator.alpha*predictions
# 返回最终预测结果
y_pred=np.sign(y_pred).flatten()
return y_pred
计算准确率
# 创建Adaboost模型实例
clf=Adaboost(n_estimators=5)
# 模型拟合
clf.fit(X_train,y_train)
# 模型预测
y_pred=clf.predict(X_test)
# 计算模型预测准确率
accuracy=accuracy_score(y_test,y_pred)
print(accuracy)
运行结果
0.9777777777777777
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.datasets import make_blobs
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
# 生成数据并查看
X, y = make_blobs(n_samples=150, n_features=2, centers=2, cluster_std=1.2, random_state=40)
# 将标签转换为1/-1
y_=y.copy()
# 把数据集中的标签为0的数据转换成标签为-1
y_[y_==0]=-1
y_.astype(float)
# 训练/测试数据集划分
X_train, X_test, y_train, y_test = train_test_split(X, y_, test_size=0.3, random_state=43)
colors={1:'red',-1:'blue'}
plt.scatter(X[:,0],X[:,1],c=pd.Series(y_).map(colors))
plt.show()
# 定义决策树桩类作为Adaboost的弱分类器,树桩,只有一个分类节点
class decision_stump:
def __init__(self):
# 基于划分阈值决定样本分类为1还是-1
self.label=1
# 特征索引
self.feature_index=None
# 特征划分阈值
self.threshold=None
# 指示分类准确率
self.alpha=None
# 定义Adaboost算法类
class Adaboost:
# 弱分类器个数
def __init__(self,n_estimators=10):
self.n_estimators=n_estimators
# Adaboost拟合算法
def fit(self,X,y):
m,n=X.shape
# (1) 初始化权重分布为均匀分布 1/N
w=np.full(m,(1/m))
# 初始化基分类器列表
self.estimators = []
# (2) for m in (1,2,...,M)
for _ in range(self.n_estimators):
# (2.a) 训练一个弱分类器:决策树桩
estimator = decision_stump()
# 设定一个最小化误差
min_error = float('inf')
# 遍历数据集特征,根据最小分类误差率选择最优划分特征
for i in range(n):
# 获取特征值
values=np.expand_dims(X[:,i],axis=1)
# 特征值去重
unique_values = np.unique(values)
# 尝试将每一个特征值作为分类阈值
for threshold in unique_values:
p=1
# 初始化所有预测值为1
pred=np.ones(np.shape(y))
# 小于分类阈值的预测值为-1
pred[X[:,i]<threshold]=-1
# 2.b 计算误差率
error=sum(w[y!=pred])
# 如果分类误差大于0.5,则进行正负预测翻转
# 例如 error = 0.6 => (1 - error) = 0.4
if error>0.5:
error=1-error
p=-1
# 一旦获得最小误差则保存相关参数配置
if error<min_error:
estimator.lable=p
estimator.threshold=threshold
estimator.feature_index=i
min_error=error
# 2.c 计算基分类器的权重
estimator.alpha=0.5*np.log((1-min_error)/(min_error+1e-9))
# 初始化所有预测值为1
preds=np.ones(np.shape(y))
# 获取所有小于阈值的负类索引
negative_idx=(estimator.lable*X[:,estimator.feature_index]<estimator.lable*estimator.threshold)
# 将负类设为 '-1'
preds[negative_idx]=-1
# 2.d 更新样本权重
w*=np.exp(-estimator.alpha*y*preds)
w/=np.sum(w)
# 保存该弱分类器
self.estimators.append(estimator)
# 定义预测函数
def predict(self,X):
m=len(X)
y_pred=np.zeros((m,1))
# 计算每个弱分类器的预测值
for estimator in self.estimators:
# 初始化所有的预测值为1
predictions=np.ones(np.shape(y_pred))
# 获取所有小于阈值的负类索引
negative_idx = (estimator.label * X[:, estimator.feature_index] < estimator.label * estimator.threshold)
# 将负类设为 '-1'
predictions[negative_idx]=-1
# 2.e 对每个弱分类器的预测结果进行加权
y_pred+=estimator.alpha*predictions
# 返回最终预测结果
y_pred=np.sign(y_pred).flatten()
return y_pred
# 计算准确率
# 创建Adaboost模型实例
clf=Adaboost(n_estimators=10)
# 模型拟合
clf.fit(X_train,y_train)
# 模型预测
y_pred=clf.predict(X_test)
# 计算模型预测准确率
accuracy=accuracy_score(y_test,y_pred)
print(accuracy)