张春成

V2

2021/06/22阅读:270主题:默认主题

屈打成招

屈打成招

本文将用一个简单的例子说明前文《从检验到瞎编》与《通往显著之路》中介绍的校正方法之必要性。


问题

本文要列举的例子取自于机器学习领域中的经典错误,即

为什么以及怎样保持测试数据的独立性

  • 一种观点是说,测试数据只能使用一次,得到评价结果之后,即作废;
  • 另一种观点,则代表一种妥协,即我们只要不在训练数据中使用测试数据,则我们总可以使用测试数据观测分类模型的得分,从而找到更好的分类器。

显然,通过我们的例子可以看到,后一种方法不仅在方法和思路上是错误的,它在实际使用上也是不可行的,因为无法避免地,涉及多重比较问题。

模拟数据

我们使用Jupyter Notebook进行实验,首先进行模拟数据的构造。

  • 使用的包如下
import numpy as np

from sklearn.svm import SVC
from sklearn.pipeline import Pipeline
from sklearn import metrics

from scipy.stats import zscore
from scipy.stats import norm
from statsmodels.stats.multitest import fdrcorrection
  • 并设置一些基本参数
train_num = 500
test_num = 50
dim = 20
  • 构造相互独立的训练和测试数据
train_X = np.random.randn(train_num, dim)
train_y = np.random.randint(11+2, (train_num,))
train_X.shape, train_y.shape

test_X = np.random.randn(test_num, dim)
test_y = np.random.randint(11+2, (test_num,))
test_X.shape, test_y.shape

不难看到,我们构造的数据“毫无意义”,因为各个样本的所有维度都服从标准正态分布。 并且,虽然我们人为地为这些样本赋予不同的标签值 ,但这些样本并没有差异。

也就是说,从理论上来讲,这些样本之间并没有统计差异,也不应该有分类器可以将它们区分来。 那么,我们有理由假设,如果我们使用机器学习方法进行分类,那么我们期望它得到 的随机水平准确率。

暴力训练

下面,我们来验证这些设想。

  • 首先,构造SVM分类器
def mk_pipe(gamma='auto'):
    pipe = Pipeline([
        ('clf', SVC(kernel='rbf', gamma=gamma))
    ])
    return pipe
  • 分别随机取若干个训练样本进行分类器训练,并使用测试集进行验证,这样的过程我们重复
scores = []
for _ in range(1000):
    select = np.random.randint(11+5, (train_num)) == 1
    X = train_X[select]
    y = train_y[select]

    pipe = mk_pipe()
    pipe.fit(X, y)
    pred_y = pipe.predict(test_X)
    acc = metrics.accuracy_score(y_true=test_y, y_pred=pred_y)

    scores.append(acc)

sorted(scores)[-1]
  • 对这些结果进行分析
print(' num | acc | z-value | p-value | fdr-p-value')
for j in [51020501002005001000]:
    s = sorted(np.random.choice(scores, size=j, replace=False))
    z = zscore(s)
    p = [1 - norm.cdf(e) for e in z]
    pc = fdrcorrection(p)
    print(f'{j: 5d} | {s[-1]: .4f} | {z[-1]: .4f} | {p[-1]: .4f} | {pc[1][-1]: .4f}')
  • 对结果稍加分析,我们得到了非常有意思的结果,如下表所示

    num acc z-value p-value fdr-p-value
    5 0.5400 1.1282 0.1296 0.5854
    10 0.6000 1.7096 0.0437 0.4367
    20 0.6000 2.1842 0.0145 0.1447
    50 0.6400 1.9727 0.0243 0.6066
    100 0.6600 2.6816 0.0037 0.3664
    200 0.6200 2.2338 0.0127 0.4437
    500 0.6600 2.8135 0.0025 0.5993
    1000 0.6600 2.8369 0.0023 0.5554

屈打成招

我们看上表

  • 列,代表我们进行了 次分类;
  • 列,代表得到了最高分类正确率;
  • 列,代表进行 变换后,最高正确率对应的 值;
  • 列,代表 值对应的 值;
  • 列,代表经过 校正后的 值。

从表中可以看到,随着尝试次数的增多,我们无疑可以得到越来越高的分类正确率。 它可以变得非常高,甚至高到可以“合理”拒绝空假设的地步,

这就像对堂下犯人屈打成招,只要板子打得够多,他总会招供的。

但这毫无意义。 因为它始终过不去多重比较校正。 可以看到,这些校正,正是为了去除多次比较的影响而生的。

分类:

数学

标签:

数学

作者介绍

张春成
V2