癌症检测是类别不平衡分类问题的一个流行示例,因为非癌症病例通常远远多于实际癌症病例。
一个标准的类别不平衡分类数据集是乳腺钼靶数据集,该数据集涉及从放射学扫描中检测乳腺癌,特别是乳腺钼靶片上显示为亮斑的微钙化簇。该数据集是通过扫描图像、将其分割成候选对象,并使用计算机视觉技术来描述每个候选对象来构建的。
由于严重的类别不平衡,它是一个受欢迎的类别不平衡分类数据集,特别是其中98%的候选微钙化点不是癌症,只有2%被经验丰富的放射科医生标记为癌症。
在本教程中,您将了解如何为类别不平衡的乳腺钼靶癌症分类数据集开发和评估模型。
完成本教程后,您将了解:
- 如何加载和探索数据集,并为数据准备和模型选择提供思路。
- 如何评估一系列机器学习模型并通过数据成本敏感技术改进它们的性能。
- 如何拟合最终模型并使用它来预测特定案例的类别标签。
通过我的新书《Python 类别不平衡分类》启动您的项目,其中包括分步教程和所有示例的Python源代码文件。
让我们开始吧。

开发用于检测微钙化点的类别不平衡分类模型
照片来源:Bernard Spragg. NZ,部分权利保留。
教程概述
本教程分为五个部分;它们是:
- 乳腺钼靶数据集
- 探索数据集
- 模型测试和基线结果
- 评估模型
- 评估机器学习算法
- 评估成本敏感算法
- 在新数据上进行预测
乳腺钼靶数据集
在这个项目中,我们将使用一个标准的类别不平衡机器学习数据集,称为“乳腺钼靶”数据集,有时也称为“Woods 乳腺钼靶”。
该数据集归功于 Kevin Woods 等人,以及1993年题为“乳腺钼靶微钙化点检测的模式识别技术比较评估”的论文。
该问题的重点是从放射学扫描中检测乳腺癌,特别是乳腺钼靶片上显示为亮斑的微钙化簇的存在。
该数据集最初始于24例已知癌症诊断的乳腺钼靶片,这些钼靶片被扫描。然后,使用图像分割计算机视觉算法对图像进行预处理,以从乳腺钼靶图像中提取候选对象。分割后,这些对象由经验丰富的放射科医生手动标记。
从分割的对象中提取了总共29个被认为与模式识别最相关的特征,然后减少到18个,最后减少到7个,如下所示(直接摘自论文):
- 对象面积(以像素为单位)。
- 对象的平均灰度级。
- 对象周长像素的梯度强度。
- 对象中的均方根噪声波动。
- 对比度,对象平均灰度级减去围绕对象的两像素宽边框的平均值。
- 基于形状描述符的低阶矩。
有两个类别,目标是使用给定分割对象的特征来区分微钙化点和非微钙化点。
- 非微钙化点:阴性病例,或多数类。
- 微钙化点:阳性病例,或少数类。
原论文中评估和比较了许多模型,例如神经网络、决策树和 k-近邻。模型使用ROC 曲线进行评估,并使用 ROC 曲线下面积,简称 ROC AUC 进行比较。
选择 ROC 曲线和 ROC 曲线下面积是为了最小化假阳性率(特异度的补数)并最大化真阳性率(敏感度),即 ROC 曲线的两个轴。使用 ROC 曲线也表明希望得到一个概率模型,从中操作员可以选择一个概率阈值作为可接受的假阳性率和真阳性率之间的截止值。
他们的结果表明,“线性分类器”(似乎是高斯朴素贝叶斯分类器)表现最好,平均 ROC AUC 为 0.936(运行 100 次)。
接下来,我们仔细看看数据。
想要开始学习不平衡分类吗?
立即参加我为期7天的免费电子邮件速成课程(附示例代码)。
点击注册,同时获得该课程的免费PDF电子书版本。
探索数据集
乳腺钼靶数据集是一个广泛使用的标准机器学习数据集,用于探索和演示许多专门为类别不平衡分类设计的技术。
一个例子是流行的SMOTE 数据过采样技术。
该数据集的一个版本已提供,该版本与原始论文中描述的数据集存在一些差异。
首先,下载数据集并将其保存在当前工作目录中,文件名为“mammography.csv”。
查看文件内容。
文件的前几行应如下所示
1 2 3 4 5 6 |
0.23001961,5.0725783,-0.27606055,0.83244412,-0.37786573,0.4803223,'-1' 0.15549112,-0.16939038,0.67065219,-0.85955255,-0.37786573,-0.94572324,'-1' -0.78441482,-0.44365372,5.6747053,-0.85955255,-0.37786573,-0.94572324,'-1' 0.54608818,0.13141457,-0.45638679,-0.85955255,-0.37786573,-0.94572324,'-1' -0.10298725,-0.3949941,-0.14081588,0.97970269,-0.37786573,1.0135658,'-1' ... |
我们可以看到该数据集有六个输入变量而不是七个。论文中列出的第一个输入变量(像素面积)可能已从此数据集版本中删除。
输入变量是数值(实值),目标变量是字符串,其中“-1”表示多数类,“1”表示少数类。这些值需要分别编码为 0 和 1,以满足分类算法在二元类别不平衡分类问题上的期望。
可以使用read_csv() Pandas 函数将数据集加载为 DataFrame,指定位置和没有标题行。
1 2 3 4 5 |
... # 定义数据集位置 filename = 'mammography.csv' # 将csv文件加载为数据框 dataframe = read_csv(filename, header=None) |
加载后,我们可以通过打印DataFrame的形状来总结行数和列数。
1 2 3 |
... # 总结数据集的形状 print(dataframe.shape) |
我们还可以使用Counter对象来汇总每个类别的示例数量。
1 2 3 4 5 6 7 |
... # 总结类别分布 target = dataframe.values[:,-1] counter = Counter(target) for k,v in counter.items(): per = v / len(target) * 100 print('Class=%s, Count=%d, Percentage=%.3f%%' % (k, v, per)) |
总而言之,下面列出了加载和汇总数据集的完整示例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# 加载并汇总数据集 from pandas import read_csv from collections import Counter # 定义数据集位置 filename = 'mammography.csv' # 将csv文件加载为数据框 dataframe = read_csv(filename, header=None) # 总结数据集的形状 print(dataframe.shape) # 总结类别分布 target = dataframe.values[:,-1] counter = Counter(target) for k,v in counter.items(): per = v / len(target) * 100 print('Class=%s, Count=%d, Percentage=%.3f%%' % (k, v, per)) |
运行示例后,首先加载数据集并确认行数和列数,即11,183行和六个输入变量以及一个目标变量。
然后汇总类别分布,确认了严重的类别不平衡,多数类别(无癌症)约占98%,少数类别(癌症)约占2%。
1 2 3 |
(11183, 7) Class='-1', Count=10923, Percentage=97.675% Class='1', Count=260, Percentage=2.325% |
就负例与正例的比例而言,该数据集似乎总体上与 SMOTE 论文中描述的数据集相符。
同样,少数类和多数类中的具体示例数量也与论文中的相符。
— SMOTE: Synthetic Minority Over-sampling Technique, 2002。
此外,少数类和多数类中的具体示例数量也与论文中的相符。
实验是在乳腺钼靶数据集上进行的。最初,多数类有10923个示例,少数类有260个示例。
— SMOTE: Synthetic Minority Over-sampling Technique, 2002。
我认为这是同一个数据集,尽管我无法解释输入特征数量的差异,例如,六个与原始论文中的七个相比。
我们还可以通过为每个变量创建直方图来查看六个数值输入变量的分布。
完整的示例如下所示。
1 2 3 4 5 6 7 8 9 10 |
# 创建数值输入变量的直方图 from pandas import read_csv from matplotlib import pyplot # 定义数据集位置 filename = 'mammography.csv' # 将csv文件加载为数据框 df = read_csv(filename, header=None) # 所有变量的直方图 df.hist() pyplot.show() |
运行示例,生成包含数据集中六个数值输入变量每个变量一个直方图子图的图形。
我们可以看到变量的尺度不同,并且大多数变量都呈指数分布,例如,大多数案例落入一个 bin 中,其余案例落入一个长尾中。最后一个变量似乎呈双峰分布。
根据建模算法的选择,我们预计将分布缩放到相同的范围将是有用的,并且可能需要使用一些幂变换。

