10、PyTorch autograd使用教程

ops/2024/11/27 1:48:02/

文章目录

  • 1. 相关思考
  • 2. 矩阵求导
  • 3. 两种方法求jacobian

1. 相关思考

在这里插入图片描述

2. 矩阵求导

假设我们有如下向量:
y 1 × 3 = x 1 × 5 [ w T ] 5 × 3 + b 1 × 3 \begin{equation} y_{1\times3}=x_{1\times5}[w^T]_{5\times3}+b_{1\times3} \end{equation} y1×3=x1×5[wT]5×3+b1×3
根据公式,我们知道偏导如下:
∂ y ∂ w = x , ∂ y ∂ b = 1 \begin{equation} \frac{\partial{y}}{\partial w}=x,\frac{\partial{y}}{\partial b}=1 \end{equation} wy=x,by=1
但通过公式我们知道,y为向量,在pytorch中一般都是标量后再进行反向传播,故我们需要引入求和公式
z = y T m , m = [ 1 1 1 ] \begin{equation} z=y^Tm,m=\begin{bmatrix}1\\\\1\\\\1\end{bmatrix} \end{equation} z=yTm,m= 111

  • 那么z就是可以当作标量,可以用z做反向传播
    ∂ z ∂ y = m \begin{equation} \frac{\partial{z}}{\partial y}=m \end{equation} yz=m
  • 由链式法则可得如下:
    ∂ z ∂ w = ∂ z ∂ y ⋅ ∂ y ∂ w = m ⋅ x \begin{equation} \frac{\partial{z}}{\partial w}=\frac{\partial{z}}{\partial y}\cdot\frac{\partial{y}}{\partial w}=m\cdot x \end{equation} wz=yzwy=mx
  • 我们定义x=[0,1,2,3,4],那么梯度可得:
    ∂ z ∂ w = [ 0 1 2 3 4 0 1 2 3 4 0 1 2 3 4 ] \begin{equation} \frac{\partial{z}}{\partial w}= \begin{bmatrix} 0&1&2&3&4\\\\ 0&1&2&3&4\\\\ 0&1&2&3&4 \end{bmatrix} \end{equation} wz= 000111222333444
  • 同理:可得关于b的导数如下:
    ∂ z ∂ b = ∂ z ∂ y ⋅ ∂ y ∂ b = m = [ 1 1 1 ] \begin{equation} \frac{\partial{z}}{\partial b}=\frac{\partial{z}}{\partial y}\cdot\frac{\partial{y}}{\partial b}=m=\begin{bmatrix} 1\\\\ 1\\\\ 1 \end{bmatrix} \end{equation} bz=yzby=m= 111
  • 小结:如果要计算向量y对向量w进行求导,但pytorch的反向传播用的是标量,一般做法是引入全1向量求和,这样就将向量y对向量w的求导转换成z对w的求导,最后运用链式法则即可求出。
  • python 代码
import torch
from torch import nnif __name__ == "__main__":run_code = 0in_x = torch.arange(5,dtype=torch.float)in_w = torch.randn((3, 5), requires_grad=True)in_b = torch.arange(3, dtype=torch.float, requires_grad=True)y = in_x @ in_w.T + in_bz = torch.sum(y)print(f"*"*50)print(f"before:")print(f"x={in_x}")print(f"w=\n{in_w}")print(f"w_grad=\n{in_w.grad}")print(f"b=\n{in_b}")print(f"b_grad=\n{in_b.grad}")print(f"y={y}")print(f"before:")print(f"*"*50)z.backward()print(f"*"*50)print(f"after:")print(f"x={in_x}")print(f"w=\n{in_w}")print(f"w_grad=\n{in_w.grad}")print(f"b=\n{in_b}")print(f"b_grad=\n{in_b.grad}")print(f"b_grad=\n{in_b.grad.shape}")print(f"y={y}")print(f"after:")print(f"*"*50)
  • 结果:
**************************************************
before:
x=tensor([0., 1., 2., 3., 4.])
w=
tensor([[-1.5762, -0.0040,  1.7958,  0.2164,  0.7108],[ 0.6488, -0.8668,  0.0572, -1.1207,  0.0568],[-0.5594,  0.1091,  0.6546,  0.0851,  1.1287]], requires_grad=True)
w_grad=
None
b=
tensor([0., 1., 2.], requires_grad=True)
b_grad=
None
y=tensor([ 7.0800, -2.8872,  8.1886], grad_fn=<AddBackward0>)
before:
**************************************************
**************************************************
after:
x=tensor([0., 1., 2., 3., 4.])
w=
tensor([[-1.5762, -0.0040,  1.7958,  0.2164,  0.7108],[ 0.6488, -0.8668,  0.0572, -1.1207,  0.0568],[-0.5594,  0.1091,  0.6546,  0.0851,  1.1287]], requires_grad=True)
w_grad=
tensor([[0., 1., 2., 3., 4.],[0., 1., 2., 3., 4.],[0., 1., 2., 3., 4.]])
b=
tensor([0., 1., 2.], requires_grad=True)
b_grad=
tensor([1., 1., 1.])
b_grad=
torch.Size([3])
y=tensor([ 7.0800, -2.8872,  8.1886], grad_fn=<AddBackward0>)
after:
**************************************************

