反转基因福娃 发表于 2025-11-5 01:30:22

呆板学习:支持向量机

全部代码和文档均在golitter/Decoding-ML-Top10: 使用 Python 优雅地实现呆板学习十大经典算法。 (github.com),接待检察。
支持向量机(Support Vector Machine)是一种二类分类模子,其根本模子界说为特性空间上的隔断最大的广义线性分类器,其学习计谋便是隔断最大化,终极可转化为一个凸二次规划题目的求解。
https://dis.qidao123.com/imgproxy/aHR0cHM6Ly9pLWJsb2cuY3NkbmltZy5jbi9kaXJlY3QvYWY0M2NiOTgzYWE5NGE3Mzg2YzQ3NzUwY2Y2MTUyZTQucG5n
假设两类数据可以被                              H                      =                               x                         :                                 w                            T                                  x                         +                         b                         ≥                         c                                  H = {x:w^Tx + b \ge c}               H=x:wTx+b≥c分离,垂直于法向量                              w                        w               w,移动                              H                        H               H直到碰到某个训练点,可以得到两个超平面                                       H                         1                                  H_1               H1​和                                       H                         2                                  H_2               H2​,两个平面称为支持超平面,标题分别支持两类数据。而位于                                       H                         1                                  H_1               H1​和                                       H                         2                                  H_2               H2​正中央的超平面是分离这两类数据的最好选择。支持向量就是离分隔超平面近来的那些点。
   法向量                                 w                              w                  w有很多种选择,超平面                                             H                            1                                       H_1                  H1​和                                             H                            2                                       H_2                  H2​之间的隔断称为隔断,这个隔断是                                 w                              w                  w的函数,**目的就是探求如许的                                 w                              w                  w使得隔断到达最大。
https://dis.qidao123.com/imgproxy/aHR0cHM6Ly9pLWJsb2cuY3NkbmltZy5jbi9kaXJlY3QvYWUwYjJmYzE2NGFkNGE1NmFjNzQ2MGIxMWQ5NjZkMDMucG5n
https://dis.qidao123.com/imgproxy/aHR0cHM6Ly9pLWJsb2cuY3NkbmltZy5jbi9kaXJlY3QvZTUyZmU0ZGZjZDI5NGE4N2FkYjRjMzUxYThhZDRhZGQucG5n
https://dis.qidao123.com/imgproxy/aHR0cHM6Ly9pLWJsb2cuY3NkbmltZy5jbi9kaXJlY3QvYjE2MTM2OTBlN2U0NDAwMjljNTdhYTRmN2I0OTIxYTEucG5n
https://dis.qidao123.com/imgproxy/aHR0cHM6Ly9pLWJsb2cuY3NkbmltZy5jbi9kaXJlY3QvNzc4MmRkNjA4MThjNGE1NWI3YmQ0OWQ0YTc4YWYxNjEucG5n
在求解最优化题目中,拉格朗日乘子法(Lagrange Multiplier)和KKT(Karush Kuhn Tucker)条件是两种最常用的方法。在有等式束缚时使用拉格朗日乘子法,在有不等束缚时使用KKT条件。


[*] 拉格朗日乘子法
拉格朗日乘子法是一种探求多元函数在一组束缚下的极值的方法。通过引入拉格朗日乘子,可将有                                        d                                  d                     d个变量与                                        k                                  k                     k个束缚条件的最优化题目转化为具有                                        d                            +                            k                                  d+k                     d+k个变量的无束缚优化题目求解。
[*] 二次规划
二次规划是一类范例的优化题目,包罗凸二次优化和非凸二次优化。在此类题目中,目的函数是变量的二次函数,而束缚条件是变量的线性不等式。
                                              m                               i                               n                                           1                                  2                                                      x                                  T                                          Q                               x                               +                                           c                                  T                                          x                                       s                               .                               t                               .                                           A                                  ⃗                                                      x                                  ⃗                                          ≤                                           b                                  ⃗                                                min \frac{1} {2} x^T Q x + c^T x \\ s.t. \vec{A} \vec{x} \le \vec{b}                         min21​xTQx+cTxs.t.A               x               ≤b               
   详细公式证明:【整理】深入明白拉格朗日乘子法(Lagrange Multiplier) 和KKT条件 - mo_wang - 博客园 (cnblogs.com)
序列最小优化(Sequential Minimal Optimization,SMO)
   序列最小优化是将大优化题目分界成多个小优化题目来求解。
SMO算法工作原理:每次循环中选择两个变量举行优化处理处罚。一旦找到一对符合的变量,那么就增大此中一个同时减小另一个。这里的“符合”指的是两个变量必须要符合肯定的条件,条件之一就是这两个变量必须要在隔断边界之外,而其第二个条件则是这两个变量还没有举行过区间化处理处罚大概不在边界上。
https://dis.qidao123.com/imgproxy/aHR0cHM6Ly9pLWJsb2cuY3NkbmltZy5jbi9kaXJlY3QvNTI0MjZmMzc5MjFjNDY3NmE0MDY3OGJjNzQ3NGY1ZjIucG5n
代码实现

   参考《呆板学习实战》,代码链接:https://github.com/golitter/Decoding-ML-Top10/tree/master/SVM
这里接纳简化的SMO代码,数据集是https://blog.caiyongji.com/assets/mouse_viral_study.csv。
data_processing.py:
import numpy as np
import pandas as pd

# https://zhuanlan.zhihu.com/p/350836534
def data_processing():
    data_csv = pd.read_csv('mouse_viral_study.csv')
    data_csv = data_csv.dropna()
    # print(data_csv)
    X = data_csv.iloc[:-1, 0:2].values
    # print(X)
    Y = data_csv.iloc[:-1, 2].map({0: -1, 1: 1}).values
    Y = Y.reshape(-1, 1)
    # print(Y.shape)
    return X, Y

# X, Y = data_processing()
# print(X)
工具模块,smo_assist.py:
import random
def select_Jrandom(i:int, m:int) -> int:
    """
    随机选择一个不等于 i 的整数
    """
    j = i
    while j == i:
      j = int(random.uniform(0, m))
    return j

def clip_alpha(alpha_j:float, H:float, L:float) -> float:
    """
    修剪 alpha_j
    """
    if alpha_j > H:
      alpha_j = H
    if alpha_j < L:
      alpha_j = L
    return alpha_j
简化SMO的代码实现,smoSimple.py:
from smo_assist import (
    select_Jrandom,
    clip_alpha)

import numpy as np
import pdb

def smoSimple(data_mat_in:np.ndarray, class_labels:np.ndarray, C:float, toler:float, max_iter:int):
    """
    data_mat_in: 数据集
    class_labels: 类别标签
    C: 松弛变量
    toler: 容错率
    max_iter: 最大迭代次数
    """
    b = 0; # 初始化b
    m, n = np.shape(data_mat_in) # m: 样本数, n: 特征数
    alphas = np.zeros((m, 1)) # 初始化alpha
    iter = 0 # 迭代次数
    while iter < max_iter:
      alphaPairsChanged = 0
      for i in range(m):
            fXi = float(np.multiply(alphas, class_labels).T @ (data_mat_in @ data_mat_in.T)) + b
            """
             (1 , m) * (m, n) * (n, 1) = (1, 1) = 标量
                再 加上 b 就是 f(x) 的值
            """
            Ei = fXi - float(class_labels)
            """
            Ei = f(x) - y 预测误差
            """
            if (
                # 第一种情况:样本被误分类,且权重可以增加
                ((class_labels * Ei < -toler) # 预测误差与标签方向相反,且误差大于容忍度
                  and (alphas < C)) # 当前权重小于正则化参数 C,可以增加权重
                or
                # 第二种情况:样本被误分类,且权重需要调整
               ((class_labels * Ei > toler) # 预测误差与标签方向相同,且误差大于容忍度
                   and (alphas > 0)) # 当前权重大于 0,需要调整权重
                ):
                j = select_Jrandom(i, m)
                fxj = float(np.multiply(alphas, class_labels).T @ (data_mat_in @ data_mat_in.T)) + b
                Ej = fxj - float(class_labels)
                alpha_j_old = alphas.copy();
                alpha_i_old = alphas.copy()

                if (class_labels != class_labels):
                  L = max(0, alphas - alphas) # 左边界
                  H = min(C, C + alphas - alphas) # 右边界
                else:
                  L = max(0, alphas + alphas - C)
                  H = min(C, alphas + alphas)
                if L == H:
                  continue # 跳出本次循环
               
                eta = 2.0 * data_mat_in @ data_mat_in.T - data_mat_in @ data_mat_in.T - data_mat_in @ data_mat_in.T
                """
                计算 eta = K11 + K22 - 2 * K12 = 2 * x_i * x_j - x_i * x_i - x_j * x_j
                """   
                if eta >= 0:
                  continue
                alphas -= class_labels * (Ei - Ej) / eta # 更新权重
                alphas = clip_alpha(alphas, H, L) # 调整权重
                if abs(alphas - alpha_j_old) < 0.00001:
                  continue # 跳出本次循环,不更新 i
                alphas += class_labels * class_labels * (alpha_j_old - alphas) # 更新权重
                b1 = b - Ei - class_labels * (alphas - alpha_i_old) * data_mat_in @ data_mat_in.T - class_labels *(alphas - alpha_j_old) * data_mat_in @ data_mat_in.T
                b2 = b - Ej - class_labels * (alphas - alpha_i_old) * data_mat_in @ data_mat_in.T - class_labels *(alphas - alpha_j_old) * data_mat_in @ data_mat_in.T
                """
                更新 b
                """   
                if 0 < alphas < C:
                  b = b1
                elif 0 < alphas < C:
                  b = b2
                else:
                  b = (b1 + b2) / 2.0
                alphaPairsChanged += 1
      if alphaPairsChanged == 0:
            iter += 1
      else:
            iter = 0
    return b, alphas



