返回列表

#1 Solution: Exploiting the Flawed Random Generation

477. Tabular Playground Series - Feb 2022 | tabular-playground-series-feb-2022

开始: 2022-02-01 结束: 2022-02-28 基因组学与生物信息 数据算法赛
#1 解决方案:利用有缺陷的随机生成

#1 解决方案:利用有缺陷的随机生成

作者:AmbrosM | 发布时间:2022-03-01

观察

我的解决方案基于对本次 TPS 竞赛的三个观察:

  1. 训练集和测试集的分布不同,这是二月份在 EDA(探索性数据分析)期间发现的事实。分布的差异是论文作者有意为之,他们希望算法在十种细菌上训练,然后在十种略有不同(变异)的细菌上进行测试。这种差异导致许多竞赛参与者对分类器的预测结果进行“后处理”。

  2. 如果正确计算交叉验证分数(即验证集中没有重复数据),ExtraTreesClassifier 达到的公共 LB 分数会大大高于 CV 分数。这一观察结果(公共 LB 分数 > CV 分数)表明训练数据和测试数据之间存在泄露。主要的挑战在于发现并利用这一泄露。

  3. 三分之一的训练数据和四分之一的测试数据是重复的。@siukeitin 记录了这些重复数据是一个奇怪的随机生成过程的结果(参见源代码)。然而,关于数百个训练行在测试数据中重复的观察结果并没有引起足够的重视。

事实证明,训练集和测试集之间的重复可以通过随机生成过程来解释。除了完全重复的数据外,还有更多的“近似重复”数据,而 ExtraTreesClassifier 之所以能达到很高的公共 LB 分数,正是因为这些近似重复数据。

随机生成过程中的缺陷

以下四行代码演示了有缺陷的随机生成。让我们生成两个包含 30 个数字(范围从 0 到 9)的数组。choice() 函数需要一个包含十个概率的数组作为输入。我们用不同的概率数组调用 choice() 两次,但使用相同的种子:

prob_train = [0.10, 0.10, 0.10, 0.10, 0.1, 0.10, 0.08, 0.12, 0.10, 0.10]
prob_test  = [0.12, 0.09, 0.09, 0.15, 0.1, 0.05, 0.10, 0.10, 0.09, 0.11]

print('Train:', np.random.RandomState(seed=231).choice(10, size=30,
      p=prob_train))
print('Test: ', np.random.RandomState(seed=231).choice(10, size=30,
      p=prob_test))

Train: [7 5 4 3 8 9 0 9 0 1 0 4 7 4 0 0 4 2 8 1 7 2 9 8 6 7 0 7 9 4]
Test:  [7 5 3 3 8 9 0 9 0 1 0 4 7 4 0 0 3 2 8 1 7 2 9 8 6 7 0 7 9 3]

你可以看到,这两个随机数组的内容几乎相同,除了三个位置不同,训练数组在这些位置是 4,而测试数组是 3。为什么会这样?答案可以在 np.random.RandomState.choice 函数的源代码中找到。为了生成 30 个随机数字,该函数首先生成 30 个介于 0 和 1 之间的随机浮点数,然后将它们映射到 p.cumsum()。如果我们对 p 应用微小的更改,输出也只会发生微小的变化:

cdf = p.cumsum()
uniform_samples = self.random_sample(shape)
idx = cdf.searchsorted(uniform_samples, side='right')

就是这样。训练数据和测试数据并不是独立的。ExtraTreesClassifier 碰巧从这种泄露中受益,但专门构建的分类管道表现甚至更好。挑战在于找到一种数据转换(以及合适的度量标准),使得用相似的 p 和相同的种子生成的数据点成为近邻。

解决方案

我在本次竞赛中的解决方案包括一个数据转换和一个 RadiusNeighborsClassifier。数据转换使成对的训练点和测试

同比赛其他方案