交叉验证的缺陷及改进(Cross Validation done wrong)

本文主要是对我们使用交叉验证可能出现的一个问题进行讨论,并提出修正方案。

本文地址:http://blog.csdn.net/shanglianlm/article/details/47207173

交叉验证(Cross validation)在统计学习中是用来估计你设计的算法精确度的一个极其重要的工具。本文主要展示我们在使用交叉验证时可能出现的一个问题,并提出修正的方法。

下面主要使用 Python scikit-learn 框架做演示。

先验理论(Theory first)

交叉验证将数据集随机地划分成 k 类,每类作为一个测试集,用剩余 k-1 个类作为训练集。交替作为测试集计算 k 次,求每次的均方误差 (MSE:mean squared error)。最后有

估计获得值,我们称之为 真是的 MSE,我们的目标是尽可能地使这个估计精确。

动手(Hands on)

全部代码见 IPython notebook on github

产生数据集(Dataset generation)

# Import pandas
import pandas as pd
from pandas import *

# Import scikit-learn
from sklearn.linear_model import LogisticRegression
from sklearn.cross_validation import *
from sklearn.metrics import *
import random

我们产生 100 个数据,每个数据 10,000 个特征(我们需要产生一些输入和输出间纯粹偶然的联系)。

features = np.random.randint(0,10,size=[100,10000])
target = np.random.randint(0,2,size=100)

df = DataFrame(features)
df[‘target‘] = target
df.head()

特征选择(Feature selection)

使用 corr() 函数,我们选出与目标最相关的两个特征。

corr = df.corr()[‘target‘][df.corr()[‘target‘] < 1].abs()
corr.sort(ascending=False)
corr.head()

# 3122 0.392430
# 830 0.367405
# 8374 0.351462
# 9801 0.341806
# 5585 0.336950
# Name: target, dtype: float64

开始训练(Start the training)

根据上面从 10,000 选出的两个特征 3122(0.36) 和 830(0.39) 训练一个简单的 LogisticRegression 。接着我们使用 cross_val_score 来计算它的 MSE,有 MSE = 0.33 。

features = corr.index[[0,1]].values
training_input = df[features].values
training_output = df[‘target‘]
logreg = LogisticRegression()

# scikit learn return the negative value for MSE
# http://stackoverflow.com/questions/21443865/scikit-learn-cross-validation-negative-values-with-mean-squared-error
mse_estimate = -1 * cross_val_score(logreg, training_input, training_output, cv=10, scoring=‘mean_squared_error‘)

mse_estimate
# array([ 0.45454545, 0.27272727, 0.27272727, 0.5 , 0.4 ,
# 0.2 , 0.2 , 0.44444444, 0.33333333, 0.22222222])

DataFrame(mse_estimate).mean()
# 0 0.33
# dtype: float64

知识泄露(Knowledge leaking)

如上,我们发现输出的结果和预测的不同,为什么?

当我们使用特征选择时,我们同时使用了来自训练集和测试集的信息。但在我们的 LogisticRegression 阶段,测试集的信息是不可见的。事实上,当我们在每次迭代计算 MSE^ 的时候,我们应该只使用来自训练集的信息。这里我们使用知识泄露(Knowledge leaking) 来描述这种情况。

下图是一个简单的描述:

证明我们的做法有错(Proof that our model is biased)

接着我们来验证我们的做法是错误的,

* 从所有数据中取出一部分数据(take_out_set)。

* 用剩余的数据来训练 LogisticRegression 。

* 在 take_out_set 上计算 MSE 。

take_out_set = df.ix[random.sample(df.index, 30)]
training_set = df[~(df.isin(take_out_set)).all(axis=1)]

corr = training_set.corr()[‘target‘][df.corr()[‘target‘] < 1].abs()
corr.sort(ascending=False)
features = corr.index[[0,1]].values

training_input = training_set[features].values
training_output = training_set[‘target‘]

logreg = LogisticRegression()
logreg.fit(training_input, training_output)

# LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True,
# intercept_scaling=1, max_iter=100, multi_class=‘ovr‘,
# penalty=‘l2‘, random_state=None, solver=‘liblinear‘, tol=0.0001,
# verbose=0)

