机器学习实验支持向量机SVM
一、支持向量机
1.1简介
支持向量机(SVM)** 是一种有监督的机器学习算法,广泛应用于分类和回归问题。其核心思想是通过寻找一个最优超平面(Hyperplane)来对数据进行分割,使得不同类别的数据点之间的间隔(Margin)最大化。SVM 在小样本、高维数据场景中表现优异,常用于图像识别、文本分类、生物信息学等领域。
1.2原理
- 超平面与间隔最大化
超平面:在 n 维空间中,超平面是 n-1 维的决策边界,用于分离不同类别的数据。例如,在二维空间中是一条直线,三维空间中是一个平面。
间隔(Margin):指两类数据点到超平面的最小距离之和。SVM 的目标是找到使间隔最大的超平面,以提高模型的泛化能力。
2. 支持向量(Support Vectors)
决定超平面位置的关键数据点,即离超平面最近的样本点。
超平面仅由支持向量决定,其他样本点不影响模型决策。
3. 线性可分与线性不可分情况
线性可分:数据可通过线性超平面完全分离,此时使用硬间隔最大化求解。
线性不可分:数据存在噪声或重叠,需引入 ** 软间隔(Soft Margin)** 允许少量样本分类错误,通过惩罚因子(C)平衡间隔大小与分类误差。
1.3数学模型
- 线性可分 SVM(硬间隔模型)假设我们有一个二分类数据集 D={(x1,y1),(x2,y2),...,(xN,yN)}D = \{(x_1, y_1), (x_2, y_2), ..., (x_N, y_N)\}D={(x1,y1),(x2,y2),...,(xN,yN)},其中 xi∈Rnx_i \in \mathbb{R}^nxi∈Rn 是特征向量,yi∈{+1,−1}y_i \in \{+1, -1\}yi∈{+1,−1} 是类别标签。1.1 超平面定义在 nnn 维空间中,一个超平面可以由以下方程定义:wTx+b=0w^T x + b = 0wTx+b=0其中:www 是法向量(Normal Vector),决定了超平面的方向。bbb 是位移项(Bias),决定了超平面与原点的距离。对于任意样本 xix_ixi,我们的分类决策函数为:f(x)=sign(wTx+b)f(x) = \text{sign}(w^T x + b)f(x)=sign(wTx+b)1.2 间隔(Margin)与约束为了使分类不仅正确而且“最稳健”,我们希望所有样本点都离超平面尽可能远。我们要求对于正样本(yi=1y_i=1yi=1),wTxi+b≥1w^T x_i + b \geq 1wTxi+b≥1;对于负样本(yi=−1y_i=-1yi=−1),wTxi+b≤−1w^T x_i + b \leq -1wTxi+b≤−1。这就引出了函数间隔的约束条件:yi(wTxi+b)≥1,i=1,...,Ny_i (w^T x_i + b) \geq 1, \quad i=1, ..., Nyi(wTxi+b)≥1,i=1,...,N此时,离超平面最近的那些点(等号成立的点)被称为支持向量(Support Vectors)。1.3 几何间隔(Geometric Margin)点 xxx 到超平面 wTx+b=0w^T x + b = 0wTx+b=0 的欧几里得距离公式为:d=∣wTx+b∣∣∣w∣∣d = \frac{|w^T x + b|}{||w||}d=∣∣w∣∣∣wTx+b∣对于支持向量,我们要么有 wTx+b=1w^T x + b = 1wTx+b=1,要么有 wTx+b=−1w^T x + b = -1wTx+b=−1。因此,支持向量到超平面的距离是 1∣∣w∣∣\frac{1}{||w||}∣∣w∣∣1。那么,正负两类支持向量之间的总距离(即间隔 Margin)为:Margin=2∣∣w∣∣\text{Margin} = \frac{2}{||w||}Margin=∣∣w∣∣21.4 优化目标(Primal Problem)我们的目标是最大化间隔 2∣∣w∣∣\frac{2}{||w||}∣∣w∣∣2。这等价于最小化 12∣∣w∣∣2\frac{1}{2}||w||^221∣∣w∣∣2(为了方便求导,通常取平方并乘以系数)。因此,硬间隔 SVM 的数学模型可以表述为一个凸二次规划问题(Convex Quadratic Programming):minw,b12∣∣w∣∣2\min_{w, b} \frac{1}{2} ||w||^2w,bmin21∣∣w∣∣2s.t. yi(wTxi+b)≥1,i=1,...,N\text{s.t. } \quad y_i (w^T x_i + b) \geq 1, \quad i=1, ..., Ns.t. yi(wTxi+b)≥1,i=1,...,N2. 对偶问题(Dual Problem)为了更容易求解上述问题,并且自然地引入核函数,我们通常利用拉格朗日乘子法将原问题转化为对偶问题。2.1 拉格朗日函数引入拉格朗日乘子 αi≥0\alpha_i \geq 0αi≥0,构建拉格朗日函数:L(w,b,α)=12∣∣w∣∣2−∑i=1Nαi[yi(wTxi+b)−1]L(w, b, \alpha) = \frac{1}{2}||w||^2 - \sum_{i=1}^{N} \alpha_i [y_i(w^T x_i + b) - 1]L(w,b,α)=21∣∣w∣∣2−i=1∑Nαi[yi(wTxi+b)−1]2.2 求解极值根据对偶性,原问题的解等价于 maxαminw,bL(w,b,α)\max_{\alpha} \min_{w, b} L(w, b, \alpha)maxαminw,bL(w,b,α)。我们需要先对 www 和 bbb 求偏导并令其为 0:∂L∂w=w−∑i=1Nαiyixi=0 ⟹ w=∑i=1Nαiyixi\frac{\partial L}{\partial w} = w - \sum_{i=1}^{N} \alpha_i y_i x_i = 0 \implies w = \sum_{i=1}^{N} \alpha_i y_i x_i∂w∂L=w−∑i=1Nαiyixi=0⟹w=∑i=1Nαiyixi$\frac{\partial L}{\partial b} = -\sum_{i=1}^{N} \alpha_i y_i = 0 \implies \sum_{i=1}^{N} \alpha_i y_i = 0$2.3 对偶形式将上述结果代回拉格朗日函数,消去 www 和 bbb,得到最终的对偶问题:maxα∑i=1Nαi−12∑i=1N∑j=1Nαiαjyiyj(xiTxj)\max_{\alpha} \sum_{i=1}^{N} \alpha_i - \frac{1}{2} \sum_{i=1}^{N} \sum_{j=1}^{N} \alpha_i \alpha_j y_i y_j (x_i^T x_j)αmaxi=1∑Nαi−21i=1∑Nj=1∑Nαiαjyiyj(xiTxj)s.t. ∑i=1Nαiyi=0,αi≥0\text{s.t. } \sum_{i=1}^{N} \alpha_i y_i = 0, \quad \alpha_i \geq 0s.t. i=1∑Nαiyi=0,αi≥0重要性质(KKT 条件):根据 KKT 条件中的互补松弛性(Complementary Slackness),对于任意样本 iii,必须满足:αi[yi(wTxi+b)−1]=0\alpha_i [y_i(w^T x_i + b) - 1] = 0αi[yi(wTxi+b)−1]=0这意味着:如果 αi>0\alpha_i > 0αi>0,则该样本必须在边界上(即 yi(wTxi+b)=1y_i(w^T x_i + b) = 1yi(wTxi+b)=1),它就是支持向量。如果样本不是支持向量,则 αi=0\alpha_i = 0αi=0。这意味着模型只由少数支持向量决定,具有稀疏性。3. 软间隔 SVM(处理噪声与不可分)现实数据往往不是完美线性可分的。为了允许少量样本分类错误(位于间隔内部或误分类),我们引入松弛变量(Slack Variables) ξi≥0\xi_i \geq 0ξi≥0。3.1 修改后的约束yi(wTxi+b)≥1−ξiy_i (w^T x_i + b) \geq 1 - \xi_iyi(wTxi+b)≥1−ξi3.2 软间隔优化目标我们需要在“最大化间隔”和“最小化分类错误”之间权衡。引入惩罚参数 CCC:minw,b,ξ12∣∣w∣∣2+C∑i=1Nξi\min_{w, b, \xi} \frac{1}{2} ||w||^2 + C \sum_{i=1}^{N} \xi_iw,b,ξmin21∣∣w∣∣2+Ci=1∑NξiCCC 很大:对错误零容忍,容易过拟合(Overfitting)。CCC 很小:允许更多错误,追求更宽的间隔,容易欠拟合(Underfitting)。对应的对偶问题仅限制条件变为:0≤αi≤C0 \leq \alpha_i \leq C0≤αi≤C。4. 核函数(Kernel Trick):处理非线性如果数据在原始空间完全无法用线性超平面分割(例如同心圆分布),SVM 采用核技巧。4.1 原理将原始低维空间的数据 xxx 通过一个映射函数 ϕ(x)\phi(x)ϕ(x) 映射到高维特征空间。在高维空间中,数据可能变得线性可分。4.2 核函数的应用观察对偶问题的公式:maxα∑αi−12∑∑αiαjyiyj(xiTxj)⏟内积\max_{\alpha} \sum \alpha_i - \frac{1}{2} \sum \sum \alpha_i \alpha_j y_i y_j \underbrace{(x_i^T x_j)}_{\text{内积}}αmax∑αi−21∑∑αiαjyiyj内积(xiTxj)我们不需要显式计算高维向量 ϕ(x)\phi(x)ϕ(x),只需要计算高维空间中的内积 K(xi,xj)=ϕ(xi)Tϕ(xj)K(x_i, x_j) = \phi(x_i)^T \phi(x_j)K(xi,xj)=ϕ(xi)Tϕ(xj)。这个函数 KKK 就是核函数。常见核函数:线性核(Linear Kernel): K(x,z)=xTzK(x, z) = x^T zK(x,z)=xTz (适用于特征多、样本少的情况)多项式核(Polynomial Kernel): K(x,z)=(xTz+1)dK(x, z) = (x^T z + 1)^dK(x,z)=(xTz+1)d高斯核(RBF Kernel): K(x,z)=exp(−∣∣x−z∣∣22σ2)K(x, z) = \exp(-\frac{||x - z||^2}{2\sigma^2})K(x,z)=exp(−2σ2∣∣x−z∣∣2) (最常用,适用于通用情况)
实验分析
将两份数据集的文件打开

