首页 > 技术文章 > SVM - 用于乳腺癌检测

Rosaany 2021-10-18 17:10 原文

1.原理

SVM(Support Vector Mahine支持向量机),是常见的二分类模型,基本思想是求解出能够正确划分训练集并且几何间隔最大的分离超平面。对于一个线性可分的数据集来说,这种超平面有无数多个,但几何间隔最大的超平面却是唯一的。

用SVM计算的过程就是帮我们找到那个超平面的过程,这个超平面就是我们的SVM分类器。

2.优缺点

优点:

  • 少数的支持向量决定了最终结果,对异常值不敏感
  • 计算的复杂性却决于支持向量的数量,而不是样本空间的维度,避免了“维数灾难”。

缺点:

  • 经典的支持向量机主要使用在二分类算法中,若需要解决多分类问题,可以通过多个二分类支持向量机来解决
  • 对参数和核函数的选择比较敏感。

3.应用场景

用于文本核超文本的分类,图像分类。

案例:SVM 进行乳腺癌检测

​ 接下来,看一个实际的项目,数据集来自美国威斯康星州的乳腺癌诊断数据集,点击这里进行下载,医疗人员采集了患者乳腺肿块经过细针穿刺 (FNA) 后的数字化图像,并且对这些数字图像进行了特征提取,这些特征可以描述图像中的细胞核呈现。肿瘤可以分成良性和恶性。

#!/usr/bin/env python
# coding: utf-8


import os
import pandas as pd

# 返回当前文件夹
os.listdir(".")
# 加载数据集
data = pd.read_csv('./data.csv')

# 数据探索
data.head()
len(data.columns) # 32
data.isna().sum()
data.describe().T


# # 数据清洗
# - 在上面运行结果中,看到有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("id", axis=1, inplace=True)

# 将非数字标签转换为数字标签
# M恶性 --> 1
# B良性 --> 0
from sklearn.preprocessing import LabelEncoder
training_labels = LabelEncoder().fit_transform(data['diagnosis'])

# - 特征字段筛选,观测各变量之间的关系,使用DataFrame的corr函数,然后用热力图可视化呈现
# 将肿瘤诊断结果可视化
import seaborn as sns
import matplotlib.pyplot as plt

sns.countplot(training_labels, label="Count")
plt.show()

# 用热力图呈现features_mean字段之间的相关性
corr = data[features_mean].corr()
plt.figure(figsize=(14,14))
sns.heatmap(corr, annot=True)
plt.show()


"""
- 颜色越浅代表相关性约大,所以看到radius_mean、perimeter_mean、area_mean、conmpactness_mean、concavity_mean、concave_points_mean相关性非常大,所以可以选取其中两个作为代表
特征选择的目的是降维,用少量的特征代表数据的特性,这样可以增强分类器的泛化能力,避免数据过拟合。
- mean、se、worst这三组特征是对同一组内容的不同度量方式,最后保留mean这组特征
- 前面提到的6个字段高相关选取其中两个,比如radius_mean、perimeter_mean,这样就把原来组内10个属性缩减为6个属性
"""
# 特征选择
features_remain = [
    "radius_mean",
    "texture_mean",
    "perimeter_mean",
    "smoothness_mean",
    "symmetry_mean",
    "fractal_dimension_mean",
]


# 准备训练集和测试集
# 抽取30%的数据作为测试集,其余作为训练集
from sklearn.model_selection import train_test_split
data['diagnosis'] = training_labels
train,test = train_test_split(data, test_size=0.3)
train_y = train['diagnosis']
test_y = test['diagnosis']
# 6个特征
train_x = train[features_remain]
test_x = test[features_remain]
# 10个特征
ten_train_x = train[features_mean]
ten_test_x = test[features_mean]


# - 数据规范,为了数据在同一个量级上,避免维度问题造成数据误差,这里采用Z-Score规范化数据,保证每个特征维度的数据均值为0,方差为1
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
# 6个特征
train_x_scaler = scaler.fit_transform(train_x)
test_x_scaler = scaler.transform(test_x)
# 10个特征
ten_train_x_scaler = scaler.fit_transform(ten_train_x)
ten_test_x_scaler = scaler.transform(ten_test_x)


# 建模(训练和预测)
# - SVM分类器

from sklearn import svm
from sklearn.metrics import accuracy_score
svc_model = svm.SVC()
svc_model.fit(train_x_scaler, train_y)
prediction = svc_model.predict(test_x_scaler)
print(f"6个特征变量-测试集准确率: {accuracy_score(test_y, prediction)}")
# 6个特征变量-测试集准确率: 0.935672514619883

train_svc_mode = svm.SVC()
train_svc_mode.fit(test_x_scaler, test_y)
train_svc_prediction = train_svc_mode.predict(train_x_scaler)
print(f"6个特征变量-训练集准确率: {accuracy_score(train_y, train_svc_prediction)}")
# 6个特征变量-训练集准确率: 0.9271356783919598

# - 10个特征
ten_svc_model = svm.SVC()
ten_svc_model.fit(ten_train_x_scaler, train_y)
ten_prediction = ten_svc_model.predict(ten_test_x_scaler)
print(f"10个特征变量-测试集准确率: {accuracy_score(test_y, ten_prediction)}")
# 10个特征变量-测试集准确率: 0.9590643274853801

