机器学习12:偏差
1,误差:误差由偏差(bias)、方差(variance)和噪声(noise)组成;
1.1,偏差:
偏差度量了学习算法的期望预测与真实结果的偏离程度,刻画了学习算法本身的拟合能力。也即,偏差(Bias)描述了预测值(估计值)的期望与真实值之间的差距,偏差越大,越偏离真实数据。
偏差可看做是“有监督的”,有人的知识参与的一个指标。
1.2,方差:
方差度量了同样大小的训练集的变动所导致的学习性能的变化,刻画了数据扰动所造成的影响。也即,方差(Variance)描述的是预测值的变化范围,离散程度,也就是离其期望值的距离,方差越大,数据的分布越分散。
方差可看做是“无监督的”,客观的指标。
1.3,噪声:
噪声表达了当前任务上任何学习算法所能达到的期望泛化误差的下界,刻画了学习问题本身的难度。
2,偏差-方差分解:
对学习算法除了通过实验估计其泛化性能,人们往往希望了解它“为什么”具有这样的性能。偏差-方差分解就是解释学习算法泛化能力的一个重要的工具。
偏差-方差分解试图对学习算法的期望泛化错误率进行分解。我们知道,算法在不同训练集上学得的结果很可能不同,即便这些训练集来自于同一个分布。
对测试样本x,令yD为x在训练集中的标记,y为x的真实标记,f(x;D)为训练集D上学得模型f在x上的预测输出。以回归任务为例,学习算法的期望预测为:
为便于讨论,假定噪声期望为零,即E.D[yD– y] = 0. 通过简单的多项式展开合并,可对算法的期望泛化误差进行分解:
由于噪声期望为0,因此倒数第二行的式子(红字交叉项)为0.
考虑到噪声不依赖于f(噪声与模型无关),上述第四行公式(红字交叉项)为0,具体推导如下:
上述推导的倒数第四行运用了连续变量的期望公式。
于是,最终的推导结果是:
偏差一方差分解说明,泛化性能是由学习算法的能力、数据的充分性以及学习任务本身的难度所共同决定的。
给定学习任务,为了取得好的泛化性能,则需使偏差较小(能够充分拟合数据),并且使方差较小(使得数据扰动产生的影响小)。
3,偏差-方差窘境:
一般来说偏差与方差是有冲突的,这称为偏差—方差窘境,给定学习任务,假定我们能控制学习算法的训练程度(例如决策树可控制层数,神经网络可控制训练轮数,集成学习方法可控制基学习器个数),则在训练不足时,学习器的拟合能力不够强,训练数据的扰动不足以便学习器产生显著变化,此时偏差主导了泛化错误率;随着训练程度的加深,学习器的拟合能力逐渐增强,训练数据发生的扰动渐渐能被学习器学到,方差逐渐主导了泛化错误率;在训练程度充足后,学习器的拟合能力已非常强,训练数据发生的轻微扰动都会导致学习器发生显著变化,若训练数据自身的、非全局的特性被学习器学到了,则将发生过拟合.
如上图所示:使用不同复杂度的模型,会影响bias与variance的分布。
灵活的模型(次数比较高的多项式)会有比较低的偏差和比较高的方差,而比较严格的模型(比如一次线性回归)就会得到比较高的偏置和比较低的方差。
为什么KNN(k最近邻k-Nearest Neighbor)算法在增大k时,偏差会变大;但RF(RandomForest随机森林)增大树的数目时偏差却保持不变;GBDT(GradientBoosting)在增大树的数目时偏差却又能变小。
对于KNN算法,k值越大,表示模型的学习能力越弱,因为k越大,它越倾向于从“面”上考虑做出判断,而不是具体地考虑一个样本近身的情况来做出判断,所以,它的偏差会越来越大。对于RF,我们实际上是部分实现了多次训练取均值的效果,每次训练得到的树都是一个很强的学习者,每一个的方差都比较大,但综合起来就会比较小。好比一个很强的学习者学习时,刮着西风,它会据此调整自己的瞄准方法,另一个很强的学习者学习时刮着东风,(西风、东风可以理解为不同训练集中的噪声)它也会据此调整自己的瞄准方法,在测试样本时,一个误差向西,一个误差向东,刚好起到互相抵消的作用,所以方差会比较小。但是由于每棵树的偏差都差不多,所以,我们取平均时,偏差不会怎么变化。为什么说是部分实现了多次训练取均值的效果而不是全部呢?因为我们在训练各棵树时,是通过抽样样本集来实现多次训练的,不同的训练集中不可避免地会有重合的情况,此时,就不能认为是独立的多次训练了,各个训练得到的树之间的方差会产生一定的相关性,训练集中重合的样本越多,则两棵树之间的方差的相关性越强,就越难达成方差互相抵消的效果。对于GBDT,N棵树之间根本就不是一种多次训练取均值的关系,而是N棵树组成了相关关联,层层递进的超级学习者,可想而知,它的方差一定是比较大的。但由于它的学习能力比较强,所以,它的偏差是很小的,而且树的棵树越多,学习能力就越强,偏差就越小。也就是说,只要学习次数够多,预测的均值会无限接近于目标。简单讲就是GBDT的N棵树实际上是一个有机关联的模型,不能认为是N个模型。
4,Bias-Variance Tradeoff 理论意义:
能够让我们更好地认识模型的复杂度, 指导我们对模型的改进方向.
偏差-方差分解实用价值很有限. 偏差和方差并不能够真正的被计算,因为我们不知道数据的真实分布. 偏置-方差分解依赖于对所有的数据集求平均,而在实际应用中我们只有一个观测数据集。
5,bagging减少方差,boosting减少偏差:
1,Bagging:
从偏差-方差分解的角度看,Bagging主要关注降低方差,因此它在不剪枝决策树、神经网络等易受样本扰动的学习器上效果更为明显。
Bagging是BootstrapAggregating 的简称,意思就是再取样 (Bootstrap) 然后在每个样本上训练出来的模型取平均,所以是降低模型的variance. Bagging 比如Random Forest 这种先天并行的算法都有这个效果。
Bagging降低的是第二项,Random forest是同时降低两项。(参考《The Elements of Statistical Learning》p588公式15.1)
2,Boosting:
从偏差-方差分解的角度看,Boosting主要关注降低偏差,因此Boosting能基于泛化性能相当弱的学习器构建出很强的集成。
Boosting 则是迭代算法,每一次迭代都根据上一次迭代的预测结果对样本进行加权,所以随着迭代不断进行,误差会越来越小,所以模型的 bias 会不断降低。这种算法无法并行,例子比如AdaptiveBoosting.
bagging, random forest并行化方法显而意见。Boosting共有的缺点为训练是按顺序的,难以并行,这样在大规模数据上可能导致速度过慢,所幸近年来XGBoost和LightGBM的出现都极大缓解了这个问题。
6,代码实现:Bagging、Boosting
code分为6块:1,加载数据;2,划分训练集,测试集;3,线性回归模型;4,Bagging线性模型;5,AdaBoost算法的线性模型;6,GBDT回归。
代码语言:javascript代码运行次数:0运行复制import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
import pandas as pd
import warnings
import sklearn
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.ensemble import BaggingRegressor
def notEmpty(s):
return s != ''
# 1,加载数据:
names = ['CRIM','ZN', 'INDUS','CHAS','NOX','RM','AGE','DIS','RAD','TAX','PTRATIO','B','LSTAT']
fd = pd.read_csv('datas/boston_housing.data', header=None)
data = np.empty((len(fd), 14))
for i, d in enumerate(fd.values):
d = map(float, filter(notEmpty, d[0].split(' ')))
data[i] = list(d)
x, y = np.split(data, (13,), axis=1)
y = y.ravel()
# 2,划分训练集,测试集:
x_train1, x_test1, y_train1, y_test1 = train_test_split(x, y, train_size=0.8, random_state=14)
x_train, x_test, y_train, y_test = x_train1, x_test1, y_train1, y_test1
# 3,线性回归模型:
lr = LinearRegression()
lr.fit(x_train,y_train)
lr_y_test_hat = lr.predict(x_test)
lr_score = lr.score(x_test, y_test)
print ("lr:", lr_score)
# 4,Bagging线性模型:
bg = BaggingRegressor(LinearRegression(), n_estimators=100, max_samples=0.7, max_features=0.8,random_state=28)
bg.fit(x_train, y_train)
bg_y_test_hat = bg.predict(x_test)
bg_score = bg.score(x_test, y_test)
print ("Bagging:", bg_score)
# 5,AdaBoost算法的线性模型:
from sklearn.ensemble import AdaBoostRegressor
adr = AdaBoostRegressor(LinearRegression(), n_estimators=50, learning_rate=0.0001, random_state=28)
adr.fit(x_train, y_train)
adr_y_test_hat = adr.predict(x_test)
adr_score = adr.score(x_test, y_test)
print ("daBoost:", adr_score)
# 6,GBDT回归:
from sklearn.ensemble import GradientBoostingRegressor
gbdt = GradientBoostingRegressor(n_estimators=50, learning_rate=0.1, random_state=28)
gbdt.fit(x_train, y_train)
gbdt_y_test_hat = gbdt.predict(x_test)
gbdt_score = gbdt.score(x_test, y_test)
print ("GBDT:", gbdt_score)