如何解决训练集、测试集分布不一致的问题

一,为什么要使得训练集测试集分布一致

训练集和测试集分布不一致即数据集偏移训,其的的均匀划分、均匀分布,对最终的模型预测结果是有影响的。当然,未来可能出现的鲁棒性更强、更智能的模型网络、算法可能更需要这种不一致的划分,以增强网络的鲁棒性和实际有效性,但在当下,其却对我们模型的训练有着比较大的影响,会导致测试集预测提交后的得分变低,出现不理想的结果。

其产生的原因主要有两种(产生原因多种多样):

  1. 样本选择偏差:训练集是通过有偏方法得到的,例如非均匀选择(Non-uniform Selection),导致训练集无法很好表征的真实样本空间
  2. 环境不平衡(即不同的时间空间有不一样的情况):当训练集数据的采集环境跟测试集不一致时会出现该问题,一般是由于时间或空间的改变引起的。

比如,在分类(图像分类,NLP分类等)任务上,数据集的随机划分或者原始数据集本身并没有考虑到类别平衡的问题,可能出现训练集中类别1数据量远小于类别2,而测试集中相反;或训练集和测试集中均是类别1远大于类别2;或训练集中某一类数据占绝大多数或少数数,而测试集中该类数据占绝大少数或多数等。这类样本选择偏差问题会导致训练好的模型在测试集上鲁棒性很差,因为训练集没有很好覆盖整个样本空间。此外,除了目标变量,输入特征也可能出现样本选择偏差问题,比如要预测泰坦尼克号乘客存活率,而训练集输入特征里“性别”下更多是男性,而测试集里“性别”更多是女性,这样也会导致模型在测试集上表现差。而输入特征导致的偏差,主要是由于自己的特征工程阶段并没有很好的对数据集进行处理,从而导致模型在测试集上得分低,鲁棒性差。

而对于环境不平衡导致的数据偏月,这主要是数据采集时的各种客观和主观因素的影响,导致数据的不一致性。

二,如何判断

  1. KDE(核密度估计)分布图
    KDE是非参数检验,用于估计分布未知的密度函数。通过绘制概率密度函数直方图或者核密度估计图来对比、观察数据集中训练集和测试集的分布情况,可以直观的观察两者间的分布状况。

#主要用到的函数:sns.kdeplot()

示例代码:

import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt

# 创建样例特征
train_mean, train_cov = [0, 2], [(1, .5), (.5, 1)]
test_mean, test_cov = [0, .5], [(1, 1), (.6, 1)]
train_feat, _ = np.random.multivariate_normal(train_mean, train_cov, size=50).T
test_feat, _ = np.random.multivariate_normal(test_mean, test_cov, size=50).T

# 绘KDE对比分布
sns.kdeplot(train_feat, shade = True, color='r', label = 'train')
sns.kdeplot(test_feat, shade = True, color='b', label = 'test')
plt.xlabel('Feature')
plt.legend()
plt.show()
  1. KS检验
    KS检验也是一种非参数检验方法(即不知道数据分布情况下,进行检验)。其是基于CDF(累积分布函数)来检验两个数据集之间分布是否一致。

示例:
在这里插入图片描述
如上图,图中展现的是两条不同数据集下的CDF曲线,图中的最大垂直差值可以用作描述分布差异

#主要用到的函数:scipy.stats.ks_2samp()[用来得到KS的统计值(最大垂直差)和假设检验下的p值]

代码:

from scipy import stats
stats.ks_2samp(train_feat,test_feat)

当KS统计值小且p值大,则接受原假设H0,即两个数据分布一致;当p值<0.01,则拒绝原假设。

  1. 对抗验证
    我们构建一个分类器去分类训练集和测试集,如果模型能清楚分类,说明训练集和测试集存在明显区别(即分布不一致)

(1)训练集和测试集合并,同时新增标签‘Is_Test’去标记训练集样本为0,测试集样本为1。

(2)构建分类器(例如LGB, XGB等)去训练混合后的数据集(可采用交叉验证的方式),拟合目标标签‘Is_Test’。

