实际上并不难证明为什么使用整个数据集(即在拆分到训练/测试之前)来选择特征会导致您误入歧途。这是一个使用 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%。让我们看看当我们在拆分之前应用错误的使用整个数据集进行特征选择的过程时会发生什么:
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% 的测试准确率,根据非常基本的统计定律,我们应该得到非常接近 50% 的结果!有人打电话给诺贝尔奖委员会,快...
...当然,事实是,我们之所以能够得到这样的测试准确率,仅仅是因为我们犯了一个非常基本的错误:我们错误地认为我们的测试数据是看不见的,但实际上测试数据已经被特征选择期间的模型构建过程,特别是这里:
X_selected = selector.fit_transform(X,y)
我们在现实中能有多糟糕?好吧,再一次不难看出:假设在我们完成模型并部署它之后(在实践中使用新的看不见的数据预计会有 76% 的准确率),我们得到了一些真正的新数据:
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% 的准确率,但实际上它的表现只是随机猜测......
那么,现在让我们看看正确的过程(即先拆分,然后仅根据训练集选择特征):
# 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 提供了简单的想法(检查所有线程,它包含其他有用的示例),尽管上下文与您在此处询问的上下文略有不同(交叉验证):
![在此处输入图像描述](https://i.stack.imgur.com/l1lgA.jpg)