交叉验证方法汇总【附代码】(留一法、K折交叉验证、分层交叉验证、对抗验证、时间序列交叉验证)_交叉验证代码-程序员宅基地

技术标签: python  机器学习  机器学习算法  Python  

交叉验证是什么?

在模型建立中,通常有两个数据集:训练集(train)和测试集(test)。训练集用来训练模型;测试集是完全不参与训练的数据,仅仅用来观测测试效果的数据。

一般情况下,训练的结果对于训练集的拟合程度通常还是挺好的,但是在测试集总的表现却可能不行。比如下面的例子:
在这里插入图片描述

  • 图一的模型是一条线型方程。 可以看到,所有的红点都不在蓝线上,所以导致了错误率很高,这是典型的不拟合的情况
  • 图二 的蓝线则更加贴近实际的红点,虽然没有完全重合,但是可以看出模型表示的关系是正确的。
  • 图三,所有点都在蓝线上,这时候模型计算出的错误率很低,(甚至将噪音都考虑进去了)。这个模型只在训练集中表现很好,在测试集中的表现就不行。 这是典型的‘过拟合’情况。

所以,训练的模型需要找出数据之间‘真正’的关系,避免‘过拟合’的情况发生。

交叉验证:就是在训练集中选一部分样本用于测试模型。
保留一部分的训练集数据作为验证集/评估集,对训练集生成的参数进行测试,相对客观的判断这些参数对训练集之外的数据的符合程度。

留一验证(LOOCV,Leave one out cross validation )

只从可用的数据集中保留一个数据点,并根据其余数据训练模型。此过程对每个数据点进行迭代,比如有n个数据点,就要重复交叉验证n次。例如下图,一共10个数据,就交叉验证十次
在这里插入图片描述

优点:

  • 适合小样本数据集
  • 利用所有的数据点,因此偏差将很低

缺点:

  • 重复交叉验证过程n次导致更高的执行时间
  • 测试模型有效性的变化大。因为针对一个数据点进行测试,模型的估计值受到数据点的很大影响。如果数据点被证明是一个离群值,它可能导致更大的变化

LOOCC是保留一个数据点,同样的你也可以保留P个数据点作为验证集,这种方法叫LPOCV(Leave P Out Cross Validation)

LOOCC代码

R

score = list()

LOOCV_function = function(x,label){
    
 for(i in 1:nrow(x)){
    
 training = x[-i,]
 model = #... train model on training
 validation = x[i,]
 pred = predict(model, validation[,setdiff(names(validation),label)])
 score[[i]] = rmse(pred, validation[[label]]) # score/error of ith fold
 }
 return(unlist(score)) # returns a vector
 }

验证集方法

交叉验证的步骤如下:

  1. 保留一个样本数据集, (取出训练集中20%的样本不用)
  2. 使用数据集的剩余部分训练模型 (使用另外的80%样本训练模型)
  3. 使用验证集的保留样本。(完成模型后,在20%的样本中测试)
  4. 如果模型在验证数据上提供了一个肯定的结果,那么继续使用当前的模型。

优点: 简单方便。直接将训练集按比例拆分成训练集和验证集,比如50:50。

缺点: 没有充分利用数据, 结果具有偶然性。如果按50:50分,会损失掉另外50%的数据信息,因为我们没有利用着50%的数据来训练模型。

验证集方法代码

Python 使用train_test_split

from sklearn.model_selection import train_test_split
train, validation = train_test_split(data, test_size=0.50, random_state = 5)

train_test_split,划分成train 和test 两部分,其中train用来训练模型,test用来评估模型,模型通过fit方法从train数据集中学习,调用score方法在test集上进行评估。

from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression

#已经导入数据
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=20, shuffle=True)

# Logistic Regression
model = LogisticRegression()
model.fit(X_train, y_train)
prediction = model.predict(X_test)
print('The accuracy of the Logistic Regression is: {0}'.format(metrics.accuracy_score(prediction,y_test)))

R

set.seed(101) # Set Seed so that same sample can be reproduced in future also

# Now Selecting 50% of data as sample from total 'n' rows of the data
sample <- sample.int(n = nrow(data), size = floor(.50*nrow(data)), replace = F)
train <- data[sample, ]
test  <- data[-sample, ]

K折交叉验证(k-fold cross validation)

