1. 概述

Softmax回归(也称多项逻辑回归多类逻辑回归)是二分类逻辑回归在多分类问题上的自然推广。与二分类逻辑回归输出一个概率值不同,Softmax回归输出一个概率分布,表示样本属于每个类别的概率。

1.1 为什么需要Softmax?

在多分类问题中,我们需要将样本分到 个类别之一。直接使用多个二分类器的输出不能保证概率之和为1,也无法有效建模类别之间的互斥关系。

Softmax函数正是解决这个问题的完美工具:

其中 是第 类的线性得分。

1.2 与逻辑回归的关系

特征逻辑回归Softmax回归
分类数2
输出 概率 维概率分布
链接函数SigmoidSoftmax
损失函数二元交叉熵多元交叉熵

详见 逻辑回归


2. 数学推导

2.1 Softmax函数

给定 个实数得分 ,Softmax函数定义为:

性质

  1. 概率归一化
  2. 单调性:如果 ,则
  3. 可微性:Softmax函数处处可微

2.2 数值稳定性

直接计算指数函数可能导致溢出(overflow)。使用以下技巧保证数值稳定:

def stable_softmax(z):
    """数值稳定的Softmax实现"""
    z = np.array(z)
    z_max = np.max(z, axis=-1, keepdims=True)
    exp_z = np.exp(z - z_max)  # 减去最大值防止溢出
    return exp_z / np.sum(exp_z, axis=-1, keepdims=True)

2.3 模型定义

对于输入 ,Softmax回归输出 维概率向量:

其中:

  • 是权重矩阵
  • 是偏置向量

展开形式:


3. 损失函数

3.1 多元交叉熵

给定训练集 ,使用负对数似然损失(即多元交叉熵):

其中 是第 个样本的真实类别 对应的预测概率。

3.2 One-Hot编码表示

将标签 用 one-hot 编码 表示,则损失函数可写成:

3.3 梯度推导

对第 类的权重 求偏导:

推导过程

,则

关于 求偏导:

因此:

再对 求偏导即可得到上述结果。


4. 与神经网络的关系

4.1 作为输出层

在神经网络中,Softmax通常与交叉熵损失配对使用,作为多分类任务的输出层

        全连接层           Softmax层
    ┌────────────┐     ┌────────────┐
    │            │     │            │
    │  z₁ ──────┼────►│  p₁        │
    │  z₂ ──────┼────►│  p₂        │───► 交叉熵损失
    │  ...      │     │  ...       │
    │  zₖ ──────┼────►│  pₖ        │
    │            │     │            │
    └────────────┘     └────────────┘

4.2 Softmax与Sigmoid的关系

时,Softmax退化为Sigmoid:

,则:

4.3 梯度计算中的”广播效应”

在神经网络中,Softmax与交叉熵损失的梯度有一个简洁形式:

这意味着Softmax层的梯度就是预测概率与真实标签的差,与 反向传播 中的链式法则完美结合。


5. 代码实现

5.1 NumPy实现

import numpy as np
 
class SoftmaxRegression:
    """Softmax回归分类器"""
    
    def __init__(self, n_classes: int, lr: float = 0.1, n_iters: int = 1000,
                 lambda_reg: float = 0.01):
        self.n_classes = n_classes
        self.lr = lr
        self.n_iters = n_iters
        self.lambda_reg = lambda_reg
        self.W = None
        self.b = None
    
    def softmax(self, z: np.ndarray) -> np.ndarray:
        """数值稳定的Softmax实现"""
        z = np.array(z)
        z_max = np.max(z, axis=1, keepdims=True)
        exp_z = np.exp(z - z_max)
        return exp_z / np.sum(exp_z, axis=1, keepdims=True)
    
    def cross_entropy(self, y_pred: np.ndarray, y_true: np.ndarray) -> float:
        """计算交叉熵损失"""
        n_samples = y_true.shape[0]
        # 添加小值防止log(0)
        y_pred = np.clip(y_pred, 1e-15, 1 - 1e-15)
        return -np.sum(y_true * np.log(y_pred)) / n_samples
    
    def one_hot(self, y: np.ndarray) -> np.ndarray:
        """One-hot编码"""
        n_samples = y.shape[0]
        one_hot = np.zeros((n_samples, self.n_classes))
        one_hot[np.arange(n_samples), y.astype(int)] = 1
        return one_hot
    
    def fit(self, X: np.ndarray, y: np.ndarray):
        """训练模型"""
        n_samples, n_features = X.shape
        
        # 初始化参数
        self.W = np.zeros((n_classes, n_features))
        self.b = np.zeros(n_classes)
        
        # One-hot编码
        y_one_hot = self.one_hot(y)
        
        for _ in range(self.n_iters):
            # 前向传播
            z = X @ self.W.T + self.b  # (n_samples, n_classes)
            y_pred = self.softmax(z)
            
            # 计算梯度
            dz = y_pred - y_one_hot  # (n_samples, n_classes)
            dW = (dz.T @ X) / n_samples + self.lambda_reg * self.W
            db = np.mean(dz, axis=0)
            
            # 更新参数
            self.W -= self.lr * dW
            self.b -= self.lr * db
    
    def predict_proba(self, X: np.ndarray) -> np.ndarray:
        """预测概率"""
        z = X @ self.W.T + self.b
        return self.softmax(z)
    
    def predict(self, X: np.ndarray) -> np.ndarray:
        """预测类别"""
        return np.argmax(self.predict_proba(X), axis=1)

