基础概念
在机器学习的有监督学习算法中,我们的目标是学习出一个稳定的且在各个方面表现都较好的模型,但实际情况往往不这么理想,有时我们只能得到多个有偏好的模型(弱监督模型,在某些方面表现的比较好)。集成学习就是组合这里的多个弱监督模型以期得到一个更好更全面的强监督模型,集成学习潜在的思想是即便某一个弱分类器得到了错误的预测,其他的弱分类器也可以将错误纠正回来。
集成方法是将几种机器学习技术组合成一个预测模型的元算法,以达到减小方差(bagging)、偏差(boosting)或改进预测(stacking)的效果。
集成学习在各个规模的数据集上都有很好的策略。
数据集大:划分成多个小数据集,学习多个模型进行组合
数据集小:利用Bootstrap方法进行抽样,得到多个数据集,分别训练多个模型再进行组合
集合方法可分为两类:
一:序列集成方法,其中参与训练的基础学习器按照顺序生成(例如 AdaBoost)。序列方法的原理是利用基础学习器之间的依赖关系。通过对之前训练中错误标记的样本赋值较高的权重,可以提高整体的预测效果。
二:并行集成方法,其中参与训练的基础学习器并行生成(例如 Random Forest)。并行方法的原理是利用基础学习器之间的独立性,通过平均可以显著降低错误。
集成学习法的特点如下:
① 将多个分类方法聚集在一起,以提高分类的准确率。(这些算法可以是不同的算法,也可以是相同的算法。)
② 集成学习法由训练数据构建一组基分类器,然后通过对每个基分类器的预测进行投票来进行分类
③ 严格来说,集成学习并不算是一种分类器,而是一种分类器结合的方法。
④ 通常一个集成分类器的分类性能会好于单个分类器
⑤ 如果把单个分类器比作一个决策者的话,集成学习的方法就相当于多个决策者共同进行一项决策。
bagging
bagging 是一种个体学习器之间不存在强依赖关系、可同时生成的并行式集成学习方法。
bagging 基于自助采样法(bootstrap sampling),也叫有放回重采样法.即给定包含m个样本的数据集,先随机从样本中取出一个样本放入采样集中,再把该样本返回初始数据集,使得下次采样时该样本仍可以被选中,这样,经过m次随机采样操作,就可以得到包含m个样本的采样集,初始数据集中有的样本多次出现,有的则未出现,其中,初始训练集中约有63.2%的样本出现在采样集中。
照上面的方式进行T次操作,采样出T个含有m个训练集的采样集,然后基于每个采样集训练出T个基学习器,再将这些基学习器进行结合,即可得到集成学习器。在对输出进行预测时,Bagging通常对分类进行简单投票法,对回归使用简单平均法。若出现形同,则任选其一。
Bagging的优点如下:
1、训练一个 Bagging集成与直接使用基分类器算法训练一个学习器的复杂度同阶,说明Bagging是一个高效的集成学习算法。
2、此外,与标准的AdaBoost算法只适用于二分类问题不同,Bagging能不经过修改用于多分类、回归等任务。
3、由于每个基学习器只使用63.2%的数据,所以剩下36.8%的数据可以用来做验证集来对泛化性能进行“包外估计”。
从偏差-方差的角度来说,boosting主要关注减小偏差,而Bagging主要关注降低方差,也就说明boosting在弱学习器上表现更好,而降低方差可以减小过拟合的风险,所以Bagging通常在强分类和复杂模型上表现得很好。举个例子:bagging在不减枝决策树、神经网络等易受样本扰动的学习器上效果更为明显。
随机森林
随机森林是bagging的扩展体。RF在以决策树为基学习器构建Bagging集成的基础上,进一步在决策树的训练过程中引入了随机属性选择。具体地,传统决策树在选择划分属性时是在当前结点的属性集合(假定有d个属性)中选择一个最优属性,而在RF上,对基决策树的每个结点,先从该结点的属性集中随机选择其中的k个属性组成属性集,然后从该属性集中选择最优的划分属性,一般情况下,推荐k=log_2\ d。
随机森林的优势:
1、能够处理很高维度的数据,并且不用做特征选择;
2、在训练完成后,可以给出哪些属性比较重要;
3、容易做成并行化方法,速度快;
4、可以进行可视化展示,便于分析。
随机森林和Bagging比较:
两者的收敛性相似,但RF的起始性能相对较差,特别只有一个基学习器时。随着基学习器数量的增加,随机森林通常会收敛到更低的泛化误差。随机森林的训练效率常优于Bagging,因为Bagging是“确定型”决策树,而随机森林使用“随机型”决策树。
平均集成结合策略
加权平均法的权重一般从训练数据中学习而得,对规模比较大额集成来说,要学习的权重比较多,较容易导致过拟合,因此加权平均法不一定优于简单平均法。一般而言,在个体学习器性能相差较大时宜使用加权平均法,而在个体学习器性能相近时宜使用简单平均法。
投票集成结合策略
Boosting
其主要思想是将弱分类器组装成一个强分类器。在PAC(probably approximately correct,概率近似正确)学习框架下,则一定可以将弱分类器组装成一个强分类器。
通过提高那些在前一轮被弱分类器分错样例的权值,减小前一轮分对样例的权值,来使得分类器对误分的数据有较好的效果。
通过加法模型将弱分类器进行线性组合:
AdaBoost算法
刚开始训练时对每一个训练例赋相等的权重,然后用该算法对训练集训练t轮,每次训练后,对训练失败的训练例赋以较大的权重,也就是让学习算法在每次学习以后更注意学错的样本,从而得到多个预测函数。通过拟合残差的方式逐步减小残差,将每一步生成的模型叠加得到最终模型。
GBDT
每一次的计算是为了减少上一次的残差,GBDT在残差减少(负梯度)的方向上建立一个新的模型。
GBDT的训练过程如下:
gbdt通过多轮迭代,每轮迭代产生一个弱分类器,每个分类器在上一轮分类器的残差基础上进行训练。对弱分类器的要求一般是足够简单,并且是低方差和高偏差的。因为训练的过程是通过降低偏差来不断提高最终分类器的精度,。
弱分类器一般会选择为CART TREE(也就是分类回归树)。由于上述高偏差和简单的要求 每个分类回归树的深度不会很深。最终的总分类器 是将每轮训练得到的弱分类器加权求和得到的(也就是加法模型)。
模型最终可以描述为:
Fm(x)=∑m=1MT(x;θm)Fm(x)=∑m=1MT(x;θm)
随机森林实现
模型实现:
import numpy as np
import pandas as pd
from ml.decision_tree.basic_tree import MyDecisionTree
class MyRF(object):
def __init__(self, max_depth=3, criterion="entropy", n_estimator=5):
"""
随机森林的实现
:param max_depth: 每棵决策树的最大深度
:param criterion: 筛选特征用的策略:
'mse': 均方误差计算,用来实现回归树
'entropy': 用信息增益来筛选特征
'gini':用gini系数来筛选特征
:param n_estimator: 评估器个数,这儿也就是决策树的个数
"""
self.max_depth = max_depth
self.n_estimator = n_estimator
self.criterion = criterion
self.trees = []
def _sample(self, data_set, columns):
"""
随机森林的随机性在这儿体现,这儿仅仅是对样本做了随机性,也可以对特征进行随机抽取
:param data_set: 输入数据:待采样的数据
:param columns: 特征名称
:return: 采样后的结果
"""
n = len(data_set)
indexs = np.random.choice(n, n)
return data_set[indexs], columns
def fit(self, data_set, columns):
"""
训练模型
:param data_set: 输入的训练数据
:param columns: 特征名称
:return:
"""
# 随机森林的'森林'就在这儿体现,也就是由多棵决策树构成
for _ in range(self.n_estimator):
data_sample, columns_sample = self._sample(data_set, columns)
tree = MyDecisionTree(max_depth=self.max_depth, criterion=self.criterion)
tree.fit(data_sample, columns_sample)
self.trees.append(tree)
def predict(self, data_set, columns):
"""
通过投票机制选择出最优的结果
:param data_set: 输入数据
:param columns: 特征名称
:return: 结果列表
"""
# 对所有样本预测
result_list = []
# 随机森林中的每棵决策树都输出结果
for tree in self.trees:
result_list.append(tree.predict(data_set, columns))
# 下面的逻辑就是投票选择出最优的结果
# 如果是'mse'作为特征选择,即是回归树,输出结果为所有树结果的均值
final_result = []
result_list = np.array(result_list)
for i in range(len(result_list[0])):
single = result_list[:, i]
if self.criterion == 'mse':
final_result.append(np.mean(single))
else:
result_dict = {}
for s in single:
if s not in result_dict.keys():
result_dict[s] = 1
else:
result_dict[s] += 1
max_ = 0
r = None
for key, value in result_dict.items():
if value > max_:
r = key
final_result.append(r)
return final_result
模型验证:
def run_my_model():
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
iris_data = load_iris()
my = MyRF()
df = pd.DataFrame(iris_data['data'], columns=['f1', 'f2', 'f3', 'f4'])
df['label'] = iris_data['target']
train_data, test_data = train_test_split(df)
print('train:')
my.fit(train_data.values, train_data.columns)
print(my.trees)
test_result = my.predict(test_data.values, test_data.columns)
test = [int(y) for y in test_data['label'].values]
pred = [int(y) for y in test_result]
print('result:')
print('test: ', test)
print('pred: ', pred)
结果: