理解数据泄露:如何通过利用测试数据获取满分?
我最近读了一篇关于数据泄露的文章,里面讲的黑客松场景里的玩法挺有意思的——明明不用看训练数据,居然能靠泄露点在测试集拿满分,但一开始我完全没搞懂核心逻辑。下面把文章里的步骤拆解给你看:
加载测试数据
注意哦,这里没有训练数据,只有测试数据,而且我们甚至不需要用到测试对象的任何特征,只需要那个用来对比的索引对文件就行。先加载测试索引数据:
test = pd.read_csv('../test_pairs.csv') test.head(10)
输出的前10条数据是这样的:
| pairId | FirstId | SecondId |
|---|---|---|
| 0 | 1427 | 8053 |
| 1 | 17044 | 7681 |
| 2 | 19237 | 20966 |
| 3 | 8005 | 20765 |
| 4 | 16837 | 599 |
| 5 | 3657 | 12504 |
| 6 | 2836 | 7582 |
| 7 | 6136 | 6111 |
| 8 | 23295 | 9817 |
| 9 | 6621 | 7672 |
执行test.shape[0]得到的结果是368550,也就是说总共有368550个待判断的样本对。
你可以把测试数据集想象成一个图像集合,每个图像都分配了唯一的Id(从0到N-1,N是图像总数)。上面这个数据框里的FirstId和SecondId就是指向这些图像Id的,我们的任务很简单:判断每一对图像是不是属于同一类别——是就预测1,否则预测0。这里不用关心图像本身或者具体的对比逻辑,只要知道这是个二分类任务就行。
再看看参与配对的图像数量:
print(test['FirstId'].nunique()) print(test['SecondId'].nunique())
输出结果是:
26325 26310
可见待分类的样本对数量(36万+)远少于所有可能的图像对总数。
要利用这个泄露点,首先得假设(或者能证明)测试集里的正样本对(也就是同类的图像对)总数远少于总样本对。举两个直观的例子:
- 比如一个有1000个类别、每个类别N张图像的数据集,正样本对的数量是
1000*N*(N-1)/2,而所有可能的图像对总数是1000*N*(1000N-1)/2,正样本占比极低。 - 还有Quora竞赛的例子:任务是判断问题对是不是重复项,所有可能的问题对数量极大,但真正的重复项(正样本对)少得可怜。
文章里还做了个小实验:提交全1的预测,看看准确率。代码是这样的:
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的样本过采样了——不然全1的准确率应该远低于0.5才对。那怎么利用这个泄露点呢?文章里用了「魔法特征」来解决:
构建魔法特征
关联矩阵
首先要构建一个关联矩阵:我们可以把每个(FirstId, SecondId)看作无向图里的一条边,关联矩阵的大小是(maxId+1, maxId+1),每行(每列)i对应第i个图像Id。如果(i,j)或者(j,i)在给定的样本对里,那矩阵的[i,j]位置就设为1,其他位置都是0。
要注意的是,这个关联矩阵通常是极度稀疏的,根本没法用密集格式存储,得用稀疏矩阵来表示。可以用scipy里的sparse.coo_matrix((data, (i,j)))来构建,当然你也可以用循环来实现。
内容的提问来源于stack exchange,提问作者shan