乳腺钼靶数据集数值输入变量的直方图
我们还可以为每对输入变量创建散点图,称为散点图矩阵。
这有助于查看任何变量是否相互关联或以相同方向变化,例如,是否相关。
我们还可以根据类别标签对每个散点图的点进行着色。在这种情况下,多数类别(无癌症)将映射为蓝色点,少数类别(癌症)将映射为红色点。
完整的示例如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# 创建数值输入变量的两两散点图 from pandas import read_csv from pandas.plotting import scatter_matrix from matplotlib import pyplot # 定义数据集位置 filename = 'mammography.csv' # 将csv文件加载为数据框 df = read_csv(filename, header=None) # 定义类别值到颜色的映射 color_dict = {"'-1'":'blue', "'1'":'red'} # 根据类别值将每行映射到颜色 colors = [color_dict[str(x)] for x in df.values[:, -1]] # 所有数值变量的两两散点图 scatter_matrix(df, diagonal='kde', color=colors) pyplot.show() |
运行示例,创建一个显示散点图矩阵的图形,该矩阵为六行六列,比较了六个数值输入变量与彼此之间的关系。矩阵的对角线显示了每个变量的密度分布。
每个配对在对角线上下各出现两次,提供了两种查看相同变量交互的方式。
我们可以看到,许多变量的分布对于两个类别标签是不同的,这表明在癌症和无癌症病例之间进行一些合理的区分是可行的。

