本案例基于公开的乳腺癌诊断数据,使用sklearn
中的KNeighborsClassifier
,即K近邻算法来建立乳腺癌自动诊断模型,并介绍了该模型性能提升的简单方法。 本案例主要包括以下内容: 1. 数据源 2. 数据探索和预处理 2.1 数据标准化 2.2 划分训练集和测试集 3. 模型训练 4. 模型性能评估 5. 模型性能提升 5.1 测试不同k取值对模型效果的影响 5.2 采用Z-score标准化方法
乳腺癌已成为当前社会的重大公共卫生问题。全球乳腺癌发病率自20世纪70年代末开始一直呈上升趋势。美国8名妇女一生中就会有1人患乳腺癌。中国不是乳腺癌的高发国家,但不宜乐观,近年我国乳腺癌发病率的增长速度却高出高发国家1~2个百分点。据国家癌症中心和卫生部疾病预防控制局2012年公布的2009年乳腺癌发病数据显示:全国肿瘤登记地区乳腺癌发病率位居女性恶性肿瘤的第1位,女性乳腺癌发病率(粗率)全国合计为42.55/10万,城市为51.91/10万,农村为23.12/10万。
早期的乳腺癌检测主要检查乳腺组织的异常肿块。如果发现一个肿块,那么就需要进行细针抽吸活检,然后在显微镜下检查细胞,从而确定肿块是良性还是恶性。如果能够通过机器学习建模,通过乳腺肿块的检测数据自动进行诊断,将会给医疗系统带来很大的益处。一方面,自动诊断能够大大提高检测效率。另一方面,结合大量不同历史案例的自动诊断能够使辅助医生进行决策,降低误判的风险。
本案例中,基于公开的乳腺癌诊断数据,我们将使用机器学习中一种简单的算法--K近邻算法来构建乳腺癌自动诊断模型。
我们将使用来自UCI的乳腺癌诊断数据集,该数据集的详细描述见网址: http://archive.ics.uci.edu/ml/datasets/Breast+Cancer+Wisconsin+%28Diagnostic%29。 该乳腺癌数据包括569例乳腺细胞活检样本,每个样本包含32个变量。其中id变量是样本识别ID,diagnosis变量是目标变量(M代表恶性,B代表良性)。其他30个变量都是由10个数字化细胞核的10个不同特征的均值、标准差和最大值构成。这10个基本特征为:
首先,使用pandas的read_csv()函数将CSV格式的乳腺癌数据集导入数据框中。
import pandas as pd
breast_cancer = pd.read_csv("./input/wisc_bc_data.csv")
print breast_cancer.shape
breast_cancer.head(5)
第一个变量为ID变量,无法为实际的模型构建提供有用的信息,所以需要将其删除。
del breast_cancer["id"]
diagnosis变量是我们的目标变量,我们首先统计一下其取值分布。观察在我们的数据集中,恶性(M)和良性样本(B)的分布情况。可以使用pandas中Series的value_counts()分别输出不同类别样本数量和占比。 diagnosis变量为字符串格式,在正式建模之前需要将其进行整数编码,将良性(B)编码为0,将恶性(M)编码为1。
print breast_cancer.diagnosis.value_counts()
print breast_cancer.diagnosis.value_counts()/len(breast_cancer)
dignosis_dict = {"B":0,"M":1}
breast_cancer["diagnosis"] = breast_cancer["diagnosis"].map(dignosis_dict)
可见,在我们的569个样本中,良性样本和恶性样本分别有357和212个,占比分别为62.7%和37.3%。 作为示例,我们详细观察30个自变量中的三个变量:radius_mean, area_mean和smoothness_mean。
breast_cancer[["radius_mean", "area_mean","smoothness_mean"]].describe()
观察这三个变量的统计信息,发现它们的取值范围不大一致。 radius_mean取值范围为6.981~28.110,area_mean取值范围为143.5~2501.0,smoothness_mean取值范围为0.05263~0.16340。不同变量的测量尺度不一致会影响K近邻算法中的样本距离计算。 例如,如果上述三个变量直接参与距离计算,则area_mean变量将会对距离计算影响最大,从而会导致我们构建的分类模型过分依赖于area_mean变量。在应用K近邻等涉及距离计算的算法构建预测模型之前,我们需要对变量取值进行标准化。 常见的标准化方法有min-max标准化和Z-score标准化等。本案例中,我们将采用min-max标准化方法将我们自变量取值调整到一个标准的范围内。
为了将自变量进行min-max标准化,我们需要实现一个min_max_normalize()函数。该函数输入为数值型向量x,对于x中的每一个取值,减去x的最小值,再除以x中数值的取值范围。具体实现代码如下:
def min_max_normalize(x):
return (x - x.min())/(x.max() - x.min())
现在,我们可以将实现的min_max_normalize()函数对数据集进行标准化。
for col in breast_cancer.columns[1:31]:
breast_cancer[col] = min_max_normalize(breast_cancer[col])
breast_cancer.iloc[:,1:].describe()
可见,30个自变量都已经正确地标准化到0和1之间。
在实际应用中,检验模型最好的方法是将模型应用到未来的数据中。 由于缺少这样的未来数据,我们采用一种模拟的方法:将我们的数据集划分为训练集和测试集两部分。 训练集用来构建模型,而测试集在模型构建中不能使用,只用来评估模型的性能。 在本案例中,我们将数据的70%(398个样本)用来训练模型,30%(171个样本)则用来测试模型。
from sklearn import cross_validation
y = breast_cancer['diagnosis']
del breast_cancer['diagnosis']
X = breast_cancer
breast_cancer_minmax_train, breast_cancer_minmax_test,\
breast_cancer_train_labels, breast_cancer_test_labels \
= cross_validation.train_test_split(X, y, test_size=0.3, random_state=0)
通过value_counts()函数,我们可以对比训练集和测试集中,恶性样本和良性样本分布是否接近:
print breast_cancer_train_labels.value_counts()/len(breast_cancer_train_labels)
print breast_cancer_test_labels.value_counts()/len(breast_cancer_test_labels)
可见,训练集和测试集中不同类别的样本分布趋向一致。现在,我们可以开始构建乳腺癌诊断模型了。
对于K近邻算法,模型训练阶段实际上不包括模型建立,只需要将训练数据及其标签存储即可。 为了使用训练数据进行分类,我们使用sklearn.neighbors包的K近邻算法实现类KNeighborsClassifier。 对于测试集中的每一个样本,将使用特定的距离计算方法找出k个近邻,其中k是一个可以指定的参数。 然后,通过这k个近邻样本的类标签进行投票,从而对测试样本进行分类。 如果不同类的票数相等,则测试样本会被随机分类。 当然,为了避免不同类票数相等这种情况的发生,对于二分类问题,通常将k设置成奇数。
sklearn.neighbors.KNeighborsClassifier类有几个主要的参数,含义如下:
各参数的详细取值及设置方式参考网址:http://scikit-learn.org/stable/modules/generated/sklearn.neighbors.KNeighborsClassifier.html
下面,我们将k设置为21,使用K近邻算法对测试数据进行预测,预测结果保存在breast_cancer_test_pred向量中。
from sklearn.neighbors import KNeighborsClassifier
knn_model = KNeighborsClassifier(n_neighbors = 21)
knn_model.fit(breast_cancer_minmax_train, breast_cancer_train_labels)
breast_cancer_test_pred = knn_model.predict(breast_cancer_minmax_test)
通过对比我们构建的K近邻模型在测试数据上的预测结果breast_cancer_test_pred和测试样本真实的类标签,我们可以评估我们构建的乳腺癌诊断模型的应用效果。我们使用sklern.metrics包中的相关函数来计算评估结果。
from sklearn import metrics
print metrics.classification_report(breast_cancer_test_labels, breast_cancer_test_pred)
print metrics.confusion_matrix(breast_cancer_test_labels, breast_cancer_test_pred)
print metrics.accuracy_score(breast_cancer_test_labels, breast_cancer_test_pred)
可见,对于108个良性样本,模型正确预测了106个,只有2个样本预测失败。 对于63个恶性样本,模型预测正确的有56个,正确预测比例为88.9%。 模型的整体预测正确率为 (106 + 56)/ 171 = 94.7%。 虽然这个正确率看起来很高,但是假阳性样本还有7个,这在实际医疗诊断中是严重的失误,因为将一个患有乳腺癌的患者诊断为良性会给患者带来严重的后果。 下一步,我们需要进一步寻找提升模型效果的方法。
k_list = (1,5,9,11,15,21,27)
for k in k_list:
knn_model = KNeighborsClassifier(n_neighbors = k)
knn_model.fit(breast_cancer_minmax_train, breast_cancer_train_labels)
breast_cancer_test_pred = knn_model.predict(breast_cancer_minmax_test)
accuracy = metrics.accuracy_score(breast_cancer_test_labels, breast_cancer_test_pred)
confusion_mat = metrics.confusion_matrix(breast_cancer_test_labels, breast_cancer_test_pred)
print "k = ",k
print "\t正确率: ", '%.2f'%(accuracy*100) + "%"
print "\t假阴性:",confusion_mat[0,1]
print "","\t假阳性:",confusion_mat[1,0]
可见,当 k = 5 时,假阳性数量最少,且假阴性数量仅为1,正确率达到最高。当然,这也只是在171个测试样本上的结果。
在上述K近邻算法构建模型的过程中,我们使用min-max方法对数据进行标准化。 现在,现在我们尝试Z-score标准化方法,探索它能够提高我们的乳腺癌诊断模型的预测性能。 可以使用sklearn.preprocessing包中的scale()函数来对数据进行Z-score标准化。
from sklearn import preprocessing
breast_cancer_zscore = pd.DataFrame(preprocessing.scale(breast_cancer),\
columns = breast_cancer.columns)
breast_cancer_zscore.head(5)
在进行Z-score标准化后,变量的均值为0,标准差为1。 以area_mean变量为例,我们对我们标准化后的结果进行统计,以检验标准化是否正确。
print breast_cancer_zscore.area_mean.mean()
print breast_cancer_zscore.area_mean.std()
可见,标准化后均值接近0,标准差为1。现在,我们可以训练模型,并测试模型性能了。
breast_cancer_zscore_train, breast_cancer_zscore_test,\
breast_cancer_train_labels, breast_cancer_test_labels \
= cross_validation.train_test_split(breast_cancer_zscore, y, test_size=0.3, random_state=0)
#模型训练
knn_model_z = KNeighborsClassifier(n_neighbors = 5)
knn_model_z.fit(breast_cancer_zscore_train, breast_cancer_train_labels)
#模型预测
breast_cancer_test_pred_z = knn_model_z.predict(breast_cancer_zscore_test)
#性能评估
accuracy_z = metrics.accuracy_score(breast_cancer_test_labels, breast_cancer_test_pred_z)
confusion_mat_z = metrics.confusion_matrix(breast_cancer_test_labels, breast_cancer_test_pred_z)
print "k = 5"
print "\t正确率: ", '%.2f'%(accuracy_z*100) + "%"
print "\t假阴性:",confusion_mat_z[0,1]
print "","\t假阳性:",confusion_mat_z[1,0]
当k=5时,新的模型假阳性数量反而增加了2个。可见在我们的数据集中,使用Z-score标准化方法的模型效果比使用min-max标准化方法的模型效果更差。