机器学习中不平衡数据集分类模型示例:乳腺钼靶微钙化摄影数据集

2020-05-09 16:23:17 浏览数 (1)

Deephub翻译组:Alexander Zhao

癌症检测是不平衡分类问题的一个普遍例子,因为非癌症病例往往比实际癌症病例多得多。

一个典型的不平衡分类数据集是乳腺摄影数据集,这个数据集用于从放射扫描中检测乳腺癌(特别是在乳腺摄影中出现明亮的微钙化簇)。研究人员通过扫描图像,对目标进行分割,然后用计算机视觉算法描述分割对象,从而获得了这一数据集。

由于类别不平衡十分严重,这是一个非常流行的不平衡分类数据集。其中98%的候选图像不是癌症,只有2%被有经验的放射科医生标记为癌症。

在本教程中,您将发现如何开发和评估乳腺癌钼靶摄影数据集的不平衡分类模型。完成本教程后,您将知道:

  • 如何加载和探索数据集,并从中获得预处理数据与选择模型的灵感。
  • 如何使用代价敏感算法评估一组机器学习模型并提高其性能。
  • 如何拟合最终模型并使用它预测特定情况下的类标签。

我们开始吧。

教程概述

本教程分为五个部分,分别是:

  1. 乳腺摄影数据集
  2. 浏览数据集
  3. 模型试验和基准结果
  4. 评估模型
    1. 评估机器学习算法
    2. 评估代价敏感算法
  5. 对新数据进行预测

乳腺摄影数据集

在这个项目中,我们将使用一个典型的不平衡机器学习数据集,即“乳腺摄影”数据集,有时称为“Woods乳腺摄影数据集”。

数据集归Kevin Woods等人所有,有关这项工作可以参考1993年发表的题为“Comparative Evaluation Of Pattern Recognition Techniques For Detection Of Microcalcifications In Mammography”的论文。

这个问题的焦点是通过放射扫描来检测乳腺癌,特别是在乳房X光片上出现的微小钙化团。

该数据集首先从24张已知癌症诊断结果的乳房X光片开始扫描,然后使用图像分割计算机视觉算法对图像进行预处理,从乳腺图像中提取候选目标。这些候选目标被分割后,就会被一位经验丰富的放射科医生手工标记。

Woods等人首先从分割的对象中选取与模式识别最相关的对象并进行特征提取,总共提取了29个特征,这些特征被减少到18个,然后最后减少到7个,具体如下(摘自论文原文):

  • 对象的面积(像素)
  • 对象的平均灰度
  • 对象周长像素的渐变强度
  • 对象中的均方根噪声波动
  • 对比度,也即对象的平均灰度减去对象周围两个像素宽边框的平均值
  • 基于形状描述子的低阶矩

这是一个二分类任务,目的是利用给定分割对象的特征来区分乳腺影片中的微钙化和非微钙化。

  • 非微钙化: 负类,或多数类
  • 微钙化: 正类,或少数类

作者评价与比较了一系列机器学习模型,包括人工神经网络、决策树、k近邻算法等。各个模型用受试者操作特性曲线(ROC)进行评估,并且用曲线下面积(AUC)进行比较。

选择ROC与AUC作为评估指标,其目的是最小化假阳性率(FPR,即特异性Specificity的补数)并最大化真阳性率(TPR,即敏感性Sensitivity),这也即ROC曲线的两轴。选用ROC曲线的另一个原因是可以让研究人员确定一个概率阈值,从而对可以接受的最大FPR与TPR进行权衡(因为随着TPR的上升FPR也不可避免地上升,译者注)。

研究结果表明,线性分类器(原文中应该是一个高斯朴素贝叶斯分类器)的AUC为0.936(100次实验的平均值),表现最好。

接下来,让我们仔细看看数据。

探索数据集

乳腺摄影数据集是一个广泛使用的标准机器学习数据集,用于探索和演示许多专门为不平衡分类设计的技术。一个典型的例子是流行的SMOTE技术。

我们使用的数据集是其中的一个版本,它与原始文件中描述的数据集有一些不同。

首先,下载数据集并将其保存在当前的工作目录中,名为“mammography.csv”

  • 下载乳房摄影数据集(maxomography.csv)

