AdaBoost算法

算法步骤

假设给定一个二分类的训练数据集AdaBoost - 图1其中,每个样本点由实例与标记组成。实例AdaBoost - 图2,标记AdaBoost - 图3AdaBoost - 图4是实例空间,AdaBoost - 图5是标记集合。AdaBoost利用以下算法,从训练数据中学习一系列弱分类器或基本分类器,并将这些若分类器线性组合成为一个强分类器。

输入:训练数据集AdaBoost - 图6,其中AdaBoost - 图7,标记AdaBoost - 图8,弱学习算法;

输出:最终分类器AdaBoost - 图9

(1)初始化训练数据的权值分布

AdaBoost - 图10

(2)对AdaBoost - 图11

  • (a)使用具有权值分布AdaBoost - 图12的训练数据集学习,得到基本分类器
  • AdaBoost - 图13
  • (b)计算AdaBoost - 图14在训练数据上的分类误差率
  • AdaBoost - 图15
  • (c)计算AdaBoost - 图16的系数
  • AdaBoost - 图17
  • (d)更新训练数据集的权值分布
  • AdaBoost - 图18
  • AdaBoost - 图19
  • AdaBoost - 图20

(3)构建基本分类器的线性组合AdaBoost - 图21,得到最终分类器
AdaBoost - 图22

步骤说明

步骤(1):假设训练数据集具有均匀的权值分布,即每个训练样本在基本分类器的学习中作用相同,这一假设保证第1步能够在原始数据上学习基本分类器AdaBoost - 图23

步骤(2):AdaBoost反复学习基本分类器,在每一轮AdaBoost - 图24顺次地执行下列操作

  • (a)使用当前分布AdaBoost - 图25加权的训练数据集,学习基本分类器AdaBoost - 图26
  • (b)计算基本分类器AdaBoost - 图27在加权训练数据集上的分类误差率:
  • AdaBoost - 图28
  • AdaBoost - 图29表示第AdaBoost - 图30轮中第AdaBoost - 图31个实例的权值,AdaBoost - 图32。这表明,AdaBoost - 图33在加权的训练数据集上的分类误差率是被AdaBoost - 图34误分类样本的权值之和,由此可以看出数据权值分布AdaBoost - 图35与基本分类器AdaBoost - 图36的分类误差率的关系。
  • (c)计算基本分类器AdaBoost - 图37的系数AdaBoost - 图38AdaBoost - 图39表示AdaBoost - 图40在最终分类器中的重要性。由AdaBoost - 图41可知,当AdaBoost - 图42时,AdaBoost - 图43,并且AdaBoost - 图44随着AdaBoost - 图45的减小而增大,所以分类误差率越小的基本分类器在最终分类器中的作用越大。
  • (d)更新训练数据的权值分布为下一轮作准备。
  • AdaBoost - 图46可写为
  • AdaBoost - 图47
  • 由此可知,被基本分类器AdaBoost - 图48误分类样本的权值得以扩大,而被正确分类的样本的权值却得以缩小。两相比较,由AdaBoost - 图49知误分类样本的权值被放大AdaBoost - 图50倍。因此,误分类样本在下一轮学习中起更大的作用。不改变所给的训练数据,而不断改变训练数据权值的分布,使得训练数据在基本分类器的学习中起不同的作用,这就是AdaBoost的一个特点

步骤(3):线性组合AdaBoost - 图51实现AdaBoost - 图52个基本分类器的加权表决。系数AdaBoost - 图53表示了基本分类器AdaBoost - 图54的重要性,这里,所有AdaBoost - 图55之和并不为AdaBoost - 图56AdaBoost - 图57的符号决定实例AdaBoost - 图58的类,AdaBoost - 图59的绝对值表示分类的确信度。利用基本分类的线性组合构建最终分类器是AdaBoost的另一特点。

例子

序号 1 2 3 4 5 6 7 8 9 10
x 0 1 2 3 4 5 6 7 8 9
y 1 1 1 -1 -1 -1 1 1 1 -1

步骤(1):初始化数据权值分布

AdaBoost - 图60

步骤(2):

AdaBoost - 图61

  • (a)在权值分布为AdaBoost - 图62的训练数据上,阈值AdaBoost - 图63AdaBoost - 图64时分类误差率最低,故基本分类器为
  • AdaBoost - 图65
  • (b)AdaBoost - 图66在训练数据集上的误差率AdaBoost - 图67(6,7,8样本错分,权值均为1)
  • (c)计算AdaBoost - 图68的系数AdaBoost - 图69
  • (d)更新训练数据的权值分布
  • AdaBoost - 图70
  • AdaBoost - 图71
  • AdaBoost - 图72
  • AdaBoost - 图73
  • 分类器AdaBoost - 图74在训练数据集上有AdaBoost - 图75个误分类点。