y_take_out = logreg.predict(take_out_set[features])
mean_squared_error(take_out_set.target, y_take_out)
# 0.53333333333333333

很明显,我们在 take_out_set 获得的 MSE(0.53) 与我们使用 CV 获得的 MSE^(0.33) 不一样。

正确的做法(Cross validation done right)

先前的部分,我们可以看到我们出错的主要原因是把测试集的信息也添加到特征选择阶段,因此我们修改我们的模型,使得特征选择阶段只使用来自训练集的信息而不是全部数据的信息。

下面的图展示了修改后的序列图:

kf = KFold(df.shape[0], n_folds=10)
mse = []
fold_count = 0
for train, test in kf:
  print("Processing fold %s" % fold_count)
  train_fold = df.ix[train]
  test_fold = df.ix[test]

  # find best features
  corr = train_fold.corr()[‘target‘][train_fold.corr()[‘target‘] < 1].abs()
  corr.sort(ascending=False)
  features = corr.index[[0,1]].values

  # Get training examples
  train_fold_input = train_fold[features].values
  train_fold_output = train_fold[‘target‘]

  # Fit logistic regression
  logreg = LogisticRegression()
  logreg.fit(train_fold_input, train_fold_output)

  # Check MSE on test set
  pred = logreg.predict(test_fold[features])
  mse.append(mean_squared_error(test_fold.target, pred))

  # Done with the fold
  fold_count += 1

print(DataFrame(mse).mean())

# Processing fold 0
# Processing fold 1
# Processing fold 2
# Processing fold 3
# Processing fold 4
# Processing fold 5
# Processing fold 6
# Processing fold 7
# Processing fold 8
# Processing fold 9

DataFrame(mse).mean()
# 0 0.53
# dtype: float64

结论(Conclusion)

If you want to make sure you don’t leak info across the train and test set scikit learn gives you additional extra tools like the feature selection pipeline4 and the classes inside the feature selection module5.

如果我们想确保我们没有泄露来自训练和测试集的信息到我们的模型中。我们可以使用 feature selection pipelinefeature selection module

参考及延伸材料:

[1] http://www.alfredo.motta.name/cross-validation-done-wrong/

[2] Scikit-learn framework

