从零实现MLP:如何编写Softmax函数导数的Python代码?
如何实现Softmax函数的导数(Python + NumPy)
嘿,我之前手动搭MLP的时候也卡过Softmax导数这一关,这个雅可比矩阵的计算确实容易绕晕,咱们结合你现有的Softmax代码一步步来解决~
首先先明确Softmax导数的数学逻辑:假设Softmax的输出是向量 ( S = [s_1, s_2, ..., s_n] ),那么导数的雅可比矩阵中,每个元素 ( \frac{\partial s_i}{\partial z_j} )(其中z是Softmax的输入)满足:
- 当 ( i = j ) 时:( s_i(1 - s_i) )
- 当 ( i \neq j ) 时:( -s_i s_j )
接下来我们基于你提供的Softmax代码,写出对应的导数实现,分单个样本和批量样本两种场景(毕竟实际训练中都是批量处理数据):
1. 先回顾你的Softmax实现
import numpy as np def softmax(input_value): # 数值稳定:减去输入的最大值,避免exp溢出 input_value -= np.max(input_value) exp_vals = np.exp(input_value) return exp_vals / np.sum(exp_vals)
2. 单个样本的导数实现
如果输入是一维数组(比如单个样本的logits,形状为 (num_classes,)),导数是一个 (num_classes, num_classes) 的雅可比矩阵:
def softmax_derivative_single(input_value): s = softmax(input_value) # 构造对角矩阵 diag(s) diag_matrix = np.diagflat(s) # 构造外积矩阵 s * s^T outer_product = np.outer(s, s) # 雅可比矩阵 = 对角矩阵 - 外积矩阵 return diag_matrix - outer_product
举个例子验证:
logits = np.array([1.0, 2.0, 3.0]) deriv = softmax_derivative_single(logits) print(deriv)
输出的矩阵中,对角线元素是 ( s_i(1-s_i) ),非对角线是 ( -s_i s_j ),完全符合数学公式。
3. 批量样本的导数实现
实际训练中输入通常是批量的,形状为 (batch_size, num_classes),此时导数应该是形状为 (batch_size, num_classes, num_classes) 的三维张量(每个样本对应一个雅可比矩阵):
def softmax_derivative_batch(input_value): s = softmax(input_value) # 扩展维度,方便广播计算:(batch_size, num_classes, 1) s_expanded = np.expand_dims(s, axis=2) # 构造批量的对角矩阵:(batch_size, num_classes, num_classes) diag_matrix = s_expanded * np.eye(s.shape[1]) # 构造批量的外积矩阵:(batch_size, num_classes, num_classes) outer_product = s_expanded @ np.expand_dims(s, axis=1) # 计算每个样本的雅可比矩阵 return diag_matrix - outer_product
这个实现利用了NumPy的广播机制,效率很高,不需要循环遍历每个样本。
额外说明
- 数值稳定性:因为你的Softmax已经做了
input_value -= np.max(input_value)的处理,所以计算得到的s是数值稳定的,导数计算不需要额外做溢出处理。 - 反向传播中的使用:在MLP的反向传播中,通常会把Softmax和交叉熵损失结合起来,此时可以推导出更简洁的导数公式(直接用预测值减真实标签),但如果你是单独实现Softmax的导数,上面的代码就是标准实现。
内容的提问来源于stack exchange,提问作者Kai Chan
相关产品推荐
相关产品推荐