import scipy.io
import numpy as np
def open_mat_file(filename):
"""
读取 .mat 文件并打印基本信息
"""
print(f"--- 正在打开文件: {filename} ---")
try:
# 核心函数: 加载 .mat 文件
data = scipy.io.loadmat(filename)
# 打印文件中包含的所有变量名 (keys)
# 注意: data.keys() 通常包含一些元数据头信息,不仅是你的变量
print(f"文件包含的变量 keys: {list(data.keys())}")
# 提取数据 (根据刚才分析,这两个文件里都有 'X' 和 'y')
if 'X' in data and 'y' in data:
X = data['X']
y = data['y']
print(f"成功提取变量:")
print(f" X (特征) 形状: {X.shape}")
print(f" y (标签) 形状: {y.shape}")
return X, y
else:
print("警告: 文件中未找到预期的 'X' 或 'y' 变量")
return data
except FileNotFoundError:
print(f"错误: 找不到文件 {filename},请确保文件在当前目录下。")
except Exception as e:
print(f"发生错误: {e}")
# --- 主程序执行 ---
# 1. 打开 ex6data1.mat
X1, y1 = open_mat_file('ex6data1.mat')
print("\n") # 空一行
# 2. 打开 ex6data2.mat
X2, y2 = open_mat_file('ex6data2.mat')
# 示例:查看 ex6data1 的前5行数据
print("\n预览 ex6data1 的前5行数据:")
print(X1[:5])
数据处理
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy.io import loadmat
from sklearn import svm
import seaborn as sns
def load_mat(path):
data=loadmat(path)
X=data['X']
y=data['y']
return X,y
path="./data/ex6data1.mat"
X,y=load_mat(path)
def plot_data(X,y,fig,ax):
data=pd.DataFrame(X,columns=['X1','X2'])
data['y']=y
positive=data[data['y'].isin([1])]
negative=data[data['y'].isin([0])]
ax.scatter(positive['X1'],positive['X2'],s=30,marker='+',c='black',label='Positive')
ax.scatter(negative['X1'],negative['X2'],s=30,marker='o',c='orange',label='Negative')
ax.set_xlabel('X1')
ax.set_ylabel('X2')
ax.legend()
fig,ax=plt.subplots(figsize=(8,6))
plot_data(X, y,fig,ax)
plt.title("Example Dataset 1")
plt.show()