[3] [R. Kohavi. A study of cross-validation and bootstrap for accuracy estimation and model selection] (http://dl.acm.org/citation.cfm?id=1643047)

[4] [Y. Bengio and Y. Grandvalet. No unbiased estimator of the variance of k-fold cross-validation] (http://dl.acm.org/citation.cfm?id=1044695)

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-11-06 03:17:22

交叉验证的缺陷及改进(Cross Validation done wrong)的相关文章

总结:Bias(偏差),Error(误差),Variance(方差)及CV(交叉验证)

犀利的开头 在机器学习中,我们用训练数据集去训练(学习)一个model(模型),通常的做法是定义一个Loss function(误差函数),通过将这个Loss(或者叫error)的最小化过程,来提高模型的性能(performance).然而我们学习一个模型的目的是为了解决实际的问题(或者说是训练数据集这个领域(field)中的一般化问题),单纯地将训练数据集的loss最小化,并不能保证在解决更一般的问题时模型仍然是最优,甚至不能保证模型是可用的.这个训练数据集的loss与一般化的数据集的loss

机器学习基础:(Python)训练集测试集分割与交叉验证

在上一篇关于Python中的线性回归的文章之后,我想再写一篇关于训练测试分割和交叉验证的文章.在数据科学和数据分析领域中,这两个概念经常被用作防止或最小化过度拟合的工具.我会解释当使用统计模型时,通常将模型拟合在训练集上,以便对未被训练的数据进行预测. 在统计学和机器学习领域中,我们通常把数据分成两个子集:训练数据和测试数据,并且把模型拟合到训练数据上,以便对测试数据进行预测.当做到这一点时,可能会发生两种情况:模型的过度拟合或欠拟合.我们不希望出现这两种情况,因为这会影响模型的可预测性.我们有

交叉验证(Cross Validation)原理小结

交叉验证是在机器学习建立模型和验证模型参数时常用的办法.交叉验证,顾名思义,就是重复的使用数据,把得到的样本数据进行切分,组合为不同的训练集和测试集,用训练集来训练模型,用测试集来评估模型预测的好坏.在此基础上可以得到多组不同的训练集和测试集,某次训练集中的某样本在下次可能成为测试集中的样本,即所谓"交叉". 那么什么时候才需要交叉验证呢?交叉验证用在数据不是很充足的时候.比如在我日常项目里面,对于普通适中问题,如果数据样本量小于一万条,我们就会采用交叉验证来训练优化选择模型.如果样本

R: Kriging interpolation and cross validation 克里金插值及交叉验证浅析

克里金插值的基本介绍可以参考ARCGIS的帮助文档[1]. 其本质就是根据已知点的数值,确定其周围点(预测点)的数值.最直观的方法就是找到已知点和预测点数值之间的关系,从而预测出预测点的数值.比如IDW插值方法,就是假设已知点和预测点的值跟它们相对距离成反比.克里金插值的精妙之处在于它不仅考虑了已知点和预测点的距离关系,还考虑了这些已知点之间的自相关关系. 如何衡量已知点之间的自相关关系呢?通常使用的就是半变异函数,其公式如下[1]: Semivariogram(distance h) = 0.

cross validation交叉验证

交叉验证是一种检测model是否overfit的方法.最常用的cross validation是k-fold cross validation. 具体的方法是: 1.将数据平均分成k份,0,1,2,,,k-1 2.使用1~k-1份数据训练模型,然后使用第0份数据进行验证. 3.然后将第1份数据作为验证数据.进行k个循环.就完成了k-fold cross validation 这个交叉验证的方法的特点是:所有的数据都参与了验证,也都参与了训练,没有浪费数据.

交叉验证(Cross Validation)

假设我们需要从某些候选模型中选择最适合某个学习问题的模型,我们该如何选择?以多元回归模型为例:,应该如何确定k的大小,使得该模型对解决相应的分类问题最为有效?如何在偏倚(bias)和方差(variance)之间寻求最佳的平衡点?更进一步,我们同样需要知道如何在加权回归模型中选择适当的波长参数,或者在基于范式的SVM模型中选择适当的参数C? 我们假设模型集合为有限集,我们的目的就是从这d个模型中,选择最有效的模型. 假设样本集为S,根据经验风险最小化原则(ERM),可能会使用这样的算法: 1.在S

交叉验证 Cross-validation

对交叉验证这个问题,一直以来,不明白是怎么回事.近期看材料,涉及到了这个问题,写的通俗易懂,有种恍然大悟的感觉.下面,我写下对这个问题的理解. 现在假设这里有一堆数据,作为统计er的任务就是从这些数据中提取有用的信息.如何提取信息呢,我们的法宝就是–模型.模型在统计当中是极其重要的,学统计就是跟各种各样的模型混个脸熟.在模型的基础上,我们利用数据对模型的参数进行估计,从而通过参数化后的模型来描述数据的内在关系,了解数据内在的关系(pattern)非常必要,有助于对未来进行预测. 那么对于手里的数

机器学习:验证数据集与交叉验证

# 问题:如果将所有的数据集都作为训练数据集,则对于训练出的模型是否发生了过拟合会不自知,因为过拟合情况下,模型在训练数据集上的误差非常的小,使人觉得模型效果很好,但实际上可能泛化能力不足: # 方案:将数据集分割为训练数据集和测试数据集,通过测试数据集判断模型的好坏--如果通过学习曲线发现,模型在训练数据集上效果较好,在测试数据集上效果不好,模型出现过拟合,需要调整参数来重新得到模型,然后再次进行测试:以此类推循环此过程,最终得到最佳模型. # 最佳模型:也就是在测试数据集上表现的比较好的模型

用交叉验证改善模型的预测表现

预测模型为何无法保持稳定? 让我们通过以下几幅图来理解这个问题: 此处我们试图找到尺寸(size)和价格(price)的关系.三个模型各自做了如下工作: 第一个模型使用了线性等式.对于训练用的数据点,此模型有很大误差.这样的模型在初期排行榜和最终排行榜都会表现不好.这是"拟合不足"("Under fitting")的一个例子.此模型不足以发掘数据背后的趋势. 第二个模型发现了价格和尺寸的正确关系,此模型误差低/概括程度高. 第三个模型对于训练数据几乎是零误差.这是因