3. 两种方法求jacobian

  • 方法一:直接使用 from torch.autograd.functional import jacobian
  • 方法二:将f(x)的向量按照每个元素是标量的方式,对每个标量进行反向传播,最后将结果叠加起来。
import torch
from torch.autograd.functional import jacobian# 定义函数 f(x)
def funx(x):return torch.stack([x[0] ** 2 + 2 * x[1],3 * x[0] + 4 * x[1] ** 2])if __name__ == "__main__":# 初始化输入张量并启用梯度in_x = torch.tensor([1.0, 2.0], dtype=torch.float, requires_grad=True)# 计算函数输出y = funx(in_x)# 初始化一个零矩阵,用于保存手动计算的雅可比矩阵manual_jacobian = torch.zeros(len(y), len(in_x))# 手动计算雅可比矩阵for i in range(len(y)):# 每次 backward 计算一个分量的梯度in_x.grad = None  # 清除之前的梯度y[i].backward(retain_graph=True)manual_jacobian[i] = in_x.grad# 使用 autograd.functional.jacobian 验证雅可比auto_jacobian = jacobian(funx, in_x)print(f"Manual Jacobian:\n{manual_jacobian}")print(f"Auto Jacobian:\n{auto_jacobian}")
  • 结果:
Manual Jacobian:
tensor([[ 2.,  2.],[ 3., 16.]])
Auto Jacobian:
tensor([[ 2.,  2.],[ 3., 16.]])

http://www.ppmy.cn/ops/136963.html

相关文章

在 ARM 平台上如何实现Linux系统的1秒启动

在ARM平台上实现Linux系统的1秒启动&#xff0c;是一项涉及深层次优化的挑战。这不仅需要对系统的各个层面进行精细调整&#xff0c;还需要确保在保持系统稳定性的同时&#xff0c;实现快速启动。以下是实现这一目标的关键步骤和优化工作&#xff1a; 1. 精简U-Boot启动过程 …

一篇专业且实用的技术博客:从离线安装 Nginx 到动态适配依赖升级20241125

一篇专业且实用的技术博客&#xff1a;从离线安装 Nginx 到动态适配依赖升级&#x1f680; &#x1f31f; 引言 在现代开发中&#xff0c;Nginx 凭借其高性能和灵活性&#xff0c;成为网站和服务托管的首选工具。然而&#xff0c;在某些限制性环境中&#xff0c;离线安装和维…

光伏电站项目-视频监控、微气象及安全警卫系统

一、项目背景 近年来&#xff0c;我国光伏发电持续快速发展。截止2019年5月装机总容量超过2.043亿千瓦&#xff0c;技术水平不断提升&#xff0c;成本显著降低&#xff0c;开发建设质量和消纳利用明显改善&#xff0c;在部分地区实现了家庭分布式光伏并入电网&#xff0c;为建…

大数据新视界 -- Hive 数据桶原理:均匀分布数据的智慧(上)(9/ 30)

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

[极客大挑战 2019]BabySQL--详细解析

信息搜集 进入界面&#xff1a; 输入用户名为admin&#xff0c;密码随便输一个&#xff1a; 发现是GET传参&#xff0c;有username和password两个传参点。 我们测试一下password点位能不能注入&#xff1a; 单引号闭合报错&#xff0c;根据报错信息&#xff0c;我们可以判断…

【AIGC】ChatGPT提示词Prompt解析:拒绝的艺术:如何优雅地说“不“

引言 在人际交往的复杂网络中,学会优雅地拒绝是一种至关重要的社交智慧。很多人往往因为害怕伤害他人的感受,而选择敷衍、拖延或不置可否。 然而,真正的智慧在于如何用尊重和同理心传达"不"的信息。 本文将深入探讨优雅拒绝的艺术,帮助你在维护自身边界的同时,…

动态调试对安全研究有什么帮助?

动态调试在安全研究中提供了多方面的帮助&#xff0c;以下是其主要作用&#xff1a; 深入了解恶意软件行为&#xff1a;动态调试允许安全研究人员实时监视程序的执行过程&#xff0c;包括指令的执行情况、内存的读写情况、寄存器的状态等&#xff0c;从而帮助分析人员理清程序的…

记录一次electron-builder报错ENOENT: no such file or directory, rename xxxx的问题

项目架构&#xff1a;vue3vitets electron版本&#xff1a;^33.2.0 电脑系统: macOs m1芯片版 根据现有的各种文档&#xff0c;在package.json中配置了 "electron:build": "vite build & electron-builder build --config electron-builder.json" 命…