【发布时间】:2018-06-17 00:28:50
【问题描述】:
训练数据所代表的分布是否需要反映测试数据和您预测的数据的分布?我可以通过查看每个特征的分布并将该分布与我正在预测或测试的数据进行比较来衡量训练数据的质量吗?理想情况下,训练数据应该足以代表真实世界的分布。
【问题讨论】:
标签: validation machine-learning cross-validation training-data
训练数据所代表的分布是否需要反映测试数据和您预测的数据的分布?我可以通过查看每个特征的分布并将该分布与我正在预测或测试的数据进行比较来衡量训练数据的质量吗?理想情况下,训练数据应该足以代表真实世界的分布。
【问题讨论】:
标签: validation machine-learning cross-validation training-data
简短回答:类似的范围是个好主意。 长答案:有时(很少)这不是问题,但让我们看看什么时候。
在理想情况下,您的模型将完美地捕捉真实现象。想象最简单的情况:线性模型 y = x。如果训练数据是无噪音的(或有可容忍的噪音)。您的线性回归自然会落在大约等于 y = x 的模型上。即使在训练范围之外,模型的泛化也将几乎完美。如果您的火车数据是 {1:1, 2:2, 3:3, 4:4, 5:5, 6:6, 7:7, 8:8, 9:9, 10:10}。测试点 500,将很好地映射到函数上,返回 500。
在大多数建模场景中,几乎可以肯定情况并非如此。如果训练数据充足并且模型相当复杂(仅此而已),那么您就是黄金。
问题在于,很少有函数(以及相应的自然现象)——尤其是当我们考虑非线性函数时——如此干净地扩展到训练范围之外的数据。想象一下根据员工舒适度对办公室温度进行采样。如果你只看从 40 度到 60 度的温度。线性函数将在训练数据中表现出色。奇怪的是,如果你在 60 到 80 上进行测试,映射就会崩溃。在这里,问题在于您对数据具有足够代表性的说法是否有信心。
现在让我们考虑噪音。想象一下,您确切地知道现实世界的函数是什么:正弦波。更好的是,你会被告知它的幅度和相位。你不知道的是它的频率。您在 1 到 100 之间有一个非常可靠的采样,您拟合的函数非常好地映射到训练数据。现在,如果有足够的噪音,您可能会通过一根头发错误地估计频率。当您在训练范围附近进行测试时,结果还不错。 在训练范围之外,事情开始变得不稳定。随着您离训练范围越来越远,实函数和函数会根据它们的相对频率发散和收敛。有时,残差似乎很好;有时它们很可怕。
您检查变量分布的想法存在问题:变量之间的相互作用。即使每个变量在训练和测试中得到适当平衡, 变量之间的关系也可能会有所不同(联合分布)。举一个纯粹人为的例子,假设您正在预测一个人在任何给定时间怀孕的可能性。在您的训练集中,女性年龄在 20 到 30 岁之间,男性年龄在 30 到 40 岁之间。在测试中,男性和女性的比例相同,但年龄范围颠倒了。独立地,变量看起来非常匹配!但是在你的训练集中,你可以很容易地得出结论,“只有 30 岁以下的人会怀孕。”奇怪的是,您的测试集将证明完全相反!问题是您的预测是从多变量空间进行的,但您考虑的分布是单变量的。然而,考虑连续变量的联合分布(并适当考虑分类变量)是一个好主意。理想情况下,您的拟合模型应该可以访问与您的测试数据相似的范围。
从根本上说,问题在于从有限的训练空间进行推断。如果模型在训练空间中拟合泛化,则可以泛化;归根结底,拥有一个分布良好的训练集通常是最安全的,以最大限度地提高捕获底层函数复杂性的可能性。
非常有趣的问题!我希望答案是有见地的;随着资源的出现,我将继续在此基础上继续发展!如果还有任何问题,请告诉我!
编辑:我认为未来的读者应该阅读 cmets 中的一个观点。 理想情况下,训练数据不应该以任何方式影响测试数据。这包括检查分布、联合分布等。有了足够的数据,训练数据中的分布应该收敛于测试数据中的分布(想想均值,大数定律)。匹配分布的操作(例如训练/测试拆分之前的 z 评分)从根本上使性能指标偏向于您。用于拆分训练和测试数据的适当技术是用于交叉验证的分层 k 折。
【讨论】:
抱歉,回复延迟。经过几个月的迭代后,我实现了以下解决方案并将其推送到生产环境中,并且运行良好。
这里的问题归结为在执行交叉验证时如何减少训练/测试分数的差异。这很重要,因为如果您的方差很大,选择最佳模型的信心就会下降。测试数据对训练数据越有代表性,您在交叉验证集上的测试分数差异就越小。分层交叉验证通过确保在所有测试/训练集中保留标签类比例来解决这个问题,特别是在存在明显的类不平衡时。但是,这并没有解决特征分布的问题。
在我的例子中,我有一些特征是非常强的预测因子,但它们的分布也很不平衡。这导致我的测试分数出现显着差异,这使得我更难自信地选择模型。本质上,解决方案是确保标签与特征集的联合分布在测试/训练集之间保持不变。这样做的方法很多,但一种非常简单的方法是简单地逐个获取每个列桶范围(如果是连续的)或标签(如果是分类的),并在生成测试和训练集时从这些桶中采样。请注意,存储桶很快就会变得非常稀疏,尤其是当您有很多分类变量时。此外,您存储桶的列顺序会极大地影响采样输出。下面是一个解决方案,我首先对标签进行分类(与分层 CV 相同),然后对其他 1 个特征(最重要的特征(称为 score_percentage),这是预先已知的)进行采样。
def train_test_folds(self, label_column="label"):
# train_test is an array of tuples where each tuple is a test numpy array and train numpy array pair.
# The final iterator would return these individual elements separately.
n_folds = self.n_folds
label_classes = np.unique(self.label)
train_test = []
fmpd_copy = self.fm.copy()
fmpd_copy[label_column] = self.label
fmpd_copy = fmpd_copy.reset_index(drop=True).reset_index()
fmpd_copy = fmpd_copy.sort_values("score_percentage")
for lbl in label_classes:
fmpd_label = fmpd_copy[fmpd_copy[label_column] == lbl]
# Calculate the fold # using the label specific dataset
if (fmpd_label.shape[0] < n_folds):
raise ValueError("n_folds=%d cannot be greater than the"
" number of rows in each class."
% (fmpd_label.shape[0]))
# let's get some variance -- shuffle within each buck
# let's go through the data set, shuffling items in buckets of size nFolds
s = 0
shuffle_array = fmpd_label["index"].values
maxS = len(shuffle_array)
while s < maxS:
max = min(maxS, s + n_folds) - 1
for i in range(s, max):
j = random.randint(i, max)
if i < j:
tempI = shuffle_array[i]
shuffle_array[i] = shuffle_array[j]
shuffle_array[j] = tempI
s = s + n_folds
# print("shuffle s =",s," max =",max, " maxS=",maxS)
fmpd_label["index"] = shuffle_array
fmpd_label = fmpd_label.reset_index(drop=True).reset_index()
fmpd_label["test_set_number"] = fmpd_label.iloc[:, 0].apply(
lambda x: x % n_folds)
print("label ", lbl)
for n in range(0, n_folds):
test_set = fmpd_label[fmpd_label["test_set_number"]
== n]["index"].values
train_set = fmpd_label[fmpd_label["test_set_number"]
!= n]["index"].values
print("for label ", lbl, " test size is ",
test_set.shape, " train size is ", train_set.shape)
print("len of total size", len(train_test))
if (len(train_test) != n_folds):
# Split doesnt exist. Add it in.
train_test.append([train_set, test_set])
else:
temp_arr = train_test[n]
temp_arr[0] = np.append(temp_arr[0], train_set)
temp_arr[1] = np.append(temp_arr[1], test_set)
train_test[n] = [temp_arr[0], temp_arr[1]]
return train_test
【讨论】:
随着时间的推移,我意识到整个问题都属于协变量偏移的范畴,这是机器学习中一个经过深入研究的领域。下面的链接或只是在谷歌搜索协变量移位。这个概念是如何检测并确保您的预测数据与您的训练数据具有相似的分布。这是在特征空间中,但理论上你也可能有标签漂移。
【讨论】: