6.1产生
当训练数据包含有关目标的信息时,就会发生数据泄漏(或泄漏),但当模型用于预测时,将无法获得类似的数据。这将导致训练集(甚至可能是验证数据)上的高性能,但模型在生产中的性能将很差。换句话说,泄漏导致模型看起来很准确,直到开始使用模型做出决策,然后模型变得非常不准确。泄漏主要有两种类型:目标泄露和训练-测试集污染。
6.2目标泄漏
目标泄漏是当预测器包含在进行预测时不可用的数据时,就会发生目标泄漏。重要的是要根据数据可用的时间或时间顺序来考虑目标泄漏,而不仅仅是一个功能是否有助于做出正确的预测。举个例子会有帮助。假设你想预测谁会患肺炎。原始数据的前几行是这样的:
人们在感染肺炎后服用抗生素药物以恢复健康。原始数据显示这些列之间存在很强的关系,但是在确定got_pneumonia的值之后,tok_antibiotic _medicine经常发生变化。这是目标泄漏。该模型将会发现,任何took_antibiotic_medicine的值为False的人都没有肺炎。由于验证数据来自与训练数据相同的来源,模式将在验证中重复自己,并且模型将具有很高的验证(或交叉验证)分数。
但是,这个模型随后在现实世界中应用时将非常不准确,因为即使是患肺炎的病人,在我们需要预测他们未来的健康状况时,也还没有接受抗生素治疗。为了防止这种类型的数据泄漏,应该排除在实现目标值之后更新(或创建)的任何变量。
6.3训练-测试集污染
没有仔细区分训练数据和验证数据时,会发生另一种类型的泄漏。回想一下,验证的目的是衡量模型如何处理以前没有考虑过的数据。如果验证数据影响预处理行为,则会以微妙的方式破坏此过程。这有时被称为训练-测试集污染。
例如,假设在调用train_test_split()之前运行预处理(比如为缺失的值拟合一个输入器)。最终结果如何?模型可能得到很好的验证分数,但是当部署它来做决策时,表现很差。毕竟,我们将来自验证或测试数据的数据合并到如何进行预测中,因此即使无法推广到新数据,也可能在特定数据上做得很好。当进行更复杂的特征工程时,这个问题变得更加危险。
如果验证基于简单的训练-测试分割,请从任何类型的拟合(包括预处理步骤的拟合)中排除验证数据。如果使用scikit-learn管道,这将更容易。在使用交叉验证时,更重要的是在管道中进行预处理!
我们将使用关于信用卡应用程序的数据集,并跳过基本的数据设置代码。最终结果是关于每个信用卡应用程序的信息存储在DataFrame x中。我们将使用它来预测哪些应用程序在Series y中被接受。
由于这是一个小数据集,我们将使用交叉验证来确保模型质量的准确度量。
from sklearn.pipeline import make_pipeline
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score
# 由于没有预处理,我们不需要管道(无论如何作为最佳实践使用!)
my_pipeline = make_pipeline(RandomForestClassifier(n_estimators=100))
cv_scores = cross_val_score(my_pipeline, X, y,
cv=5,
scoring='accuracy')
print("Cross-validation accuracy: %f" % cv_scores.mean())
---------------------输出----------------------------------
Cross-validation accuracy: 0.981052
根据经验,会发现很难找到98%的准确率的模型。这种情况时有发生,但并不常见,我们应该更仔细地检查数据是否有目标泄漏。
例如,支出是指在这张卡上的支出还是在申请前使用的卡上的支出?在这一点上,基本的数据比较是非常有用的:
expenditures_cardholders = X.expenditure[y]
expenditures_noncardholders = X.expenditure[~y]
print('Fraction of those who did not receive a card and had no expenditures: %.2f' \
%((expenditures_noncardholders == 0).mean()))
print('Fraction of those who received a card and had no expenditures: %.2f' \
%(( expenditures_cardholders == 0).mean()))
--------------------输出-----------------------
Fraction of those who did not receive a card and had no expenditures: 1.00
Fraction of those who received a card and had no expenditures: 0.02
如上所示,所有没有收到卡片的人都没有支出,而只有2%的收到卡片的人没有支出。我们的模型似乎具有很高的准确性,但这似乎也是目标泄露的情况,其中的支出可能是指他们申请的信用卡上的支出。由于份额部分由支出决定,它也应该被排除在外。变量active和majorcards不太清楚,但从描述来看令人担忧。在大多数情况下,如果无法找到创建数据的人以了解更多信息,那么安全总比担忧好。我们将运行一个没有目标泄漏的模型如下:
# 从数据集中删除泄漏的预测器
potential_leaks = ['expenditure', 'share', 'active', 'majorcards']
X2 = X.drop(potential_leaks, axis=1)
# 在去掉有漏洞的预测因子后评估模型
cv_scores = cross_val_score(my_pipeline, X2, y,
cv=5,
scoring='accuracy')
print("Cross-val accuracy: %f" % cv_scores.mean())
----------------------输出---------------------
Cross-val accuracy: 0.830919
这个精度相当低,这可能令人失望。然而,在新应用程序上使用时,我们可以期望它在80%的时间内是正确的,而泄漏模型可能会比这差得多(尽管它在交叉验证中明显得分更高)。
在许多数据科学应用程序中,数据泄露可能是一个价值数百万美元的错误。仔细分离训练数据和验证数据可以防止训练测试污染,而管道可以帮助实现这种分离。同样,谨慎、常识和数据探索的结合可以帮助识别目标泄漏。