乳腺钼靶数据集数值输入变量按类别的散点图矩阵
现在我们已经审阅了数据集,接下来我们将开发一个测试工具来评估候选模型。
模型测试和基线结果
我们将使用重复分层 k 折交叉验证来评估候选模型。
k 折交叉验证过程提供了对模型性能的良好通用估计,至少与单个训练测试分割相比,其乐观偏差不会太大。我们将使用 k=10,这意味着每个折叠大约包含 11183/10 或约 1,118 个示例。
分层意味着每个折叠将包含相同比例的类别示例,即约 98% 的无癌症对象和 2% 的癌症对象。重复表示将执行多次评估过程,以帮助避免偶然结果并更好地捕捉所选模型的方差。我们将使用三次重复。
这意味着一个模型将被拟合和评估10 * 3或30次,并将报告这些运行的均值和标准差。
这可以通过使用RepeatedStratifiedKFold scikit-learn 类来实现。
我们将使用 ROC 曲线下面积或 ROC AUC 进行评估和比较模型,该面积通过roc_auc_score() 函数计算。
我们可以定义一个函数来加载数据集并将列分割为输入和输出变量。我们将正确地将类别标签编码为 0 和 1。下面的load_dataset()函数实现了这一点。
1 2 3 4 5 6 7 8 9 10 11 |
# 加载数据集 def load_dataset(full_path): # 将数据集加载为numpy数组 data = read_csv(full_path, header=None) # 检索numpy数组 data = data.values # 分割为输入和输出元素 X, y = data[:, :-1], data[:, -1] # 对目标变量进行标签编码,使其具有类别0和1 y = LabelEncoder().fit_transform(y) return X, y |
然后,我们可以定义一个函数,该函数将对给定模型进行数据集评估,并返回每个折叠和重复的 ROC AUC 分数列表。
下面的 evaluate_model() 函数实现了这一点,它将数据集和模型作为参数,并返回分数列表。
1 2 3 4 5 6 7 |
# 评估模型 def evaluate_model(X, y, model): # 定义评估过程 cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1) # 评估模型 scores = cross_val_score(model, X, y, scoring='roc_auc', cv=cv, n_jobs=-1) return scores |
最后,我们可以使用这个测试工具评估数据集上的基线模型。
预测类别比例与各类别基础比例相同的模型将产生 ROC AUC 0.5,这是该数据集的基线性能。这被称为“无技能”分类器。
这可以通过 scikit-learn 库中的DummyClassifier类来实现,并将“strategy”参数设置为‘stratified’。
1 2 3 |
... # 定义参考模型 model = DummyClassifier(strategy='stratified') |
模型评估完成后,我们可以直接报告 ROC AUC 分数的均值和标准差。
1 2 3 4 5 |
... # 评估模型 scores = evaluate_model(X, y, model) # 总结性能 print('Mean ROC AUC: %.3f (%.3f)' % (mean(scores), std(scores))) |
总而言之,加载数据集、评估基线模型和报告性能的完整示例列在下面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
# 测试框架和基线模型评估 from collections import Counter from numpy import mean from numpy import std from pandas import read_csv from sklearn.preprocessing import LabelEncoder from sklearn.model_selection import cross_val_score from sklearn.model_selection import RepeatedStratifiedKFold from sklearn.dummy import DummyClassifier # 加载数据集 def load_dataset(full_path): # 将数据集加载为numpy数组 data = read_csv(full_path, header=None) # 检索numpy数组 data = data.values # 分割为输入和输出元素 X, y = data[:, :-1], data[:, -1] # 对目标变量进行标签编码,使其具有类别0和1 y = LabelEncoder().fit_transform(y) 返回 X, y # 评估模型 def evaluate_model(X, y, model): # 定义评估过程 cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1) # 评估模型 scores = cross_val_score(model, X, y, scoring='roc_auc', cv=cv, n_jobs=-1) 返回 分数 # 定义数据集位置 full_path = 'mammography.csv' # 加载数据集 X, y = load_dataset(full_path) # 总结已加载的数据集 print(X.shape, y.shape, Counter(y)) # 定义参考模型 model = DummyClassifier(strategy='stratified') # 评估模型 scores = evaluate_model(X, y, model) # 总结性能 print('Mean ROC AUC: %.3f (%.3f)' % (mean(scores), std(scores))) |
运行示例首先加载并总结数据集。
我们可以看到已加载的行数正确,并且有六个计算机视觉衍生的输入变量。重要的是,我们可以看到类别标签已正确映射到整数,其中 0 代表多数类,1 代表少数类,这对于类别不平衡的二元分类数据集来说是惯例。
接下来,报告 ROC AUC 分数的平均值。
正如预期的那样,无技能分类器实现了最差的性能,平均 ROC AUC 约为 0.5。这提供了性能基准,高于此性能的模型可被认为在该数据集上具有技能。
1 2 |
(11183, 6) (11183,) Counter({0: 10923, 1: 260}) Mean ROC AUC: 0.503 (0.016) |
现在我们有了测试工具和性能基线,我们可以开始评估该数据集上的一些模型。
评估模型
在本节中,我们将使用上一节中开发的测试工具,评估数据集上的一系列不同技术。
目标是演示如何系统地解决问题,并展示一些针对不平衡分类问题设计的技术的能力。
报告的性能良好,但尚未高度优化(例如,超参数未进行调整)。
你能做得更好吗?如果你能使用相同的测试框架获得更好的 ROC AUC 性能,我很乐意听到。请在下面的评论中告知我。
评估机器学习算法
让我们开始在数据集上评估一系列机器学习模型。
在数据集上对一系列不同的线性和非线性算法进行快速检查,可以很快地发现哪些效果好值得进一步关注,哪些效果不好。
我们将在乳腺钼靶数据集上评估以下机器学习模型:
- 逻辑回归 (LR)
- 支持向量机 (SVM)
- 装袋决策树(BAG)
- 随机森林 (RF)
- 梯度提升机 (GBM)
我们将使用大多数默认模型超参数,除了集成算法中的树的数量,我们将将其设置为合理的默认值1000。
我们将依次定义每个模型并将它们添加到一个列表中,以便我们可以按顺序评估它们。下面的 get_models() 函数定义了要评估的模型列表,以及用于稍后绘制结果的模型简称列表。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# 定义要测试的模型 定义 获取_模型(): models, names = list(), list() # LR models.append(LogisticRegression(solver='lbfgs')) names.append('LR') # SVM models.append(SVC(gamma='scale')) names.append('SVM') # Bagging models.append(BaggingClassifier(n_estimators=1000)) names.append('BAG') # RF models.append(RandomForestClassifier(n_estimators=1000)) names.append('RF') # GBM models.append(GradientBoostingClassifier(n_estimators=1000)) names.append('GBM') return models, names |
然后,我们可以按顺序枚举模型列表并评估每个模型,报告平均 ROC AUC 并存储分数以供以后绘图。
1 2 3 4 5 6 7 8 9 10 11 |
... # 定义模型 models, names = get_models() results = list() # 评估每个模型 for i in range(len(models)): # 评估模型并存储结果 scores = evaluate_model(X, y, models[i]) results.append(scores) # 总结并存储 print('>%s %.3f (%.3f)' % (names[i], mean(scores), std(scores))) |
在运行结束时,我们可以将每个样本的分数绘制成箱须图,并具有相同的比例,以便直接比较其分布。
1 2 3 4 |
... # 绘制结果图 pyplot.boxplot(results, labels=names, showmeans=True) pyplot.show() |
将所有内容整合在一起,在乳腺钼靶数据集上评估一系列机器学习算法的完整示例列在下面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
# 在乳腺钼靶数据集上抽样检查机器学习算法 from numpy import mean from numpy import std from pandas import read_csv from matplotlib import pyplot from sklearn.preprocessing import LabelEncoder from sklearn.model_selection import cross_val_score from sklearn.model_selection import RepeatedStratifiedKFold from sklearn.linear_model import LogisticRegression from sklearn.svm import SVC from sklearn.ensemble import RandomForestClassifier from sklearn.ensemble import GradientBoostingClassifier from sklearn.ensemble import BaggingClassifier # 加载数据集 def load_dataset(full_path): # 将数据集加载为numpy数组 data = read_csv(full_path, header=None) # 检索numpy数组 data = data.values # 分割为输入和输出元素 X, y = data[:, :-1], data[:, -1] # 对目标变量进行标签编码,使其具有类别0和1 y = LabelEncoder().fit_transform(y) 返回 X, y # 评估模型 def evaluate_model(X, y, model): # 定义评估过程 cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1) # 评估模型 scores = cross_val_score(model, X, y, scoring='roc_auc', cv=cv, n_jobs=-1) 返回 分数 # 定义要测试的模型 定义 获取_模型(): models, names = list(), list() # LR models.append(LogisticRegression(solver='lbfgs')) names.append('LR') # SVM models.append(SVC(gamma='scale')) names.append('SVM') # Bagging models.append(BaggingClassifier(n_estimators=1000)) names.append('BAG') # RF models.append(RandomForestClassifier(n_estimators=1000)) names.append('RF') # GBM models.append(GradientBoostingClassifier(n_estimators=1000)) names.append('GBM') return models, names # 定义数据集位置 full_path = 'mammography.csv' # 加载数据集 X, y = load_dataset(full_path) # 定义模型 models, names = get_models() results = list() # 评估每个模型 for i in range(len(models)): # 评估模型并存储结果 scores = evaluate_model(X, y, models[i]) results.append(scores) # 总结并存储 print('>%s %.3f (%.3f)' % (names[i], mean(scores), std(scores))) # 绘制结果图 pyplot.boxplot(results, labels=names, showmeans=True) pyplot.show() |
运行示例,依次评估每个算法,并报告平均和标准差 ROC AUC。
注意:鉴于算法或评估过程的随机性质,或数值精度的差异,您的结果可能会有所不同。考虑多次运行示例并比较平均结果。
在这种情况下,我们可以看到所有测试过的算法都有技能,实现了高于默认值 0.5 的 ROC AUC。
结果表明,决策树算法的集成在该数据集上表现更好,也许随机森林表现最好,ROC AUC 约为 0.950。
有趣的是,这比论文中描述的 0.93 的 ROC AUC 还要好,尽管我们使用了不同的模型评估程序。
由于我们在拟合模型之前没有缩放输入变量,因此对 LR 和 SVM 算法的评估有点不公平。我们可以在下一节中探讨这一点。
1 2 3 4 5 |
>LR 0.919 (0.040) >SVM 0.880 (0.049) >BAG 0.941 (0.041) >RF 0.950 (0.036) >GBM 0.918 (0.037) |
生成一个图形,显示每个算法的结果样本的一个箱形图。箱形图显示了数据的中间 50%,每个箱形图中间的橙色线显示了样本的中位数,每个箱形图中的绿色三角形显示了样本的平均值。
我们可以看到,BAG 和 RF 都有紧密的分布,其均值和中位数也紧密对齐,这可能表明分数分布不是偏斜的,而是高斯分布的,例如,是稳定的。

类别不平衡乳腺钼靶数据集机器学习模型的箱须图
现在我们有了一组不错的初步结果,让我们看看是否可以通过成本敏感分类器来改进它们。
评估成本敏感算法
一些机器学习算法在拟合模型时可以调整以更关注一个类别而非另一个类别。
这些被称为成本敏感机器学习模型,它们可以通过指定一个与类别分布成反比的成本来用于类别不平衡分类。例如,对于多数类和少数类分别为 98% 和 2% 的分布,我们可以指定将少数类的错误加权为 98,将多数类的错误加权为 2。
提供此功能的三个算法是:
- 逻辑回归 (LR)
- 支持向量机 (SVM)
- 随机森林 (RF)
这可以通过在 scikit-learn 中将“class_weight”参数设置为“balanced”来实现,使这些算法具有成本敏感性。
例如,下面更新的get_models()函数定义了将在我们的数据集上评估的这三个算法的成本敏感版本。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# 定义要测试的模型 定义 获取_模型(): models, names = list(), list() # LR models.append(LogisticRegression(solver='lbfgs', class_weight='balanced')) names.append('LR') # SVM models.append(SVC(gamma='scale', class_weight='balanced')) names.append('SVM') # RF models.append(RandomForestClassifier(n_estimators=1000)) names.append('RF') return models, names |
此外,在探索数据集时,我们注意到许多变量似乎具有指数级的数据分布。有时,我们可以通过对每个变量使用幂变换来更好地分散数据。这对于 LR 和 SVM 算法特别有帮助,也可能有助于 RF 算法。
我们可以在交叉验证模型评估过程的每个折叠中使用Pipeline来实现这一点。第一步将在训练集折叠上学习PowerTransformer,并将其应用于训练集和测试集折叠。第二步将是我们正在评估的模型。然后,我们可以使用我们的evaluate_model()函数直接评估 pipeline,例如:
1 2 3 4 5 6 7 |
... # 定义 pipeline 步骤 steps = [('p', PowerTransformer()), ('m',models[i])] # 定义流水线 pipeline = Pipeline(steps=steps) # 评估 pipeline 并存储结果 scores = evaluate_model(X, y, pipeline) |
将所有内容整合在一起,在乳腺钼靶数据集上评估幂变换的成本敏感机器学习算法的完整示例列在下面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# 乳腺钼靶数据集上的成本敏感机器学习算法 from numpy import mean from numpy import std from pandas import read_csv from matplotlib import pyplot from sklearn.preprocessing import LabelEncoder from sklearn.preprocessing import PowerTransformer from sklearn.pipeline import Pipeline from sklearn.model_selection import cross_val_score from sklearn.model_selection import RepeatedStratifiedKFold from sklearn.linear_model import LogisticRegression from sklearn.svm import SVC from sklearn.ensemble import RandomForestClassifier # 加载数据集 def load_dataset(full_path): # 将数据集加载为numpy数组 data = read_csv(full_path, header=None) # 检索numpy数组 data = data.values # 分割为输入和输出元素 X, y = data[:, :-1], data[:, -1] # 对目标变量进行标签编码,使其具有类别0和1 y = LabelEncoder().fit_transform(y) 返回 X, y # 评估模型 def evaluate_model(X, y, model): # 定义评估过程 cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1) # 评估模型 scores = cross_val_score(model, X, y, scoring='roc_auc', cv=cv, n_jobs=-1) 返回 分数 # 定义要测试的模型 定义 获取_模型(): models, names = list(), list() # LR models.append(LogisticRegression(solver='lbfgs', class_weight='balanced')) names.append('LR') # SVM models.append(SVC(gamma='scale', class_weight='balanced')) names.append('SVM') # RF models.append(RandomForestClassifier(n_estimators=1000)) names.append('RF') return models, names # 定义数据集位置 full_path = 'mammography.csv' # 加载数据集 X, y = load_dataset(full_path) # 定义模型 models, names = get_models() results = list() # 评估每个模型 for i in range(len(models)): # 定义 pipeline 步骤 steps = [('p', PowerTransformer()), ('m',models[i])] # 定义 pipeline pipeline = Pipeline(steps=steps) # 评估模型并存储结果 scores = evaluate_model(X, y, pipeline) results.append(scores) # 总结并存储 print('>%s %.3f (%.3f)' % (names[i], mean(scores), std(scores))) # 绘制结果图 pyplot.boxplot(results, labels=names, showmeans=True) pyplot.show() |
运行示例,依次评估每个算法,并报告平均和标准差 ROC AUC。
注意:鉴于算法或评估过程的随机性质,或数值精度的差异,您的结果可能会有所不同。考虑多次运行示例并比较平均结果。
在这种情况下,我们可以看到,与未经转换且不考虑成本的版本相比,所有三个测试算法在 ROC AUC 上都实现了提升。有趣的是,我们可以重复不进行转换的实验,看看是转换、算法的成本敏感版本,还是两者共同导致了性能的提升。
在这种情况下,我们可以看到 SVM 取得了最佳性能,在本节和上一节中都优于 RF,并且平均 ROC AUC 约为 0.957。
1 2 3 |
>LR 0.922 (0.036) >SVM 0.957 (0.024) >RF 0.951 (0.035) |
然后创建箱须图来比较 ROC AUC 分数的分布。
与另外两个模型相比,SVM 的分布显得更加紧凑。因此,其性能可能稳定,并且可能是最终模型的不错选择。

