交叉验证、留一交叉验证、自助法
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了交叉验证、留一交叉验证、自助法相关的知识,希望对你有一定的参考价值。
参考技术A机器学习包括许多算法:线性回归、逻辑回归、决策树、SVM、随机森林、GBDT等等。那么该如何评价某个算法在数据集上的表现呢?这里阐述3个评估方法:交叉验证、留一交叉验证、自助法。
当数据集数量有限时,我们需要将一定数量的数据用于训练,剩下的数据用于测试,也就是旁置法。当然,也许不巧:用于训练(或测试)的数据不具代表性,那么需要将数据按照某种比例进行划分,也就是分层。
一个减少由于旁置法取样而引起的任何偏差的更为通用的方法是,重复整个过程,用不同的随机样本重复进行多次训练和测试,将每次不同迭代过程中所得的误差率进行平均得到一个综合误差率,这就是重复旁置法的误差率估计。
在重复旁置法中,可能在整个过程中,有些数据一直作为测试数据或者训练数据,这样不可避免的由于训练集和测试集数据代表性不一致产生了影响。因此,进行了一个简单的变体方法,引出了交叉验证。
在交叉验证中,需要先固定一个折数,也就是划分。假设折数为3,代表将数据集大致划分为了3等分,然后2/3数据用于训练,1/3数据用于测试,这样此过程循环3次,从而使得每一个实例都可以用来训练和测试。如果同时采用了分层技术,这就是分层3折交叉验证。
设折数为 n ,那么会重复 n 次过程,也会产生 n 个测试结果。将 n 个测试结果作平均产生的结果就是该算法在数据集上的综合误差估计。
那么 n 值如何确定呢?书上说:经过大量的实验表明10折正是获得最好的错误估计的恰当选择,而且也有一些理论根据可以支持这一点。当然,为了得到可靠的误差估计,单次的10折交叉验证可能不够,有一个标准的程序是重复10次交叉验证——即10次10折交叉验证,然后取平均值。( _ 偶的天,变成计算密集型的任务了,对于加快计算,可以采用并发、GPU等方式进行。)
留一交叉验证,名字和交叉验证类似,事实也是这样。留一交叉验证,其折数就是样本的个数,设样本个数为 m ,需要重复 m 次,最后取平均值。这样的验证方法与上面的交叉验证方法比有优点也有缺点。
上面方法在采样时都是一旦从数据集中被取出放入训练集或测试集中,那么它就不能被放回。也就是说,一个实例只能被选择一次。而自助法不同,它是有放回的抽样,那么训练集中数据存在重复。
我们来阐述一个特例:0.632自助法。假设数据集中 n 个数据,那么每一个数据被取到的概率是 1/n ,抽样 n 次,某个实例不被取到的概率是 (1-1/n)^n≈0.368 .也就是说训练集中只有63.2%的数据。因此其训练数据比10折交叉验证(90%的数据)小,为了补偿这点,将测试集误差和用训练集数据计算的再带入误差率组合得到误差估计值。
这方法也存在误差,同样考虑一个完全随机的数据集,含有数量相等的两个类,面对一个随机数据,所能给出的最好的预测方式是预测它属于多数类,其真实误差率为50%。但是对于自助法而言,可能存在一个完美的算法,能够完全预测正确,导致组合后的误差估计会过于乐观。
威腾, Witten L H, Frank E. 数据挖掘: 实用机器学习技术及 Java 实现: 英文版[M]. 机械工业出版社, 2003.
留一法交叉验证 Leave-One-Out Cross Validation
交叉验证法,就是把一个大的数据集分为 k k k 个小数据集,其中 k − 1 k-1 k−1 个作为训练集,剩下的 1 1 1 个作为测试集,在训练和测试的时候依次选择训练集和它对应的测试集。这种方法也被叫做 k k k 折交叉验证法(k-fold cross validation)。最终的结果是这 k 次验证的均值。
此外,还有一种交叉验证方法就是 留一法(Leave-One-Out,简称LOO),顾名思义,就是使 k k k 等于数据集中数据的个数,每次只使用一个作为测试集,剩下的全部作为训练集,这种方法得出的结果与训练整个测试集的期望值最为接近,但是成本过于庞大。
我们用SKlearn库来实现一下LOO:
from sklearn.model_selection import LeaveOneOut
# 一维示例数据
data_dim1 = [1, 2, 3, 4, 5]
# 二维示例数据
data_dim2 = [[1, 1, 1, 1],
[2, 2, 2, 2],
[3, 3, 3, 3],
[4, 4, 4, 4],
[5, 5, 5, 5]]
loo = LeaveOneOut() # 实例化LOO对象
# 取LOO训练、测试集数据索引
for train_idx, test_idx in loo.split(data_dim1):
# train_idx 是指训练数据在总数据集上的索引位置
# test_idx 是指测试数据在总数据集上的索引位置
print("train_index: %s, test_index %s" % (train_idx, test_idx))
# 取LOO训练、测试集数据值
for train_idx, test_idx in loo.split(data_dim1):
# train_idx 是指训练数据在总数据集上的索引位置
# test_idx 是指测试数据在总数据集上的索引位置
train_data = [data_dim1[i] for i in train_idx]
test_data = [data_dim1[i] for i in test_idx]
print("train_data: %s, test_data %s" % (train_data, test_data))
data_dim1的输出:
train_index: [1 2 3 4], test_index [0]
train_index: [0 2 3 4], test_index [1]
train_index: [0 1 3 4], test_index [2]
train_index: [0 1 2 4], test_index [3]
train_index: [0 1 2 3], test_index [4]
train_data: [2, 3, 4, 5], test_data [1]
train_data: [1, 3, 4, 5], test_data [2]
train_data: [1, 2, 4, 5], test_data [3]
train_data: [1, 2, 3, 5], test_data [4]
train_data: [1, 2, 3, 4], test_data [5]
data_dim2的输出:
train_index: [1 2 3 4], test_index [0]
train_index: [0 2 3 4], test_index [1]
train_index: [0 1 3 4], test_index [2]
train_index: [0 1 2 4], test_index [3]
train_index: [0 1 2 3], test_index [4]
train_data: [[2, 2, 2, 2], [3, 3, 3, 3], [4, 4, 4, 4], [5, 5, 5, 5]], test_data [[1, 1, 1, 1]]
train_data: [[1, 1, 1, 1], [3, 3, 3, 3], [4, 4, 4, 4], [5, 5, 5, 5]], test_data [[2, 2, 2, 2]]
train_data: [[1, 1, 1, 1], [2, 2, 2, 2], [4, 4, 4, 4], [5, 5, 5, 5]], test_data [[3, 3, 3, 3]]
train_data: [[1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3], [5, 5, 5, 5]], test_data [[4, 4, 4, 4]]
train_data: [[1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3], [4, 4, 4, 4]], test_data [[5, 5, 5, 5]]
以上是关于交叉验证、留一交叉验证、自助法的主要内容,如果未能解决你的问题,请参考以下文章
留一法交叉验证 Leave-One-Out Cross Validation
留一法交叉验证 Leave-One-Out Cross Validation