基本概念
支持向量机是一种监督学习算法,主要用于分类和回归问题。它的基本原理是找到一个最优的超平面,将不同类别的样本分隔开来。在二维空间中,超平面相当于一条直线,而在高维空间中,它相当于一个超平面。这个超平面被称为最大间隔超平面。间隔指的是两个支持向量(距离超平面最近的样本点)之间的距离。
几何间隔和函数间隔
分离超平面:
点到直线距离:
为2-范数:
直线为超平面,样本可表示为:
,
定义:几何间隔, 点到直线
的距离为
如果点x相对于直线与方向w同侧, 则
, 异侧则
, 因此导出几何间隔
定义函数间隔
最大间隔
支持向量机最简单的情况是线性可分支持向量机,或最大间隔支持向量机。构建它的条件是训练数据线性可分。其学习策略是最大间隔法。可以表示为凸二次规划问题,其原始最优化问题为
求得最优化问题的解,
,得到线性可分支持向量机,分离超平面是
分类决策函数是
最大间隔法中,函数间隔与几何间隔是重要的概念。
线性可分支持向量机的最优解存在且唯一。位于间隔边界上的实例点为支持向量。最优分离超平面由支持向量完全决定。 二次规划问题的对偶问题是
通常,通过求解对偶问题学习线性可分支持向量机,即首先求解对偶问题的最优值,然后求最优值
和
,得出分离超平面和分类决策函数。
软间隔
现实中训练数据是线性可分的情形较少,训练数据往往是近似线性可分的,这时使用线性支持向量机,或软间隔支持向量机。线性支持向量机是最基本的支持向量机。
对于噪声或例外,通过引入松弛变量,使其“可分”,得到线性支持向量机学习的凸二次规划问题,其原始最优化问题是
求解原始最优化问题的解和
,得到线性支持向量机,其分离超平面为
分类决策函数为
线性可分支持向量机的解唯一但
不唯一。对偶问题是
线性支持向量机的对偶学习算法,首先求解对偶问题得到最优解, 然后求原始问题最优解
和
,得出分离超平面和分类决策函数。
对偶问题的解中满
的实例点
称为支持向量。支持向量可在间隔边界上,也可在间隔边界与分离超平面之间,或者在分离超平面误分一侧。最优分离超平面由支持向量完全决定。
线性支持向量机学习等价于最小化二阶范数正则化的合页函数
非线性支持向量机
对于输入空间中的非线性分类问题,可以通过非线性变换将它转化为某个高维特征空间中的线性分类问题,在高维特征空间中学习线性支持向量机。由于在线性支持向量机学习的对偶问题里,目标函数和分类决策函数都只涉及实例与实例之间的内积,所以不需要显式地指定非线性变换,而是用核函数来替换当中的内积。核函数表示,通过一个非线性转换后的两个实例间的内积。具体地,K(x,z)是一个核函数,或正定核,意味着存在一个从输入空间x到特征空间的映射X→H,对任意X,有
对称函数K(x,z)K(x,z)K(x,z)为正定核的充要条件如下:对任意
任意正整数m,对称函数K(x,z)对应的Gram矩阵是半正定的。
所以,在线性支持向量机学习的对偶问题中,用核函数K(x,z)替代内积,求解得到的就是非线性支持向量机
目标函数:
对偶问题:
SMO算法
SMO算法是支持向量机学习的一种快速算法,其特点是不断地将原二次规划问题分解为只有两个变量的二次规划子问题,并对子问题进行解析求解,直到所有变量满足KKT条件为止。这样通过启发式的方法得到原二次规划问题的最优解。因为子问题有解析解,所以每次计算子问题都很快,虽然计算子问题次数很多,但在总体上还是高效的。
首先定义特征到结果的输出函数:,因为
有
下面展示鸢尾花数据集的训练集和测试集在二维平面上的分布情况
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
%matplotlib inline
# 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)
X_T_1=[]
X_T_2=[]
for i in range(len(y_test)):
if y_test[i]==1:
X_T_1=X_T_1+[X_test[i]]
else:
X_T_2=X_T_2+[X_test[i]]
X_S_1=[]
X_S_2=[]
for i in range(len(y_train)):
if y_train[i]==1:
X_S_1=X_S_1+[X_train[i]]
else:
X_S_2=X_S_2+[X_train[i]]
X_T_1=np.array(X_T_1)
X_T_2=np.array(X_T_2)
X_S_1=np.array(X_S_1)
X_S_2=np.array(X_S_2)
plt.scatter(X_T_1[:,0],X_T_1[:,1], label='Test0')
plt.scatter(X_T_2[:,0],X_T_2[:,1], label='Test1')
plt.scatter(X_S_1[:,0],X_S_1[:,1], label='Train0')
plt.scatter(X_S_2[:,0],X_S_2[:,1], label='Train1')
plt.legend()
plt.show()
X.shape
(100, 2)
from sklearn.svm import SVC
clf = SVC(kernel='linear', C=100)
clf.fit(X_train, y_train)
xaxis = np.linspace(min(X[:][0]),max(X[:][0])+2,10 )
w = clf.coef_[0]
# 计算斜率
a = -w[0] / w[1]
# 得到分离超平面
y_sep = a * xaxis - (clf.intercept_[0]) / w[1]
# 下边界超平面
b = clf.support_vectors_[0]
yy_down = a * xaxis + (b[1] - a * b[0])
# 上边界超平面
b = clf.support_vectors_[-1]
yy_up = a * xaxis + (b[1] - a * b[0])
# 绘制超平面
plt.plot(xaxis, y_sep, 'k-')
plt.plot(xaxis, yy_down, 'k--')
plt.plot(xaxis, yy_up, 'k--')
plt.scatter(X_T_1[:,0],X_T_1[:,1], label='Test0')
plt.scatter(X_T_2[:,0],X_T_2[:,1], label='Test1')
plt.scatter(X_S_1[:,0],X_S_1[:,1], label='Train0')
plt.scatter(X_S_2[:,0],X_S_2[:,1], label='Train1')
垃圾邮件分类
加载数据集
# 加载训练集和测试集数据
train_data = loadmat('D:/python/svm_data/data/spamTrain.mat')
test_data = loadmat('D:/python/svm_data/data/spamTest.mat')
# 获取训练集和测试集特征和标签
X_train = train_data['X'].astype(np.float64)
y_train = train_data['y'].ravel().astype(np.float64)
X_test = test_data['Xtest'].astype(np.float64)
y_test = test_data['ytest'].ravel().astype(np.float64)
文本数据转换为数值型特征向量
# 加载词汇表文件
vocab_path = 'D:/python/svm_data/data/vocab.txt'
with open(vocab_path, 'rt', encoding='utf-8') as f:
vocab_list = [line.strip() for line in f.readlines()]
# 初始化 CountVectorizer 对象,指定词汇表
vectorizer = CountVectorizer(vocabulary=vocab_list)
# 将特征向量化
X_train = vectorizer.fit_transform(X_train.astype(str).ravel())
X_test = vectorizer.transform(X_test.astype(str).ravel())
训练
# 训练 SVM 模型
C = 0.1
clf = svm.SVC(C=C, kernel='linear')
clf.fit(X_train, y_train)
# 在测试集上进行预测并计算准确率
y_pred = clf.predict(X_test)
accuracy = np.mean(y_pred == y_test)
print('Accuracy:', accuracy)
结果
实验总结
支持向量机是一种强大的机器学习算法,用于解决分类和回归问题。它通过找到一个最优的超平面来准确地分离不同类别的数据点。SVM在处理线性可分或近似可分的情况下表现出色,并且由于其能够有效地处理高维度数据集和非线性问题而受到广泛应用。该算法的核心思想是最大化支持向量与超平面的边际距离,从而提高分类的准确性。此外,SVM还可以通过使用不同的核函数来灵活地处理非线性问题。