代码:
import numpy as np # 导入用于数值计算的库
import matplotlib.pyplot as plt # 导入用于绘图的库
# class1_points 和 class2_points 分别定义了两个类别的数据点,二维坐标
class1_points = np.array([[1.9, 1.2],
[1.5, 2.1],
[1.9, 0.5],
[1.5, 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]])
# 将 class1 和 class2 的 x 和 y 坐标合并为一个数据集
x1_data = np.concatenate((class1_points[:, 0], class2_points[:, 0])) # 所有的 x1 坐标
x2_data = np.concatenate((class1_points[:, 1], class2_points[:, 1])) # 所有的 x2 坐标
# 创建标签,class1 点标记为 1,class2 点标记为 -1
y = np.concatenate((np.ones(class1_points.shape[0]), -np.ones(class2_points.shape[0])))
# 初始化超平面的参数 w1, w2 和偏置 b
w1 = 0.1
w2 = 0.1
b = 0
# 学习率
learning_rate = 0.05
# 数据集的大小
l_data = x1_data.size
# 创建图形和子图
fig, (ax1, ax2) = plt.subplots(2, 1)
# 初始化存储每一步的损失值和迭代步数
step_list = np.array([])
loss_values = np.array([])
# 设定迭代次数,控制模型训练的周期
num_iterations = 1000
for n in range(1, num_iterations + 1):
# 计算超平面预测值:z = w1 * x1 + w2 * x2 + b
z = w1 * x1_data + w2 * x2_data + b
# 计算每个点的损失:Hinge loss
yz = y * z # 预测值与真实标签的乘积
loss = 1 - yz # hinge loss 为 1 - yz,当 yz > 1 时,损失为 0
loss[loss < 0] = 0 # 如果损失小于 0,置为 0
hinge_loss = np.mean(loss) # 计算平均损失(取所有数据点的损失均值)
loss_values = np.append(loss_values, hinge_loss) # 保存当前步的损失值
step_list = np.append(step_list, n) # 保存当前迭代步数
# 初始化梯度
gradient_w1 = 0
gradient_w2 = 0
gradient_b = 0
# 梯度下降法计算梯度
for i in range(len(y)):
if loss[i] > 0: # 仅考虑损失大于 0 的点
gradient_w1 += -y[i] * x1_data[i]
gradient_w2 += -y[i] * x2_data[i]
gradient_b += -y[i]
# 平均化梯度
gradient_w1 /= len(y)
gradient_w2 /= len(y)
gradient_b /= len(y)
# 更新超平面参数:w1, w2, b
w1 -= learning_rate * gradient_w1
w2 -= learning_rate * gradient_w2
b -= learning_rate * gradient_b
# 每 50 步或第一次迭代时,绘制一次更新图
frequence_display = 50
if n % frequence_display == 0 or n == 1:
if np.abs(w2) < 1e-5: # 避免 w2 太小导致无法计算
continue
# 计算超平面的直线方程,用于绘制超平面
x1_min, x1_max = 0, 6 # x1 的范围
x2_min, x2_max = -(w1 * x1_min + b) / w2, -(w1 * x1_max + b) / w2 # x2 的值,基于超平面方程计算
# 清除上一轮绘制的图像,绘制新的图
ax1.clear()
ax1.scatter(x1_data[:len(class1_points)], x2_data[:len(class1_points)], c='red', label='Class 1') # class1 红色
ax1.scatter(x1_data[len(class1_points):], x2_data[len(class1_points):], c='blue', label='Class 2') # class2 蓝色
ax1.plot([x1_min, x1_max], [x2_min, x2_max], 'r-') # 绘制超平面
ax1.set_title(f"SVM: w1={round(w1.item(), 3)}, w2={round(w2.item(), 3)}, b={round(b.item(), 3)}")
# 绘制损失函数的变化图
ax2.clear()
ax2.plot(step_list, loss_values, 'g-') # 损失图,绿色线
ax2.set_xlabel("Step") # x 轴为步数
ax2.set_ylabel("Loss") # y 轴为损失值
# 每次绘图后暂停 1 秒,展示图像
plt.pause(1)
# 显示最终图形
plt.show()
结果: