1 支持向量机理论讲解
1.1 支持向量机(support vector machines. ,SVM)概念
1.支持向量机是一种二类分类模型,它的基本模型是定义在特征空间上的间隔最大的线性分类器,间隔最大使它有别于感知机;
2.支持向量机还包括核技巧,这使它成为实质上的非线性分类器;
3.支持向量机的学习策略就是间隔最大化,可形式化为一个求解凸二次规划(convex quadratic programming)的问题,也等价于正则化的合页损失函数的最小化问题。
4.支持向量机的学习算法就是求解凸二次规划的最优化算法。
1.2 支持向量机(supportvectormachines. SVM)分类
1.线性可分支持向量机(linear support vector machine in linearly separable case ):
当训练数据线性可分时,通过硬间隔最大化(hard margin maximization);
2.线性支持向量机(linear supportvector machine):
训练数据近似线性可分时,通过软间隔最大化(soft margin maximization);
3.非线性支持向量机(non-linear support vector machine):
当训练数据线性不可分时,通过使用核技巧(kernel trick)及软间隔最大化。
2 线性可分支持向量机
应用场景 :
1.适用于数据集线性可分的情况,即数据点能够被一个超平面清晰地分割成两类。
2.对噪声敏感,因为它要求所有的样本都严格位于各自的类别一侧,容忍不了任何错误。
3.在训练数据线性可分的情况下,线性可分支持向量机能够找到全局最优解。

2.1 线性可分支持向量机
下图所示的二维特征空间中的分类问题。图中
表示正例,
表示负例。训练数据集线性可分,这时有很多直线能将两类数据正确划分,线性可分支持向量机对应着两类数据正确划分且间隔最大的直线。



2.2 点到超平面的距离
要想获得最大间隔,就要计算训练数据集到超平面的距离。

假设二维空间中 点(x,y)到直线Ax+By+C=0的距离公式

扩展到n维空间后,点x=(x1,x2……xn)到超平面 w⋅x+b=0 的距离

点在平面的不同侧时, w⋅x+b=0 正负号是不同的,法向量指向的一侧为"+"号,另一侧为"-"号.
其中:
2.3 函数间隔(functional margin)
一般来说,一个点距离分离超平面的远近可以表示分类预测的确信程度。在超平面 w*x+b=0 确定的情况下,|w*x+b| 能够相对地表示点x距离超平面的远近,而w*x+b与y的符号是否一致能够表示分类是否正确。将 |w*x+b| 绝对值去掉,得到y(w*x+b)。当y的取值为+1时,称
为正例;当y
的取值为 -1时
=-1时,称
为负例。

2.4 几何间隔(geometric margin)
如果将超平面的表示规范化,使得函数间隔固定,那么就变成几何间隔。

2.5 函数间隔和几何间隔的关系

2.6 硬间隔最大化
对线性可分的训练集而言,这里的间隔最大化又称为硬间隔最大化。直观解释是对训练集找到几何间隔最大的超平面意味着以充分大的确信度对训练数据进行分类。求最大间隔分离超平面即约束最优化问题:

(解释 s.t. 表示为subject to 约束 后面的式子表示所有的距离都要大于最小间隔 xi表示所有的数据点)
2.7 几何间隔和函数间隔的关系
2.8 得到最终的目标函数和约束如下

3 线性支持向量机
应用场景
1.适用于数据集不是完全线性可分的情况,即数据集中存在一些异常点或噪声。
2.允许一些样本出现在超平面的错误一侧,通过引入松弛变量和损失函数来处理分类错误。
3.对于实际中存在噪声或离群点的情况更具鲁棒性,可以更好地适应复杂的数据分布。
3.1 线性支持向量机
训练数据中有一些特异点(outlier),不能满足函数间隔大于等于1的约束条件。

3.2 解决方法:
3.2.1 松弛变量的引入

3.2.2 惩罚参数的引入

3.2.3 线性支持向量机的学习问题

3.3 合页损失函数