针对上面通过train_test_split划分,从而进行模型评估方式存在的弊端,提出Cross Validation 交叉验证。

Cross Validation:简言之,就是进行多次train_test_split划分;每次划分时,在不同的数据集上进行训练、测试评估,从而得出一个评价结果;如果是5折交叉验证,意思就是在原始数据集上,进行5次划分,每次划分进行一次训练、评估,最后得到5次划分后的评估结果,一般在这几次评估结果上取平均得到最后的 评分。k-fold cross-validation ,其中,k一般取5或10。
在这里插入图片描述

  1. 训练模型需要在大量的数据集基础上,否则就不能够识别数据中的趋势,导致错误产生
  2. 同样需要适量的验证数据点。 验证集太小容易导致误差
  3. 多次训练和验证模型。需要改变训练集和验证集的划分,有助于验证模型。
    步骤:
  4. 随机将整个数据集分成k折;
  5. 如图中所示,依次取每一折的数据集作验证集,剩余部分作为训练集
  6. 算出每一折测试的错误率
  7. 取这里K次的记录平均值 作为最终结果

优点:

  • 适合大样本的数据集
  • 经过多次划分,大大降低了结果的偶然性,从而提高了模型的准确性。
  • 对数据的使用效率更高。train_test_split,默认训练集、测试集比例为3:1。如果是5折交叉验证,训练集比测试集为4:1;10折交叉验证训练集比测试集为9:1。数据量越大,模型准确率越高。

缺点:

  • 对数据随机均等划分,不适合包含不同类别的数据集。比如:数据集有5类数据(ABCDE各占20%),抽取出来的也正好是按照类别划分的5类,第一折全是A,第二折全是B……这样就会导致,模型学习到测试集中数据的特点,用BCDE训练的模型去测试A类数据、ACDE的模型测试B类数据,这样准确率就会很低。

如何确定K值?

  • 一般情况下3、5是默认选项,常建议用K=10。
  • k值越低,就越有偏差;K值越高偏差就越小,但是会受到很大的变化。
  • k值越小,就越类似于验证集方法;而k值越大,则越接近LOOCV方法。

k-fold代码

Python 使用cross_val_score或者KFold

cross_val_score直接将整个交叉验证过程连接起来。

from sklearn.model_selection import cross_val_score
model  = LogisticRegression()
scores = cross_val_score(model,X, y,cv=3) #cv:默认是3折交叉验证,可以修改cv=5,变成5折交叉验证。
print("Cross validation scores:{}".format(scores))
print("Mean cross validation score:{:2f}".format(scores.mean()))

KFold 可以显示具体的划分情况。

from sklearn.model_selection import KFold 

kf = KFold(n_splits=5, random_state=None) # 5折

#显示具体划分情况
for train_index, test_index in kf.split(X):
      print("Train:", train_index, "Validation:",test_index)
      X_train, X_test = X[train_index], X[test_index] 
      y_train, y_test = y[train_index], y[test_index] 

i = 1
for train_index, test_index in kf.split(X, y):
    print('\n{} of kfold {}'.format(i,kf.n_splits))
    X_train, X_test = X[train_index], X[test_index] 
    y_train, y_test = y[train_index], y[test_index]
    model = LogisticRegression(random_state=1)
    model.fit(X_train, y_train)
    pred_test = model.predict(X_test)
    score = metrics.accuracy_score(y_test, pred_test)
    print('accuracy_score', score)
    i += 1
    #pred_test = model.predict(X_test)
    pred = model.predict_proba(X_test)[:, 1]

R code

library(caret)
data(iris)

# Define train control for k fold cross validation
train_control <- trainControl(method="cv", number=10)
# Fit Naive Bayes Model
model <- train(Species~., data=iris, trControl=train_control, method="nb")
# Summarise Results
print(model)

分层交叉验证 (Stratified k-fold cross validation)

分层是重新将数据排列组合,使得每一折都能比较好地代表整体。

比如下面这个例子:在一个二分类问题上,原始数据一共有两类(F和M),F:M的数据量比例大概是 1:3;划分了5折,每一折中F和M的比例都保持和原数据一致(1:3)。

这样就避免了随机划分可能产生的的情况,像是一折全是F,其他3折都是M。

在这里插入图片描述

下图是标准交叉验证和分层交叉验证的区别:
标准交叉验证(即K折交叉验证):直接将数据分成几折;
分层交叉验证:先将数据分类(class1,2,3),然后在每个类别中划分三折。
在这里插入图片描述