检查文件的内容。文件的前几行应如下所示:

代码语言:javascript复制
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'

我们可以看到数据集有6个输入变量,而不是7个输入变量。有可能从这个版本的数据集中删除了论文中列出的第一个输入变量(用像素描述的对象面积)。

输入变量是数值类型,而目标变量是多数类置为“-1”、少数类置为“1”的字符串。这些值需要分别编码为0和1,以满足分类算法对二进制不平衡分类问题的期望。

可以使用read_csv()这一Pandas函数将数据集加载为DataFrame数据结构,注意指定header=None

代码语言:javascript复制
...
# define the dataset location
filename = 'mammography.csv'
# load the csv file as a data frame
dataframe = read_csv(filename, header=None)

载入完毕后,我们调用DataFrame的shape方法打印其行列数。

代码语言:javascript复制
...
# summarize the shape of the dataset
print(dataframe.shape)

我们还可以通过使用Counter来确认数据,获取各类的比例。

代码语言:javascript复制
...
# summarize the class distribution
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))

把这两步放在一起,完整的载入与确认数据的代码如下:

代码语言:javascript复制
# load and summarize the dataset
from pandas import read_csv
from collections import Counter
# define the dataset location
filename = 'mammography.csv'
# load the csv file as a data frame
dataframe = read_csv(filename, header=None)
# summarize the shape of the dataset
print(dataframe.shape)
# summarize the class distribution
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))

运行示例代码,首先加载数据集并确认行和列的数量,即11183行、6个输入变量和1个目标变量。

然后确认类别分布,我们会观察到严重的类别不平衡:多数类(无癌症)约占98%,少数类(癌症)约占2%。

代码语言:javascript复制
(11183, 7)
Class='-1', Count=10923, Percentage=97.675%
Class='1', Count=260, Percentage=2.325%

从反例和正例的比例来看,数据集似乎与SMOTE论文中描述的数据集基本匹配。

A typical mammography dataset might contain 98% normal pixels and 2% abnormal pixels.

— SMOTE: Synthetic Minority Over-sampling Technique, 2002.

此外,正反例的数量也基本与论文相符。

The experiments were conducted on the mammography dataset. There were 10923 examples in the majority class and 260 examples in the minority class originally.

— SMOTE: Synthetic Minority Over-sampling Technique, 2002.

我相信这是同一个数据集,尽管我无法解释输入特征数量的不匹配现象,例如我们的数据集中只有6个输入数据,而原始论文中有7个。

我们还可以为每个变量创建直方图来观察输入变量的分布,下面列出了完整的示例。

代码语言:javascript复制
# create histograms of numeric input variables
from pandas import read_csv
from matplotlib import pyplot
# define the dataset location
filename = 'mammography.csv'
# load the csv file as a data frame
df = read_csv(filename, header=None)
# histograms of all variables
df.hist()
pyplot.show()

运行该示例代码将为数据集中的六个输入变量分别创建一个直方图。我们可以看到,这些变量有不同的取值范围,而且大多数变量都是指数分布的,例如,大多数情况下变量只占据直方图的一列,而其他情况下则留下一个长尾,而最后一个变量则似乎具有双峰分布。

根据我们选择的算法,将数据分布缩放到相同的取值范围是可能是很有用的,也许还需要使用一些幂变换,这将在后文进行讨论。

数据分布

我们还可以为每对输入变量创建一个散点图,称为散点图矩阵。这有助于我们了解是否有任何变量是相互关联的,或在同一方向上发生变化。我们还可以根据类标签给每个散点图上色。我们将多数类(没有癌症)标记为蓝点,少数类(癌症)标记为红点。

下面列出了完整的示例。

代码语言:javascript复制
# create pairwise scatter plots of numeric input variables
from pandas import read_csv
from pandas.plotting import scatter_matrix
from matplotlib import pyplot
# define the dataset location
filename = 'mammography.csv'
# load the csv file as a data frame
df = read_csv(filename, header=None)
# define a mapping of class values to colors
color_dict = {"'-1'":'blue', "'1'":'red'}
# map each row to a color based on the class value
colors = [color_dict[str(x)] for x in df.values[:, -1]]
# pairwise scatter plots of all numerical variables
scatter_matrix(df, diagonal='kde', color=colors)
pyplot.show()

