一、K折交叉验证训练单个模型
1.1 k 折交叉验证(K-Fold Cross Validation)原理
通过对 k 个不同分组训练的结果进行平均来减少方差,因此模型的性能对数据的划分就不那么敏感,经过多次划分数据集,大大降低了结果的偶然性,从而提高了模型的准确性。具体做法如下:
- step1:不重复抽样将原始数据随机分为 k 份。
- step2:每一次挑选其中 1 份作为验证集,剩余 k-1 份作为训练集用于模型训练。一共训练k个模型。
- step3:在每个训练集上训练后得到一个模型,用这个模型在测试集上测试,计算并保存模型的评估指标,
- step4:计算 k 组测试结果的平均值作为模型最终在测试集上的预测值,求k 个模型评估指标的平均值,并作为当前 k 折交叉验证下模型的性能指标。
要注意的是:
(1)K折交叉验证适合大样本的数据集,在小样本数据集上就很难识别数据中的趋势,导致错误产生。
(2)K折交叉验证不适合包含不同类别的数据集。比如:若数据集有5类数据(ABCDE各占20%),抽取出来的也正好是按照类别划分的5类,第一折全是A,第二折全是B……这样划分的数据集建立的模型显然没什么意义。第一折训练的模型把样本全判为A、第二折训练的模型把样本全判为B…这种情况下,可以使用分层交叉验证 (Stratified k-fold cross validation)。sklearn.model_selection中有相关函数可以调用。StratifiedKFold
from sklearn.model_selection import StratifiedKFold
skf = StratifiedKFold(n_splits=5,shuffle=False,random_state=0)
(3)如果训练集不能很好地代表整个样本总体,分层交叉验证就没有意义了。这时候,可以使用重复交叉验证,即每次用不同的划分方式划分数据集,每次划分完后的其他步骤和K折交叉验证一样。(重复K折交叉验证可以提高模型评估的精确度,同时保持较小的偏差。)
下面这段代码就是重复两次划分数据集(n_repeats=2),每次数据集都进行5折交叉验证(n_splits=5)。
kf = RepeatedKFold(n_splits=5, n_repeats=2, random_state=None)
1.2 代码
下面将交叉验证过程与模型训练过程融合在一起,编写成一个可以自动化处理的函数,可以保存为py文件,建模的时候直接调用。该函数需要输入训练集X,测试集X_test、训练集对应的标签列y,模型的最优参数组合字典params,还有已经划分好的K折样本folds,还有模型类型model_type(3个选择:LightGBM、Xgboost、Catboost,都是Kaggle中很常用的模型,如果想加入其他的可选择模型,在代码中再多增加一个if判断就可以)、决定是回归问题还是分类问题的eval_type。输出的是交叉验证中过程中几个小模型在训练集上得出的预测值oof(保存好之后可以用于stacking融合),几个小模型在测试集上的平均预测 predictions, 模型评估指标scores(回归问题用RMSE、分类问题用logloss)
def train_model(X, X_test, y, params, folds, model_type='lgb', eval_type='regression'):
#生成一个和训练集样本量一样大的空数列,用来存放交叉验证中的小模型在训练集上的预测值,以后会在stacking中用到这些数据
oof = np.zeros(X.shape[0])
#存放测试集的预测结果
predictions = np.zeros(X_test.shape[0])#
scores = []
#enumerate(folds.split(X, y))返回第i折的测试集索引trn_idx、验证集索引val_idx
for fold_n, (trn_idx, val_idx) in enumerate(folds.split(X, y)):
print('Fold', fold_n, 'started at', time.ctime())
if model_type == 'lgb':
#lightgbm要封装数据
trn_data = lgb.Dataset(X[trn_idx], y[trn_idx])
val_data = lgb.Dataset(X[val_idx], y