5.2 使用sklearn

from sklearn.linear_model import LogisticRegression
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
 
# 加载数据(3类分类问题)
iris = load_iris()
X, y = iris.data, iris.target
 
# 划分数据集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
 
# 标准化
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
 
# 使用sklearn的逻辑回归(multi_class='multinomial'即Softmax)
model = LogisticRegression(
    multi_class='multinomial',  # 使用Softmax回归
    solver='lbfgs',             # 优化器
    C=1.0,                       # 正则化参数(越小越强)
    max_iter=1000
)
 
# 训练
model.fit(X_train_scaled, y_train)
 
# 预测
y_pred = model.predict(X_test_scaled)
y_prob = model.predict_proba(X_test_scaled)
 
print(f"Test Accuracy: {(y_pred == y_test).mean():.4f}")
print(f"\nPredicted probabilities for first 3 samples:")
print(y_prob[:3].round(3))

6. 与One-vs-Rest的关系

6.1 One-vs-Rest的局限性

使用多个二分类器的 One-vs-Rest 策略存在以下问题:

  1. 概率不一致:各分类器的概率阈值独立设定,不保证和为1
  2. 分类不平衡:某些类别样本少,分类器性能差
  3. 效率低:需要训练 个分类器

6.2 Softmax的优势

方面OvR策略Softmax回归
概率归一化不保证严格保证
类别互斥建模较弱
训练效率 分类器 模型
参数共享共享底层特征

6.3 等价性证明

在特定条件下,Softmax回归与OvR策略等价。考虑第 类 vs 其他类:

对于第 类的二分类器,其概率为:

可以证明,当使用适当的参数约束时,两者可以相互转换。


7. 常见变体

7.1 标签平滑(Label Smoothing)

为防止模型对训练数据过度自信,可以在标签上引入平滑:

def label_smoothing(y_one_hot, epsilon=0.1):
    """标签平滑"""
    K = y_one_hot.shape[1]
    return (1 - epsilon) * y_one_hot + epsilon / K

7.2 温度Softmax(Temperature Softmax)

在Softmax前引入温度参数

  • :分布更平滑(更不确定)
  • :分布更尖锐(更确定)

这在RLHF和知识蒸馏中有重要应用。

7.3 稀疏Softmax(Sparse Softmax)

对于超大规模分类(如词表),使用稀疏Softmax避免计算所有

  1. 采样近似:只计算部分类别的概率
  2. 层次Softmax:构建二叉树结构

8. 应用场景

8.1 图像分类

在深度学习时代之前,Softmax回归常用于手写数字识别(MNIST)等任务:

# 使用Softmax回归进行MNIST分类
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
 
digits = load_digits()
X, y = digits.data, digits.target
 
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
 
model = LogisticRegression(multi_class='multinomial', solver='lbfgs', max_iter=1000)
model.fit(X_train, y_train)
print(f"Accuracy: {model.score(X_test, y_test):.4f}")

8.2 自然语言处理

  1. 文本分类:情感分析、主题分类
  2. 词性标注:每个词标注一个词性
  3. 命名实体识别:识别实体类别

8.3 神经网络输出层

几乎所有多分类神经网络的输出层都使用Softmax:

图像分类(ImageNet)     1000类
NLP分类(情感分析)      2类(正面/负面)
目标检测(RetinaNet)    K类 + 背景

9. 优缺点分析

优点

优点说明
概率解释输出归一化的概率分布
可解释性权重反映特征对各类别的贡献
计算效率训练和推理的时间复杂度较低
与深度学习兼容作为神经网络的标准输出层
端到端学习可以与其他层联合优化

缺点

缺点说明
线性决策边界无法建模特征间的非线性关系
类别数限制类别数过多时计算量大
互斥假设假设类别互斥,不适合多标签问题
特征工程依赖需要手工特征工程

10. 与相关算法对比

10.1 Softmax vs 多个Sigmoid

场景推荐使用
类别互斥Softmax
类别独立(多标签)多个Sigmoid
类别不平衡严重多个Sigmoid + 阈值调整

10.2 Softmax vs SVM

特征Softmax回归SVM
输出概率分布类别标签
决策边界基于概率基于间隔
损失函数交叉熵合页损失
扩展性易扩展到神经网络需要核方法

参考资料