(3)输出交叉验证中最优的AUC分数。AUC越大(越接近1),越说明训练集和测试集分布不一致。

三,如何解决数据集分布不一致

  1. 构造合适的验证集(去构建跟测试集分布近似的验证集)

(1)人工划分验证集
以时间序列举例,因为一般测试集也会是未来数据,所以我们也要保证训练集是历史数据,而划分出的验证集是未来数据,不然会发生“时间穿越”的数据泄露问题,导致模型过拟合(例如用未来预测历史数据),这个时候就有两种验证划分方式可参考使用:

a. TimeSeriesSplit:Sklearn提供的
b. TimeSeriesSplit。

固定窗口滑动划分法:固定时间窗口,不断在数据集上滑动,获得训练集和验证集。
在这里插入图片描述
除了时间序列数据,其它数据集的验证集划分都要遵循一个原则,即尽可能符合测试集的数据模式。

(2)选择和测试集最相似的样本作为验证集

对抗验证,我们训练出一个分类器去分类训练集和测试集,那么自然我们也能预测出训练集属于测试集的概率(即训练集在‘Is_Test’标签下预测概率),我们对训练集的预测概率进行降序排列,选择概率最大的前20%样本划分作为验证集,这样我们就能从原始数据集中,得到分布跟测试集接近的一个验证集了。之后,我们还可以评估划分好的验证集跟测试集的分布状况,**评估方法:**将验证集和测试集做对抗验证,若AUC越小,说明划分出的验证集和测试集分布越接近(即分类器越分不清验证集和测试集)。
在这里插入图片描述
(3) 有权重的交叉验证

如果我们对训练集里分布更偏向于测试集分布的样本更大的样本权重,给与测试集分布不太一致的训练集样本更小权重,也能一定程度上,帮助我们线下得到不易抖动的评估分数。在lightgbm库的Dataset初始化参数中,便提供了样本加权的参数weight,详见文档[8]。图7中,对抗验证的分类器预测训练集的Is_Test概率作为权重即可。

  1. 删除分布不一致的特征

当我们遇到分布不一致且不太重要的特征,我们可以选择直接删除这种特征。

而对于那些分布不一致但又很重要的特征,我们则需要根据实际情况,自己协调特征分布和特征重要性的关系,做出一定的处理。

  1. 修正分布不一致的预测输出

检查目标特征的分布,看是否存在可修正的空间。即,对训练后的模型结果进行修正处理。(根据实际情况进行调整,以获得更好、更有效的结果)

  1. 修正分布不一致的特征输入

当我们对比观察训练集和测试集的KDE(核密度概率图)时,若发现对数据做数学运算(例如加减乘除) 或对增删样本就能修正分布,使得分布接近一致,则可以对数据进行此类预处理,以更好地训练模型。

  1. 伪标签

伪标签是半监督方法,利用未标注数据加入训练。

一般方法和步骤:
(1) 使用有标注的训练集训练模型M;

(2) 然后用模型M预测未标注的测试集;

(3) 选取测试集中预测置信度高的样本加入训练集中;

(4) 使用标注样本和高置信度的预测样本训练模型M’;

(5) 预测测试集,输出预测结果。

模型的训练引入了部分测试集的样本,这样相当于引入了部分测试集的分布。但需要注意:(1) 相比于前面的方法,伪标签通常没有表现的很好,因为它引入的是置信度高的测试集样本,这些样本很可能跟训练集分布接近一致,所以才会预测概率高。因此引入的测试集分布也没有很不同,所以使用时常发生过拟合的情况。(2) 注意引入的是高置信度样本,如果引入低置信度样本,会带来很大的噪声。另外,高置信度样本也不建议选取过多加入训练集,这也是为了避免模型过拟合。(3) 伪标签适用于图像领域更多一点。

在这里插入图片描述

原文 :
链接

本文仅作学习记录和分享,侵删

猜你喜欢

转载自blog.csdn.net/qq_53250079/article/details/128385744