if __name__ == '__main__':
    print(smoSimple(np.array([, ]), np.array([[-1],]), 0.6, 0.001, 40))
test.py:
from data_processing import *
from smoSimple import *
import numpy as np
import matplotlib.pyplot as plt

# 数据处理和 SVM 训练
data_mat_in, class_labels = data_processing()
b, alphas = smoSimple(data_mat_in, class_labels, 0.6, 0.001, 40)

# 打印结果
print("Bias (b):", b)
print("Non-zero alphas:", alphas)

# 打印数据形状
print("Shape of data_mat_in:", np.shape(data_mat_in))
print("Shape of class_labels:", np.shape(class_labels))

# 将 Y 转换为一维数组(如果它是二维的)
Y = class_labels
# 提取不同类别的索引
class_1_indices = np.where(Y == 1)# 类别为 1 的样本索引
class_2_indices = np.where(Y == -1)# 类别为 -1 的样本索引
X = data_mat_in

# 绘制散点图
plt.figure(figsize=(8, 6))
plt.scatter(X, X, c='blue', label='Class 1', alpha=0.5)
plt.scatter(X, X, c='red', label='Class -1', alpha=0.5)

# 计算权重向量 w
w = np.dot((alphas * Y).T, X).flatten()
# print(f"w: {w}")
print("Shape of X:", X.shape)# 应该是 (m, n)
print("Shape of Y:", Y.shape)# 应该是 (m, 1)
print("Shape of alphas:", alphas.shape)# 应该是 (m, 1)

# 绘制超平面
# 超平面方程:w * x1 + w * x2 + b = 0
# 解出 x2: x2 = -(w * x1 + b) / w
x1 = np.linspace(np.min(X[:, 0]), np.max(X[:, 0]), 100)
x2 = -(w * x1 + b) / w
print(f"w_shape: {w.shape}")
# 绘制超平面
plt.plot(x1, x2, label='SVM Hyperplane', color='green', linewidth=2)

# 标出支持向量
support_vectors_indices = np.where(alphas > 0)# 找到所有支持向量的索引
plt.scatter(X, X,
            facecolors='none', edgecolors='k', s=50, label='Support Vectors')

# 添加图例和标签
plt.xlabel('Feature 1')
plt.ylabel('Feature 2')
plt.title('Scatter Plot of Data with SVM Hyperplane')
plt.legend()

# 显示图形
plt.show()
https://dis.qidao123.com/imgproxy/aHR0cHM6Ly9pLWJsb2cuY3NkbmltZy5jbi9kaXJlY3QvM2I1MDliOTE5MjI0NDNjN2FkN2JlZTdhOGMzMDQzMmEucG5n
ML_AI_SourceCode-/支持向量机 at master · sjyttkl/ML_AI_SourceCode- (github.com)
呆板学习:支持向量机(SVM)-CSDN博客
【整理】深入明白拉格朗日乘子法(Lagrange Multiplier) 和KKT条件 - mo_wang - 博客园 (cnblogs.com)
呆板学习(四):平凡明白支持向量机SVM及代码实践 - 知乎 (zhihu.com)

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 呆板学习:支持向量机