AdaBoost - 图76

  • (a)在权值分布为AdaBoost - 图77的训练数据上,阈值AdaBoost - 图78AdaBoost - 图79时分类误差率最低,故基本分类器为
  • AdaBoost - 图80
  • (b)AdaBoost - 图81在训练数据集上的误差率AdaBoost - 图82(4,5,6样本错分,权值均为0.07143)
  • (c)计算AdaBoost - 图83的系数AdaBoost - 图84
  • (d)更新训练数据的权值分布
  • AdaBoost - 图85
  • AdaBoost - 图86
  • 分类器AdaBoost - 图87在训练数据集上有AdaBoost - 图88个误分类点。

AdaBoost - 图89

  • (a)在权值分布为AdaBoost - 图90的训练数据上,阈值AdaBoost - 图91AdaBoost - 图92时分类误差率最低,故基本分类器为
  • AdaBoost - 图93
  • (b)AdaBoost - 图94在训练数据集上的误差率AdaBoost - 图95(4-9样本错分,权值见AdaBoost - 图96)
  • (c)计算AdaBoost - 图97的系数AdaBoost - 图98
  • (d)更新训练数据的权值分布
  • AdaBoost - 图99
  • AdaBoost - 图100
  • 分类器AdaBoost - 图101在训练数据集上有AdaBoost - 图102个误分类点。

步骤(3):于是最终分类器为

AdaBoost - 图103

AdaBoost训练误差分析

AdaBoost最基本的性质是它能在学习过程中不断减少训练误差,即在训练数据集上的分类学习误差率。关于这个问题有下面的定理:

AdaBoost的训练误差界:AdaBoost算法最终分类器的训练误差界为

AdaBoost - 图104

其中,AdaBoost - 图105AdaBoost - 图106AdaBoost - 图107

证明如下:

(1)当AdaBoost - 图108时,不等式左边每个误分权值为AdaBoost - 图109,不等式右边因为AdaBoost - 图110,所以每个误分权值AdaBoost - 图111,所以不等式AdaBoost - 图112得证

(2)证等式部分AdaBoost - 图113
AdaBoost - 图114

AdaBoost - 图115AdaBoost - 图116代入移项得到AdaBoost - 图117,代入需要证明式子得

AdaBoost - 图118

这一定理说明,可以在每一轮选取适当的AdaBoost - 图119使得AdaBoost - 图120最小,从而使训练误差下降最快。

Code实现

数据

  1. import numpy as np
  2. import pandas as pd
  3. from sklearn.datasets import load_iris
  4. from sklearn.model_selection import train_test_split
  5. import matplotlib.pyplot as plt
  6. %matplotlib inline
  7. # data
  8. def create_data():
  9. iris = load_iris()
  10. df = pd.DataFrame(iris.data, columns=iris.feature_names)
  11. df['label'] = iris.target
  12. df.columns = ['sepal length', 'sepal width', 'petal length', 'petal width', 'label']
  13. data = np.array(df.iloc[:100, [0, 1, -1]])
  14. for i in range(len(data)):
  15. if data[i,-1] == 0:
  16. data[i,-1] = -1
  17. # print(data)
  18. return data[:,:2], data[:,-1]
  19. X, y = create_data()
  20. X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
  21. plt.scatter(X[:50,0],X[:50,1], label='0')
  22. plt.scatter(X[50:,0],X[50:,1], label='1')
  23. plt.legend()

