You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将手写二分类SVM改造为支持20+类别的多分类SVM?

改造二分类SVM为多分类SVM的方案与思路

现有一段从零实现的二分类SVM代码,仅能输出0或1的预测结果,无法处理MNIST这类多分类任务,代码如下:

import numpy as np

class SVM:
    def __init__(self, learning_rate=1e-3, lambda_param=1e-2, n_iters=1000):
        self.lr = learning_rate
        self.lambda_param = lambda_param
        self.n_iters = n_iters
        self.w = None
        self.b = None

    def _init_weights_bias(self, X):
        n_features = X.shape[1]
        self.w = np.zeros(n_features)
        self.b = 0

    def _get_cls_map(self, y):
        return np.where(y <= 0, -1, 1)

    def _satisfy_constraint(self, x, idx):
        linear_model = np.dot(x, self.w) + self.b 
        return self.cls_map[idx] * linear_model >= 1
    
    def _get_gradients(self, constrain, x, idx):
        if constrain:
            dw = self.lambda_param * self.w
            db = 0
            return dw, db
        
        dw = self.lambda_param * self.w - np.dot(self.cls_map[idx], x)
        db = - self.cls_map[idx]
        return dw, db
    
    def _update_weights_bias(self, dw, db):
        self.w -= self.lr * dw
        self.b -= self.lr * db
    
    def fit(self, X, y):
        self._init_weights_bias(X)
        self.cls_map = self._get_cls_map(y)

        for _ in range(self.n_iters):
            for idx, x in enumerate(X):
                constrain = self._satisfy_constraint(x, idx)
                dw, db = self._get_gradients(constrain, x, idx)
                self._update_weights_bias(dw, db)
    
    def predict(self, X):
        estimate = np.dot(X, self.w) + self.b
        prediction = np.sign(estimate)
        return np.where(prediction == -1, 0, 1)

请问如何将其改造为支持20个以上类别的多分类SVM?能否提供相关思路提示?


核心思路:采用一对多(One-vs-Rest, OvR)策略

对于20+类的多分类场景,**一对多(OvR)*是效率更高的选择——它只需要训练N个二分类SVM(N为类别数),远少于一对一(OvO)策略需要的N(N-1)/2个模型,更适合类别数量较多的场景。

OvR的核心逻辑

  • 训练阶段:为每个类别单独训练一个二分类SVM,将当前类别标记为正类(+1),所有其他类别标记为负类(-1)
  • 预测阶段:对输入样本,用所有N个SVM计算线性得分(np.dot(X, w) + b),选择得分最高的类别作为最终预测结果

具体改造步骤

  1. 参数存储改造:原代码只存一组w和b,现在需要为每个类别保存一组参数,用列表存储所有模型的(w, b)对
  2. 标签适配:不再是固定的0/1转-1/1,而是针对每个目标类别,动态生成二分类标签
  3. 训练逻辑扩展:遍历每个类别,生成对应二分类标签后调用原有的训练逻辑,保存每个类的模型参数
  4. 预测逻辑重构:对每个样本计算所有类别的得分,取最大值对应的类别作为输出

改造后的完整代码

import numpy as np

class MultiClassSVM:
    def __init__(self, learning_rate=1e-3, lambda_param=1e-2, n_iters=1000):
        self.lr = learning_rate
        self.lambda_param = lambda_param
        self.n_iters = n_iters
        # 存储每个类对应的(w, b),列表元素为元组
        self.models = []
        # 记录所有类别
        self.classes = None

    # 复用原二分类SVM的权重初始化逻辑
    def _init_weights_bias(self, n_features):
        return np.zeros(n_features), 0

    # 针对单个类别生成二分类标签映射
    def _get_cls_map_for_class(self, y, target_cls):
        return np.where(y == target_cls, 1, -1)

    # 复用原二分类SVM的约束检查逻辑
    def _satisfy_constraint(self, x, w, b, cls_label):
        linear_model = np.dot(x, w) + b 
        return cls_label * linear_model >= 1
    
    # 复用原二分类SVM的梯度计算逻辑
    def _get_gradients(self, constrain, x, w, cls_label):
        if constrain:
            dw = self.lambda_param * w
            db = 0
            return dw, db
        
        dw = self.lambda_param * w - np.dot(cls_label, x)
        db = - cls_label
        return dw, db
    
    # 复用原二分类SVM的权重更新逻辑
    def _update_weights_bias(self, w, b, dw, db):
        w -= self.lr * dw
        b -= self.lr * db
        return w, b
    
    # 训练单个二分类SVM
    def _train_binary_svm(self, X, y):
        n_features = X.shape[1]
        w, b = self._init_weights_bias(n_features)
        cls_map = self._get_cls_map_for_class(y, self.current_cls)

        for _ in range(self.n_iters):
            for idx, x in enumerate(X):
                constrain = self._satisfy_constraint(x, w, b, cls_map[idx])
                dw, db = self._get_gradients(constrain, x, w, cls_map[idx])
                w, b = self._update_weights_bias(w, b, dw, db)
        return w, b

    def fit(self, X, y):
        # 获取所有唯一类别
        self.classes = np.unique(y)
        # 为每个类别训练一个二分类SVM
        for cls in self.classes:
            self.current_cls = cls
            w, b = self._train_binary_svm(X, y)
            self.models.append((w, b))
    
    def predict(self, X):
        # 存储每个样本在所有类别上的得分
        all_scores = []
        for w, b in self.models:
            scores = np.dot(X, w) + b
            all_scores.append(scores)
        # 转置后,每行对应一个样本的所有类别得分,取最大值索引对应类别
        all_scores = np.array(all_scores).T
        pred_indices = np.argmax(all_scores, axis=1)
        return self.classes[pred_indices]

额外说明

  • 如果追求更高精度,也可以尝试一对一(OvO)策略,但20类的话需要训练190个模型,训练和预测效率会低很多
  • 原代码是基于随机梯度下降(SGD)的硬间隔SVM,实际应用中可以考虑加入软间隔的容错逻辑,或者改用批量梯度下降提升稳定性

内容的提问来源于stack exchange,提问作者Not Bedo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 01:50:25