运行该示例将创建一个6组*6组的散点图矩阵,用于六个输入变量的相互比较。矩阵的对角线表示每个变量的密度分布。

每一对变量的分布比较都出现了两次,分别位于主对角线元素的左侧与上侧(或右侧与下侧)。这提供了两种查看变量分布的尺度。

我们可以看到,对于正类与负类,许多变量的分布确实不同,这表明在癌症病例和非癌症病例之间进行合理的区分是可行的。

散点图矩阵

现在我们已经回顾了数据集,接下来让我们来评估与测试备选模型。

模型测试与基准结果

我们将使用重复的分层k折交叉验证来评估候选模型。

k-fold交叉验证程序可以提供一个良好的模型性能总体估计值,与单次的留出验证相比,这种方法不容易带来过高的偏差。我们取k=10,这意味着每折将包含约11183/10或约1118个示例。

分层意味着每折的数据分布将与整体数据分布保持一致,即大约98%的无癌症对象与2%的有癌症对象。重复则意味着会进行多次重复实验,以帮助避免侥幸结果并更好地确定所选模型的方差。我们将重复进行三次实验。这意味着将对单个模型进行10×3即30次拟合和评估,并统计运行结果的平均值和标准差。这可以通过使用重复的scikit-learn库的RepeatedStratifiedKFold类来实现。

我们将使用roc_auc_score()函数计算的AUC来评估和比较模型效果。

我们可以定义一个函数来加载数据集,并将列拆分为输入和输出变量。我们将类标签重新编码为0和1。下面的load_dataset()函数实现了这一点。

代码语言:javascript复制
# load the dataset
def load_dataset(full_path):
	# load the dataset as a numpy array
	data = read_csv(full_path, header=None)
	# retrieve numpy array
	data = data.values
	# split into input and output elements
	X, y = data[:, :-1], data[:, -1]
	# label encode the target variable to have the classes 0 and 1
	y = LabelEncoder().fit_transform(y)
	return X, y

然后,我们可以定义一个函数,用于在数据集上评估给定模型,并返回每折和与每次重复实验的AUC列表。

下面的evaluate_model()函数实现了这一点,将数据集和模型作为参数并返回分数列表。

代码语言:javascript复制
# evaluate a model
def evaluate_model(X, y, model):
	# define evaluation procedure
	cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
	# evaluate model
	scores = cross_val_score(model, X, y, scoring='roc_auc', cv=cv, n_jobs=-1)
	return scores

最后,我们可以使用这个测试工具评估数据集上的基准模型。对每个样本进行随机预测的分类器的AUC期望值为0.5,这是该数据集性能的基线。这个随机预测的分类器一个所谓的“无效”分类器。

这可以通过scikit-learn库的DummyClassifier类来实现,将参数“strategy”设置为‘stratified‘。

代码语言:javascript复制
...
# define the reference model
model = DummyClassifier(strategy='stratified')

一旦模型评估完成,我们就可以直接获得AUC得分的平均值和标准差。

代码语言:javascript复制
...
# evaluate the model
scores = evaluate_model(X, y, model)
# summarize performance
print('Mean ROC AUC: %.3f (%.3f)' % (mean(scores), std(scores)))

将这几部分结合起来,下面列出了加载数据集、评估基准模型和报告分类器性能的完整示例。

代码语言:javascript复制
# test harness and baseline model evaluation
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

# load the dataset
def load_dataset(full_path):
	# load the dataset as a numpy array
	data = read_csv(full_path, header=None)
	# retrieve numpy array
	data = data.values
	# split into input and output elements
	X, y = data[:, :-1], data[:, -1]
	# label encode the target variable to have the classes 0 and 1
	y = LabelEncoder().fit_transform(y)
	return X, y

# evaluate a model
def evaluate_model(X, y, model):
	# define evaluation procedure
	cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
	# evaluate model
	scores = cross_val_score(model, X, y, scoring='roc_auc', cv=cv, n_jobs=-1)
	return scores

