元算法
元算法(meta-algorithm)是对其他算法进行组合的一种方式。(或称集成方法,ensemble method)
集成方法的不同形式:
- 不同算法的集成
- 同一算法在不同设置下的集成
- 数据集不同部分分配给不同分类器之后的集成
- 基于同一种分类器在多个不同实例下的集成
bagging:基于数据随机重抽样的分类器构建方法
bagging也称自举汇聚法(bootstrap aggregating)。
核心思想:假设训练集有
N
N
N个样本,从中随机抽取
S
S
S次,每次有放回的获取
M
M
M个样本,用某个单独的算法对
S
S
S个数据集(每个数据集有
M
M
M个样本)进行训练,这样就可以获得
S
S
S个分类器,最后通过投票来获取最后的结果。
随机森林(Random Forest,RF)是bagging方法的一种,其基于决策树,不仅对数据随机化抽取,也对特征随机化抽取。
1. 数据的随机化:利用bootstrap方法有放回地随机抽取
S
S
S个新的样本集。
2. 特征随机化:
n
n
n个特征,每棵树随机选择
m
m
m个特征划分数据集。
boosting
boosting方法是先在原数据集上训练出一个分类器,然后将前一个分类器没能完美分类的数据样本重新赋权重(weight),用新的权重数据样本再训练出一个分类器,以此循环,最终的分类结果由加权投票决定。
- boosting和bagging均使用单个学习算法。
- boosting是串行算法(必须依赖上一个分类器),bagging是并行算法(可以同时进行);
- boosting的分类器权重不同,bagging相同。
boosting:AdaBoost
AdaBoost是boosting最流行的一个版本。
AdaBoost的基础理论:使用弱分类器和多个实例构建一个强分类器,弱意味着分类器的性能比随机猜测的要略好,但不会好太多,而强分类器的错误率要低很多。
AdaBoost的算法流程:
- 给每个训练样本赋权重,这些权重构成向量 D D D(初始权重相等,如1000个数据,那每个数据样本初始权重为1/1000)。
- 在该数据上训练一个弱分类器并计算错误率和该分类器的权重值 α \alpha α(基于每个弱分类器的错误率进行计算)。
- 基于该 α \alpha α值重新计算权重(分错的样本权重变大,分对的权重变小)。
- 循环2,3步,在完成给定的迭代次数或错误阈值时,停止循环。
- 最终的分类结果由加权投票决定。
错误率:
ε = 分 类 错 误 的 样 本 数 所 有 样 本 数 目 \varepsilon=\frac{分类错误的样本数}{所有样本数目} ε=所有样本数目分类错误的样本数
α \alpha α值的计算:
α = 1 2 l n 1 − ε ε \alpha=\frac{1}{2}ln\frac{1-\varepsilon}{\varepsilon} α=21lnε1−ε
AdaBoost算法示意图(来源机器学习是实战):
计算出 α \alpha α值后,可以对权重向量 D D D进行更新,以使得那些正确分类的样本的权重降低而分错样本的权重增大。
样本正确分类,其权重修改为:
D t ( t + 1 ) = D t ( t ) e − α ∑ ( D ) D_t^{(t+1)}=\frac{D_t^{(t)}e^{-\alpha}}{\sum(D)} Dt(t+1)=∑(D)Dt(t)e−α
样本被错误分类,其权重修改为:
D t ( t + 1 ) = D t ( t ) e α ∑ ( D ) D_t^{(t+1)}=\frac{D_t^{(t)}e^{\alpha}}{\sum(D)} Dt(t+1)=∑(D)Dt(t)eα
基于单层决策树构建弱分类器
加载数据:
import numpy as np
import matplotlib.pyplot as plt
def read_data():
data = np.array([[1, 2.1], [2, 1.1], [1.3, 1], [1, 1], [2, 1]])
label = [1, 1, -1, -1, 1]
return data,label
if __name__ =="__main__":
data,label = read_data()
x =data[:,0]
y =data[:,1]
# print(x)
plt.scatter(x,y,c=label)
plt.show()```
构建单层决策树:
```python
import numpy as np
def stump_classify(x, dim, thresh_val, thresh_ineq):
'''
通过阈值比较对数据进行分类。
所有在阈值一边的数据会分到类别-1,而另一边的数据会被分类到+1。
通过数组过滤实现。
'''
retArray = np.ones((x.shape[0], 1))
if thresh_ineq == 'lt':
retArray[x[:, dim] <= thresh_val] = -1
else:
retArray[x[:, dim] > thresh_val] = -1
return retArray
def build_stump(x, y, D):
'''
:param x: 输入数据
:param y: 输入数据的标签
:param D: 权重向量
:return:
'''
x_mat = np.matrix(x) # 转换成矩阵形式
y_mat = np.matrix(y).T
m, n = x.shape
num_steps = 10
best_stump = {}
best_class_est = np.matrix(np.zeros((m, 1)))
min_err = np.inf
for i in range(n): # 遍历所有特征
row_min = x_mat[:, i].min()
row_max = x_mat[:, i].max()
step_size = (row_max - row_min) / num_steps
for j in range(-1, num_steps + 1):
for eq in ['lt', 'gt']:
thresh_val = row_min + j * step_size
pre_val = stump_classify(x, i, thresh_val, eq)
err_arr = np.matrix(np.ones((m, 1)))
err_arr[pre_val == y_mat] = 0
weight_err = D.T * err_arr
print("split: dim %d, thresh %.2f, thresh ineqal: %s, "
"the weighted error is %.3f"
% (i, thresh_val, eq, weight_err))
if weight_err < min_err:
min_err = weight_err
best_class_est = pre_val.copy()
best_stump['dim'] = i
best_stump['thresh'] = thresh_val
best_stump['ineq'] = eq
return best_stump, min_err, best_class_est
分类实现代码:
import numpy as np
from stump import *
def adaboost_trian(x, y, num_it=40):
week_class_arr = []
m = x.shape[0]
D = np.matrix(np.ones((m, 1)) / m) # 初始化权重向量D
agg_class_est = np.matrix(np.zeros((m, 1)))
for i in range(num_it):
best_stump, err, class_est = build_stump(x, y, D)
print('D: ', D.T)
alpha = 0.5 * np.log((1 - err) // max(err, 1e-16)) # 计算alpha值
best_stump['alpha'] = alpha
week_class_arr.append(best_stump)
print('class_est: ', class_est.T)
expon = np.multiply(np.matrix(-1 * alpha * y).T, class_est)
D = np.multiply(D, np.exp(expon))
D = D / np.sum(D)
agg_class_est += np.multiply(alpha, class_est)
print('agg_class_est: ', agg_class_est.T)
agg_err = np.multiply(np.sign(agg_class_est) != np.matrix(y).T, np.ones((m, 1)))
err_rate = np.sum(agg_err) / m
print('total error: ', err_rate, '\n')
if err_rate == 0:
break
return week_class_arr
def adaClassify(test, classifierArr):
dataMatrix = np.matrix(test) # do stuff similar to last aggClassEst in adaBoostTrainDS
m = dataMatrix.shape[0]
aggClassEst = np.matrix(np.zeros((m, 1)))
for i in range(len(classifierArr)):
classEst = stump_classify(dataMatrix, classifierArr[i]['dim'],
classifierArr[i]['thresh'],
classifierArr[i]['ineq']) # call stump classify
aggClassEst += np.multiply(classifierArr[i]['alpha'], classEst)
print(aggClassEst)
return np.sign(aggClassEst)
测试代码:
from adaboost import adaboost_trian,adaClassify
from load_data import read_data
if __name__ =='__main__':
data,label = read_data()
classifier_array = adaboost_trian(data,label,9)
# print(classifier_array)
re = adaClassify([[5,5],[0,0]],classifier_array)
print(re)```