pytorch教程之自动求导机制(AUTOGRAD)-从梯度和Jacobian矩阵讲起

2024-04-02 01:38

本文主要是介绍pytorch教程之自动求导机制(AUTOGRAD)-从梯度和Jacobian矩阵讲起,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!


文章目录

  • 1. 梯度和Jacobian矩阵
  • 2. pytorch求变量导数的过程

1. 梯度和Jacobian矩阵

f ( x ) ∈ R 1 f(x)\in R^1 f(x)R1是关于向量 x ∈ R n x\in R^n xRn的函数,则它关于 x x x的导数定义为:
d f ( x ) d x : = [ ∂ f ( x ) ∂ x i ] ∈ R n (1-1) \frac{df(x)}{dx}:=\left[\frac{\partial f(x)}{\partial x_i}\right]\in R^{n}\tag{1-1} dxdf(x):=[xif(x)]Rn(1-1)
函数 f ( x ) ∈ R 1 f(x)\in R^1 f(x)R1关于向量 x ∈ R n x\in R^n xRn的导数是一个列向量,称之为 f ( x ) f(x) f(x)关于 x x x的梯度。
d f ( x ) T d x : = ( d f ( x ) d x ) T = [ ∂ f ( x ) ∂ x i ] T ∈ R 1 × n (1-2) \frac{df(x)^T}{dx}:=\left(\frac{df(x)}{dx}\right)^T=\left[\frac{\partial f(x)}{\partial x_i}\right]^T\in R^{1\times n}\tag{1-2} dxdf(x)T:=(dxdf(x))T=[xif(x)]TR1×n(1-2)
如果 f ( x ) ∈ R M f(x)\in R^M f(x)RM是关于向量 x ∈ R n x\in R^n xRn的函数向量,则 f ( x ) f(x) f(x)关于 x x x的导数定义为:
d f ( x ) d x : = d f ( x ) d x T = [ ∂ f ( x ) ∂ x 1 , ∂ f ( x ) ∂ x 2 , ⋯ , ∂ f ( x ) ∂ x n ] ∈ R m × n (1-3) \frac{df(x)}{dx}:=\frac{df(x)}{dx^T}=\left[\frac{\partial f(x)}{\partial x_1},\frac{\partial f(x)}{\partial x_2},\cdots,\frac{\partial f(x)}{\partial x_n}\right]\in R^{m\times n}\tag{1-3} dxdf(x):=dxTdf(x)=[x1f(x),x2f(x),,xnf(x)]Rm×n(1-3)
称上述矩阵为Jacobian矩阵。
一些常用推论:

  1. 假设 v , x ∈ R n v,x\in R^n v,xRn:
    d d x ( v T x ) = d d x ( x T v ) = v (1-4) \frac{d}{dx}(v^Tx)=\frac{d}{dx}(x^Tv)=v\tag{1-4} dxd(vTx)=dxd(xTv)=v(1-4)
  2. 假设 y ∈ R 1 y\in R^1 yR1, z ∈ R m z\in R^m zRm, x ∈ R n x\in R^n xRn, z = g ( x ) z=g(x) z=g(x),y=f(z):
    d y d x = ( d z d x ) T d y d z (1-5) \frac{dy}{dx}=\left(\frac{dz}{dx}\right)^T\frac{dy}{dz}\tag{1-5} dxdy=(dxdz)Tdzdy(1-5)
    可以从向量矩阵的维度适配上去理解和记忆,因为 d y d x ∈ R n \frac{dy}{dx}\in R^n dxdyRn, d y d z ∈ R m \frac{dy}{dz}\in R^m dzdyRm, d z d x ∈ R m × n \frac{dz}{dx}\in R^{m\times n} dxdzRm×n,所以必须有上述的公式才能适配。
  3. 假设 y ∈ R k y\in R^k yRk, z ∈ R m z\in R^m zRm, x ∈ R 1 x\in R^1 xR1, z = g ( x ) z=g(x) z=g(x),y=f(z):
    d y d x = d y d z d z d x (1-6) \frac{dy}{dx}=\frac{dy}{dz}\frac{dz}{dx}\tag{1-6} dxdy=dzdydxdz(1-6)
  4. 假设 y ∈ R k y\in R^k yRk, z ∈ R m z\in R^m zRm, x ∈ R n x\in R^n xRn, z = g ( x ) z=g(x) z=g(x),y=f(z):
    d y d x = d y d z d z d x (1-7) \frac{dy}{dx}=\frac{dy}{dz}\frac{dz}{dx}\tag{1-7} dxdy=dzdydxdz(1-7)