在本部分的实验中,将尝试对SVMs使用不同的C参数值。C参数是一个正数,它控制了对错误分类的训练数据的惩罚,其中C较大,说明SVM尝试正确地分类所有的数据。
svc = svm.SVC(C=1, kernel='linear')
SVC(C=1, kernel='linear')
svc.fit(X,y.flatten())
svc.score(X,y.flatten())
边界
def plot_boundary(svc,X,y,C,diff=10**-3):
x1,x2=decision_boundary(svc,X,diff)
fig,ax=plt.subplots(figsize=(8,6))
plot_data(X, y,fig,ax)
ax.scatter(x1,x2,s=5,c='blue',label='Boundary')
plt.title("SVM Decision Boundary with C = {} (Example Dataset 1)".format(C))
plt.show()
def decision_boundary(svc,X,diff=10**-3):
x1_min,x1_max=X[:,0].min(),X[:,0].max()
x2_min,x2_max=X[:,1].min(),X[:,1].max()
x1=np.linspace(x1_min,x1_max,2000)
x2=np.linspace(x2_min,x2_max,2000)#将整个平面分割为2000*2000的小方格,如果后面感觉运行太久可以适当缩小到500
cordinates = [(x, y) for x in x1 for y in x2]
x1, x2 = zip(*cordinates)
c_val=pd.DataFrame({'x1':x1,'x2':x2})
c_val['cval']=svc.decision_function(c_val[['x1','x2']])#对所有的小方格的:预测样本的置信度得分(接近0的就是分割边界)
decision=c_val[np.abs(c_val['cval'])<diff]
return decision.x1,decision.x2

