【Dive into Deep Learning / 动手学深度学习】第二章 - 第五节:自动微分

前言

Hello!
非常感谢您阅读海轰的文章,倘若文中有错误的地方,欢迎您指出~
 
自我介绍 ଘ(੭ˊᵕˋ)੭
昵称:海轰
标签:程序猿|C++选手|学生
简介:因C语言结识编程,随后转入计算机专业,获得过国家奖学金,有幸在竞赛中拿过一些国奖、省奖…已保研。
学习经验:扎实基础 + 多做笔记 + 多敲代码 + 多思考 + 学好英语!
 
唯有努力💪
 

知其然 知其所以然!

 
本文仅记录自己感兴趣的内容

2.5. 自动微分

2.5.1. 标量变量的反向传播

求函数 f ( x ) = 2 x T x f(x) = 2 x^Tx f(x)=2xTx对列向量 x x x求导后的梯度

  1. 初始化向量 x x x
    在这里插入图片描述
  2. 设置x可计算梯度
    在这里插入图片描述

requires_grad: 如果需要为张量计算梯度,则为True,否则为False

  1. 计算 y = 2 x T x y = 2x^Tx y=2xTx
    在这里插入图片描述

dot用来计算两个向量之间内积(对应元素相乘,再累加,最终得到一个标量)

grad_fn: grad_fn用来记录变量是怎么来的,方便计算梯度(比如这里便于使用反向传播计算梯度)

  1. 使用反向传播法计算梯度

在这里插入图片描述

理论上 f ( x ) = 2 x T x f(x) = 2 x^Tx f(x)=2xTx x x x求导后结果为 4 x 4x 4x
初始时 x = [ 0 , 1 , 2 , 3 ] x = [0,1,2,3] x=[0,1,2,3]
利用 x x x x = [ 0 , 1 , 2 , 3 ] x = [0,1,2,3] x=[0,1,2,3]时的梯度为 4 x = [ 0 , 4 , 8 , 12 ] 4x = [0, 4, 8, 12] 4x=[0,4,8,12]
计算得出的结果和理论值一样

  1. 判断计算得出的结果和理论值是否一样

在这里插入图片描述


现在设 y = x 1 + x 2 + x 3 + x 4 y = x_1 + x_2 + x_3 + x_4 y=x1+x2+x3+x4,其中 x = [ x 1 , x 2 , x 3 , x 4 ] x = [x_1, x_2, x_3, x_4] x=[x1,x2,x3,x4],求 x x x的梯度

import torch

x = torch.arange(4.0, requires_grad = True)
y = x.sum() # 相当于 x_1 + x_2 + ... + x_n = y
y.backward()
x.grad

# tensor([1., 1., 1., 1.])

解释:对 x 1 x_1 x1求偏导,结果为1,同理,对 x 2 、 x 3 、 x 4 x_2、x_3、x_4 x2x3x4求偏导,结果都为1

注意:在默认情况下,PyTorch会累积梯度

比如多次运行y.backward(),会有

  1. 第一次
    在这里插入图片描述
  2. 第二次
    在这里插入图片描述
  3. 第三次
    在这里插入图片描述

解决办法:使用grad.zero_()清除梯度

在这里插入图片描述

这样多次运行,结果都是[1,1,1,1]

2.5.2. 非标量变量的反向传播

y = x ∗ x y = x * x y=xx x x x求偏导( x x x为一个向量)

说明:

  • y = x ∗ x y= x * x y=xx: 对应元素相乘,此时结果 y y y是一个非标量

在这里插入图片描述

x = [0, 1, 2, 3] ===> x * x = [0, 1 * 1 , 2 * 2 , 3 * 3] = [0, 1, 4, 9]

为了求 x x x的偏导,则需要先对 y y y进行累加求和,再使用反向传播

在这里插入图片描述

利用 y = x * x x x x求导后,结果为 2 x 2 x 2x
x = [0,1,2,3] ==> 2x = [0,2,4,6]

注:这里还是有点不懂 ??? 以下为书中原话:

在这里插入图片描述

2.5.3. 分离计算

在这里插入图片描述


若不丢弃计算图中如何计算y的任何信息,也就是不使用detach()函数

y = x ∗ x u = y z = u ∗ x y = x * x\\ u = y\\ z = u * x y=xxu=yz=ux

等效于

z = x ∗ x ∗ x z = x * x * x z=xxx

此时z关于x求导,梯度为(此时z为非标量,需要先求和)

在这里插入图片描述

得到梯度为[0,3,12,27] ,与理论值 3 x 2 3 x^2 3x2 吻合


使用detach()函数后

在这里插入图片描述

这里可以理解为:

  • u = y.detach()使得u仅仅是一个向量[0,1,4, 9]
  • z = u * x, u仅仅作为一个常向量,对x求导,得到结果为u

总结:

  • 不使用detach(), 那么y是由x计算出来的,也就是可以用x的算式进行替换
  • 使用detach(), u仅仅表示一个向量,仅有y的值,其它信息都无