# define the location of the dataset
full_path = 'mammography.csv'
# load the dataset
X, y = load_dataset(full_path)
# summarize the loaded dataset
print(X.shape, y.shape, Counter(y))
# define the reference model
model = DummyClassifier(strategy='stratified')
# evaluate the model
scores = evaluate_model(X, y, model)
# summarize performance
print('Mean ROC AUC: %.3f (%.3f)' % (mean(scores), std(scores)))

运行示例,首先加载并汇总数据集。我们可以看到加载的行数是正确的,并且有6个输入变量。重要的是,我们可以看到类标签具有到整数的正确映射,多数类记为0,少数类记为1,通常用于不平衡的二分类数据集。

接下来,报告AUC得分的平均值。

如预期的那样,无效分类器获得了平均AUC约为0.5的最坏性能。这为性能提供了一个基线,在这个基线之上,可以认为模型在这个数据集是有效的。

代码语言:javascript复制
(11183, 6) (11183,) Counter({0: 10923, 1: 260})
Mean ROC AUC: 0.503 (0.016)

现在我们有了测试工具和基准性能,我们可以开始在这个数据集上评估一些模型。

模型评估

在本节中,我们将使用上一节中开发的测试工具在数据集上评估不同的分类算法。

我们的目的是演示如何系统地解决问题,并展示某些专门为不平衡分类问题设计的算法的效果。我们获得的模型性能良好,但是仍未高度优化(例如,我们没有优化模型超参数)。

你能做得更好吗?我很乐意看到读者们能用同样的测试工具获得更好的AUC,欢迎在评论区留言。

机器学习算法评估

首先,我们在这个数据集上评估一些普通的机器学习模型。

我们可以在数据集上检测一系列不同的线性或非线性算法,这会很有用:这样我们可以快速了解到哪些算法在数据集上表现良好,而哪些算法则不值得我们关注。我们将在乳腺摄影数据集上评估以下机器学习模型:

  • 逻辑回归(LR)
  • 支持向量机(SVM)
  • Bagging算法(BAG)
  • 随机森林(RF)
  • 梯度提升机(GBM)

我们将主要使用默认的模型超参数,除了集成学习算法中的n_estimator,我们将其设置为1000。

我们将依次定义每个模型并将它们添加到列表中,以便可以按顺序对它们进行评估。我们定义下面的*get_models()*函数来评估模型效果并绘图。

代码语言:javascript复制
# define models to test
def get_models():
	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

然后,我们可以依次列举模型列表中的每个模型并进行评估,记录AUC并存储在列表中以供以后绘制。

代码语言:javascript复制
...
# define models
models, names = get_models()
results = list()
# evaluate each model
for i in range(len(models)):
	# evaluate the model and store results
	scores = evaluate_model(X, y, models[i])
	results.append(scores)
	# summarize and store
	print('>%s %.3f (%.3f)' % (names[i], mean(scores), std(scores)))

在运行结束时,我们可以绘制模型表现的箱线图,以便直接比较模型表现。

代码语言:javascript复制
# plot the results
pyplot.boxplot(results, labels=names, showmeans=True)
pyplot.show()

将这些结合起来,下面列出了评估乳腺摄影数据集上一套机器学习算法的完整示例。

代码语言:javascript复制
# spot check machine learning algorithms on the mammography dataset
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
 
# load the dataset
def load_dataset(full_path):
	# load the dataset as a numpy array
	data = read_csv(full_path, header=None)
	# retrieve numpy array
	data = data.values
	# split into input and output elements
	X, y = data[:, :-1], data[:, -1]
	# label encode the target variable to have the classes 0 and 1
	y = LabelEncoder().fit_transform(y)
	return X, y
 
# evaluate a model
def evaluate_model(X, y, model):
	# define evaluation procedure
	cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
	# evaluate model
	scores = cross_val_score(model, X, y, scoring='roc_auc', cv=cv, n_jobs=-1)
	return scores
 
# define models to test
def get_models():
	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
 
# define the location of the dataset
full_path = 'mammography.csv'
# load the dataset
X, y = load_dataset(full_path)
# define models
models, names = get_models()
results = list()
# evaluate each model
for i in range(len(models)):
	# evaluate the model and store results
	scores = evaluate_model(X, y, models[i])
	results.append(scores)
	# summarize and store
	print('>%s %.3f (%.3f)' % (names[i], mean(scores), std(scores)))