svc2 = svm.SVC(C=100,kernel='linear')
svc2
SVC(C=100, kernel='linear')
svc2.fit(X,y.flatten())
svc2.score(X,y.flatten())
当C=100时,发现SVM对每个数据都正确分类,但是该决策边界与数据匹配并不自然。
plot_boundary(svc2,X,y,100,10**-3)

综上:
当C比较小时模型对错误分类的惩罚较小,比较宽松,允许一定的错误分类存在,间隔较大。
当C比较大时模型对错误分类的惩罚较大,比较严格,错误分类少,间隔较小。
例二
接下来的实验中的数据的分布图如下图所示,可以观察到,该数据集的正例子和负例子之间没有线性决策边界。不过,通过使用高斯核SVM,将能够学习到一个非线性决策边界,它在该数据集表现得相当好。
path="./data/ex6data2.mat"
X2,y2=load_mat(path)
fig,ax=plt.subplots(figsize=(8,6))
plot_data(X2,y2,fig,ax)
plt.title("Example Dataset 2")
plt.show()

上面已经正确地实现了高斯核函数GaussKernel,接下来将继续在这个数据集上用高斯核训练SVM。下图绘制了具有高斯核的SVM所找到的决策边界,该决策边界能够正确地分离大多数正负例子,并且很自然地遵循了数据集的轮廓。
sigma=0.1
gamma=np.power(sigma,-2)/2
clf=svm.SVC(C=1,kernel='rbf',gamma=gamma)
model=clf.fit(X2, y2.flatten())
clf.score(X2, y2.flatten())
dx1,dx2=decision_boundary(model,X2,diff=0.01) #找到决策边界的点
fig,ax=plt.subplots(figsize=(8,6))
plot_data(X2, y2, fig, ax)
ax.scatter(dx1,dx2,s=5)
plt.title("SVM (Gaussian Kernel) Decision Boundary (Example Dataset 2)")
plt.show()

更多推荐
所有评论(0)