2. pytorch求变量导数的过程

在pytorch和TensorFlow中,是不支持张量对张量的求导。这不是因为数学上没法求,而是因为工程实现上比较麻烦。因为向量对向量求导是个矩阵,二阶张量(矩阵)对二阶张量(矩阵)求导得到一个四阶张量,这样很容易会产生阶数爆炸。所以pytorch和TensorFlow(猜测其他深度学习框架也是这样)对外的接口干脆不支持张量对张量求导。如果遇到张量对张量求导的情况,例如向量对向量求导的情况,需要对因变量乘以一个维度一样的向量,转换为标量对向量的求导,这样可以大大减少计算量(具体见后文)。并且,因为pytorch和TensorFlow是为了机器学习/深度学习模型设计的,机器学习/深度模型的求导基本上都是损失函数(标量)对参数的求导,很少直接用到向量对向量求导,因此上述过程是有实际意义和需求的。

假设有一个三维tensor x = [ x 1 , x 2 , x 3 ] T = [ 1 , 2 , 3 ] T x=[x_1,x_2,x_3]^T=[1,2,3]^T x=[x1,x2,x3]T=[1,2,3]T,另一个三维tensor y:
y = f ( x ) = [ x 1 3 + 2 x 2 2 + 3 x 3 3 x 1 + 2 x 2 2 + x 3 3 2 x 1 + x 2 3 + 3 x 3 2 ] (2-1) y=f(x)= \begin{bmatrix} {x_1}^3+2{x_2}^2+3x_3 \\ 3x_1+2{x_2}^2+{x_3}^3\\ 2x_1+{x_2}^3+3{x_3}^2 \end{bmatrix} \tag{2-1} y=f(x)=x13+2x22+3x33x1+2x22+x332x1+x23+3x32(2-1)
那么在计算y相对于x的导数时,
d y d x = [ 3 x 1 2 , 4 x 2 , 3 3 , 4 x 2 , 3 x 3 2 2 , 3 x 2 2 , 6 x 3 ] (2-2) \frac{dy}{dx}= \begin{bmatrix} &3{x_1}^2,&4x_2,&3 \\ &3,&4{x_2},&3{x_3}^2\\ &2,&3{x_2}^2,&6{x_3} \end{bmatrix} \tag{2-2} dxdy=3x12,3,2,4x2,4x2,3x22,33x326x3(2-2)
在pytorch中实际计算时,不能直接用y对x求导,需要先用一个向量 w w w左乘y,再转置。例如, w T = [ 3 , 2 , 1 ] w^T=[3,2,1] wT=[3,2,1]。因此,pytorch算的其实是:
d y T d x w = ( w T d y d x ) T = [ 17 52 81 ] (2-3) \frac{dy^T}{dx}w= \left(w^T\frac{dy}{dx}\right)^T =\begin{bmatrix} 17\\ 52\\ 81\\ \end{bmatrix} \tag{2-3} dxdyTw=(wTdxdy)T=175281(2-3)
w w w可以理解为是对 [ ∂ y 1 ∂ x , ∂ y 2 ∂ x , ∂ y 3 ∂ x ] T [\frac{\partial y_1}{\partial x},\frac{\partial y_2}{\partial x},\frac{\partial y_3}{\partial x}]^T [xy1,xy2,xy3]T的权重参数。因此我们得到的是y的各个分量的导数的加权求和。

代码如下:

import torch
x1=torch.tensor(1, requires_grad=True, dtype = torch.float)
x2=torch.tensor(2, requires_grad=True, dtype = torch.float)
x3=torch.tensor(3, requires_grad=True, dtype = torch.float)
y=torch.randn(3)
y[0]=x1**3+2*x2**2+3*x3
y[1]=3*x1+2*x2**2+x3**3
y[2]=2*x1+x2**3+3*x3**2
v=torch.tensor([3,2,1],dtype=torch.float)
y.backward(v)
print(x1.grad)
print(x2.grad)
print(x3.grad)

利用链式求导的原理来理解,可以理解为 w w w是(远方)某个标量对 y y y的导数。pytorch之所以要这么设计,是因为在机器学习/深度学习模型中,求导的最终目的一般是为了让损失函数最小。损失函数一般都是一个标量,因此无论链式求导的过程多么复杂,中间过程也许有很多向量对向量求导的子过程,但是最开始一定会有一个标量(损失函数)对向量的求导过程,这个导数就是前面的 w w w

下面看一个带两个隐藏层的神经网络解决线性回归问题的例子,来进一步说明这点。
为了简单起见,考虑batch_size=1的情况。设输入数据为 x = [ x 1 , x 2 ] T x=[x_1,x_2]^T x=[x1,x2]T,输入层到第一个隐藏层的权重矩阵为
W = [ w 1 T w 2 T ] = [ w 11 , w 12 w 21 , w 22 ] (2-4) W=\begin{bmatrix} w_1^T\\ w_2^T \end{bmatrix} = \begin{bmatrix} w_{11},w_{12}\\ w_{21},w_{22}\\ \end{bmatrix} \tag{2-4} W=[w1Tw2T]=[w11,w12w21,w22](2-4)
第一个隐藏层的值为 z = [ z 1 , z 2 ] T z=[z_1,z_2]^T z=[z1,z2]T,
第一个隐藏层到第二个隐藏层的权重矩阵为
U = [ u 1 T u 2 T ] = [ u 11 , u 12 u 21 , u 22 ] (2-5) U=\begin{bmatrix} u_1^T\\ u_2^T \end{bmatrix} = \begin{bmatrix} u_{11},u_{12}\\ u_{21},u_{22}\\ \end{bmatrix} \tag{2-5} U=[u1Tu2T]=[u11,u12u21,u22](2-5)
第二个隐藏层的值为 s = [ s 1 , s 2 ] T s=[s_1,s_2]^T s=[s1,s2]T,
输出层的值为 y y y,隐藏层到输出层的权重参数为 v = [ v 1 , v 2 ] T v=[v_1,v_2]^T v=[v1,v2]T。则有:
z = [ z 1 z 2 ] = [ w 11 , w 12 w 21 , w 22 ] [ x 1 x 2 ] = [ w 11 x 1 + w 12 x 2 w 21 x 1 + w 22 x 2 ] (2-6) z=\begin{bmatrix} z_1\\ z_2\\ \end{bmatrix}= \begin{bmatrix} w_{11},w_{12}\\ w_{21},w_{22}\\ \end{bmatrix}\begin{bmatrix} x_1\\ x_2\\ \end{bmatrix}= \begin{bmatrix} w_{11}x_1+w_{12}x_2\\ w_{21}x_1+w_{22}x_2\\ \end{bmatrix}\tag{2-6} z=[z1z2]=[w11,w12w21,w22][x1x2]=[w11x1+w12x2w21x1+w22x2](2-6)

s = [ s 1 s 2 ] = [ u 11 , u 12 u 21 , u 22 ] [ z 1 z 2 ] = [ u 11 ( w 11 x 1 + w 12 x 2 ) + u 12 ( w 21 x 1 + w 22 x 2 ) u 21 ( w 11 x 1 + w 12 x 2 ) + u 22 ( w 21 x 1 + w 22 x 2 ) ] (2-7) \begin{aligned} s&=\begin{bmatrix} s_1\\ s_2\\ \end{bmatrix}= \begin{bmatrix} u_{11},u_{12}\\ u_{21},u_{22}\\ \end{bmatrix}\begin{bmatrix} z_1\\ z_2\\ \end{bmatrix}\\ &= \begin{bmatrix} u_{11}(w_{11}x_1+w_{12}x_2)+u_{12}(w_{21}x_1+w_{22}x_2)\\ u_{21}(w_{11}x_1+w_{12}x_2)+u_{22}(w_{21}x_1+w_{22}x_2)\\ \end{bmatrix}\tag{2-7} \end{aligned} s=[s1s2]=[u11,u12u21,u22][z1z2]=[u11(w11x1+w12x2)+u12(w21x1+w22x2)u21(w11x1+w12x2)+u22(w21x1+w22x2)](2-7)

