模型评估涉及使用可用数据集来拟合模型,并估计其在对未见过的数据进行预测时的性能。
这是一个具有挑战性的问题,因为用于拟合模型的训练数据集和用于评估模型的测试集都必须足够大并且能代表底层问题,这样模型性能的估计结果才不会过于乐观或悲观。
用于模型评估的两种最常见方法是训练/测试拆分和k折交叉验证程序。这两种方法在一般情况下都可能非常有效,但它们可能会导致误导性的结果,并且在用于具有严重类别不平衡的分类问题时可能会失败。相反,必须修改这些技术,按类别标签进行分层抽样,称为分层训练/测试拆分或分层k折交叉验证。
在本教程中,您将了解如何评估不平衡数据集上的分类器模型。
完成本教程后,您将了解:
- 使用训练/测试拆分和交叉验证评估分类器的挑战。
- 在不平衡数据集上评估分类器时,k折交叉验证和训练/测试拆分的简单应用会失败。
- 修改后的k折交叉验证和训练/测试拆分如何用于保留数据集中类别的分布。
通过我的新书 《Python 不平衡分类》来启动您的项目,其中包含分步教程和所有示例的Python源代码文件。
让我们开始吧。

如何使用k折交叉验证进行不平衡分类
照片来源:Bonnie Moreland,部分权利保留。
教程概述
本教程分为三个部分;它们是:
- 评估分类器的挑战
- k折交叉验证的失败
- 修复不平衡分类的交叉验证
评估分类器的挑战
评估分类模型是一项挑战,因为在模型使用之前,我们无法知道它的好坏。
相反,我们必须使用我们已经拥有目标或结果的可用数据来估计模型的性能。
模型评估不仅仅是评估模型;它还包括测试不同的数据准备方案、不同的学习算法以及用于性能良好的学习算法的不同超参数。
- 模型 = 数据准备 + 学习算法 + 超参数
理想情况下,具有最佳分数(使用您选择的指标)的模型构建过程(数据准备、学习算法和超参数)可以选择并使用。
最简单的模型评估程序是将数据集分成两部分,一部分用于训练模型,另一部分用于测试模型。因此,数据集的各个部分根据其功能命名,分别为训练集和测试集。
如果您的收集的数据集非常大且具有代表性,那么这种方法是有效的。所需示例的数量将因问题而异,但可能需要成千上万、数十万甚至数百万个示例才能满足要求。
50/50 的训练集和测试集拆分是理想的,尽管更不均衡的拆分也很常见,例如训练集和测试集的 67/33 或 80/20。
我们很少有足够的数据通过单次训练/测试拆分来获得对模型性能的无偏估计。相反,我们拥有的数据集通常比我们期望的要小得多,因此必须在此数据集上使用重采样策略。
分类器最常用的模型评估方案是 10 折交叉验证程序。
k折交叉验证程序涉及将训练数据集分成 *k* 折。前 *k-1* 折用于训练模型,而保留的第 *k* 折用作测试集。此过程会重复进行,并且每折都有机会用作保留的测试集。总共拟合和评估 *k* 个模型,模型的性能计算为这些运行的平均值。
该程序已被证明比单一训练/测试拆分能提供对小型训练数据集上模型性能的更乐观的估计。对于各种数据集大小和模型类型,*k*=10 的值已被证明是有效的。
想要开始学习不平衡分类吗?
立即参加我为期7天的免费电子邮件速成课程(附示例代码)。
点击注册,同时获得该课程的免费PDF电子书版本。
k折交叉验证的失败
可悲的是,k折交叉验证不适用于评估不平衡分类器。
10 折交叉验证,特别是机器学习中最常用的误差估计方法,即使类别偏差不如前面考虑的那样极端,也容易在类别不平衡的情况下失效。
— 第188页,《不平衡学习:基础、算法与应用》,2013年。
原因是数据以均匀概率分布被分成 *k* 折。
对于类别分布均衡的数据,这可能效果很好,但当分布严重偏斜时,一个或多个折很可能只有很少或没有少数类别的样本。这意味着一些或可能很多的模型评估结果将具有误导性,因为模型只需要正确预测多数类别即可。
我们可以通过一个例子来具体说明这一点。
首先,我们可以定义一个少数类与多数类分布为 1:100 的数据集。
这可以通过 make_classification() 函数来实现,该函数用于创建合成数据集,指定示例数量(1,000)、类别数量(2)以及每个类别的权重(99% 和 1%)。
1 2 |
# 生成 2 类数据集 X, y = make_classification(n_samples=1000, n_classes=2, weights=[0.99, 0.01], flip_y=0, random_state=1) |
下面的示例生成合成的二元分类数据集并总结了类别分布。
1 2 3 4 5 6 7 8 9 10 11 12 |
# 创建一个二元分类数据集 from numpy import unique from sklearn.datasets import make_classification # 生成 2 类数据集 X, y = make_classification(n_samples=1000, n_classes=2, weights=[0.99, 0.01], flip_y=0, random_state=1) # 总结数据集 classes = unique(y) total = len(y) for c in classes: n_examples = len(y[y==c]) percent = n_examples / total * 100 print('> Class=%d : %d/%d (%.1f%%)' % (c, n_examples, total, percent)) |
运行该示例会创建数据集并总结每个类别的示例数量。
通过设置 *random_state* 参数,可以确保每次运行代码时都能获得相同的随机生成示例。
1 2 |
> Class=0 : 990/1000 (99.0%) > Class=1 : 10/1000 (1.0%) |
少数类别的总共只有 10 个示例。如果使用 10 折交叉验证,在理想情况下,每折只有一个示例,这不足以训练模型。出于演示目的,我们将使用 5 折。
在理想情况下,我们将有 10/5 或每折有两个示例,这意味着训练数据集中有 4*2(8)个示例,给定测试数据集中有 1*2(2)个示例。
首先,我们将使用 KFold 类将数据集随机分成 5 折,并检查每个训练集和测试集的组成。完整的示例列在下面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# 具有不平衡数据集的k折交叉验证示例 from sklearn.datasets import make_classification from sklearn.model_selection import KFold # 生成 2 类数据集 X, y = make_classification(n_samples=1000, n_classes=2, weights=[0.99, 0.01], flip_y=0, random_state=1) kfold = KFold(n_splits=5, shuffle=True, random_state=1) # 枚举拆分并总结分布 for train_ix, test_ix in kfold.split(X): # 选择行 train_X, test_X = X[train_ix], X[test_ix] train_y, test_y = y[train_ix], y[test_ix] # 总结训练集和测试集的组成 train_0, train_1 = len(train_y[train_y==0]), len(train_y[train_y==1]) test_0, test_1 = len(test_y[test_y==0]), len(test_y[test_y==1]) print('>Train: 0=%d, 1=%d, Test: 0=%d, 1=%d' % (train_0, train_1, test_0, test_1)) |
运行该示例会创建与之前相同的数据集,并枚举数据的每个拆分,显示每个训练集和测试集的类别分布。
在这种情况下,我们可以看到有些拆分具有预期的 8/2 训练集和测试集拆分,而有些则差得多,例如 6/4(乐观)和 10/0(悲观)。
在这些数据拆分上评估模型将无法提供可靠的性能估计。
1 2 3 4 5 |
>Train: 0=791, 1=9, Test: 0=199, 1=1 >Train: 0=793, 1=7, Test: 0=197, 1=3 >Train: 0=794, 1=6, Test: 0=196, 1=4 >Train: 0=790, 1=10, Test: 0=200, 1=0 >Train: 0=792, 1=8, Test: 0=198, 1=2 |
我们可以演示,如果我们使用简单的训练/测试拆分数据集,也会出现类似的问题,尽管问题不那么严重。
我们可以使用 train_test_split() 函数将数据集进行 50/50 拆分,并且如果我们多次执行此拆分,平均而言,我们期望在每个数据集中出现五个少数类别的示例。
完整的示例如下所示。
1 2 3 4 5 6 7 8 9 10 11 |
# 具有不平衡数据集的训练/测试拆分示例 from sklearn.datasets import make_classification from sklearn.model_selection import train_test_split # 生成 2 类数据集 X, y = make_classification(n_samples=1000, n_classes=2, weights=[0.99, 0.01], flip_y=0, random_state=1) # 以相同类别比例分割为训练/测试集 trainX, testX, trainy, testy = train_test_split(X, y, test_size=0.5, random_state=2) # 总结 train_0, train_1 = len(trainy[trainy==0]), len(trainy[trainy==1]) test_0, test_1 = len(testy[testy==0]), len(testy[testy==1]) print('>Train: 0=%d, 1=%d, Test: 0=%d, 1=%d' % (train_0, train_1, test_0, test_1)) |
运行该示例会创建与之前相同的数据集,并将其拆分为随机的训练集和测试集。
在这种情况下,我们可以看到训练集中只有三个少数类别的示例,测试集中有七个。
在此拆分上评估模型将导致模型没有足够的示例可供学习,过多的示例可供评估,并且可能导致性能不佳。您可以想象,在更严重的随机拆分情况下,情况可能会更糟。
1 |
>Train: 0=497, 1=3, Test: 0=493, 1=7 |
修复不平衡分类的交叉验证
解决方案是在使用 k 折交叉验证或训练/测试拆分时,不要随机拆分数据。
具体来说,我们可以随机拆分数据集,但要以在每个子集中保持相同的类别分布的方式进行。这称为分层或分层抽样,目标变量(*y*),即类别,用于控制抽样过程。
例如,我们可以使用一种 k 折交叉验证版本,该版本可在每个折中保留不平衡的类别分布。它称为分层 k 折交叉验证,并将强制每个数据拆分的类别分布与完整训练数据集中的分布相匹配。
…在类别不平衡的情况下,通常使用分层 10 折交叉验证,它确保原始分布中的正负示例比例在所有折中都得到尊重。
— 第205页,《不平衡学习:基础、算法与应用》,2013年。
我们可以通过一个例子来具体说明这一点。
我们可以使用 StratifiedKFold 类来分层拆分,顾名思义,它支持分层 k 折交叉验证。
下面是相同的数据集和使用分层版本交叉验证的相同示例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# 具有不平衡数据集的分层 k 折交叉验证示例 from sklearn.datasets import make_classification from sklearn.model_selection import StratifiedKFold # 生成 2 类数据集 X, y = make_classification(n_samples=1000, n_classes=2, weights=[0.99, 0.01], flip_y=0, random_state=1) kfold = StratifiedKFold(n_splits=5, shuffle=True, random_state=1) # 枚举拆分并总结分布 for train_ix, test_ix in kfold.split(X, y): # 选择行 train_X, test_X = X[train_ix], X[test_ix] train_y, test_y = y[train_ix], y[test_ix] # 总结训练集和测试集的组成 train_0, train_1 = len(train_y[train_y==0]), len(train_y[train_y==1]) test_0, test_1 = len(test_y[test_y==0]), len(test_y[test_y==1]) print('>Train: 0=%d, 1=%d, Test: 0=%d, 1=%d' % (train_0, train_1, test_0, test_1)) |
运行该示例会像以前一样生成数据集,并总结每个拆分的训练集和测试集的类别分布。
在这种情况下,我们可以看到每个拆分都符合我们对理想情况的预期。
少数类别的每个示例都有一次机会在测试集中使用,并且每个拆分的训练集和测试集具有相同的类别分布。
1 2 3 4 5 |
>Train: 0=792, 1=8, Test: 0=198, 1=2 >Train: 0=792, 1=8, Test: 0=198, 1=2 >Train: 0=792, 1=8, Test: 0=198, 1=2 >Train: 0=792, 1=8, Test: 0=198, 1=2 >Train: 0=792, 1=8, Test: 0=198, 1=2 |
此示例突显了在 k 折交叉验证之前选择 *k* 值以确保训练集和测试集中有足够数量的示例来拟合和评估模型(测试集中的两个少数类别示例可能太少)。
它还突显了在不平衡数据集上使用分层 k 折交叉验证的要求,以在每个模型评估的训练集和测试集中保留类别分布。
我们也可以使用训练/测试拆分的有层析的版本。
这可以通过在调用 *train_test_split()* 时设置“*stratify*”参数,并将其设置为包含数据集中目标变量的“*y*”变量来实现。通过此设置,函数将确定所需的类别分布,并确保训练集和测试集都具有此分布。
我们可以通过下面的示例来演示这一点。
1 2 3 4 5 6 7 8 9 10 11 |
# 具有不平衡数据集的分层训练/测试拆分示例 from sklearn.datasets import make_classification from sklearn.model_selection import train_test_split # 生成 2 类数据集 X, y = make_classification(n_samples=1000, n_classes=2, weights=[0.99, 0.01], flip_y=0, random_state=1) # 以相同类别比例分割为训练/测试集 trainX, testX, trainy, testy = train_test_split(X, y, test_size=0.5, random_state=2, stratify=y) # 总结 train_0, train_1 = len(trainy[trainy==0]), len(trainy[trainy==1]) test_0, test_1 = len(testy[testy==0]), len(testy[testy==1]) print('>Train: 0=%d, 1=%d, Test: 0=%d, 1=%d' % (train_0, train_1, test_0, test_1)) |
运行该示例会将数据集随机拆分为训练集和测试集,确保类别分布得到保留,在本例中,每个数据集都留下五个示例。
1 |
>Train: 0=495, 1=5, Test: 0=495, 1=5 |
进一步阅读
如果您想深入了解,本节提供了更多关于该主题的资源。
教程
书籍
API
- sklearn.model_selection.KFold API.
- sklearn.model_selection.StratifiedKFold API.
- sklearn.model_selection.train_test_split API.
总结
在本教程中,您了解了如何评估不平衡数据集上的分类器模型。
具体来说,你学到了:
- 使用训练/测试拆分和交叉验证评估分类器的挑战。
- 在不平衡数据集上评估分类器时,k折交叉验证和训练/测试拆分的简单应用会失败。
- 修改后的k折交叉验证和训练/测试拆分如何用于保留数据集中类别的分布。
你有什么问题吗?
在下面的评论中提出你的问题,我会尽力回答。
谢谢!
不客气!
一如既往,赞!
谢谢!
train_X, test_y = X[train_ix], X[test_ix] 应该是 train_X, test_X = X[train_ix], X[test_ix]
谢谢,已修复!
你好,精彩的文章。谢谢。
我的问题是,在我的原始数据集中,我有 1% 的响应率,所以我对其进行了欠采样,使其成为 50% 的响应者和 50% 的非响应者。在此之后,我进行了 k 折交叉验证。在采样数据(非真实数据)上进行验证是否不正确?另外,我将另外 20% 的真实数据(未采样)作为测试数据集。
不行。
重采样必须在交叉验证折内进行。请参阅此处的示例
https://machinelearning.org.cn/smote-oversampling-for-imbalanced-classification/
谢谢。即使在那个示例中,SMOTE 和多数类欠采样是在将分层 k 折交叉验证应用于 X 和 y 之前完成的。这意味着验证是在平衡数据上进行的。如果我的理解有误,请纠正。再次感谢。
并非如此。
我们使用管道来确保数据采样发生在交叉验证过程内部。
也许重读一下教程?
谢谢你的教程.. 🙂
好的……假设我的二元分类是 {0:500,1:150}。如果进行随机欠采样,得到 {0:150, 1:150},
在这种情况下,如果我对采样数据进行交叉验证,就不会发生数据泄露。对吗?因为,它不是用相同的副本过采样或合成的,只是随机移除了某些值……
那么,先进行随机欠采样,然后像处理未采样数据一样进行普通交叉验证是可以的吗?
不可以,这是无效的。您将改变测试集的构成。
是否存在可以使用 train_test_split 中的分层抽样*和*过采样或欠采样相结合的情况?
如果存在,那么想法是先拆分数据,然后应用过采样?如果是这样,过采样是否应该仅应用于训练数据?
你好 Daniel…以下资源将为你提供一些可以考虑的想法
https://machinelearning.org.cn/training-validation-test-split-and-cross-validation-done-right/
根据示例,这是管道
steps = [(‘over’, over), (‘under’, under), (‘model’, model)]
管道 = Pipeline(steps=steps)
然后此步骤执行交叉验证
scores = cross_val_score(pipeline, X, y, scoring=’roc_auc’, cv=cv, n_jobs=-1)
这是否意味着管道应用于原始数据集 (X 和 y) – 这意味着首先
先 SMOTE 过采样,然后随机欠采样,然后应用模型并通过 ROC 曲线进行验证?
是的,但过采样/欠采样是在交叉验证的每个折*内部*应用的。
你好,
方法 s 的缺点是什么?
分层交叉验证?
计算成本略高。
它仅用于分类,不用于回归。
我不明白这一点。您的意思是我们不应该将 StratifiedKFold 用于逻辑回归等回归问题吗?
Stratified 不能用于回归问题(预测数字)。
“逻辑回归”是一种分类算法(用于预测标签)。
我现在明白了你的意思,谢谢!另外,你的网站是宝贵的机器学习资源。继续加油!
谢谢!
> Jason Brownlee 2021年1月21日 上午6:49 #
> Stratified 不能用于回归问题(预测数字)。
我一直在阅读一些文章,这正是我想要的答案。
我想知道如何为模型(可以将其视为回归模型)创建一个数据集,该模型可以预测一系列实数(例如 0-100),但又认为预测结果偏向于某个特定范围。
首先,我曾认为我需要对每个范围进行某种分层抽样,但在回归模型中,随机抽样是否合适?
前者非常复杂,因为模型的性能取决于范围的大小。
回归问题落入某个特定范围(在本例中为 0-100)的情况可能很少见。
但是,我认为不仅在这种情况下,而且对于回归来说,问题、训练数据都可以根据获得的数据的分布来获得。
您有这样的讨论吗?
嗨 Ogawa… 我不太明白你的问题。请详细说明你想 accomplish 什么,这样我才能更好地帮助你。
此致,
James,感谢您对我问题的关注。
更具体地说,我感兴趣的是从图像数据中预测某些数字。
例如,这类似于通过大头照估计年龄的努力。(*)
在此示例中,如果学习数据偏向于某个年龄组,则将无法做出正确的预测。
(更具体地说,即使我们只从成人照片中学习年龄,也很难从脸部照片中推断出孩子的年龄。)
因此,我认为即使是预测数值的回归问题,在学习过程中也应避免偏差。
对于许多回归问题,无法指定预测值的范围,因此无法进行分层提取。
但是,我认为这些回归问题可能需要某种处理。
(在此示例中,一种方法是按年龄组(例如,每 10 年一个范围)相等地(或按总数据量的比例)提取数据。
是否存在一般的讨论?
在这样的疑问中,我看到了这篇文章,所以在这里提问。
问题就由此产生。
我希望得到 Jason 博士和浏览此网站的其他志愿者的建议。
(*) 如果您有兴趣,可以从以下网站获取数据集
https://susanqq.github.io/UTKFace/
谢谢你
不客气。
嗨,很棒的文章!对于 GridSearchCV,(Stratified)KFolds 是隐式的吗?这是一个例子
gs_clf = GridSearchCV(clf_pipe, param_grid=params, verbose=0, cv=5, n_jobs=-1)
感谢您的回复!
谢谢。
我认为是的。如果可能,最好在网格搜索中明确指定你的 cv 方法。
你好 Jason,文章写得很好,谢谢!
一个问题
我有一个包含 100 个样本和 10 个特征的数据。我想拟合一个准确的模型来预测其中一个(变量)。
方法 1:我将数据分成 80% 用于训练,使用 k-fold 交叉验证,然后在新数据(20%)上验证模型。
方法 2:我使用所有数据来拟合模型,使用 k-fold 交叉验证。
在这两种方法中,哪种是正确的方法?
两者都是可行/正确的,请使用您认为最能为您的特定项目提供模型性能鲁棒估计的方法。
谢谢您,Jason
不客气。
看起来 train_test_split() 的 “stratify” 标志非常有用。此外,几乎任何真实的分类问题都是不平衡的。那么我猜这个策略应该总是作为默认值使用?(因为没有什么可以损失的,对吧?)
完全正确,所有观点都如此!
我猜唯一实际的缺点是计算成本略高。
嗨 Jason。很棒的文章:-)。我只有一个疑问。train_test_split() 的“stratify”标志通过确保训练集和测试集中不平衡的比例相同来帮助分割不平衡数据。在分层 k-fold cv 方法中,它确保每当我们的“训练”数据集被分成 n_folds 时,它在每个分割中都保持相同的数据不平衡比例。因此,当我们处理不平衡数据时,我们需要在 train_test_splti 中使用“stratify 标志”,并且还使用“stratified k-fold cv”。我说的对吗?
谢谢。
正确。
嗨,Jason,
如果我们有 2 个平衡的类,并且我们的解释变量包含一个 class_guess,我们是否应该基于这个错误分类的数据进行 k 折分层?数值上
20k 个样本,10k 类 A 和 10k 类 B
f(label_guess, x2, x3, … xn) = label_true
其中 400 个样本的 label_guess = A 但 label_true = B。直观地说,我认为剩余数据将有助于分类 A/B,但我担心这 400 个点是独特的,并且需要围绕它们构建数据分层。
谢谢!
抱歉,我不明白你的问题,也许你可以重新表述一下?
如果你有平衡的类标签,除非你想,否则不需要分层交叉验证。
谢谢 Jason,抱歉措辞不当,让我用鸢尾花问题来说明我的困境。
鸢尾花数据集有 2 个测量值和一个标签。如果除了 2 个测量值之外,我还有一个专家做出快速的分类判断,那会怎样?所以每个数据点看起来像这样
[2.3, 7.1, Setosa_expert] = [setosa],专家 90% 的时间是正确的,但有时也会出错。我的“理论”是,通过结合专家的猜测和数据,我可以使模型比“仅数据”或“仅专家”表现更好。实际上,我正在处理一项调查,其中受访者经常会错一个问题,这直到后来才根据后续调查(这是 label_truth 的来源)被发现。
现在有一个常见的错误是专家会犯的——他们将 setosa 识别为 versicolor。您是否建议进行分层,以便将 [x, y, setosa] = [versicolor] 视为一个单独的案例,即使 [x, y, setosa] = [setosa] 和 [x, y, versicolor] = [versicolor] 的频率相同?
如果我还是不明白,请随时删除,我可能正在将异常检测方案错误地应用于分类问题。
再次感谢!
是的,这个想法是集成学习的基础,其中专家是一个模型(另一个数据源)。
尝试使用和不使用它,并使用效果最好的方法。
您需要有足够多的案例,说明专家何时正确、何时出错以及以何种不同方式出错。如果分层有助于您确保样本的平衡足以满足模型,那么请使用它。
非常感谢 Jason!非常棒的博客,一直都是!
谢谢!
嗨 Jason,只是在阅读了您的文章后的一些思考:正如您在文章中所演示的那样,考虑到不平衡数据集对交叉验证的影响很重要。但是预处理不平衡数据的其他方法之一是过采样或欠采样。无论哪种方式,都可以获得更平衡的数据。但是为什么要分层抽样呢?谢谢
过采样/欠采样必须在交叉验证过程中发生!
否则,我怀疑由于数据泄露,结果将是无效/乐观的。
嗨,Jason,
我有一个时间序列数据集。我认为我不能在此情况下使用 Stratified KFold。有什么建议吗?
正确。
您可以使用前向验证。
https://machinelearning.org.cn/backtest-machine-learning-models-time-series-forecasting/
谢谢你的回复。
我处理的数据集是不平衡的(99.6% 对 0.4%),这意味着在 K-Fold 迭代中,某些验证集将只包含主要类别!我可以使用分层方法吗?谢谢。
是的,需要分层 k-fold 交叉验证。
更改折数,以便在每个折中都能获得一些每个类别的样本。
嗨,
我有一个不平衡的数据集。我正在尝试使用分层 K 折交叉验证,但它给了我一个错误。错误是 keyvalue error。请帮助我。谢谢。
也许可以尝试减少折数。
嗨 Jason,感谢您富有启发性的文章!
我想问一个关于这篇的问题。
因此,根本问题似乎是由于类别不平衡而导致的模型性能估计不准确,以及由于正例数量在不同折叠之间差异很大而导致估计值变得不稳定。
但是,这个问题只在使用基于准确率的评估指标时(将正确预测数与所有样本数进行比较)才会出现,对吧?例如,使用 ROC 曲线下面积,即使正例的比例不同,估计值也应该在不同折叠之间相似,因为 ROC 曲线基于正确或不正确的正预测数*相对于*它们的真实数量/负例的真实数量。是这样吗?
此致
Jonas
不客气。
不完全是。除非你分层训练/测试集,否则测试框架可能是无效的。
非常感谢您的回复!但是为什么会这样呢?仅仅是因为分配给训练集和测试集的少数类示例的数量在不同折叠之间有所不同吗?难道这只会意味着估计值不太可靠(不同折叠之间的方差很大)而不是完全无效吗?
我不明白的是,基于比例(如 TPR 和 FPR)的估计如何会因为改变它们运行的基准集的大小(真阳性对于 TPR,真阴性对于 FPR)而变得无效。我认为给定模型产生的比例应该始终相同,除了关于哪些示例*确切*——容易或困难的——进入训练集和测试集之外的统计波动。
不,分层意味着每个折叠都具有与原始数据集相同的类别分布。
再次感谢 Jason,我真的很感谢您的努力!但是,我清楚分层的含义。我的前提是您在之前的回复中提到,即使使用 AUC 进行评估,分层训练和测试也是必要的,否则评估指标将无效。这让我感到惊讶,我想知道像 AUC 这样的指标是如何通过只查看比率来“分离”类别分布不平衡的,如何会被不分层而干扰。我将购买您的书来获取回复(只是开玩笑,我可能还是会买)。
分层是一种抽样方法。这意味着当我们抽取样本或分割数据集时,我们确保新样本具有与原始样本相同的类别比例。
指标的选择与数据集如何分割成训练/测试集无关。
嗨,Jason,
也许这段引文可以澄清我的意思
“ROC 曲线具有吸引人的特性:它们对类别分布的变化不敏感。如果测试集中的正负实例比例发生变化,ROC 曲线将不会改变。”
from
Fawcett, T. (2006) An Introduction to ROC Analysis. Pattern Recognition Letters, 27, 861-874. https://doi.org/10.1016/j.patrec.2005.10.010
如果真是这样,那么为什么在使用 AUC 作为评估指标时,我应该费心对样本进行分层。
此致
为了确保模型评估是公平/有代表性的。
如果对有偏差的数据集使用 ROC 曲线,它会产生误导。
https://machinelearning.org.cn/roc-curves-and-precision-recall-curves-for-imbalanced-classification/
嗨 Jason,是否有用于回归的分层交叉验证器(用于超参数调优)?即能够传递一个单独的列用于分层,就像在 train_test_split 中一样?
例如:如果我们试图对汽车发动机温度何时升高的模型进行建模。特征主要是连续的,除了汽车的品牌/型号。由于温度升高存在一定的连续性,我们希望在进行训练-测试分割时避免分层随机化(正如 train_test_split 可以做的那样)。我们执行自己的分层抽样,将每个品牌/型号的最后约 20% 的数据集保留为测试集。对于超参数调优,我们希望在每个折叠中包含每个品牌/型号的比例。但是,可用的交叉验证器(如 StratifiedKFold)只根据目标进行分层,而不是根据您可以传入的单独列。
“分层”对于回归没有意义。
训练集和测试集应独立同分布并代表更广泛的数据集。
嗨,Jason
每个 K-fold 是否大小相同?我的意思是,数据集是否被分成 K 个相等的部分?
提前感谢!
通常是,或者尽可能接近。
嗨,Jason,
好文!
我正在处理一个小的二元分类问题,大约有 130 个标记数据点(类别分布 = 90/40,特征 = 12)。哪种方法更适合此问题?
训练/测试分割还是交叉验证?分层的还是不分层的?
该模型将被用于对另外 700 个未标记的数据点进行分类,并且我们对这些未见数据中的类别分布没有了解。
谢谢!
我建议使用分层重复 k-fold 交叉验证。
你好,
如果我的数据集是多类别的,有 6 个类别标记为 0,1,2,3,4,5。那么我需要对此代码进行什么更改
train_0, train_1 = len(train_y[train_y==0]), len(train_y[train_y==1])
test_0, test_1 = len(test_y[test_y==0]), len(test_y[test_y==1])
print(‘>Train: 0=%d, 1=%d, Test: 0=%d, 1=%d’ % (train_0, train_1, test_0, test_1))
我尝试通过取 train_0,train_1, train_2,train_3= len(train_y[train_y==0]),……
但后来就糊涂了。是否有可能在每个折叠中显示所有类别的实例?
如果您的类标签已经是序数编码,则无需额外的数据准备。
谢谢!!!是的,标签已经编码了。输出中
>Train: 0=792, 1=8, Test: 0=198, 1=2 0 类训练实例数为 792,1 类为 8,测试数据 0 类为 198,1 类为 2。我说的对吗?
所以如果我想显示每个类别的实例数,我需要将相应的标签传递给长度函数,如下所示
train_0, train_1, train_2, train_3,train_4, train_5= len(train_y[train_y==0]), len(train_y[train_y==1]),len(train_y[train_y==2]), len(train_y[train_y==3]),len(train_y[train_y==4]), len(train_y[train_y==5])
我说的对吗?
也许可以使用 Counter() 对象 on train_y 来获取每个标签的计数。
嗨,Jason,
一如既往,感谢您精彩且非常有帮助的文章。
我有一个问题。
我正在进行某种增量/在线学习用于分类器,我不断用更多数据更新我的网络(您也为此写了一篇文章)。
我的数据高度不平衡,所以我所做的就是
1. 使用分层 k-fold 交叉验证(k=5)。
2. 使用类别权重或过采样(训练数据)。
3. 拟合模型并在该折叠的未修改的验证数据上进行验证。
4. 我设置了回调,所以如果它在第一个 epoch 找到最低的“val_loss”并等待其他 epoch 没有减少,它就会用第一个 epoch 的权重重新更新网络。
现在我真的很想知道为什么我的模型会出现欠拟合(至少在我的案例中,类别权重比过采样好得多)。我的意思是,我可以假设我的验证集对我的训练集没有代表性,因此它过早停止。我尝试将 k 降低到 3,但问题仍然存在。
即使我用更多数据更新网络,它的表现也没有改变。
我是否遗漏了什么?如果我可以提供更多信息,请告诉我。
抱歉,我评估有误。但是,非常感谢您对我们开发者的支持。
不客气。
也许您可以进行一些实验来检验您的验证集代表性不足的观点,例如,将结果与更大的验证集进行比较。
嗨,jason
感谢您的精彩文章
您能稍微解释一下如何在 matlab 代码中编写吗?
抱歉,我没有 matlab 示例。
我们应该使用 SMOTE 算法处理不平衡数据,还是使用分层 k-fold 可以解决不平衡数据问题?
两者都使用。SMOTE 和 CV 解决不同的问题。
Smote 将解决训练数据的不平衡问题,CV 将估计模型在新数据上的性能。
感谢您的回复。抱歉,还有一个问题,您知道 crossvalind 命令吗?它使用全部数据,而不是单独使用每个类别。如果我使用 smote,我会对样本量较少的类别进行过采样,那么如何使用 crossvalind 命令?抱歉,我是机器学习新手。
此致
抱歉,我没听说过“crossvalind”命令。
使用 SMOTE 时,您可以使用带有交叉验证的管道来确保过采样仅在每次迭代的训练折叠上应用。
你好 Jason,
非常感谢您让这么多关于交叉验证的事情变得清晰。
我有一个关于 RepeatedStratifiedKFold 的问题。
它究竟是如何工作的?
1) 将数据集分割成 K 个折叠。在分割成 k-fold 时,它确保每个类别在数据集中都具有相同的比例。(创建分层)。
2) 使用 k-1 折构建模型,并使用剩余的 1 个保留数据集来验证模型。
现在我的问题是,“重复”何时引入?
是在整个 k-fold 过程完成后,在用所有折叠验证模型后,然后通过重新分配数据集并创建新折叠,还是在完成 k-split 的每个分割时就重新分配数据?
您能在此问题上给我澄清一下吗?
此致
重复发生在您的步骤 2 中:当 k-1 折用于模型,1 折用于验证时,是哪一个?这里有 k 种不同的可能性。因此,您可以进行循环,重复 k 次,每次使用不同的折叠。
嗨
我们应该在特征提取后还是特征提取前对数据进行重采样,例如 smote 或随机欠采样?
在特征提取之前进行采样,可以使您的总计算成本更低。
感谢 Jason 博士的宝贵博文。
我有一个观察。我们总是执行归一化(乘以 1/255 用于图像),然后进行 train_test_split。然而,在任何 k-fold 交叉验证博客中,您都没有在 k-fold 的 for 循环中显示归一化步骤。
我的问题是:我们需要在每个数据折叠中执行图像(在数组中)的归一化/重新缩放吗?
当我进行k折交叉验证的for循环中对每个折叠数据进行重缩放时,我的训练错误会缓慢增长到非常低的准确率,而交叉验证的准确率在所有时期都保持在0.00(零)。
恳请指导。
谢谢,顺颂时祺。
不是每一个折叠,而是更像是您在开始k折之前对图像进行预处理的一个阶段。原因是使输入的像素数据保持在0到1的范围内,以便于训练。对于其他类型的数据,您进行独热编码等操作,同样是为了帮助模型学习。
非常感谢您快速而准确的解决方案。感谢您的支持,并且是一位“真正的向导”。一切顺利。
我可以在以下页面描述的多输出模型中使用分层抽样吗?
https://machinelearning.org.cn/neural-network-models-for-combined-classification-and-regression
在您展示分层K折划分的示例中,您应该使用iloc,否则您的示例代码就会出错。
for train_ix, test_ix in kfold.split(X)
# select rows
train_X, test_X = X[train_ix], X[test_ix]
train_y, test_y = y[train_ix], y[test_ix]
应该是这样的
for train_ix, test_ix in kfold.split(X)
# select rows
train_X, test_X = X.iloc[train_ix], X.iloc[test_ix]
train_y, test_y = y.iloc[train_ix], y.iloc[test_ix]
你好Panagis…谢谢你的反馈!
您好,感谢这些信息…我不明白的是,为什么SMOTE与StratifiedKFold一起使用,如果StratifiedKFold本身就可以解决不平衡问题?
你好Christian…以下资源可能会帮助您更清楚地理解
https://machinelearning.org.cn/smote-oversampling-for-imbalanced-classification/
感谢精彩的教程。
我只是想请您为我澄清一件事。
根据我的理解,我们必须在定义了交叉验证折叠后应用欠采样/过采样,否则会有数据泄露问题,但我对这种方法的实现有点困惑。
当使用
# 定义数据集
X, y = make_classification(n_samples=10000, n_features=2, n_redundant=0,
n_clusters_per_class=1, weights=[0.99], flip_y=0, random_state=1)
# 定义流水线
steps = [(‘over’, SMOTE()), (‘model’, DecisionTreeClassifier())]
管道 = Pipeline(steps=steps)
# 评估流水线
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
scores = cross_val_score(pipeline, X, y, scoring=’roc_auc’, cv=cv, n_jobs=-1)
print(‘平均 ROC AUC:%.3f’ % scores 的平均值)
代码是否(按此顺序)执行了这些操作?
1) 对X,y(数据集)应用分层CV(交叉验证)
2) 从CV获取训练集和测试集折叠
3) 仅将管道应用于训练折叠
5) 在未采样的测试折叠上应用评估指标
4) 重复n次迭代
你好Mohamed…以下内容可能值得参考
https://machinelearning.org.cn/repeated-k-fold-cross-validation-with-python/
你好Jason,我认为你在术语使用上犯了一个错误
“我们可以看到,在这种情况下,有些划分具有预期的8/2的训练集和测试集划分,而有些则差得多,例如6/4(乐观)和10/0(悲观)。”
10/0的情况是乐观的,6/4是悲观的,难道不是吗?
感谢您的反馈Nima!
你好。
如果我们为类分配权重……
我们可以从原始数据集的频率中获取这些值吗?
还是正确的方法是使用每个k折的频率?(以避免泄露)
晚上好,先生。很高兴从您的页面学习到分层k折交叉验证。但我对我的数据集有一个担忧。我正在处理一个与慢性肾脏病6个阶段相关的数据集。我的Y变量是受调查患者的疾病阶段。我的目标是比较机器学习模型,以确定哪个模型能最好地预测CKD的阶段,并在使用该模型后,识别与CKD阶段最相关的变量。作为交叉验证方法,我认为分层k折交叉验证是最好的。但在我的数据中,有一个变量SEX,分为M和F。我想,这一定也需要考虑在分层中。我该怎么办?
你好Floriane…有关重复k折交叉验证的最佳实践可以在这里找到
https://machinelearning.org.cn/repeated-k-fold-cross-validation-with-python/