# plot the results
pyplot.boxplot(results, labels=names, showmeans=True)
pyplot.show()

运行该示例以依次评估每个算法并报告AUC的平均值和标准差。由于学习算法的随机性,您的特定结果会有所不同;您可以考虑多次运行这一程序。

我们看到,我们评估的所有算法都是有效的,都实现了高于基准值0.5的AUC。

结果表明,基于决策树的集成学习算法(包括Bagging算法与随机森林)在这个数据集上表现得更好,其中随机森林表现最好,AUC约为0.950。

值得注意的是,我们获取的结果好于论文中描述的结果(0.93),虽然我们的评估流程略有不同。

评估结果对LR和支持向量机算法有点不公平,因为我们在拟合模型之前没有缩放输入变量。我们可以在下一节探讨这个问题。

代码语言:javascript复制
>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()函数定义了要在数据集上进行计算的三种算法的代价敏感版本。

代码语言:javascript复制
# define models to test
def get_models():
	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类在交叉验证模型评估的每一折上中实现它。Pipeline的第一步定义PowerTransformer,然后应用于每一折的训练集和测试集;第二步则是我们正在评估的模型。然后可以使用evaluate_model()函数直接对Pipeline进行计算,示例代码如下:

代码语言:javascript复制
...
# defines pipeline steps
steps = [('p', PowerTransformer()), ('m',models[i])]
# define pipeline
pipeline = Pipeline(steps=steps)
# evaluate the pipeline and store results
scores = evaluate_model(X, y, pipeline)

把这一步结合在我们之前的代码中,下面列出了在乳腺摄影数据集上评估进行幂变换后的代价敏感型机器学习算法的完整示例。

代码语言:javascript复制
# cost-sensitive machine learning algorithms on the mammography dataset
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
 
# load the dataset
def load_dataset(full_path):
	# load the dataset as a numpy array
	data = read_csv(full_path, header=None)
	# retrieve numpy array
	data = data.values
	# split into input and output elements
	X, y = data[:, :-1], data[:, -1]
	# label encode the target variable to have the classes 0 and 1
	y = LabelEncoder().fit_transform(y)
	return X, y
 
# evaluate a model
def evaluate_model(X, y, model):
	# define evaluation procedure
	cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
	# evaluate model
	scores = cross_val_score(model, X, y, scoring='roc_auc', cv=cv, n_jobs=-1)
	return scores
 
# define models to test
def get_models():
	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
 
# define the location of the dataset
full_path = 'mammography.csv'
# load the dataset
X, y = load_dataset(full_path)
# define models
models, names = get_models()
results = list()
# evaluate each model
for i in range(len(models)):
	# defines pipeline steps
	steps = [('p', PowerTransformer()), ('m',models[i])]
	# define pipeline
	pipeline = Pipeline(steps=steps)
	# evaluate the pipeline and store results
	scores = evaluate_model(X, y, pipeline)
	results.append(scores)
	# summarize and store
	print('>%s %.3f (%.3f)' % (names[i], mean(scores), std(scores)))
# plot the results
pyplot.boxplot(results, labels=names, showmeans=True)
pyplot.show()

运行该示例以依次评估每个算法并报告AUC的平均值和标准差。由于学习算法的随机性,您的特定结果会有所不同;您可以考虑多次运行这一程序。

在这种情况下,我们可以看到,相比进行幂变换与添加代价敏感性之前,所有三个被测试的算法在AUC上都实现了提升。读者还可以试着在不进行幂变换的情况下进行重复实验,来探索算法性能提升的原因是幂变换,还是代价敏感算法,亦或是二者皆有。

在这种情况下,我们可以看到支持向量机取得了最好的性能,在本节和上一节中的性能优于RF,并且实现了大约0.957的平均AUC。

代码语言:javascript复制
>LR 0.922 (0.036)
>SVM 0.957 (0.024)
>RF 0.951 (0.035)

我们绘制了箱线图来对比不同算法AUC的分布。与其它两种模型相比,支持向量机的分布更为紧凑。因此,它的性能可能是最稳定的,可以作为最终模型的一个良好候选。

代价敏感箱线图