y = [ v 1 , v 2 ] [ s 1 s 2 ] = ( v 1 u 11 x 1 + v 2 u 21 x 1 ) w 11 + ( v 1 u 11 x 2 + v 2 u 21 x 2 ) w 12 + ( v 1 u 12 x 1 + v 2 u 22 x 1 ) w 21 + ( v 1 u 12 x 2 + v 2 u 22 x 2 ) w 22 (2-8) \begin{aligned} y&= [v_1,v_2]\begin{bmatrix} s_1\\ s_2\\ \end{bmatrix}\\ &=(v_1u_{11}x_1+v_2u_{21}x_1)w_{11}\\ &+(v_1u_{11}x_2+v_2u_{21}x_2)w_{12}\\ &+(v_1u_{12}x_1+v_2u_{22}x_1)w_{21}\\ &+(v_1u_{12}x_2+v_2u_{22}x_2)w_{22} \end{aligned}\tag{2-8} y=[v1,v2][s1s2]=(v1u11x1+v2u21x1)w11+(v1u11x2+v2u21x2)w12+(v1u12x1+v2u22x1)w21+(v1u12x2+v2u22x2)w22(2-8)
损失函数为 L = ( y − y ^ ) 2 / 2 L=(y-\hat y)^2/2 L=(yy^)2/2
则损失函数关于权重参数 w 1 w_1 w1的导数为:
d L d w 1 = ( y − y ^ ) d y d x = ( y − y ^ ) d s T d x d y d s = ( y − y ^ ) d z T d x d s T d z d y d s = ( y − y ^ ) [ x 1 , 0 x 2 , 0 ] [ u 11 , u 21 u 12 , u 22 ] [ v 1 v 2 ] = ( y − y ^ ) [ v 1 x 1 u 11 + v 2 x 1 u 21 v 1 x 2 u 11 + v 2 x 2 u 21 ] (2-9) \begin{aligned} \frac{dL}{dw_1}&=(y-\hat y)\frac{dy}{dx}\\ &=(y-\hat y)\frac{ds^T}{dx}\frac{dy}{ds}\\ &=(y-\hat y)\frac{dz^T}{dx}\frac{ds^T}{dz}\frac{dy}{ds}\\ &=(y-\hat y)\begin{bmatrix} x_1,0\\ x_2,0\\ \end{bmatrix} \begin{bmatrix} u_{11},u_{21}\\ u_{12},u_{22}\\ \end{bmatrix} \begin{bmatrix} v_1\\ v_2 \end{bmatrix}\\ &=(y-\hat y)\begin{bmatrix} v_1x_1u_{11}+v_2x_1u_{21}\\ v_1x_2u_{11}+v_2x_2u_{21} \end{bmatrix}\\ \end{aligned}\tag{2-9} dw1dL=(yy^)dxdy=(yy^)dxdsTdsdy=(yy^)dxdzTdzdsTdsdy=(yy^)[x1,0x2,0][u11,u21u12,u22][v1v2]=(yy^)[v1x1u11+v2x1u21v1x2u11+v2x2u21](2-9)
可以验证 ( 2 − 9 ) (2-9) (29)和前面 ( 2 − 8 ) (2-8) (28)中直接求得的导数值是一样的。
这里发现了一个小彩蛋:
假设在pytorch的底层实现中,如果从左往右计算,则需要进行进行大量的矩阵乘法。如果有n个 2 × 2 2\times 2 2×2的方阵相乘,那么需要进行 4 × ( n − 1 ) 4\times (n-1) 4×(n1)次内积。如果从又往左计算,只需要进行 2 × n 2\times n 2×n次内积。