4 非线性支持向量机
应用场景
之前介绍的硬间隔和软间隔都是指样本完全线性可分或者大部分样本点线性可分的情况,
如果分类问题是非线性的,就要使用非线性支持向量机。主要特点是使用核技巧。
4.1 非线性分类问题
⽤线性分类⽅法求解⾮线性分类问题分为两步:
第一步:使⽤⼀个变换将原空间的数据映射到新空间。
第二步:在新空间⾥⽤线性分类学习⽅法从训练数据中学习分类模型。
4.2 常见的核函数
多项式核函数:
多项式核函数可以实现将低维的输入空间映射到高维的特征空间,但是多项式核函数的参数多,当多项式的阶数比较高的时候,核矩阵的元素值将趋于无穷大或者无穷小,计算复杂度会大到无法计算。
高斯核函数:(存在需要调整的超参数)

高斯径向基函数是一种局部性强的核函数,其可以将一个样本映射到一个更高维的空间内,该核函数是应用最广的一个,无论大样本还是小样本都有比较好的性能,而且其相对于多项式核函数参数要少,因此大多数情况下在不知道用什么核函数的时候,优先使用高斯核函数。
4.3 核技巧
基本思想是通过⼀个⾮线性变换将输⼊空间对应于⼀个特征空间,使得在输⼊空间中 的超曲⾯模型对应于特征空间中的超平⾯模型 ( ⽀持向量机 ) 。在学习和预测中只定义核函数 K(x,z),⽽不显式地定义映射函数。在实际应⽤中,往往依赖领域知识直接选择核函数。
4.4 非线性支持向量机学习算法
选取适当的核函数 K(x,z) 和适当的参数 C,将线性⽀持向量机对偶形式中的内积换成核函数,构造并求解最优化问题。

