一、基本概念二、鸢尾花实验三、乳腺癌检测实验
【024】SVM有监督学习LinearSVC, LinearSVR,SVC,SVR参数解释
线性支持向量机
在超平面确定的情况下,可以相对地表示点距离超平面的远近。对于两类分类问题,如果,则的类别被判定为1;否则判定为-1(参考:https://zhuanlan.zhihu.com/p/31886934)。训练完成后,大部分的训练样本都不需要保留,最终模型仅与支持向量有关。(https://cloud.tencent.com/developer/article/1424413 https://www.kesci.com/home/project/5de08a8dca27f8002c4afe3b)
所以如果,则认为的分类结果是正确的,否则是错误的。且的值越大,分类结果的确信度越大。反之亦然。
所以样本点与超平面之间的函数间隔定义为
但是该定义存在问题:即和同时缩小或放大M倍后,超平面并没有变化,但是函数间隔却变化了。所以,需要将的大小固定,如,使得函数间隔固定。这时的间隔也就是几何间隔 。
几何间隔的定义如下
实际上,几何间隔就是点到超平面的距离。想像下中学学习的点到直线的距离,点到直线的距离公式
所以在二维空间中,几何间隔就是点到直线的距离。在三维及以上空间中,就是点到超平面的距离。而函数距离,就是上述距离公式中的分子,即未归一化的距离。
定义训练集到超平面的最小几何间隔是 ,实际上这个距离就是我们所谓的支持向量到超平面的距离。
SVM训练分类器的方法是寻找到超平面,使正负样本在超平面的两侧,且样本到超平面的几何间隔最大。根据以上定义,SVM模型的求解最大分割超平面问题可以表示为以下约束最优化问题
将约束条件两边同时除以 ,得到
因为 都是标量,所以为了表达式简洁起见,令
得到
又因为最大化 ,等价于最大化 ,也就等价于最小化 ( 是为了后面求导以后形式简洁,不影响结果),因此SVM模型的求解最大分割超平面问题又可以表示为以下约束最优化问题
几何间隔:在支持向量机中,当样本点被超平面正确分类时,该点与超平面的距离被定义为几何间隔。
SVM学习的基本想法是求解能够正确划分训练数据集并且几何间隔最大的分离超平面。如下图所示, 即为分离超平面,对于线性可分的数据集来说,这样的超平面有无穷多个(即感知机),但是几何间隔最大的分离超平面却是唯一的。
对于输入空间中的非线性分类问题
对于输入空间中的非线性分类问题,可以通过非线性变换将它转化为某个维特征空间中的线性分类问题,在高维特征空间中学习线性支持向量机。由于在线性支持向量机学习的对偶问题里,目标函数和分类决策函数都只涉及实例和实例之间的内积,所以不需要显式地指定非线性变换,而是用核函数替换当中的内积。
核函数表示,通过一个非线性转换后的两个实例间的内积。
支持向量机有两种:SVC,支持向量分类,用于分类问题;SVR,支持向量回归,用于回归问题。鸢尾花数据集,它是基于鸢尾花的花萼的长度和宽度进行分类的。
import numpy as np
import pandas as pd
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
# data
def create_data():
iris = load_iris()
df = pd.DataFrame(iris.data, columns=iris.feature_names)
df['label'] = iris.target
df.columns = ['sepal length', 'sepal width', 'petal length', 'petal width', 'label']
data = np.array(df.iloc[:100, [0, 1, -1]])
for i in range(len(data)):
if data[i, -1] == 0:
data[i, -1] = -1
# print(data)
return data[:, :2], data[:, -1]
X, y = create_data()
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25)
plt.scatter(X[:50, 0], X[:50, 1], label='0')
plt.scatter(X[50:, 0], X[50:, 1], label='1')
plt.legend()
plt.show()
class SVM:
def __init__(self, max_iter=100, kernel='linear'):
self.max_iter = max_iter
self._kernel = kernel
def init_args(self, features, labels):
self.m, self.n = features.shape
self.X = features
self.Y = labels
self.b = 0.0
# 将Ei保存在一个列表里
self.alpha = np.ones(self.m)
self.E = [self._E(i) for i in range(self.m)]
# 松弛变量
self.C = 1.0
def _KKT(self, i):
y_g = self._g(i) * self.Y[i]
if self.alpha[i] == 0:
return y_g >= 1
elif 0 < self.alpha[i] < self.C:
return y_g == 1
else:
return y_g <= 1
# g(x)预测值,输入xi(X[i])
def _g(self, i):
r = self.b
for j in range(self.m):
r += self.alpha[j] * self.Y[j] * self.kernel(self.X[i], self.X[j])
return r
# 核函数
def kernel(self, x1, x2):
if self._kernel == 'linear':
return sum([x1[k] * x2[k] for k in range(self.n)])
elif self._kernel == 'poly':
return (sum([x1[k] * x2[k] for k in range(self.n)]) + 1) ** 2
return 0
# E(x)为g(x)对输入x的预测值和y的差
def _E(self, i):
return self._g(i) - self.Y[i]
def _init_alpha(self):
# 外层循环首先遍历所有满足0= 0:
j = min(range(self.m), key=lambda x: self.E[x])
else:
j = max(range(self.m), key=lambda x: self.E[x])
return i, j
def _compare(self, _alpha, L, H):
if _alpha > H:
return H
elif _alpha < L:
return L
else:
return _alpha
def fit(self, features, labels):
self.init_args(features, labels)
for t in range(self.max_iter):
# train
i1, i2 = self._init_alpha()
# 边界
if self.Y[i1] == self.Y[i2]:
L = max(0, self.alpha[i1] + self.alpha[i2] - self.C)
H = min(self.C, self.alpha[i1] + self.alpha[i2])
else:
L = max(0, self.alpha[i2] - self.alpha[i1])
H = min(self.C, self.C + self.alpha[i2] - self.alpha[i1])
E1 = self.E[i1]
E2 = self.E[i2]
# eta=K11+K22-2K12
eta = self.kernel(self.X[i1], self.X[i1]) + self.kernel(self.X[i2], self.X[i2]) - 2 * self.kernel(
self.X[i1], self.X[i2])
if eta <= 0:
# print('eta <= 0')
continue
alpha2_new_unc = self.alpha[i2] + self.Y[i2] * (E1 - E2) / eta # 此处有修改,根据书上应该是E1 - E2,书上130-131页
alpha2_new = self._compare(alpha2_new_unc, L, H)
alpha1_new = self.alpha[i1] + self.Y[i1] * self.Y[i2] * (self.alpha[i2] - alpha2_new)
b1_new = -E1 - self.Y[i1] * self.kernel(self.X[i1], self.X[i1]) * (alpha1_new - self.alpha[i1]) - self.Y[
i2] * self.kernel(self.X[i2], self.X[i1]) * (alpha2_new - self.alpha[i2]) + self.b
b2_new = -E2 - self.Y[i1] * self.kernel(self.X[i1], self.X[i2]) * (alpha1_new - self.alpha[i1]) - self.Y[
i2] * self.kernel(self.X[i2], self.X[i2]) * (alpha2_new - self.alpha[i2]) + self.b
if 0 < alpha1_new < self.C:
b_new = b1_new
elif 0 < alpha2_new < self.C:
b_new = b2_new
else:
# 选择中点
b_new = (b1_new + b2_new) / 2
# 更新参数
self.alpha[i1] = alpha1_new
self.alpha[i2] = alpha2_new
self.b = b_new
self.E[i1] = self._E(i1)
self.E[i2] = self._E(i2)
return 'train done!'
def predict(self, data):
r = self.b
for i in range(self.m):
r += self.alpha[i] * self.Y[i] * self.kernel(data, self.X[i])
return 1 if r > 0 else -1
def score(self, X_test, y_test):
right_count = 0
for i in range(len(X_test)):
result = self.predict(X_test[i])
if result == y_test[i]:
right_count += 1
return right_count / len(X_test)
def _weight(self):
# linear model
yx = self.Y.reshape(-1, 1) * self.X
self.w = np.dot(yx.T, self.alpha)
return self.w
svm = SVM(max_iter=200)
print('svm.fit(X_train, y_train)',svm.fit(X_train, y_train))
print('svm.score(X_test, y_test)',svm.score(X_test, y_test))
svm.fit(X_train, y_train) train done!
svm.score(X_test, y_test) 0.48
正如我们所见,kernel="linear"
(线性核函数)给了我们线性的决策边界:两类之间的分离边界是直线。
多分类的工作方式就是"one versus one" :在任意两类样本之间设计一个SVM,
因此k个类别的样本就需要设计k(k-1)/2个SVM。当对一个未知样本进行分类时,
最后得票最多的类别即为该未知样本的类别。
线性支持向量分类器(LinearSVC):对于线性核函数,有一个新的对象LinearSVC
,
它使用了不同的算法。在某些数据集上运行地更快(比如稀疏数据集,文本挖掘就是
典型的例子)。它对于多分类采用的是"one versus all"策略。
支持向量:就是最靠近分离边界的样本点。支持向量机的工作方式就是找到这些支持向量,它们被认为是在二分类问题中最具代表性的样本点。
为了更方便的可视化,我们选择二分类问题,也就是只考虑鸢尾花数据集中的1类和2类样本。这两类不是线性可分的,所以我们可以看到更有趣的一些东西。
支持向量的坐标可以通过方法support_vectors_
来找到。
正则化 :只考虑支持向量其实就是一种正则化的形式。实际上,它强迫模型在处理样本特征的时候变得更加简单。This regularization can be tuned with the C parameter:
正则项可以通过调整系数 C 来决定:
小的C值:将会有很多支持向量。决策边界=类别A的平均值-类别B的平均值
大的C值:将会有较少的支持向量。决策边界是被大多数支持向量所决定。
支持向量机的有个好处是对于许多数据集,默认参数'C=1'其实工作得很好。
实践经验:对样本正则化:对于许多分离器,采用标准差正则方法是非常重要的提升预测效果的手段。
linear,线性核,会产生线性分类边界。一般来说它的计算效率最高,而且需要数据最少。
poly ,多项式核,会产生多项式分类边界。
rbf,径向基函数,也就是高斯核,是根据与每一个支持向量的距离来决定分类边界的。它的映射到无线维的。它是最灵活的方法,但是也需要最多的数据。
from sklearn.svm import SVC
clf = SVC()
print('clf.fit(X_train, y_train)',clf.fit(X_train, y_train))
print('clf.score(X_test, y_test)',clf.score(X_test, y_test))
clf.fit(X_train, y_train) SVC(C=1.0, cache_size=200, class_weight=None, coef0=0.0,
decision_function_shape='ovr', degree=3, gamma='auto_deprecated',
kernel='rbf', max_iter=-1, probability=False, random_state=None,
shrinking=True, tol=0.001, verbose=False)
clf.score(X_test, y_test) 1.0
# 导入相关库
from sklearn import svm
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn import preprocessing
from sklearn.metrics import accuracy_score
pd.set_option('display.max_columns', 10000)
#显示所有行
pd.set_option('display.max_rows', 10000)
#设置value的显示长度为100,默认为50
pd.set_option('max_colwidth',10000)
pd.set_option('display.width', 10000)#不换行
model = svm.SVC(kernel='rbf',C=1.0,gamma='auto') # 默认 kernel 为高斯核函数
df = pd.read_csv('D:\\A\\AI-master\\py-data\\breast-cancer.csv')
df.head()
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 Unnamed: 32
0 842302 M 17.99 10.38 122.80 1001.0 0.11840 0.27760 0.3001 0.14710 0.2419 0.07871 1.0950 0.9053 8.589 153.40 0.006399 0.04904 0.05373 0.01587 0.03003 0.006193 25.38 17.33 184.60 2019.0 0.1622 0.6656 0.7119 0.2654 0.4601 0.11890 NaN
1 842517 M 20.57 17.77 132.90 1326.0 0.08474 0.07864 0.0869 0.07017 0.1812 0.05667 0.5435 0.7339 3.398 74.08 0.005225 0.01308 0.01860 0.01340 0.01389 0.003532 24.99 23.41 158.80 1956.0 0.1238 0.1866 0.2416 0.1860 0.2750 0.08902 NaN
2 84300903 M 19.69 21.25 130.00 1203.0 0.10960 0.15990 0.1974 0.12790 0.2069 0.05999 0.7456 0.7869 4.585 94.03 0.006150 0.04006 0.03832 0.02058 0.02250 0.004571 23.57 25.53 152.50 1709.0 0.1444 0.4245 0.4504 0.2430 0.3613 0.08758 NaN
3 84348301 M 11.42 20.38 77.58 386.1 0.14250 0.28390 0.2414 0.10520 0.2597 0.09744 0.4956 1.1560 3.445 27.23 0.009110 0.07458 0.05661 0.01867 0.05963 0.009208 14.91 26.50 98.87 567.7 0.2098 0.8663 0.6869 0.2575 0.6638 0.17300 NaN
4 84358402 M 20.29 14.34 135.10 1297.0 0.10030 0.13280 0.1980 0.10430 0.1809 0.05883 0.7572 0.7813 5.438 94.44 0.011490 0.02461 0.05688 0.01885 0.01756 0.005115 22.54 16.67 152.20 1575.0 0.1374 0.2050 0.4000 0.1625 0.2364 0.07678 NaN
df.shape #(569, 33)
df.dtypes
id int64
diagnosis object
radius_mean float64
texture_mean float64
perimeter_mean float64
area_mean float64
smoothness_mean float64
compactness_mean float64
concavity_mean float64
concave points_mean float64
symmetry_mean float64
fractal_dimension_mean float64
radius_se float64
texture_se float64
perimeter_se float64
df.info()
RangeIndex: 569 entries, 0 to 568
Data columns (total 33 columns):
id 569 non-null int64
diagnosis 569 non-null object
radius_mean 569 non-null float64
texture_mean 569 non-null float64
perimeter_mean 569 non-null float64
area_mean 569 non-null float64
smoothness_mean 569 non-null float64
compactness_mean 569 non-null float64
concavity_mean 569 non-null float64
concave points_mean 569 non-null float64
symmetry_mean 569 non-null float64
fractal_dimension_mean 569 non-null float64
radius_se 569 non-null float64
texture_se 569 non-null float64
perimeter_se 569 non-null float64
area_se 569 non-null float64
smoothness_se 569 non-null float64
compactness_se 569 non-null float64
concavity_se 569 non-null float64
concave points_se 569 non-null float64
symmetry_se 569 non-null float64
fractal_dimension_se 569 non-null float64
radius_worst 569 non-null float64
texture_worst 569 non-null float64
perimeter_worst 569 non-null float64
area_worst 569 non-null float64
smoothness_worst 569 non-null float64
compactness_worst 569 non-null float64
concavity_worst 569 non-null float64
concave points_worst 569 non-null float64
symmetry_worst 569 non-null float64
fractal_dimension_worst 569 non-null float64
Unnamed: 32 0 non-null float64
dtypes: float64(31), int64(1), object(1)
memory usage: 146.8+ KB
df.describe()
id 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 Unnamed: 32
count 5.690000e+02 569.000000 569.000000 569.000000 569.000000 569.000000 569.000000 569.000000 569.000000 569.000000 569.000000 569.000000 569.000000 569.000000 569.000000 569.000000 569.000000 569.000000 569.000000 569.000000 569.000000 569.000000 569.000000 569.000000 569.000000 569.000000 569.000000 569.000000 569.000000 569.000000 569.000000 0.0
mean 3.037183e+07 14.127292 19.289649 91.969033 654.889104 0.096360 0.104341 0.088799 0.048919 0.181162 0.062798 0.405172 1.216853 2.866059 40.337079 0.007041 0.025478 0.031894 0.011796 0.020542 0.003795 16.269190 25.677223 107.261213 880.583128 0.132369 0.254265 0.272188 0.114606 0.290076 0.083946 NaN
std 1.250206e+08 3.524049 4.301036 24.298981 351.914129 0.014064 0.052813 0.079720 0.038803 0.027414 0.007060 0.277313 0.551648 2.021855 45.491006 0.003003 0.017908 0.030186 0.006170 0.008266 0.002646 4.833242 6.146258 33.602542 569.356993 0.022832 0.157336 0.208624 0.065732 0.061867 0.018061 NaN
min 8.670000e+03 6.981000 9.710000 43.790000 143.500000 0.052630 0.019380 0.000000 0.000000 0.106000 0.049960 0.111500 0.360200 0.757000 6.802000 0.001713 0.002252 0.000000 0.000000 0.007882 0.000895 7.930000 12.020000 50.410000 185.200000 0.071170 0.027290 0.000000 0.000000 0.156500 0.055040 NaN
25% 8.692180e+05 11.700000 16.170000 75.170000 420.300000 0.086370 0.064920 0.029560 0.020310 0.161900 0.057700 0.232400 0.833900 1.606000 17.850000 0.005169 0.013080 0.015090 0.007638 0.015160 0.002248 13.010000 21.080000 84.110000 515.300000 0.116600 0.147200 0.114500 0.064930 0.250400 0.071460 NaN
50% 9.060240e+05 13.370000 18.840000 86.240000 551.100000 0.095870 0.092630 0.061540 0.033500 0.179200 0.061540 0.324200 1.108000 2.287000 24.530000 0.006380 0.020450 0.025890 0.010930 0.018730 0.003187 14.970000 25.410000 97.660000 686.500000 0.131300 0.211900 0.226700 0.099930 0.282200 0.080040 NaN
75% 8.813129e+06 15.780000 21.800000 104.100000 782.700000 0.105300 0.130400 0.130700 0.074000 0.195700 0.066120 0.478900 1.474000 3.357000 45.190000 0.008146 0.032450 0.042050 0.014710 0.023480 0.004558 18.790000 29.720000 125.400000 1084.000000 0.146000 0.339100 0.382900 0.161400 0.317900 0.092080 NaN
max 9.113205e+08 28.110000 39.280000 188.500000 2501.000000 0.163400 0.345400 0.426800 0.201200 0.304000 0.097440 2.873000 4.885000 21.980000 542.200000 0.031130 0.135400 0.396000 0.052790 0.078950 0.029840 36.040000 49.540000 251.200000 4254.000000 0.222600 1.058000 1.252000 0.291000 0.663800 0.207500 NaN
从上面可以看出字段中没有确实值,不用进行处理,除了diagnosis 列是object 字段外,其他都是float 字段,符合我们的数据处理的要求。
df['diagnosis'].value_counts() # 查看患有乳腺癌的人的人数,B为正常,M为患者
B 357
M 212
Name: diagnosis, dtype: int64
df[df.isnull().values == True] # 如果有缺失值,直接查看缺失值的列
df.isnull().any() #列中是否存在空值
# 数据清洗
# 删除ID列,没有实际意义
df.drop('id',axis = 1,inplace = True)
# 将Diagnosis列的数据转换成0,1的形式
df['diagnosis'] = df['diagnosis'].map({'B':0,'M':1})
# 将数据分成三组,分别是平均值,标准差,和最大值
features_mean = (df.columns[1:11]).to_list()
features_se = (df.columns[11:21]).to_list()
features_wost = (df.columns[21:31]).to_list()
features_mean
['radius_mean',
'texture_mean',
'perimeter_mean',
'area_mean',
'smoothness_mean',
'compactness_mean',
'concavity_mean',
'concave points_mean',
'symmetry_mean',
'fractal_dimension_mean']
# 将肿瘤的结果可视化
sns.countplot(df['diagnosis'],label='Count')
plt.show()
# 用热力图展现features-mean 字段之间的相关性
corr = df[features_mean].corr()
plt.figure(figsize=(14,14))
# annot = True 显示每个方格的数据
sns.heatmap(corr,annot = True)
颜色越浅为相关性大的特征,如果相关性大则从中选取其中一个特征作为代表即可。从上面的图中可以看出来radius_mean,parameter_mean,area_mean 相关性特别大,compactness_mean,cavity_mean,concave points_mean相关性特别大,所以选取其中一个作为代表即可,至于选择哪个特作为代表,结果不会有特别大的影响。
特征选择的目的是降唯,用少量的特征代表数据的特性,这样也可以增强模型的泛化能力,避免数据过拟合。
在本次特征选择中,我们选择忽略掉se,和worst 特征。因为这些特征都是对于同一个数据的不同表达方式,这样的话,我们就可以从30个特征当中选择10个特征,又根据上面的相关性数据,还剩下6个特征。因此特征选择如下:
# 特征选择
features_remain = ['radius_mean','texture_mean','smoothness_mean',
'compactness_mean','symmetry_mean','fractal_dimension_mean']
# 抽取30%的数据作为测试集,其余为训练集
train,test = train_test_split(df,test_size = 0.3)
train_x = train[features_remain] # 这里只是选取部分特征作为主要特征去分类
train_y = train['diagnosis']
test_x = test[features_remain]
test_y = test['diagnosis']
# 对数据进行Z-score规范化,避免因为数据量级不同而导致的权重不同(数据转换)
ss = preprocessing.StandardScaler()
train_x = ss.fit_transform(train_x)
test_x = ss.transform(test_x)
print('train_x.shape,test_x.shape',train_x.shape,test_x.shape)
train_x.shape,test_x.shape (398, 6) (171, 6)
模型创建
# 创建SVM 分类器
model = svm.SVC()
# 用训练集做训练
model.fit(train_x,train_y)
# 预测测试集
prediction = model.predict(test_x)
# 计算预测的准确率
print("预测的准确率是:",accuracy_score(prediction,test_y))
预测的准确率是:0.9122807017543859
# 将除了ID外的列选择为所有的特征
data = df
features_main = [features_mean,features_se,features_wost]
# 将30% 的数据作为测试数据,其余为训练数据
train,test = train_test_split(data,test_size = 0.3)
train_xx = train[train.columns[1:31]]
train_yy = train['diagnosis']
test_xx = test[test.columns[1:31]]
test_yy = test['diagnosis']
# 对数据进行Z-score规范化,避免因为数据量级不同而导致的权重不同(数据转换)
ss = preprocessing.StandardScaler()
train_xx = ss.fit_transform(train_xx)
test_xx = ss.transform(test_xx)
# 创建分类器
model_linear_svc = svm.LinearSVC()
# 对训练数据进行训练
model_linear_svc.fit(train_xx,train_yy)
# 预测测试集
prediction_lsvc = model_linear_svc.predict(test_xx)
# 预测的准确率
print('预测的准确率是:',accuracy_score(prediction_lsvc,test_yy))
# 对数据进行Z-score规范化,避免因为数据量级不同而导致的权重不同(数据转换)
ss = preprocessing.StandardScaler()
train_xx = ss.fit_transform(train_xx)
test_xx = ss.transform(test_xx)
预测的准确率是:0.9707602339181286
特征的选择对于数据挖掘以及预测的结果非常重要,尤其是数据清洗,数据转换,不同的特征选择会导致不同的预测准确率,同时算法的选择也会影响模型的准确率。
About Me:小麦粒
● 本文作者:小麦粒,专注于python、数据分析、数据挖掘、机器学习相关技术,也注重技术的运用
● 作者博客地址:https://blog.csdn.net/u010986753
● 本系列题目来源于作者的学习笔记,部分整理自网络,若有侵权或不当之处还请谅解
● 版权所有,欢迎分享本文,转载请保留出处
● 微信:tinghai87605025 联系我加微信群
● QQ:87605025
● QQ交流群pythobao :483766429
● 公众号:python宝 或 DB宝
● 提供OCP、OCM和高可用最实用的技能培训
● 题目解答若有不当之处,还望各位朋友批评指正,共同进步
如果您觉得到文章对您有帮助,欢迎赞赏哦!有您的支持,小麦粒一定会越来越好!