分层验证代码

Python 使用cross_val_scoreStratifiedKFold

from sklearn.model_selection import StratifiedKFold
skf = StratifiedKFold(n_splits=5,shuffle=False,random_state=0)

# X is the feature set and y is the target
for train_index, test_index in skf.split(X,y): 
    print("Train:", train_index, "Validation:", test_index) 
    X_train, X_test = X[train_index], X[test_index] 
    y_train, y_test = y[train_index], y[test_index]
    
model = LogisticRegression()
scores = cross_val_score(model,X,y,cv=skf)
print("straitified cross validation scores:{}".format(scores))
print("Mean score of straitified cross validation:{:.2f}".format(scores.mean()))

** R code**

library(caret)
# Folds are created on the basis of target variable
folds <- createFolds(factor(data$target), k = 10, list = FALSE)

重复交叉验证( k-fold cross validation with repetition)

如果训练集不能很好地代表整个样本总体,分层交叉验证就没有意义了。这时候,可以使用重复交叉验证。

重复验证代码

Python:RepeatedKFold重复K折交叉验证

kf = RepeatedKFold(n_splits=5, n_repeats=2, random_state=None)  #默认是5折

for train_index, test_index in kf.split(X):
      print("Train:", train_index, "Validation:",test_index)
      X_train, X_test = X[train_index], X[test_index] 
      y_train, y_test = y[train_index], y[test_index]
        
i = 1
for train_index, test_index in kf.split(X, y):
    print('\n{} of kfold {}'.format(i,i))
    X_train, X_test = X[train_index], X[test_index] 
    y_train, y_test = y[train_index], y[test_index]
    model = LogisticRegression(random_state=1)
    model.fit(X_train, y_train)
    pred_test = model.predict(X_test)
    score = metrics.accuracy_score(y_test, pred_test)
    print('accuracy_score', score)
    i += 1
    #pred_test = model.predict(X_test)
    pred = model.predict_proba(X_test)[:, 1]

对抗验证(Adversarial Validation)

在处理实际数据集时,经常会出现测试集和训练集截然不同的情况。因此,可能导致交叉验证结果不一致

在这种情况下,可以使用对抗验证法:总体思路是根据特征分布创建一个分类模型,以检查训练集和测试集之间的相似程度。

