Nav*_*rty 14 machine-learning feature-selection train-test-split
实际上,有两个事实的矛盾是问题的可能答案:
传统的答案是在拆分后进行,因为如果之前进行过,则可能会从测试集泄漏信息。
矛盾的答案是,如果仅从整个数据集中选择的训练集用于特征选择,那么特征选择或特征重要性得分顺序可能会随着 Train_Test_Split 的 random_state 的变化而动态变化。如果任何特定工作的特征选择发生变化,则无法进行特征重要性的泛化,这是不可取的。其次,如果仅使用训练集进行特征选择,则测试集可能包含某些实例集,这些实例与仅在训练集上进行的特征选择相悖/矛盾,因为未分析整体历史数据。此外,只有在给定一组实例而不是单个测试/未知实例时,才能评估特征重要性分数。
des*_*aut 16
常规答案 #1 在这里是正确的;矛盾的答案#2 中的论点实际上并不成立。
当有这样的疑问时,想象一下在模型拟合过程中您根本没有任何测试集的访问权(包括特征重要性)是有用的;您应该将测试集视为字面上看不见的数据(并且,由于看不见,它们不能用于特征重要性分数)。
Hastie 和 Tibshirani 很久以前就明确地争论过执行此类过程的正确和错误方法;我在博客文章中总结了这个问题,如何不执行特征选择!- 尽管讨论是关于交叉验证的,但可以很容易地看出,这些论点也适用于训练/测试拆分的情况。
在您的矛盾答案#2 中实际成立的唯一论点是
未分析整体历史数据
尽管如此,这是为了拥有一个独立的测试集进行性能评估而必须付出的代价,否则,按照相同的逻辑,我们也应该使用测试集进行训练,不是吗?
结束语:试验组有专为模型的绩效考核,应该不是在建立模型的任何阶段使用,包括特征选择。
更新(评论后):
测试集中的趋势可能不同
这里的一个标准(但通常是隐含的)假设是训练集和测试集在性质上是相似的;正是由于这个假设,我们觉得可以使用简单的随机拆分来获得它们。如果我们有理由相信我们的数据发生了重大变化(不仅在训练和测试之间,而且在模型部署期间也是如此),那么整个基本原理就会崩溃,需要完全不同的方法。
此外,这样做时,过拟合的可能性很高
唯一确定的过拟合方法是在管道期间以任何方式使用测试集(包括您建议的特征选择)。可以说,链接的博客文章有足够的论据(包括引号和链接)令人信服。经典例子,The Dangers of Overfitting or How to Drop 50 points in 1 minute 中的证词:
随着比赛的进行,我开始使用更多的特征选择和预处理。但是,我在交叉验证方法中犯了一个经典错误,因为没有将其包含在交叉验证折叠中(有关此错误的更多信息,请参阅此简短描述或统计学习要素中的第 7.10.2 节)。这导致越来越乐观的交叉验证估计。
正如我已经说过的,虽然这里的讨论是关于交叉验证的,但不难说服自己它也完全适用于训练/测试用例。
特征选择应该以增强模型性能的方式进行
好吧,当然,没有人可以对此争论不休!问题是 -我们在谈论哪个确切的表现?因为上面引用的 Kaggler 确实在进行过程中(应用了错误的程序)变得越来越“性能”,直到他的模型面临真正看不见的数据(关键时刻!),并且不出所料地失败了。
诚然,这不是微不足道的事情,可能需要一些时间才能将它们内化(正如 Hastie 和 Tibshirani 所证明的那样,甚至存在错误执行该程序的研究论文,这并非巧合)。在那之前,我的建议,让您安全,方法是:在所有的建筑模型(包括功能选择)的阶段,假装是你没有进入测试组所有,而且它变得可用,只有当你需要评估最终模型的性能。
des*_*aut 11
实际上并不难证明为什么使用整个数据集(即在拆分训练/测试之前)来选择特征会导致您误入歧途。这是使用 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)
Run Code Online (Sandbox Code Playgroud)
由于我们的数据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
Run Code Online (Sandbox Code Playgroud)
哇!我们在一个二元问题上得到了76% 的测试准确率,根据非常基本的统计定律,我们应该得到非常接近 50% 的结果!有人打电话给诺贝尔奖委员会,快...
...当然,我们能够得到这样的测试准确率仅仅是因为我们犯了一个非常基本的错误:我们错误地认为我们的测试数据是看不见的,但实际上测试数据已经被测试者看到了。特征选择期间的模型构建过程,特别是这里:
X_selected = selector.fit_transform(X,y)
Run Code Online (Sandbox Code Playgroud)
我们在现实中有多糟糕?好吧,同样不难看出:假设,在我们完成模型并部署它之后(期望在实践中使用新的未见数据达到 76% 的准确率),我们会得到一些真正的新数据:
X_new = np.random.randn(500, 10000)
Run Code Online (Sandbox Code Playgroud)
当然,没有任何质的变化,即新趋势或任何东西——这些新数据是由相同的基本程序生成的。假设我们也碰巧知道y如上生成的真实标签:
y_new = np.random.choice(2, size=500)
Run Code Online (Sandbox Code Playgroud)
当面对这些真正看不见的数据时,我们的模型将如何在这里执行?不难检查:
# 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
Run Code Online (Sandbox Code Playgroud)
嗯,这是真的:我们将我们的模型发送到战斗中,认为它的准确度可以达到 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
Run Code Online (Sandbox Code Playgroud)
在这种情况下,测试精度 0f 0.528 足够接近理论预测的 0.5 之一(即实际上是随机猜测)。
感谢 Jacob Schreiber 提供了简单的想法(检查所有线程,它包含其他有用的示例),尽管与您在这里询问的上下文略有不同(交叉验证):
| 归档时间: |
|
| 查看次数: |
10525 次 |
| 最近记录: |