乳腺钼靶数据集上成本敏感机器学习模型的箱须图
接下来,让我们看看如何使用最终模型对新数据进行预测。
在新数据上进行预测
在本节中,我们将拟合最终模型并使用它来预测单个数据行。
我们将使用 SVM 模型的成本敏感版本作为最终模型,并在拟合模型和进行预测之前对数据进行幂转换。使用管道将确保对输入数据始终正确执行转换。
首先,我们可以将模型定义为管道。
1 2 3 4 5 |
... # 定义要评估的模型 model = SVC(gamma='scale', class_weight='balanced') # 幂转换然后拟合模型 pipeline = Pipeline(steps=[('t',PowerTransformer()), ('m',model)]) |
定义好后,我们就可以在整个训练数据集上对其进行拟合。
1 2 3 |
... # 拟合模型 pipeline.fit(X, y) |
拟合后,我们可以通过调用 `predict()` 函数来使用它为新数据进行预测。这将返回 0(“无癌症”)或 1(“癌症”)的类别标签。
例如
1 2 3 4 5 |
... # 定义一行数据 row = [...] # 进行预测 yhat = model.predict([row]) |
为了演示这一点,我们可以使用拟合的模型对一些已知为无癌症或癌症的案例进行标签预测。
完整的示例如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
# 拟合模型并对乳腺钼靶数据集进行预测 from pandas import read_csv from sklearn.preprocessing import LabelEncoder from sklearn.preprocessing import PowerTransformer from sklearn.svm import SVC from sklearn.pipeline import Pipeline # 加载数据集 def load_dataset(full_path): # 将数据集加载为numpy数组 data = read_csv(full_path, header=None) # 检索numpy数组 data = data.values # 分割为输入和输出元素 X, y = data[:, :-1], data[:, -1] # 对目标变量进行标签编码,使其具有类别0和1 y = LabelEncoder().fit_transform(y) 返回 X, y # 定义数据集位置 full_path = 'mammography.csv' # 加载数据集 X, y = load_dataset(full_path) # 定义要评估的模型 model = SVC(gamma='scale', class_weight='balanced') # 幂转换然后拟合模型 pipeline = Pipeline(steps=[('t',PowerTransformer()), ('m',model)]) # 拟合模型 pipeline.fit(X, y) # 评估一些无癌症案例(已知类别 0) print('无癌症:') data = [[0.23001961,5.0725783,-0.27606055,0.83244412,-0.37786573,0.4803223], [0.15549112,-0.16939038,0.67065219,-0.85955255,-0.37786573,-0.94572324], [-0.78441482,-0.44365372,5.6747053,-0.85955255,-0.37786573,-0.94572324]] for row in data: # 进行预测 yhat = pipeline.predict([row]) # 获取标签 label = yhat[0] # 总结 print('>预测=%d (预期为 0)' % (label)) # 评估一些癌症案例(已知类别 1) print('癌症:') data = [[2.0158239,0.15353258,-0.32114211,2.1923706,-0.37786573,0.96176503], [2.3191888,0.72860087,-0.50146835,-0.85955255,-0.37786573,-0.94572324], [0.19224721,-0.2003556,-0.230979,1.2003796,2.2620867,1.132403]] for row in data: # 进行预测 yhat = pipeline.predict([row]) # 获取标签 label = yhat[0] # 总结 print('>预测=%d (预期为 1)' % (label)) |
首先运行示例,在整个训练数据集上拟合模型。
然后,使用从数据集文件中选择的拟合模型来预测无癌症病例的标签。我们可以看到所有病例都被正确预测。
然后将一些实际癌症病例作为输入提供给模型并预测标签。正如我们所期望的,所有病例都得到了正确的标签预测。
1 2 3 4 5 6 7 8 |
无癌症 >预测值=0(期望值 0) >预测值=0(期望值 0) >预测值=0(期望值 0) 癌症 >预测值=1(期望值 1) >预测值=1(期望值 1) >预测值=1(期望值 1) |
进一步阅读
如果您想深入了解,本节提供了更多关于该主题的资源。
论文
- 用于检测乳腺钼靶中微钙化点的模式识别技术的比较评估, 1993.
- SMOTE:合成少数过采样技术, 2002.
API
- sklearn.model_selection.RepeatedStratifiedKFold API.
- sklearn.metrics.roc_auc_score API.
- sklearn.dummy.DummyClassifier API.
- sklearn.svm.SVC API.
数据集 (Dataset)
总结
在本教程中,您了解了如何为不平衡的乳腺钼靶癌症分类数据集开发和评估模型。
具体来说,你学到了:
- 如何加载和探索数据集,并为数据准备和模型选择提供思路。
- 如何评估一系列机器学习模型并通过数据成本敏感技术改进它们的性能。
- 如何拟合最终模型并使用它来预测特定案例的类别标签。
你有什么问题吗?
在下面的评论中提出你的问题,我会尽力回答。
回归是否受不平衡数据集的影响?
确实存在这样的情况,但这与分类中的不平衡概念不同。
此处有示例书籍
https://machinelearning.org.cn/resources-for-imbalanced-classification/
一如既往,非常棒的参考。非常感谢!
谢谢。
嗨
在这篇博文中写道:
“这意味着将对单个模型进行 10 * 3 或 30 次的拟合和评估,并报告这些运行的平均值和标准差。”
我原以为在 k 折交叉验证的每一步都会从头开始训练一个新模型,而不是同一个模型,根据我在这里读到的内容
https://machinelearning.org.cn/k-fold-cross-validation/
在步骤 4 中提到
4. 保留评估分数并丢弃模型
谢谢
是的,每个折叠都会拟合一个新模型并计算一个分数。然后我们有一个分数群体,我们对其进行总结。这之间没有矛盾。
嗨,Jason,
我认为你这里没有使用 SMOTE,预测也是在没有 SMOTE 的情况下进行的。
另外,如果我们有更大的数据集,该怎么办?对于这类数据集,不使用 SMOTE 是否可以?
本教程未使用 SMOTE。
如果你想使用 SMOTE,尝试一下,看看它与其他方法相比如何。
嗨,Jason,
又一个很棒的教程,用于巩固不平衡模型训练和输入数据准备,例如 PowerTransformer() sklearn 函数。
在我看来,我也应用了 StandardScaler() sklearn 函数来“正则化”数据集输入。
我不知道科学文献中是否也称这些数据集输入准备为“正则化器”,就像我一样?但是,我更倾向于使用这个类比。
我还在你建议的 5 种模型中添加了另外 2 种模型(ExtraTreesClassifier 和 XGBClassifier),以比较它们之间的性能。
我的结果是:
– 不应用任何数据准备(StandardScaler 或 PowerTransformer)且没有不平衡权重惩罚时,ExtraTreesClassifier 模型是最佳的(“roc_auc”指标约为 0.955)。
– 使用 StandardScaler 加 PowerTransformer,但没有不平衡权重补偿时,ExtraTreesClassifier 和 RandomForestClassifier 是最佳的,约为 0.95。
– 最后,使用 StandardScaler 加 PowerTransformer 加权重类别补偿时,SVC 模型后跟 BaggingClassifier 是最佳的。
因此,SVC 最终是最佳的,其“roc_auc”得分约为 0.96。
所以我得出的结论是,不同的模型对这些工具(StandardScalar、PowerTransformer、class_weights 补偿等)有不同的敏感性……所以这取决于具体的研究案例。
最后一个问题。我看到 Sklearn 模型没有办法在训练后保存模型的权重(就像 tensorflow/keras 那样),所以在应用它们(例如为了预测新输出)之前,每次都必须重新训练模型吗?
感谢 Jason 的所有这些精彩教程,非常有启发性。
我刚应用了 SMOTE,结果显著提高。简直是天壤之别。
>LR 0.934 (0.005)
>SVM 0.980 (0.003)
>BAG 0.993 (0.001)
>RF 0.993 (0.001)
>GBM 0.994 (0.001)
>XGB 0.995 (0.001)
最后一个——没有 SMOTE 的 XGBClassifier 效果很差。
我进行了一些网格搜索来给 XGB 一些优势,即使没有 SMOTE:XGBClassifier(n_estimators=100, use_label_encoder=False, scale_pos_weight=0.1, eval_metric=’logloss’)
没有 SMOTE 的结果
>LR 0.922 (0.038)
>SVM 0.959 (0.018)
>BAG 0.942 (0.027)
>RF 0.949 (0.028)
>GBM 0.919 (0.034)
>XGB 0.955 (0.023)
干得漂亮!
干得好!
嗨,Jason,
我有一个与图像分析相关的问题——图像是如何分割以进行分析的?
这个主题非常有趣且重要,尤其是在断层扫描或磁共振成像的分析中。
我将认为创建一个关于这种重要图像分析技术的教程将非常有意义。
非常感谢!
你说的图像分析是什么意思?
你好 Jason,无监督方法应该采用什么方法?你能分享一个代码片段或示例吗?
也许你可以看看这里的代码:https://machinelearning.org.cn/one-class-classification-algorithms/
希望这能帮助你开始。