手写实现

  1. class AdaBoost:
  2. def __init__(self, n_estimators=50, learning_rate=1.0):
  3. self.clf_num = n_estimators
  4. self.learning_rate = learning_rate
  5. def init_args(self, datasets, labels):
  6. self.X = datasets
  7. self.Y = labels
  8. self.M, self.N = datasets.shape
  9. # 弱分类器数目和集合
  10. self.clf_sets = []
  11. # 初始化weights
  12. self.weights = [1.0/self.M]*self.M
  13. # G(x)系数 alpha
  14. self.alpha = []
  15. def _G(self, features, labels, weights):
  16. m = len(features)
  17. error = 100000.0 # 无穷大
  18. best_v = 0.0
  19. # 单维features
  20. features_min = min(features)
  21. features_max = max(features)
  22. n_step = (features_max - features_min + self.learning_rate) // self.learning_rate
  23. # print('n_step:{}'.format(n_step))
  24. direct, compare_array = None, None
  25. for i in range(1, int(n_step)):
  26. v = features_min + self.learning_rate * i
  27. if v not in features:
  28. # 误分类计算
  29. compare_array_positive = np.array([1 if features[k] > v else -1 for k in range(m)])
  30. weight_error_positive = sum([weights[k] for k in range(m) if compare_array_positive[k] != labels[k]])
  31. compare_array_nagetive = np.array([-1 if features[k] > v else 1 for k in range(m)])
  32. weight_error_nagetive = sum([weights[k] for k in range(m) if compare_array_nagetive[k] != labels[k]])
  33. if weight_error_positive < weight_error_nagetive:
  34. weight_error = weight_error_positive
  35. _compare_array = compare_array_positive
  36. direct = 'positive'
  37. else:
  38. weight_error = weight_error_nagetive
  39. _compare_array = compare_array_nagetive
  40. direct = 'nagetive'
  41. # print('v:{} error:{}'.format(v, weight_error))
  42. if weight_error < error:
  43. error = weight_error
  44. compare_array = _compare_array
  45. best_v = v
  46. return best_v, direct, error, compare_array
  47. # 计算alpha
  48. def _alpha(self, error):
  49. return 0.5 * np.log((1-error)/error)
  50. # 规范化因子
  51. def _Z(self, weights, a, clf):
  52. return sum([weights[i]*np.exp(-1*a*self.Y[i]*clf[i]) for i in range(self.M)])
  53. # 权值更新
  54. def _w(self, a, clf, Z):
  55. for i in range(self.M):
  56. self.weights[i] = self.weights[i]*np.exp(-1*a*self.Y[i]*clf[i])/ Z
  57. # G(x)的线性组合
  58. def _f(self, alpha, clf_sets):
  59. pass
  60. def G(self, x, v, direct):
  61. if direct == 'positive':
  62. return 1 if x > v else -1
  63. else:
  64. return -1 if x > v else 1
  65. def fit(self, X, y):
  66. self.init_args(X, y)
  67. for epoch in range(self.clf_num):
  68. best_clf_error, best_v, clf_result = 100000, None, None
  69. # 根据特征维度, 选择误差最小的
  70. for j in range(self.N):
  71. features = self.X[:, j]
  72. # 分类阈值,分类误差,分类结果
  73. v, direct, error, compare_array = self._G(features, self.Y, self.weights)
  74. if error < best_clf_error:
  75. best_clf_error = error
  76. best_v = v
  77. final_direct = direct
  78. clf_result = compare_array
  79. axis = j
  80. # print('epoch:{}/{} feature:{} error:{} v:{}'.format(epoch, self.clf_num, j, error, best_v))
  81. if best_clf_error == 0:
  82. break
  83. # 计算G(x)系数a
  84. a = self._alpha(best_clf_error)
  85. self.alpha.append(a)
  86. # 记录分类器
  87. self.clf_sets.append((axis, best_v, final_direct))
  88. # 规范化因子
  89. Z = self._Z(self.weights, a, clf_result)
  90. # 权值更新
  91. self._w(a, clf_result, Z)
  92. # print('classifier:{}/{} error:{:.3f} v:{} direct:{} a:{:.5f}'.format(epoch+1, self.clf_num, error, best_v, final_direct, a))
  93. # print('weight:{}'.format(self.weights))
  94. # print('\n')
  95. def predict(self, feature):
  96. result = 0.0
  97. for i in range(len(self.clf_sets)):
  98. axis, clf_v, direct = self.clf_sets[i]
  99. f_input = feature[axis]
  100. result += self.alpha[i] * self.G(f_input, clf_v, direct)
  101. # sign
  102. return 1 if result > 0 else -1
  103. def score(self, X_test, y_test):
  104. right_count = 0
  105. for i in range(len(X_test)):
  106. feature = X_test[i]
  107. if self.predict(feature) == y_test[i]:
  108. right_count += 1
  109. return right_count / len(X_test)
  110. X = np.arange(10).reshape(10, 1)
  111. y = np.array([1, 1, 1, -1, -1, -1, 1, 1, 1, -1])
  112. clf = AdaBoost(n_estimators=3, learning_rate=0.5)
  113. clf.fit(X, y)
  114. X, y = create_data()
  115. X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33)
  116. clf = AdaBoost(n_estimators=10, learning_rate=0.2)
  117. clf.fit(X_train, y_train)
  118. clf.score(X_test, y_test)
  119. # 100次结果
  120. result = []
  121. for i in range(1, 101):
  122. X, y = create_data()
  123. X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33)
  124. clf = AdaBoost(n_estimators=100, learning_rate=0.2)
  125. clf.fit(X_train, y_train)
  126. r = clf.score(X_test, y_test)
  127. # print('{}/100 score:{}'.format(i, r))
  128. result.append(r)
  129. print('average score:{:.3f}%'.format(sum(result)))

sklearn实现

https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.AdaBoostClassifier.html

  1. from sklearn.ensemble import AdaBoostClassifier
  2. clf = AdaBoostClassifier(n_estimators=100, learning_rate=0.5)
  3. clf.fit(X_train, y_train)
  4. clf.score(X_test, y_test)