接下来,让我们看看如何使用最终模型对新数据进行预测。

对新数据进行预测

在本节中,我们将拟合一个最终模型,并使用它对单行数据进行预测。

我们将使用代价敏感的支持向量机模型作为最终模型,在对模型进行拟合和预测之前对数据进行幂变换。使用pipeline将确保始终正确地对输入数据执行转换。

首先,我们可以将模型定义为pipeline。

代码语言:javascript复制
...
# define model to evaluate
model = SVC(gamma='scale', class_weight='balanced')
# power transform then fit model
pipeline = Pipeline(steps=[('t',PowerTransformer()), ('m',model)])

定义之后,我们可以在整个训练集上拟合模型。

代码语言:javascript复制
...
# fit the model
pipeline.fit(X, y)

拟合完成后,我们可以调用*predict()*函数来对新数据进行预测。函数将返回0(意味着没有癌症)或1(意味着癌症)。示例代码如下:

代码语言:javascript复制
...
# define a row of data
row = [...]
# make prediction
yhat = model.predict([row])

为了演示上述流程,我们使用拟合后的模型对我们已经知道癌症与否的部分数据进行预测。示例代码如下:

代码语言:javascript复制
# fit a model and make predictions for the on the mammography dataset
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
 
# load the dataset
def load_dataset(full_path):
	# load the dataset as a numpy array
	data = read_csv(full_path, header=None)
	# retrieve numpy array
	data = data.values
	# split into input and output elements
	X, y = data[:, :-1], data[:, -1]
	# label encode the target variable to have the classes 0 and 1
	y = LabelEncoder().fit_transform(y)
	return X, y
 
# define the location of the dataset
full_path = 'mammography.csv'
# load the dataset
X, y = load_dataset(full_path)
# define model to evaluate
model = SVC(gamma='scale', class_weight='balanced')
# power transform then fit model
pipeline = Pipeline(steps=[('t',PowerTransformer()), ('m',model)])
# fit the model
pipeline.fit(X, y)
# evaluate on some no cancer cases (known class 0)
print('No Cancer:')
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:
	# make prediction
	yhat = pipeline.predict([row])
	# get the label
	label = yhat[0]
	# summarize
	print('>Predicted=%d (expected 0)' % (label))
# evaluate on some cancer (known class 1)
print('Cancer:')
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:
	# make prediction
	yhat = pipeline.predict([row])
	# get the label
	label = yhat[0]
	# summarize
	print('>Predicted=%d (expected 1)' % (label))

运行该示例,首先会在整个培训数据集上拟合模型。接下来,从数据集中选择一些没有癌症的数据进行预测,我们可以看到所有的情况都被正确地预测了;然后我们输入一些癌症数据再对标签进行预测,正如我们所希望的那样,在这两种情况下数据的标签都得到了正确的预测。

代码语言:javascript复制
No Cancer:
>Predicted=0 (expected 0)
>Predicted=0 (expected 0)
>Predicted=0 (expected 0)
Cancer:
>Predicted=1 (expected 1)
>Predicted=1 (expected 1)
>Predicted=1 (expected 1)

延伸阅读

如果您想深入了解,本节将提供有关此主题的更多资源。

论文

  • Comparative Evaluation Of Pattern Recognition Techniques For Detection Of Microcalcifications In Mammography, 1993.
  • SMOTE: Synthetic Minority Over-sampling Technique, 2002.

API

  • sklearn.model_selection.RepeatedStratifiedKFold API.
  • sklearn.metrics.roc_auc_score API.
  • sklearn.dummy.DummyClassifier API.
  • sklearn.svm.SVC API.

数据集

  • Mammography Dataset.
  • Mammography Dataset Description

总结

在本教程中,您学习了如何开发和评估乳腺摄影数据集的不平衡分类模型。具体来说,您学到了:

  • 如何加载和探索数据集,并从中获得预处理数据与选择模型的灵感。
  • 如何使用代价敏感算法评估一组机器学习模型并提高其性能。
  • 如何拟合最终模型并使用它预测特定情况下的类标签。

还有什么问题吗?欢迎在评论区提出你的问题,我会尽力回答。

代码较多,需要pdf版本请关注后索取百度网盘下载地址

0 人点赞