代码运行步骤:
散点输入→Wb初始化设置→损失函数→优化函数→训练函数→机器学习输出
合页损失函数代码运行
# 1.导入必要的库
import numpy as np
import matplotlib.pyplot as plt
# 2.定义数据
class1_points = np.array([[1.9, 1.2],
[1.5, 2.1],
[1.7, 0.5],
[1.3, 0.9],
[0.9, 1.2],
[1.1, 1.7],
[1.4, 1.1]])
class2_points = np.array([[3.2, 3.2],
[3.7, 2.9],
[3.2, 2.6],
[1.7, 3.3],
[3.4, 2.6],
[4.1, 2.3],
[3.0, 2.9]])
"""
定义了两个类别的二维数据点,每个类别有7个样本
使用np.array()创建NumPy数组,便于后续数学运算
每一行是一个样本,两列分别叫 x₁(横坐标)、x₂(纵坐标)。
"""
# 3.数据预处理,将两个特征单独提取
x1_data = np.concatenate((class1_points[:, 0], class2_points[:, 0]), axis=0)
x2_data = np.concatenate((class1_points[:, 1], class2_points[:, 1]), axis=0)
y = np.concatenate((np.ones(class1_points.shape[0]),
-np.ones(class2_points.shape[0])))
"""
class1_points[:, 0]:提取第一类所有点的第一个特征(x1坐标)
class2_points[:, 0]:提取第二类所有点的第一个特征(x1坐标)
np.concatenate():将两个数组合并成一个
同样处理第二个特征(x2坐标)
创建标签数组y:第一类标签为1,第二类标签为-1
"""
# 4.参数初始化
w1 = 0.1
w2 = 0.1
b = 0
"""
w1, w2:权重参数初始值
b:偏置项初始值
线性 SVM 的决策函数是 f(x) = w₁x₁ + w₂x₂ + b
如果 f(x)>0 就判红,<0 就判蓝。一开始不知道最优值,就随机给很小的数(0.1)。
"""
learning_rate = 0.03
num_iterations = 1500
"""
learning_rate:学习率,控制每次参数更新的步长,
每次沿梯度走多大一步。太大容易“跨过”最优点,太小走得慢。
num_iterations:迭代次数,决定训练过程重复多少次
"""
# 5.设置可视化
fig, (ax1, ax2) = plt.subplots(2, 1) # 创建包含两个子图的图形窗口(2行1列)
# step_list = np.array([]) # 用于记录训练步数
step_list = []
loss_values = np.array([]) # 用于记录每一步的损失值
# 6.训练循环
for n in range(1, num_iterations + 1): # 循环执行2000次,每次代表一次训练迭代
# 7.计算预测值和合页损失
z = w1 * x1_data + w2 * x2_data + b # 计算线性预测值 w1*x1 + w2*x2 + b
yz = y * z # 将真实标签与预测值相乘,正值表示分类正确
# hinge loss
loss = 1 - yz # loss = 1 - yz:计算合页损失(hinge loss)
loss[loss < 0] = 0 # loss[loss < 0] = 0:将负损失值置零(合页损失特性)
hinge_loss = np.mean(loss) # hinge_loss = np.mean(loss):计算平均合页损失
"""
把 margin 不够 1 的样本挑出来惩罚,够 1 的惩罚为 0。
取平均,得到“经验风险”。
"""
# 8.计算总损失,Hinge + 正则化项. 总损失 = L2正则化项(防止过拟合) + 合页损失(衡量分类错误)
total_loss = 0.5 * (w1 ** 2 + w2 ** 2) + hinge_loss
"""
前面 0.5‖w‖² 是“正则项”,防止 w 过大导致过拟合。
两者相加就是 SVM 的目标函数——我们要让它最小。
"""
# 9.记录损失值
loss_values = np.append(loss_values, total_loss)
step_list.append(n)
# 将当前步数和损失值添加到记录数组中
# 10.计算梯度
gradient_w1 = w1 # 来自 0.5 * w1^2 的导数
gradient_w2 = w2 # 来自 0.5 * w2^2 的导数
gradient_b = 0
# 正则项对 w₁ 的导数就是 w₁ 本身,对 w₂ 同理,对 b 没有贡献
for i in range(len(y)):
if loss[i] > 0: # 只有 margin < 1 的点会影响梯度
gradient_w1 = gradient_w1 + -y[i] * x1_data[i] / len(y)
gradient_w2 += -y[i] * x2_data[i] / len(y)
gradient_b += -y[i] / len(y)
"""
遍历所有样本,只有那些分类不正确或位于边界内的样本(损失>0)才会影响梯度
计算合页损失对参数的梯度
"""
# 11.更新参数
w1 -= learning_rate * gradient_w1
w2 -= learning_rate * gradient_w2
b -= learning_rate * gradient_b
"""
使用梯度下降法更新参数:新 w = 旧 w – 学习率 × 梯度。
走完这一步,模型对那 14 个点的打分就会变一点,损失也会变小一点。
"""
# 12.可视化更新,显示频率设置
frequence_display = 50
if n % frequence_display == 0 or n == 1:
if np.abs(w2) < 1e-5: # 如果 w2 的绝对值小于 0.00001
continue # 就跳过本次画图,直接进入下一次迭代
"""
防止 除以 0(或除以一个非常接近 0 的数),避免程序报错或画出一条“无限斜”的怪线。
在前面画决策边界时,我们用到了直线方程:
w1*x1+w2*x2+b=0⇒x2=−(w1*x1+b)/w2
"""
x1_min, x1_max = 0, 6
x2_min, x2_max = -(w1 * x1_min + b) / w2, -(w1 * x1_max + b) / w2
"""
每50次迭代或第一次迭代时更新可视化
检查w2是否接近0(避免除以0错误)
计算决策边界线的两个端点
把当前 w₁,w₂,b 画成分界线:
由 w₁x₁ + w₂x₂ + b = 0 ⇒ x₂ = –(w₁x₁ + b)/w₂
取 x₁ 从 0 到 6,算出对应 x₂,用红线画在 ax1。
同时把之前存好的 step_list 与 loss_values 画成绿折线,放在 ax2。
plt.pause(0.5) 让窗口停 0.5 秒,人眼能看到动画效果。
"""
ax1.clear()
ax1.scatter(x1_data[:len(class1_points)], x2_data[:len(class1_points)], c='red', label='Class 1')
ax1.scatter(x1_data[len(class1_points):], x2_data[len(class1_points):], c='blue', label='Class 2')
ax1.plot((x1_min, x1_max), (x2_min, x2_max), 'r-')
ax1.set_title(f"SVM: w1={round(w1, 3)}, w2={round(w2, 3)}, b={round(b, 3)}")
"""
清除上一个子图
绘制两个类别的散点图
绘制当前的决策边界
设置标题显示当前参数值
"""
ax2.clear()
ax2.plot(step_list, loss_values, 'g-')
ax2.set_xlabel("Step")
ax2.set_ylabel("Loss")
plt.pause(0.5)
"""
清除损失图
绘制损失曲线
设置坐标轴标签
暂停0.1秒,使动画可见
"""
plt.savefig('heye.png')
# 显示最终结果
plt.show()
代码运行结果:heye.png

271

被折叠的 条评论
为什么被折叠?