ten_train_svc_mode = svm.SVC()
ten_train_svc_mode.fit(ten_test_x_scaler, test_y)
ten_train_svc_prediction = ten_train_svc_mode.predict(ten_train_x_scaler)
print(f"10个特征变量-训练集准确率: {accuracy_score(train_y, ten_train_svc_prediction)}")
# 10个特征变量-训练集准确率: 0.9422110552763819

# - LinearSVC训练模型(6个特征)

linearsvc_model = svm.LinearSVC()
linearsvc_model.fit(train_x_scaler, train_y)
linearsvc_prediction = linearsvc_model.predict(test_x_scaler)
print(f"6个特征变量-测试集准确率: {accuracy_score(test_y, linearsvc_prediction)}")
# 6个特征变量-测试集准确率: 0.9298245614035088

train_linearsvc_model = svm.LinearSVC()
train_linearsvc_model.fit(test_x_scaler, test_y)
train_linearsvc_prediction = train_linearsvc_model.predict(train_x_scaler)
print(f"6个特征变量-训练集准确率: {accuracy_score(train_y, train_linearsvc_prediction)}")
# 6个特征变量-训练集准确率: 0.9346733668341709

ten_linearsvc_model = svm.LinearSVC()
ten_linearsvc_model.fit(ten_train_x_scaler, train_y)
ten_linearsvc_prediction = ten_linearsvc_model.predict(ten_test_x_scaler)
print(f"10个特征变量-测试集准确率: {accuracy_score(test_y, ten_linearsvc_prediction)}")
# 10个特征变量-测试集准确率: 0.9473684210526315

ten_train_linearsvc_model = svm.LinearSVC()
ten_train_linearsvc_model.fit(ten_test_x_scaler, test_y)
ten_train_linearsvc_prediction = ten_train_linearsvc_model.predict(ten_train_x_scaler)
print(f"10个特征变量-训练集准确率: {accuracy_score(train_y, ten_train_linearsvc_prediction)}")
# 10个特征变量-训练集准确率: 0.9246231155778895

# 6个特征增加到10个特征,增加特征变量可以提高准确率,模型复杂度也提高了
# 训练集准确率对比测试集准确率下降了

肿瘤诊断结果可视化:
image
热力图呈现features_mean字段之间的相关性:
image

4.延伸提问

1.SVM为什么要间隔最大化?

  • 若训练数据线性可分,会存在无数多个超平面,利用间隔最大化的原理能够得到唯一解,且这类结果对于实例的泛化能力更强。

2.非线性可分问题可以用支持向量机吗?

  • 当数据近似线性可分的时候,可以引入松弛变量,求解软间隔最大化,此时为线性支持向量机;

  • 当数据线性不可分的时候,引入核函数以及软间隔最大化,得到非线性支持向量机。

3.有监督学习和无监督学的理解?

  • 有监督学习和无监督学习区别在于训练数据是否带有标签,有监督学习的数据既有特征又有标签,而无监督学习的数据只有特征没有标签,核心分别是分类和聚类、同维和降维。
  • 有监督学习,有点类似父母在家教育孩子识别事物,然后孩子自己再去识别新的事物;
  • 无监督学习,有点类似留守儿童,孩子靠自己去学习识别事物,然后孩子自己再去识别新的事物;

4.SVM主要的思想就是硬间隔、软间隔和核函数,如何理解?

  • 从数据是否线性可分、容错能力去区分它们;
  • 硬间隔,它的数据是线性可分,最理想的数据,容错为零;
  • 软间隔,它的数据是不完全可分,比较接近现实,允许一定分类错误,有容错能力;
  • 核函数,原先数据是线性不可分,经过核函数把原样本空间投射到高维空间,使得样本在新的空间中线性可分。

5.SVM的核函数?

  • 在原始数据线性不可分的时候,需要引入核函数,将原始样本从现有空间映射到一个更高维的空间,使得样本在高维空间中线性可分。常见的核函数包括:线性核(线性不可分),高斯核(线性可分),多项式核(线性可分),核函数(线性可分);
  • 线性核-linear:主要用于特征非常多的情况,线性可分,参数少并且运算的速度快,不足的是不能处理线性不可分的数据;
  • 高斯核-rbf:参数相对较少,不论是大样本还是小样本都有比较好的效果,在不知道该使用什么核函数的时候,优先使用高斯核或者线性核;(SVC构造函数中的kernel参数默认就是高斯核函数rbf)

  • 多项式核-poly:核参数较多,尤其是当多项式的阶数较高的时候,计算复杂度大。

  • 核函数-sigmoid:实现的是多层神经网络

6.如何区分svm分类器LinearSVC 和 SVC?

  • LinearSVC没有Kernel参数,限制只能用线性核函数;对于数据量大的线性可分问题,使用LinearSVC的效率高于SVC;
  • SVC有Kernel参数,能使用其它核函数,当我们不知道数据集是否为线性,直接使用SVC类创建SVM分类器。

相关参考:

支持向量机

https://time.geekbang.org/column/article/79975

https://www.nowcoder.com/tutorial/10080/ea364e74dc694595a8cfc714e80f57aa

推荐阅读