首页 > 解决方案 > 通过利用测试数据了解数据泄漏并获得满分

问题描述

我读过一篇关于数据泄露的文章。在黑客马拉松中,有两组数据,参与者训练算法的训练数据和测量性能的测试集。数据泄漏有助于在测试数据中获得满分,而无需通过利用泄漏来查看火车数据。我已经阅读了这篇文章,但我错过了如何利用泄漏的关键。如文章所示的步骤如下:

让我们加载测试数据。

请注意,我们这里没有任何训练数据,只有测试数据。此外,我们甚至不会使用测试对象的任何特征。我们需要解决这个任务是包含我们需要比较的对索引的文件。让我们用测试索引加载数据。

test = pd.read_csv('../test_pairs.csv')
test.head(10)

  pairId    FirstId SecondId
0   0       1427    8053
1   1       17044   7681
2   2       19237   20966
3   3       8005    20765
4   4       16837   599
5   5       3657    12504
6   6       2836    7582
7   7       6136    6111
8   8       23295   9817
9   9       6621    7672

test.shape[0]
368550

例如,我们可以认为有一个图像的测试数据集,每个图像都被分配了一个从 0 到 N−1 的唯一 Id(N -- 是图像的数量)。在上面的数据框中,FirstId 和 SecondId 指向这些 Id 并定义对,我们应该比较:例如,对中的两个图像是否属于同一类。因此,例如对于第一行:如果 Id=1427 和 Id=8053 的图像属于同一类,我们应该预测 1,否则预测 0。但在我们的例子中,我们并不真正关心图像,以及我们如何比较图像(只要比较器是二进制的)。

 print(test['FirstId'].nunique())
 print(test['SecondId'].nunique())
 26325
 26310

因此,与总对数相比,我们要分类的对数非常少。为了利用泄漏,我们需要假设(或证明)正对的总数与对的总数相比很小。例如:考虑一个包含 1000 个类的图像数据集,每个类 N 个图像。那么如果任务是判断一对图像是否属于同一类,我们将有 1000*N*(N−1)/2 个正对,而对的总数为 1000*N(1000N−1 )/2。

另一个例子:在 Quora 竞赛中,任务是分类一对 qustions 是否彼此重复。当然,问题对的总数非常庞大,而重复(正对)的数量要少得多。

最后,让我们得到 1 类对的一小部分。我们只需要提交一个常量预测“all one”并检查返回的准确性。创建一个包含 pairId 和 Prediction 列的数据框,填充它并将其导出到 .csv 文件。然后提交

 test['Prediction'] = np.ones(test.shape[0])
 sub=pd.DataFrame(test[['pairId','Prediction']])
 sub.to_csv('sub.csv',index=False)
 All ones have accuracy score is 0.500000.

因此,我们假设对的总数远高于阳性对的数量,但测试集并非如此。这意味着测试集不是通过随机抽样对构建的,而是通过特定的抽样算法构建的。第 1 类的对被过采样。现在想一想,我们如何利用这个事实?这里的泄漏是什么?如果您现在得到它,您可以尝试自己获得最终答案,否则您可以按照以下说明进行操作。

构建一个神奇的功能

在本节中,我们将构建一个神奇的功能,它将几乎完美地解决问题。这些说明将引导您找到正确的解决方案,但请尝试向您自己解释我们执行这些步骤的目的——这非常重要。

发生矩阵

首先,我们需要建立一个关联矩阵。您可以将对 (FirstId, SecondId) 视为无向图中的边。关联矩阵是一个大小为 (maxId + 1, maxId + 1) 的矩阵,其中每一行(列)i 对应于第 i 个 Id。在这个矩阵中,我们将值 1 放在位置 [i, j],当且仅当一对 (i, j) 或 (j, i) 存在于给定的一组 pais (FirstId, SecondId) 中。关联矩阵中的所有其他元素都为零。重要的!关联矩阵通常非常稀疏(少数非零值)。同时,关联矩阵的元素总数通常很大,不可能以密集格式将它们存储在内存中。但是由于它们的稀疏性,关联矩阵可以很容易地表示为稀疏矩阵。如果你不熟悉稀疏矩阵,请参阅 wiki 和 scipy.sparse 参考。请使用任何 scipy.sparseconstructors 来构建关联矩阵。例如,您可以使用此构造函数:scipy.sparse.coo_matrix((data, (i, j)))。我们强烈建议学习使用不同的 scipy.sparseconstructors 和矩阵类型,但如果您不想使用它们,您可以随时使用简单的 for 循环构建此矩阵。您需要首先使用 scipy.sparse.coo_matrix((M, N), [dtype]) 创建一个具有适当形状 (M, N) 的矩阵,然后遍历 (FirstId, SecondId) 对并填充矩阵中的相应元素与那些。sparseconstructors 和矩阵类型,但如果你不想使用它们,你总是可以用一个简单的 for 循环来构建这个矩阵。您需要首先使用 scipy.sparse.coo_matrix((M, N), [dtype]) 创建一个具有适当形状 (M, N) 的矩阵,然后遍历 (FirstId, SecondId) 对并填充矩阵中的相应元素与那些。sparseconstructors 和矩阵类型,但如果你不想使用它们,你总是可以用一个简单的 for 循环来构建这个矩阵。您需要首先使用 scipy.sparse.coo_matrix((M, N), [dtype]) 创建一个具有适当形状 (M, N) 的矩阵,然后遍历 (FirstId, SecondId) 对并填充矩阵中的相应元素与那些。

