神经网络中 标量求导和向量求导

0. 引出问题

在神经网络反向传播过程中 loss = loss₁,loss₂, loss₃,为什么 ∂loss/∂w

bash 复制代码
∂loss₁/∂w 
∂loss₂/∂w
∂loss₃/∂w 

∂loss₁/∂w 和 loss 维度一样都是三位向量 ,∂loss₁/∂w, ∂loss₂/∂w, ∂loss₃/∂w 就变成3*3的矩阵

如下所示:

bash 复制代码
import torch

w = torch.tensor([1.0, 2.0,3.0], requires_grad=True)
loss = w * 3  
print("loss: \n", loss)


loss_m = []

for i, val in enumerate(loss):
    w.grad = None  # 清零
    val.backward(retain_graph=True)
    print(f"∂loss{i+1}/∂w = {w.grad}")
    loss_m.append(w.grad.clone())

print("loss_m: \n", torch.stack(loss_m))

输出结果:

bash 复制代码
loss: 
 tensor([3., 6., 9.], grad_fn=<MulBackward0>)

∂loss1/∂w = tensor([3., 0., 0.])
∂loss2/∂w = tensor([0., 3., 0.])
∂loss3/∂w = tensor([0., 0., 3.])

loss_m: 
 tensor([[3., 0., 0.],
        [0., 3., 0.],
        [0., 0., 3.]])

loss: tensor(3., 6., 9.) 为向量,对w求导时为矩阵

但是 w.grad 必须 是标量或张量,不能是向量矩阵

1. 标量求导

bash 复制代码
import torch

w = torch.tensor([1.0, 2.0,3.0], requires_grad=True)
loss = w * 3  
print("loss: \n", loss)


loss_m = []
# 方法1:分别计算
for i, val in enumerate(loss):
    w.grad = None  # 清零
    val.backward(retain_graph=True)
    print(f"∂loss{i+1}/∂w = {w.grad}")
    loss_m.append(w.grad.clone())

print("loss_m: \n", torch.stack(loss_m))

grads = torch.autograd.grad(loss.sum(), w,retain_graph=True)
print("grads: \n", grads)  

grads1 = torch.autograd.grad(loss.mean(), w)[0]
print("grads1: \n", grads1) 

输出;

bash 复制代码
loss: 
 tensor([3., 6., 9.], grad_fn=<MulBackward0>)
∂loss1/∂w = tensor([3., 0., 0.])
∂loss2/∂w = tensor([0., 3., 0.])
∂loss3/∂w = tensor([0., 0., 3.])
loss_m: 
 tensor([[3., 0., 0.],
        [0., 3., 0.],
        [0., 0., 3.]])
grads: 
 (tensor([3., 3., 3.]),)
grads1: 
 tensor([1., 1., 1.])

同样的例子:

python 复制代码
import torch

# 3个样本的真实数据
x = torch.tensor([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]], requires_grad=True)
y_true = torch.tensor([1.0, 2.0, 3.0])

# 线性模型:y = w₁x₁ + w₂x₂
w = torch.tensor([0.5, 0.5], requires_grad=True)
predictions = (x @ w)  # [1.5, 3.5, 5.5]
print("预测值:", predictions)
# 计算每个样本的梯度
individual_grads = []
for i in range(3):
    loss = (predictions[i] - y_true[i])**2
    loss.backward(retain_graph=True)
    individual_grads.append(w.grad.clone())
    w.grad.zero_()

print("样本1梯度:", individual_grads[0]) 
print("样本2梯度:", individual_grads[1])  
print("样本3梯度:", individual_grads[2])  

# 标量梯度:自动综合
total_loss = ((predictions - y_true)**2).mean()
total_loss.backward()


# 验证:标量梯度 = 向量梯度的平均
manual_average = (individual_grads[0] + 
                  individual_grads[1] + 
                  individual_grads[2]) / 3
                  
print("手动平均:", manual_average)  
print("标量结果:", w.grad)  

输出结果:

bash 复制代码
预测值: tensor([1.5000, 3.5000, 5.5000], grad_fn=<MvBackward0>)
样本1梯度: tensor([1., 2.])
样本2梯度: tensor([ 9., 12.])
样本3梯度: tensor([25., 30.])
手动平均: tensor([11.6667, 14.6667])
标量结果: tensor([11.6667, 14.6667])

训练神经网络是为了最小化整体损失,不是单独优化每个样本

python 复制代码
# 实际训练:最小化平均损失
batch_loss = individual_losses.mean()  # 标量
batch_loss.backward()  # 得到平均梯度
optimizer.step()       # 朝平均最优方向更新

2. 什么时候需要向量梯度?

仅用于研究:分析样本敏感性

python 复制代码
def compute_sample_gradients(model, x, y):
    """仅用于分析,不用于训练"""
    grads = []
    for xi, yi in zip(x, y):
        model.zero_grad()
        pred = model(xi.unsqueeze(0))
        loss = ((pred - yi) ** 2)
        loss.backward()
        grads.append(model.weight.grad.clone())
    return grads  # 每个样本的单独梯度
相关推荐
超大青花鱼28 分钟前
Ubuntu20.04安装CUDA11.8教程
人工智能·深度学习·计算机视觉
Ivanqhz1 小时前
BURG(自底向上重写生成器)
服务器·数据库·人工智能·深度学习·算法
Ivanqhz11 小时前
激活函数在 Transformer 中的作用及各种变体简述
java·linux·数据库·人工智能·深度学习
晚安code13 小时前
大模型是什么?讲透 Token、上下文窗口、Temperature 和 MoE
人工智能·深度学习·机器学习
Zguigo14 小时前
【CUDA6】CUDA Stream 是什么,为什么 CUDA 是异步执行,如何正确测量 GPU 时间以及多个任务如何重叠执行
人工智能·pytorch·深度学习
梦帮科技16 小时前
vLLM / TensorRT-LLM 极限推理:PagedAttention 细粒度物理页表管理与连续批处理(Continuous Batching)实战
数据结构·人工智能·分布式·python·深度学习·算法·vllm
v:PLLucky2018 小时前
农作物病虫害检测识别系统
深度学习·scrapy·算法·百度·微信公众平台
医学图像分割大王18 小时前
【医学图像分割模块】StarNet —— 星操作(元素级乘法)—— 把"相加"换成"相乘"的轻量卷积块
深度学习
在所不辞兄18 小时前
【零基础学智能仿真-38】从规则矩形到带孔薄板:Gmsh 网格与 FEniCSx 联合求解
人工智能·深度学习·算法·机器学习·工程仿真
架构师那点事儿19 小时前
将 HuggingFace 自己的英译中模型迁移到 ONNX
人工智能·python·深度学习