8.1 个体与集成
集成学习就是通过构建并结合多个学习器来完成学习任务。如图所示,集成学习一般先产生一组“个体学习器”,再通过某种方法将它们结合起来。通过判断集成是否只包含同种类型的个体学习器可分为同质集成和异质集成。
同质集成:集成中只包含同种类型的个体学习器。同质集成中的个体学习器称为“基学习器”,相应的算法为“基学习算法”;
异质集成:集成中包含不同类型的个体学习器。
优点:集成学习通过把多个个体学习器结合在一起,通常比单一学习器的泛化性能更有显著优势。
通常情况下,如果把效果好坏不等的个体学习器掺杂在一起,那么得到结果通常会介于最好和最坏的个体学习器之间,那么,集成学习如何才能获得比最好的个体学习器更好的性能呢?结论是个体学习器要“好而不同”,即个体学习器要有一定的准确性,且每个个体学习器之间具有差异。接下来通过一个例子说明该结论,如下表所示,其中√表示分类正确,×表示分类错误,集成学习采用投票法,即“少数服从多数”,那么(a)中,每个分类器的精度为66.6%,但集成学习的精度达到100%;在(b)中,集成学习与每个分类器的精度无差别;在(c)中,每个分类器的精度为33.3%,而集成学习的精度却为0。以上例子恰好说明个体学习器应“好而不同”。
样例1 | 样例2 | 样例3 | 样例1 | 样例2 | 样例3 | 样例1 | 样例2 | 样例3 | |||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
√ | √ | × | √ | √ | × | √ | × | × | |||||
× | √ | √ | √ | √ | × | × | √ | × | |||||
√ | × | √ | √ | √ | × | × | × | √ | |||||
集成 | √ | √ | √ | 集成 | √ | √ | × | 集成 | × | × | × |
事实上,个体学习器的“准确性”和“多样性”本身存在一定的冲突,在确保准确性之后,则不得不牺牲其多样性;而在增加多样性的同时,有不得不牺牲其准确性。进而,如何产生并且结合准确度和多样性的个体学习器,恰好是集成学习的研究核心。
集成学习大致可分为以下两大类:
序列化方法:个体学习器之间存在强依赖关系、必须串行生成的方法,代表方法是Boosting;
并行化方法:个体学习器之间不存在强依赖关系、可同时生成的方法,代表方法是Bagging和“随机森林”。
8.2 Boosting
Boosting是一族可以将弱学习器提升为强学习器的算法。
算法思想:从初始的训练集中训练出一个基学习器,再从基学习器的表现对训练集进行合理的调整,即先前基学习器做错的训练样本进行重点关注,然后用调整后的样本来训练下一个基学习器;重复进行前面步骤,直到基学习器数目达到事先指定的值T,最终对这T个基学习器进行加权结合。
Boosting族算法的代表是AdaBoost,如图8.2所示,其中,
是真是函数。采用基学习器的线性组合
来最小化指数损失函数
若指数损失函数能最小化,则可以对求偏导
令上式等于零,可解得
故有
此时,达到了贝叶斯最优错误解,也就是指数损失函数最小化,那么分类错误率也最小化。

Boosting的特点是主要关注降低偏差,因此Boosting可以将泛化能力相当弱的学习器构建出很强的集成。
8.3 Bagging
Bagging是并行式集成学习方法最著名的代表。给定包含个样本的数据集,我们先随机取出一个样本放入采样集中,再把该样本放回初始数据集,使得下次采样时该样本仍有可能被选中,这样,经过次随机采样操作,我们得到含个样本的采样集,初始训练集中有的样本在采样集里多次出现,有的则从未出现。我们可采样出个含个训练样本的采样集,然后基于每个采样集训练出一个基学习器,再将这些基学习器进行结合,这就是 Bagging 的基本流程。Bagging的算法描述如图8.5所示。
则Bagging泛化误差的包外估计为
8.4 多样性度量
顾名思义,多样性度量就是估算个体学习器的多样化程度,做法就是考虑个体分类器两两是否相似。
给定数据集,
,分类器
和
的预测结果列联表为
其中,a表示和
均预测为正类的样本数目,b、c、d同理可得,
。基于上面的列联表,可以给出常见的多样性度量。
不合度量
相关系数
的值域为
,若
和
无关,则值为0;若
和
正相关则值为正,否则为负。
Q-统计量
与相关系数
符号相同,且
。
统计量
若分类器和
在D上完全一致,则
;若它们仅是偶然达成一致,则
。
8.5 实验
下面是用Python实现AdaBoost算法的示例代码,使用了numpy和sklearn库。我们先手动实现一个简单的AdaBoost算法,然后使用sklearn中的实现进行比较。
import numpy as np
class WeakClassifier:
"""简单的弱分类器"""
def __init__(self):
self.feature_index = None
self.threshold = None
self.polarity = None
def fit(self, X, y, weights):
m, n = X.shape
self.feature_index = 0
self.threshold = 0
self.polarity = 1
min_error = float('inf')
for feature in range(n):
thresholds = np.unique(X[:, feature])
for threshold in thresholds:
for polarity in [-1, 1]:
predictions = np.ones(m)
predictions[X[:, feature] < threshold] = -1
predictions *= polarity
error = sum(weights[y != predictions])
if error < min_error:
min_error = error
self.threshold = threshold
self.feature_index = feature
self.polarity = polarity
def predict(self, X):
m = X.shape[0]
predictions = np.ones(m)
predictions[X[:, self.feature_index] < self.threshold] = -1
return predictions * self.polarity
class AdaBoost:
def __init__(self, n_classifiers=50):
self.n_classifiers = n_classifiers
self.classifiers = []
self.alphas = []
def fit(self, X, y):
m = X.shape[0]
weights = np.ones(m) / m
for _ in range(self.n_classifiers):
classifier = WeakClassifier()
classifier.fit(X, y, weights)
predictions = classifier.predict(X)
# Calculate the error
error = sum(weights[y != predictions])
# Calculate the alpha value (weight of the classifier)
alpha = 0.5 * np.log((1 - error) / (error + 1e-10))
# Update weights
weights *= np.exp(-alpha * y * predictions)
weights /= sum(weights) # Normalize
# Store the classifier and its weight
self.classifiers.append(classifier)
self.alphas.append(alpha)
def predict(self, X):
m = X.shape[0]
final_predictions = np.zeros(m)
for alpha, classifier in zip(self.alphas, self.classifiers):
final_predictions += alpha * classifier.predict(X)
return np.sign(final_predictions)
# 示例使用
if __name__ == "__main__":
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
# 创建数据
X, y = make_classification(n_samples=100, n_features=20, n_informative=10, n_redundant=2, random_state=42)
y = np.where(y == 0, -1, 1) # 将类标签转换为-1和1
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 训练和预测
model = AdaBoost(n_classifiers=50)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
# 输出准确率
print("Accuracy:", accuracy_score(y_test, y_pred))
参考文献:《机器学习》周志华