这篇关于pytorch教程之自动求导机制(AUTOGRAD)-从梯度和Jacobian矩阵讲起的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/868652

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Makefile简明使用教程

文章目录 规则makefile文件的基本语法:加在命令前的特殊符号:.PHONY伪目标: Makefilev1 直观写法v2 加上中间过程v3 伪目标v4 变量 make 选项-f-n-C Make 是一种流行的构建工具,常用于将源代码转换成可执行文件或者其他形式的输出文件(如库文件、文档等)。Make 可以自动化地执行编译、链接等一系列操作。 规则 makefile文件

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL

SWAP作物生长模型安装教程、数据制备、敏感性分析、气候变化影响、R模型敏感性分析与贝叶斯优化、Fortran源代码分析、气候数据降尺度与变化影响分析

查看原文>>>全流程SWAP农业模型数据制备、敏感性分析及气候变化影响实践技术应用 SWAP模型是由荷兰瓦赫宁根大学开发的先进农作物模型,它综合考虑了土壤-水分-大气以及植被间的相互作用;是一种描述作物生长过程的一种机理性作物生长模型。它不但运用Richard方程,使其能够精确的模拟土壤中水分的运动,而且耦合了WOFOST作物模型使作物的生长描述更为科学。 本文让更多的科研人员和农业工作者

hdu 4565 推倒公式+矩阵快速幂

题意 求下式的值: Sn=⌈ (a+b√)n⌉%m S_n = \lceil\ (a + \sqrt{b}) ^ n \rceil\% m 其中: 0<a,m<215 0< a, m < 2^{15} 0<b,n<231 0 < b, n < 2^{31} (a−1)2<b<a2 (a-1)^2< b < a^2 解析 令: An=(a+b√)n A_n = (a +

基于51单片机的自动转向修复系统的设计与实现

文章目录 前言资料获取设计介绍功能介绍设计清单具体实现截图参考文献设计获取 前言 💗博主介绍:✌全网粉丝10W+,CSDN特邀作者、博客专家、CSDN新星计划导师,一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP430/AVR等单片机设计 主要对象是咱们电子相关专业的大学生,希望您们都共创辉煌!✌💗 👇🏻 精彩专栏 推荐订阅👇🏻 单片机

【编程底层思考】垃圾收集机制,GC算法,垃圾收集器类型概述

Java的垃圾收集(Garbage Collection,GC)机制是Java语言的一大特色,它负责自动管理内存的回收,释放不再使用的对象所占用的内存。以下是对Java垃圾收集机制的详细介绍: 一、垃圾收集机制概述: 对象存活判断:垃圾收集器定期检查堆内存中的对象,判断哪些对象是“垃圾”,即不再被任何引用链直接或间接引用的对象。内存回收:将判断为垃圾的对象占用的内存进行回收,以便重新使用。

【Tools】大模型中的自注意力机制

摇来摇去摇碎点点的金黄 伸手牵来一片梦的霞光 南方的小巷推开多情的门窗 年轻和我们歌唱 摇来摇去摇着温柔的阳光 轻轻托起一件梦的衣裳 古老的都市每天都改变模样                      🎵 方芳《摇太阳》 自注意力机制(Self-Attention)是一种在Transformer等大模型中经常使用的注意力机制。该机制通过对输入序列中的每个元素计算与其他元素之间的相似性,

如何通俗理解注意力机制?

1、注意力机制(Attention Mechanism)是机器学习和深度学习中一种模拟人类注意力的方法,用于提高模型在处理大量信息时的效率和效果。通俗地理解,它就像是在一堆信息中找到最重要的部分,把注意力集中在这些关键点上,从而更好地完成任务。以下是几个简单的比喻来帮助理解注意力机制: 2、寻找重点:想象一下,你在阅读一篇文章的时候,有些段落特别重要,你会特别注意这些段落,反复阅读,而对其他部分