其实并不难证明为什么使用整个数据集(即在分割训练/测试之前)来选择特征可能会让您误入歧途。下面是一个使用 Python 和 scikit-learn 的随机虚拟数据的演示:
import numpy as np
from sklearn.feature_selection import SelectKBest
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
# random data:
X = np.random.randn(500, 10000)
y = np.random.choice(2, size=500)
由于我们的数据X
是随机的(500 个样本,10,000 个特征)和我们的标签y
是二进制的,我们期望永远不能超过这种设置的基线精度,即 ~ 0.5,或大约 50%。让我们看看当我们应用时会发生什么wrong在分割之前使用整个数据集进行特征选择的过程:
selector = SelectKBest(k=25)
# first select features
X_selected = selector.fit_transform(X,y)
# then split
X_selected_train, X_selected_test, y_train, y_test = train_test_split(X_selected, y, test_size=0.25, random_state=42)
# fit a simple logistic regression
lr = LogisticRegression()
lr.fit(X_selected_train,y_train)
# predict on the test set and get the test accuracy:
y_pred = lr.predict(X_selected_test)
accuracy_score(y_test, y_pred)
# 0.76000000000000001
哇!我们得到76% test二进制问题的准确率,根据基本的统计定律,我们应该得到非常接近 50% 的结果!有人打电话给诺贝尔奖委员会,并且fast...
...当然,事实是我们能够获得这样的测试精度只是因为我们犯了一个非常基本的错误:我们错误地think我们的测试数据是看不见的,但实际上测试数据在特征选择过程中已经被模型构建过程看到了,特别是在这里:
X_selected = selector.fit_transform(X,y)
How 境况不佳我们可以在现实中吗?嗯,不难看出:假设,在我们完成模型之后,我们有deployed它(期望在实践中使用新的未见过的数据达到 76% 的准确率),我们得到了一些really新数据:
X_new = np.random.randn(500, 10000)
当然,没有任何质的变化,即新趋势或任何东西——这些新数据是由完全相同的基本程序生成的。假设我们碰巧知道真正的标签y
,如上生成:
y_new = np.random.choice(2, size=500)
当面对这些真正看不见的数据时,我们的模型将如何表现?不难检查:
# select the same features in the new data
X_new_selected = selector.transform(X_new)
# predict and get the accuracy:
y_new_pred = lr.predict(X_new_selected)
accuracy_score(y_new, y_new_pred)
# 0.45200000000000001
嗯,确实如此:我们将模型投入战斗,认为它的准确率约为 76%,但实际上它的表现只是随机猜测......
那么,现在让我们看看correct过程(即首先拆分,然后根据training仅设置):
# split first
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=42)
# then select features using the training set only
selector = SelectKBest(k=25)
X_train_selected = selector.fit_transform(X_train,y_train)
# fit again a simple logistic regression
lr.fit(X_train_selected,y_train)
# select the same features on the test set, predict, and get the test accuracy:
X_test_selected = selector.transform(X_test)
y_pred = lr.predict(X_test_selected)
accuracy_score(y_test, y_pred)
# 0.52800000000000002
在这种情况下,测试精度 0f 0.528 足够接近理论上预测的 0.5(即实际上随机猜测)。
感谢 Jacob Schreiber 提供了简单的想法(检查所有thread https://twitter.com/jmschreiber91/status/1137464236156702720,它包含其他有用的示例),尽管与您在此处询问的情况略有不同(交叉验证):