步骤:

  1. 组合训练集和测试集;
  2. 分配0/1标签(0-训练、1-测试);
  3. 建立模型,(如果模型AUC在0.7以上,表示分类器表现较好,也间接说明train 和test 差异度较大
  4. 评估二进制分类任务来量化两个数据集的分布是否一致;
  5. 找出和测试集最相似的数据样本
  6. 构成与测试集最相似的验证集;

优点: 使验证策略在训练集和测试集高度不同的情况下更加可靠。

缺点: 一旦测试集的分布发生变化,验证集可能不再适合评估模型。

对抗验证代码

#1. 将目标变量删除
train.drop(['target'], axis = 1, inplace = True)

#2. 创建新的目标变量:训练集为1;测试集为0
train['is_train'] = 1
test['is_train'] = 0

#3. 合并训练集和测试集
df = pd.concat([train, test], axis = 0)

#4. 使用新变量训练分类模型,并预测概率
y = df['is_train']; df.drop('is_train', axis = 1, inplace = True) 
# Xgboost parameters
xgb_params = {
    'learning_rate': 0.05, 
              'max_depth': 4,
              'subsample': 0.9,        
              'colsample_bytree': 0.9,
              'objective': 'binary:logistic',
              'silent': 1, 
              'n_estimators':100, 
              'gamma':1,         
              'min_child_weight':4}   
clf = xgb.XGBClassifier(**xgb_params, seed = 10)
probs = clf.predict_proba(x1)[:,1]

#5. 使用步骤4中计算的概率对序列集进行排序,并将前n%个样本/行作为验证集(n%是您希望保留在验证集中的序列集的分数)
new_df = pd.DataFrame({
    'id':train.id, 'probs':probs})
new_df = new_df.sort_values(by = 'probs', ascending=False) # 30% validation set
val_set_ids = new_df.iloc[1:np.int(new_df.shape[0]*0.3),1]
#val_set_ids将为提供列训练集的ID,这些ID将构成与测试集最相似的验证集。

时间序列的交叉验证(Cross Validation for time series)

对于时间序列的数据集,不能像上述方法一样随机地划分验证集。为了解决时间序列的预测问题,可以尝试时间序列交叉验证:采用正向链接的策略,即按照时间顺序划分每一折的数据集。

假设我们有一个时间序列,表示在n年内消费者对某一产品的年需求量。
在这里插入图片描述
我们逐步选择新的训练集和测试集。我们从一个最小的训练集开始(这个训练集具有拟合模型所需的最少观测数)逐步地,每次都会更换训练集和测试集。在大多数情况下,不必一个个点向前移动,可以设置一次跨5个点/10个点。在回归问题中,可以使用以下代码执行交叉验证。

时间序列代码

pythonTimeSeriesSplit

from sklearn.model_selection import TimeSeriesSplit
X = np.array([[1, 2], [3, 4], [1, 2], [3, 4]])
y = np.array([1, 2, 3, 4])
tscv = TimeSeriesSplit(n_splits=3)

for train_index, test_index in tscv.split(X):
     print("Train:", train_index, "Validation:", val_index)
     X_train, X_test = X[train_index], X[val_index]
     y_train, y_test = y[train_index], y[val_index]

TRAIN: [0] TEST: [1]
TRAIN: [0 1] TEST: [2]
TRAIN: [0 1 2] TEST: [3]

R code

library(fpp)
library(forecast)
e <- tsCV(ts, Arima(x, order=c(2,0,0), h=1) #CV for arima model
sqrt(mean(e^2, na.rm=TRUE)) # RMSE
#h =1意味着我们只接受1步超前预报的误差。
#(h=4)4步前进误差如下图所示。如果想评估多步预测的模型,可以使用此选项。

在这里插入图片描述

参考链接:https://www.analyticsvidhya.com/blog/2018/05/improve-model-performance-cross-validation-in-python-r/
其他网上找到的有关文章:
关于时间序列的:https://zhuanlan.zhihu.com/p/99674163
关于对抗验证的:https://zhuanlan.zhihu.com/p/137580733

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/WHYbeHERE/article/details/108192957

智能推荐

Mysql学习_InnoDB On-Disk Structures_TABLE_mysql table is disabled-程序员宅基地

文章浏览阅读409次。InnoDB On-Disk StructuresTableCreating InnoDB Tables:CREATE TABLE t1 (a INT, b CHAR (20), PRIMARY KEY (a)) ENGINE=InnoDB;You do not need to specify the ENGINE=InnoDB clause if InnoDB is defined ..._mysql table is disabled

bzoj3296: [USACO2011 Open] Learning Languages(并查集)-程序员宅基地

文章浏览阅读248次。题目传送门 。解法: 并查集。 一头牛连向他能说的语言。 然后可以通过翻译的话就相当于双向边咯。 意思就是牛可以去找语言,语言也可以找牛。 最后记录有多少个不同的集合-1即可代码实现:#include&lt;cstdio&gt;#include&lt;cstring&gt;#include&lt;cstdlib&gt;#include&lt;iostream&gt..._bzoj3296

无线接入认证web服务器搭建,基于WEB和RADIUS的无线局域网接入认证系统-程序员宅基地

文章浏览阅读1.2k次。摘要:无线局域网的安全运营管理是近年的研究热点,而用户的接入认证是网络安全管理和运营的基础.常见的接入认证技术有PPPoE,IEEE 802.1x和Web认证三种.本文针对无线局域网的特点,以用户进行认证的便利性和易于维护性为依据,致力于开发基于WEB和RADIUS(远端拨入用户认证服务)的接入认证系统. 论文首先进行WEB认证系统总体设计,选择Linux作为开发环境.通过深入剖析Netfilte..._wifi web认证服务器搭建

解决conda 安装时的HTTP报错CondaHTTPError: HTTP 000 CONNECTION FAILED for url <https://repo.anaconda.com/pkgs_linux conda 报错 condahttperror: http 000 connection-程序员宅基地

文章浏览阅读4.3k次,点赞5次,收藏11次。错误代码:CondaHTTPError: HTTP 000 CONNECTION FAILED for url <https://repo.anaconda.com/pkgs/main/linux-64/current_repodata.json>Elapsed: -An HTTP error occurred when trying to retrieve this URL.HTTP errors are often intermittent, and a simple retry _linux conda 报错 condahttperror: http 000 connection failed for url <>

linux终端声音设置,终端命令设置音量?-程序员宅基地

文章浏览阅读510次。问题描述我有一个外部声卡,大部分工作正常,除了第一次插入时或打开笔记本电脑时,音量设置为100%。也就是整个系统音量,如声音指示器所示。我正在寻找的是一个终端命令,将该音量设置为50%,这样我就可以在登录时运行它,而不必担心如果我忘记了转动,我播放的第一个音频会在我身上发出声音音量降低。哪些命令允许您更改该音量,即声音指示器中的音量?我用gsettings和dbus四处探索,但一直无法找到任何可以..._amixer -q -d pulse sset master 5%- unmute

form表单,formdata对象,实现文件上传_formdata文件上传-程序员宅基地

文章浏览阅读3.1k次。action提交表单时,向何处发送表单数据target在何处打开url_blank在新窗口打开_self在同一个窗口打开默认_parent_topmethod属性以何种方式把表单数据提交到actionurlGET/POSTenctype属性发送表单数据之前如何对数据进行编码——上传图片post......_formdata文件上传

随便推点

android Listview 软引用SoftReference异步加载图片_listview软引用-程序员宅基地

文章浏览阅读1.9k次。网上软应用的例子很多,我也是借鉴别人修改了下,推荐一个网址:http://www.iteye.com/topic/685986 之前总是不太理解,今天认真的推敲下,记录下来方便以后用到. HashMap> imageCache 关于SoftReference这个类多少知道些机制,会用就ok了。 机制:简单来说,她会帮助我们管理内存,防止内存溢出,另外一点也_listview软引用

spark1.0-集群搭建_e5 2690-程序员宅基地

文章浏览阅读832次。背景机器环境:部门有10台服务器,每台配置为:intel E5-2690 v3 48核,775Gb内存。搭建了hdfs,hive,spark,并且spark的资源调度方案为yarn模式。因为资源分配有限。故而在自己组所拥有的6台服务器上,手动搭建spark集群,每台配置为:intel E5-2670 v3 48核,128Gb内存,18T硬盘(一个驱动控制器口)。 任务:20T压缩包(压缩率3左右,_e5 2690

《自然语言处理:数字化时代的语言智慧》_体系化智能nlp-程序员宅基地

文章浏览阅读1.1k次,点赞45次,收藏31次。随着数字化时代的来临,自然语言处理(NLP)技术正在成为信息处理和人机交互的核心技术之一。本文将围绕NLP的技术进展、技术原理、行业应用案例、面临的挑战与机遇以及未来趋势进行详细探讨。_体系化智能nlp

Python 3+Django 3 结合Vue.js框架构建前后端分离Web开发平台实战-程序员宅基地

文章浏览阅读3k次,点赞2次,收藏35次。学习全文大概需要 12分钟,内容实战性较强。1. 前言本篇将基于Python 3.7+Django 3.0结合Vue.js前端框架,为大家介绍如何基于这三者的技术栈来实现一个前端后离的W..._python+django+mysql+vue前后端分离开发的小型电子商务管理系统

flutter框架简单学习_flutter框架语法学习-程序员宅基地

文章浏览阅读269次。flutter框架最近在搞大创,主要课题就是flutter开发,简单认识一下本文将widget称为控件项目开篇介绍一个Flutter项目从main函数中的runApp调用开始。在ranApp函数中所接收的控件会成为整个屏幕的根控件,并覆盖在整个屏幕。(可以将这个控件理解成iOS中的rootViewController或android中在manifest文件中配置的mainActivity的界面)。而其他的控件(widget)都是在这个根控件上添加的。import 'package:flutter/_flutter框架语法学习

兼容性测试神器 responsively-app_responsivelyapp-程序员宅基地

文章浏览阅读5.8k次,点赞3次,收藏11次。官网 https://responsively.app/下载地址 https://github.com/manojVivek/responsively-app/releases当时v0.1.6,DIY设备信息后,不然新设备不会显示,还自动添加所有默认设备。几十个设备一起添加到首页,当时我的电脑直接蜂鸣了。。目前版本v0.2.0,v0.1.6那个致命bug终于修复了。自定义设备只需要填写相关信息就可以了,User-Agent可以从浏览器Copy过来比较真实。..._responsivelyapp