跳转至

自定义交叉验证的样本划分方法

交叉验证是缓解模型过拟合的手段之一。本文以 XGBoost 算法为例,介绍了如何自定义交叉验证中划分训练集和验证集的方法。

方法 1:在folds中传入自定义的训练集和测试集的索引

Python
import pandas as pd
import numpy as np
from sklearn.model_selection import (
    TimeSeriesSplit
)
from xgboost import XGBClassifier
import xgboost as xgb

关于交叉验证中的folds参数,官方文档是这样写的:

Text Only
folds : a KFold or StratifiedKFold instance or list of fold indices
        Sklearn KFolds or StratifiedKFolds object.
        Alternatively may explicitly pass sample indices for each fold.
        For ``n`` folds, **folds** should be a length ``n`` list of tuples.
        Each tuple is ``(in,out)`` where ``in`` is a list of indices to be used
        as the training samples for the ``n`` th fold and ``out`` is a list of
        indices to be used as the testing samples for the ``n`` th fold.

因此,我们可以自定义训练集和测试集的索引,它实际上是一个列表,列表中的元素是二元 tuple,每个 tuple 中存放([训练集的索引], [验证集的索引]),然后传入 folds 中。

更多例子,可以参考这篇帖子

Python
# 定义空列表,用于存储交叉验证选取的训练集和验证集的索引
folds_data = []
# 使用时序交叉验证方法
cv = TimeSeriesSplit(n_splits=2)
# 将 train 进一步划分为训练集和验证集
for train_index, validation_index in cv.split(x_train):
    # 将训练集和验证集的索引存储到 folds_data 中
    folds_data.append((train_index, validation_index))

在上一段代码中,我们构造了folds_data,它实际上只是将TimeSeriesSplit(n_splits=2)划分的训练集和验证集的索引存储了起来。如果我们想要高度地定制化训练集和验证集的索引,也可以不用现成的TimeSeriesSplitSplit方法,而是自己设定train_indexvalidation_index

folds中传入自定义的训练集和测试集的索引:

Python
# 交叉验证
# 设定本次交叉验证使用的模型参数
xgb_clf = XGBClassifier(max_depth=3,
                     eta=0.1,
                     n_estimators=5000,
                     gamma=0,
                     min_child_weight=1)
# 储存本次交叉验证的结果
cv_results = xgb.cv(
    params=xgb_clf.get_xgb_params(),
    dtrain=xgb.DMatrix(x_train, y_train),
    num_boost_round=1000,
    nfold=len(folds_data),
    folds=folds_data, # 这里传入的是训练集和验证集的索引元组构成的列表
    metrics='auc',
    early_stopping_rounds=5, # 如果连续 5 轮迭代后 test-auc 值没有比之前的最大值有所提升,则停止训练
    verbose_eval=True,
    show_stdv=True,
    seed=0
)

这样就完成了一次交叉验证,得到的结果就是:给定参数下,在各个验证集下的评价指标的均值。

方法 2:直接传入一个交叉验证对象的实例

这个方法比较简单。如果不需要在默认划分方法上做修改的话,更推荐使用这个方法,并且得到的结果和方法 1 是一模一样的。

Python
# 使用时序交叉验证方法,将 gram_train 划分为训练集和验证集
cv = TimeSeriesSplit(n_splits=2)

# 交叉验证
# 设定本次交叉验证使用的模型参数
xgb_clf = XGBClassifier(max_depth=3,
                     eta=0.1,
                     n_estimators=5000,
                     gamma=0,
                     min_child_weight=1)
# 储存本次交叉验证的结果
cv_results = xgb.cv(
    params=xgb_clf.get_xgb_params(),
    dtrain=xgb.DMatrix(gram_train, train['Label'].values),
    num_boost_round=1000,
    nfold=len(folds_data),
    folds=cv, # 这里直接使用 cv,在之前定义的一个 TimeSeriesSplit 对象的实例
    metrics='auc',
    early_stopping_rounds=5, # 如果连续 5 轮迭代后 test-auc 值没有比之前的最大值有所提升,则停止训练
    verbose_eval=True,
    show_stdv=True,
    seed=0
)

评论