2.5.4. Python控制流的梯度计算

在这里插入图片描述

大概意思:

  • f(a)函数是一个分段函数,其中每一段都是线性的(直线)
  • 输入a的值不同,对应不同的线段(需要经过while循环、if-else等)
  • 但是最后依然是可以算出来x的梯度,不受循环等的影响
  • 比如经过多次循环、if-else, 得到的a与d的对应关系为 d = 1231 * a,梯度依然可以算出来为1231
  • 也就是说经过多次Python控制流(例如,条件、循环或任意函数调用),我们仍然可以计算得到的变量的梯度

练习

在这里插入图片描述

import numpy as np
import torch
from d2l import torch as d2l
x = torch.arange(-4.0, 4.0, 0.1, requires_grad = True)
x

在这里插入图片描述

y = torch.sin(x)
y.sum().backward()
x.grad == torch.cos(x)

在这里插入图片描述

y2 = x.grad
y2

在这里插入图片描述

y2_np = y2.numpy()
y2_np

在这里插入图片描述

x_np = x.detach().numpy()
x_np

在这里插入图片描述

def f(x):
    return np.sin(x)
y1_np = f(x_np)
y1_np

在这里插入图片描述

d2l.plot(x_np, [y1_np, y2_np], 'x', 'f(x)', legend=['sin(x)', 'cos(x)'])

在这里插入图片描述

结语

学习资料:http://zh.d2l.ai/

文章仅作为个人学习笔记记录,记录从0到1的一个过程

希望对您有一点点帮助,如有错误欢迎小伙伴指正

在这里插入图片描述

PyTorch默认只计算一阶导数(梯度),不直接支持计算二阶导数。但是,可以通过以下方法来计算二阶导数: 1.手动计算Hessian矩阵 可以使用PyTorch中的自动微分机制计算一阶导数,在此基础上,可以手动计算二阶导数。假设我们要计算一个标量函数$f(x)$的二阶导数,第一步是计算$f(x)$的一阶导数$g=\frac{\partial f(x)}{\partial x}$,然后计算$g$的一阶导数$h=\frac{\partial g}{\partial x}$,最终得到$f$的二阶导数$h$。可以使用链式法则来计算$h$,例如: ```python import torch x = torch.tensor([1.0], requires_grad=True) # 计算一阶导数 y = x ** 2 g = torch.autograd.grad(y, x, create_graph=True)[0] # 计算二阶导数 h = torch.autograd.grad(g, x)[0] ``` 在这个例子中,我们首先计算$x^2$的一阶导数,即$2x$,然后计算$2x$的一阶导数,即$2$,从而得到二阶导数为$2$。 如果要计算多个变量的二阶导数,可以将它们放在一个向量中,并将梯度和Hessian矩阵表示为矩阵形式。例如,假设我们要计算一个函数$f(x,y)=x^2+y^2$的一阶导数向量和二阶导数矩阵,可以这样做: ```python import torch x = torch.tensor([1.0], requires_grad=True) y = torch.tensor([2.0], requires_grad=True) # 计算一阶导数 z = x ** 2 + y ** 2 g = torch.autograd.grad(z, (x,y), create_graph=True) # 计算二阶导数 h = torch.zeros((2,2)) for i, var in enumerate([x,y]): for j, gvar in enumerate(g): h[i, j] = torch.autograd.grad(gvar, var)[0] ``` 在这个例子中,我们首先计算$f(x,y)$的一阶导数向量$(2x,2y)$,然后计算每个元素的一阶导数,从而得到二阶导数矩阵$\begin{bmatrix}2 & 0 \\ 0 & 2 \end{bmatrix}$。注意,我们需要手动创建一个零矩阵来存储Hessian矩阵。 2.使用torch.autograd.functional.hessian函数 PyTorch 1.8版本新增了一个torch.autograd.functional.hessian函数,可以用来计算二阶导数(Hessian矩阵)。使用该函数,我们只需要提供要计算Hessian矩阵的函数和输入变量,就可以得到Hessian矩阵。例如,假设我们要计算函数$f(x)=\sin(x_1+x_2)+x_1x_2$在输入$(\pi/4, \pi/4)$处的Hessian矩阵,可以这样做: ```python import torch x = torch.tensor([3.14/4, 3.14/4], requires_grad=True) def f(x): return torch.sin(x[0]+x[1]) + x[0]*x[1] hessian = torch.autograd.functional.hessian(f, x) ``` 在这个例子中,我们定义了一个函数$f(x)$,然后计算在输入$x=(\pi/4, \pi/4)$处的Hessian矩阵。注意,输入$x$应该是一个张量,并且需要将requires_grad属性设置为True。在计算Hessian矩阵时,我们只需要调用torch.autograd.functional.hessian函数,并传入函数$f$和输入变量$x$即可。 需要注意的是,计算Hessian矩阵的运算量常大,因此在实际应用中应该尽量避免计算Hessian矩阵。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

海轰Pro

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值