请注意,矩阵应该是对称的,并且只包含零和一。这是一种检查自己的方法。

import networkx as nx
import numpy as np
import pandas as pd 
import scipy.sparse
import matplotlib.pyplot as plt

test = pd.read_csv('../test_pairs.csv')


x = test[['FirstId','SecondId']].rename(columns={'FirstId':'col1', 'SecondId':'col2'})
y = test[['SecondId','FirstId']].rename(columns={'SecondId':'col1', 'FirstId':'col2'})
comb = pd.concat([x,y],ignore_index=True).drop_duplicates(keep='first')

 comb.head()
   col1      col2
0  1427      8053
1  17044    7681
2  19237   20966
3  8005    20765
4  16837   599

 data = np.ones(comb.col1.shape, dtype=int)

 inc_mat = scipy.sparse.coo_matrix((data,(comb.col1,comb.col2)), shape=(comb.col1.max() + 1, comb.col1.max() + 1))

  rows_FirstId   = inc_mat[test.FirstId.values,:]
  rows_SecondId  = inc_mat[test.SecondId.values,:]
  f = rows_FirstId.multiply(rows_SecondId)
  f = np.asarray(f.sum(axis=1))
  f.shape
  (368550, 1)

  f = f.sum(axis=1)
  f = np.squeeze(np.asarray(f))
  print (f.shape)

现在构建魔术功能

我们为什么要建立关联矩阵?我们可以将此矩阵中的行视为对象的表示。第 i 行是 Id = i 的对象的表示。然后,为了测量两个对象之间的相似性,我们可以测量它们表示之间的相似性。我们将看到,这样的表示非常好。

现在从关联矩阵中选择对应于 test.FirstId 和 test.SecondId 的行。

所以不要忘记转换pd.seriesnp.array 这些行通常应该运行得非常快
 rows_FirstId   = inc_mat[test.FirstId.values,:] 
 rows_SecondId  = inc_mat[test.SecondId.values,:] 

我们的神奇功能将是一对对象表示之间的点积。点积可以看作是相似性度量——对于我们的非负表示,当表示不同时,点积接近 0,而当表示相似时,点积很大。现在计算 rows_FirstId 和 rows_SecondId 矩阵中对应行之间的点积。

从魔术特征到二元预测

但是我们如何将此特征转换为二进制预测呢?我们没有训练集来学习模型,但我们有一条关于测试集的信息:提交常量时获得的基线准确度分数。而且我们对数据生成过程也有非常强烈的考虑,所以即使没有训练集,我们也可能会很好。我们可以尝试选择一个阈值,如果特征值 f 大于阈值,则将预测设置为 1,否则设置为 0。你会选择什么门槛?我们如何找到正确的阈值?让我们首先检查这个特征:打印特征 f 中每个值的频率(或计数)。

例如使用np.unique函数,检查标志函数来计算每个元素的频率
 from scipy.stats import itemfreq

 itemfreq(f)

 array([[    14, 183279],
        [    15,    852],
        [    19,    546],
        [    20, 183799],
        [    21,      6],
        [    28,     54],
        [    35,     14]])

你看到这个特征是如何聚集成对的了吗?也许您可以通过查看值来猜测一个好的阈值?事实上,在其他情况下,它可能不是那么明显,但通常要选择一个阈值,您只需要记住基线提交的分数并使用此信息。在下面选择一个阈值:

      pred = f  > 14 # SET THRESHOLD HERE
      pred

      array([ True, False,  True, ..., False, False, False], dtype=bool)

      submission = test.loc[:,['pairId']]
      submission['Prediction'] = pred.astype(int)
      submission.to_csv('submission.csv', index=False)

我想了解这背后的想法。我们如何仅利用测试数据的泄漏。

标签: python-3.xpython-2.7numpymatrixscipy

解决方案


文章中有提示。正对数应为 1000*N*(N-1)/2,而所有对数应为 1000*N(1000N-1)/2。当然,如果测试集是随机抽样的,那么所有对的数量会大得多。

正如作者所提到的,在您评估您在测试集上对 1 的恒定预测之后,您可以看出抽样不是随机进行的。您获得的准确率为 50%。如果采样正确完成,这个值应该低得多。

因此,他们构建关联矩阵并计算我们的 ID 特征表示之间的点积(相似性度量)。然后,他们重复使用通过恒定预测(50%)获得的准确度信息来获得相应的阈值(f > 14)。它设置为大于 14,因为这构成了我们测试集的大约一半,这反过来又映射回 50% 的准确度。

“魔法”值不必大于 14。它可以等于 14。你可以在一些排行榜探测后调整这个值(只要你捕获了一半的测试集)。


推荐阅读