CategoryResourceRepost/极客时间专栏/数据分析实战45讲/第二模块:数据分析算法篇/23丨SVM(下):如何进行乳腺癌检测?.md
louzefeng d3828a7aee mod
2024-07-11 05:50:32 +00:00

244 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<audio id="audio" title="23丨SVM如何进行乳腺癌检测" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/b3/cc/b338f169d9377c97fe6353c962591dcc.mp3"></audio>
讲完了SVM的原理之后今天我来带你进行SVM的实战。
在此之前我们先来回顾一下SVM的相关知识点。SVM是有监督的学习模型我们需要事先对数据打上分类标签通过求解最大分类间隔来求解二分类问题。如果要求解多分类问题可以将多个二分类器组合起来形成一个多分类器。
上一节中讲到了硬间隔、软间隔、非线性SVM以及分类间隔的公式你可能会觉得比较抽象。这节课我们会在实际使用中讲解对工具的使用以及相关参数的含义。
## 如何在sklearn中使用SVM
在Python的sklearn工具包中有SVM算法首先需要引用工具包
```
from sklearn import svm
```
SVM既可以做回归也可以做分类器。
当用SVM做回归的时候我们可以使用SVR或LinearSVR。SVR的英文是Support Vector Regression。这篇文章只讲分类这里只是简单地提一下。
当做分类器的时候我们使用的是SVC或者LinearSVC。SVC的英文是Support Vector Classification。
我简单说一下这两者之前的差别。
从名字上你能看出LinearSVC是个线性分类器用于处理线性可分的数据只能使用线性核函数。上一节我讲到SVM是通过核函数将样本从原始空间映射到一个更高维的特质空间中这样就使得样本在新的空间中线性可分。
如果是针对非线性的数据需要用到SVC。在SVC中我们既可以使用到线性核函数进行线性划分也能使用高维的核函数进行非线性划分
如何创建一个SVM分类器呢
我们首先使用SVC的构造函数model = svm.SVC(kernel=rbf, C=1.0, gamma=auto)这里有三个重要的参数kernel、C和gamma。
kernel代表核函数的选择它有四种选择只不过默认是rbf即高斯核函数。
<li>
linear线性核函数
</li>
<li>
poly多项式核函数
</li>
<li>
rbf高斯核函数默认
</li>
<li>
sigmoidsigmoid核函数
</li>
这四种函数代表不同的映射方式你可能会问在实际工作中如何选择这4种核函数呢我来给你解释一下
线性核函数,是在数据线性可分的情况下使用的,运算速度快,效果好。不足在于它不能处理线性不可分的数据。
多项式核函数可以将数据从低维空间映射到高维空间,但参数比较多,计算量大。
高斯核函数同样可以将样本映射到高维空间,但相比于多项式核函数来说所需的参数比较少,通常性能不错,所以是默认使用的核函数。
了解深度学习的同学应该知道sigmoid经常用在神经网络的映射中。因此当选用sigmoid核函数时SVM实现的是多层神经网络。
上面介绍的4种核函数除了第一种线性核函数外其余3种都可以处理线性不可分的数据。
参数C代表目标函数的惩罚系数惩罚系数指的是分错样本时的惩罚程度默认情况下为1.0。当C越大的时候分类器的准确性越高但同样容错率会越低泛化能力会变差。相反C越小泛化能力越强但是准确性会降低。
参数gamma代表核函数的系数默认为样本特征数的倒数即gamma = 1 / n_features。
在创建SVM分类器之后就可以输入训练集对它进行训练。我们使用model.fit(train_X,train_y)传入训练集中的特征值矩阵train_X和分类标识train_y。特征值矩阵就是我们在特征选择后抽取的特征值矩阵当然你也可以用全部数据作为特征值矩阵分类标识就是人工事先针对每个样本标识的分类结果。这样模型会自动进行分类器的训练。我们可以使用prediction=model.predict(test_X)来对结果进行预测传入测试集中的样本特征矩阵test_X可以得到测试集的预测分类结果prediction。
同样我们也可以创建线性SVM分类器使用model=svm.LinearSVC()。在LinearSVC中没有kernel这个参数限制我们只能使用线性核函数。由于LinearSVC对线性分类做了优化对于数据量大的线性可分问题使用LinearSVC的效率要高于SVC。
如果你不知道数据集是否为线性可以直接使用SVC类创建SVM分类器。
在训练和预测中LinearSVC和SVC一样都是使用model.fit(train_X,train_y)和model.predict(test_X)。
## 如何用SVM进行乳腺癌检测
在了解了如何创建和使用SVM分类器后我们来看一个实际的项目数据集来自美国威斯康星州的乳腺癌诊断数据集[点击这里进行下载](https://github.com/cystanford/breast_cancer_data/)。
医疗人员采集了患者乳腺肿块经过细针穿刺(FNA)后的数字化图像,并且对这些数字图像进行了特征提取,这些特征可以描述图像中的细胞核呈现。肿瘤可以分成良性和恶性。部分数据截屏如下所示:
<img src="https://static001.geekbang.org/resource/image/97/6a/97a33c5bfc182d571e9707db653eff6a.png" alt=""><br>
数据表一共包括了32个字段代表的含义如下
<img src="https://static001.geekbang.org/resource/image/1e/13/1e6af6fa8bebdfba10457c111b5e9c13.jpg" alt="">
上面的表格中mean代表平均值se代表标准差worst代表最大值3个最大值的平均值。每张图像都计算了相应的特征得出了这30个特征值不包括ID字段和分类标识结果字段diagnosis实际上是10个特征值radius、texture、perimeter、area、smoothness、compactness、concavity、concave points、symmetry和fractal_dimension_mean的3个维度平均、标准差和最大值。这些特征值都保留了4位数字。字段中没有缺失的值。在569个患者中一共有357个是良性212个是恶性。
好了我们的目标是生成一个乳腺癌诊断的SVM分类器并计算这个分类器的准确率。首先设定项目的执行流程
<img src="https://static001.geekbang.org/resource/image/97/f9/9768905bf3cf6d8946a64caa8575e1f9.png" alt="">
<li>
首先我们需要加载数据源;
</li>
<li>
在准备阶段,需要对加载的数据源进行探索,查看样本特征和特征值,这个过程你也可以使用数据可视化,它可以方便我们对数据及数据之间的关系进一步加深了解。然后按照“完全合一”的准则来评估数据的质量,如果数据质量不高就需要做数据清洗。数据清洗之后,你可以做特征选择,方便后续的模型训练;
</li>
<li>
在分类阶段选择核函数进行训练如果不知道数据是否为线性可以考虑使用SVC(kernel=rbf) 也就是高斯核函数的SVM分类器。然后对训练好的模型用测试集进行评估。
</li>
按照上面的流程,我们来编写下代码,加载数据并对数据做部分的探索:
```
# 加载数据集,你需要把数据放到目录中
data = pd.read_csv(&quot;./data.csv&quot;)
# 数据探索
# 因为数据集中列比较多我们需要把dataframe中的列全部显示出来
pd.set_option('display.max_columns', None)
print(data.columns)
print(data.head(5))
print(data.describe())
```
这是部分的运行结果,完整结果你可以自己跑一下。
```
Index(['id', 'diagnosis', 'radius_mean', 'texture_mean', 'perimeter_mean',
'area_mean', 'smoothness_mean', 'compactness_mean', 'concavity_mean',
'concave points_mean', 'symmetry_mean', 'fractal_dimension_mean',
'radius_se', 'texture_se', 'perimeter_se', 'area_se', 'smoothness_se',
'compactness_se', 'concavity_se', 'concave points_se', 'symmetry_se',
'fractal_dimension_se', 'radius_worst', 'texture_worst',
'perimeter_worst', 'area_worst', 'smoothness_worst',
'compactness_worst', 'concavity_worst', 'concave points_worst',
'symmetry_worst', 'fractal_dimension_worst'],
dtype='object')
id diagnosis radius_mean texture_mean perimeter_mean area_mean \
0 842302 M 17.99 10.38 122.80 1001.0
1 842517 M 20.57 17.77 132.90 1326.0
2 84300903 M 19.69 21.25 130.00 1203.0
3 84348301 M 11.42 20.38 77.58 386.1
4 84358402 M 20.29 14.34 135.10 1297.0
```
接下来,我们就要对数据进行清洗了。
运行结果中你能看到32个字段里id是没有实际含义的可以去掉。diagnosis字段的取值为B或者M我们可以用0和1来替代。另外其余的30个字段其实可以分成三组字段下划线后面的mean、se和worst代表了每组字段不同的度量方式分别是平均值、标准差和最大值。
```
# 将特征字段分成3组
features_mean= list(data.columns[2:12])
features_se= list(data.columns[12:22])
features_worst=list(data.columns[22:32])
# 数据清洗
# ID列没有用删除该列
data.drop(&quot;id&quot;,axis=1,inplace=True)
# 将B良性替换为0M恶性替换为1
data['diagnosis']=data['diagnosis'].map({'M':1,'B':0})
```
然后我们要做特征字段的筛选首先需要观察下features_mean各变量之间的关系这里我们可以用DataFrame的corr()函数,然后用热力图帮我们可视化呈现。同样,我们也会看整体良性、恶性肿瘤的诊断情况。
```
# 将肿瘤诊断结果可视化
sns.countplot(data['diagnosis'],label=&quot;Count&quot;)
plt.show()
# 用热力图呈现features_mean字段之间的相关性
corr = data[features_mean].corr()
plt.figure(figsize=(14,14))
# annot=True显示每个方格的数据
sns.heatmap(corr, annot=True)
plt.show()
```
这是运行的结果:
<img src="https://static001.geekbang.org/resource/image/a6/4d/a65435de48cee8091bd5f83d286ddb4d.png" alt="">
<img src="https://static001.geekbang.org/resource/image/07/6e/0780e76fd3807759ab4881c2c39cb76e.png" alt=""><br>
热力图中对角线上的为单变量自身的相关系数是1。颜色越浅代表相关性越大。所以你能看出来radius_mean、perimeter_mean和area_mean相关性非常大compactness_mean、concavity_mean、concave_points_mean这三个字段也是相关的因此我们可以取其中的一个作为代表。
那么如何进行特征选择呢?
特征选择的目的是降维,用少量的特征代表数据的特性,这样也可以增强分类器的泛化能力,避免数据过拟合。
我们能看到mean、se和worst这三组特征是对同一组内容的不同度量方式我们可以保留mean这组特征在特征选择中忽略掉se和worst。同时我们能看到mean这组特征中radius_mean、perimeter_mean、area_mean这三个属性相关性大compactness_mean、daconcavity_mean、concave points_mean这三个属性相关性大。我们分别从这2类中选择1个属性作为代表比如radius_mean和compactness_mean。
这样我们就可以把原来的10个属性缩减为6个属性代码如下
```
# 特征选择
features_remain = ['radius_mean','texture_mean', 'smoothness_mean','compactness_mean','symmetry_mean', 'fractal_dimension_mean']
```
对特征进行选择之后,我们就可以准备训练集和测试集:
```
# 抽取30%的数据作为测试集,其余作为训练集
train, test = train_test_split(data, test_size = 0.3)# in this our main data is splitted into train and test
# 抽取特征选择的数值作为训练和测试数据
train_X = train[features_remain]
train_y=train['diagnosis']
test_X= test[features_remain]
test_y =test['diagnosis']
```
在训练之前,我们需要对数据进行规范化,这样让数据同在同一个量级上,避免因为维度问题造成数据误差:
```
# 采用Z-Score规范化数据保证每个特征维度的数据均值为0方差为1
ss = StandardScaler()
train_X = ss.fit_transform(train_X)
test_X = ss.transform(test_X)
```
最后我们可以让SVM做训练和预测了
```
# 创建SVM分类器
model = svm.SVC()
# 用训练集做训练
model.fit(train_X,train_y)
# 用测试集做预测
prediction=model.predict(test_X)
print('准确率: ', metrics.accuracy_score(test_y,prediction))
```
运行结果:
```
准确率: 0.9181286549707602
```
准确率大于90%,说明训练结果还不错。完整的代码你可以从[GitHub](https://github.com/cystanford/breast_cancer_data)上下载。
## 总结
今天我带你一起做了乳腺癌诊断分类的SVM实战从这个过程中你应该能体会出来整个执行的流程包括数据加载、数据探索、数据清洗、特征选择、SVM训练和结果评估等环节。
sklearn已经为我们提供了很好的工具对上节课中讲到的SVM的创建和训练都进行了封装让我们无需关心中间的运算细节。但正因为这样我们更需要对每个流程熟练掌握通过实战项目训练数据化思维和对数据的敏感度。
<img src="https://static001.geekbang.org/resource/image/79/82/797fe646ae4668139600fca2c50c5282.png" alt=""><br>
最后给你留两道思考题吧。还是这个乳腺癌诊断的数据请你用LinearSVC选取全部的特征除了ID以外作为训练数据看下你的分类器能得到多少的准确度呢另外你对sklearn中SVM使用又有什么样的体会呢
欢迎在评论区与我分享你的答案,也欢迎点击“请朋友读”,把这篇文章分享给你的朋友或者同事,一起来交流,一起来进步。