-
2020-11-12 22:14:35
标量反向传播
当目标张量为标量时,backward()无需传入参数。
- 例子:假设 w , x , b w,x,b w,x,b都是标量, z = w x + b z=wx+b z=wx+b ,对标量 z z z调用backward()方法。
自动求导的主要步骤
import torch
1.定义叶子结点,算子节点
如果需要对Tensor求导,requires_grad要设置为True。
# 定义输入张量x x = torch.Tensor([2]) # 初始化权重参数w,偏置b,#设置requires_grad为True,使用自动求导 w = torch.randn(1,requires_grad=True) b = torch.randn(1,requires_grad=True) # 设置前向传播 y = torch.mul(w,x) z = torch.add(y,b) # 查看requires_grad属性 print(x.requires_grad) print(y.requires_grad) # 因为与w,b具有y依赖关系,所以x,y的requires_grad也是True。
False True
2.查看叶子结点,非叶子结点的其他属性
- grad_fn:表示梯度函数
通过运算创建的Tensor(非叶子结点)会自动被赋予grad_fn属性。
叶子结点的grad_fn为None。# 查看非叶子结点y,z的requires_grad属性。 print(y.requires_grad) # 查看各节点是不是叶子节点 print(x.is_leaf) print(y.is_leaf) # 叶子结点:x,w,b # 非叶子结点:y,z # 查看叶子结点的grad_fn属性 print("x的grad_fn属性:",x.grad_fn) # 查看非叶子结点的grad_fn属性 print("y的grad_fn属性:",y.grad_fn)
True True False x的grad_fn属性: None y的grad_fn属性: <MulBackward0 object at 0x7fe83935dbb0>
3.自动求导,实现梯度反向传播
非叶子节点的梯度调用backward()之后,梯度将被清空。
# 基于z对张量进行反向传播,执行backward之后计算图会清空。 z.backward() # 如果需要多次backward()需要设置参数retain_graph为True。此时梯度是累加的。 # z.backward(retain_graph=True) # 查看叶子结点的梯度。 # 因为x未设置requires_grad属性,默认为False,不求导,所以grad为None。 print("x的梯度是:",x.grad) print("w的梯度是:",w.grad) # 查看非叶子结点的梯度 # 非叶子节点的梯度调用backward()之后,梯度将被清空。故y,z此时没有梯度。 # print("y的梯度是:",y.grad) # print("z的梯度是:",z.grad)
x的梯度是: None w的梯度是: tensor([2.])
非标量反向传播
Pytorch只允许标量对张量进行求导
步骤
1.定义叶子结点,计算结点
import torch
# 定义叶子张量x,形状为1x2 x = torch.tensor([[2,3]],dtype=torch.float,requires_grad=True) print(x) # 初始化雅可比矩阵 J = torch.zeros(2,2) print(J[0]) # 初始化目标张量,形状为1x2 y = torch.zeros(1,2) # 定义y与x之间的映射关系 # y1 = x1**2+3*x2 # y2 = x2**2+2*x1 y[0,0]=x[0,0]**2+3*x[0,1] y[0,1]=x[0,1]**2+2*x[0,0] print(y)
tensor([[2., 3.]], requires_grad=True) tensor([0., 0.]) tensor([[13., 13.]], grad_fn=<CopySlices>)
2.调用backward()获取y对x的梯度
需要重复使用backward()时,retain_graph=True
# 生成y1对x的梯度 y.backward(torch.Tensor([[1,0]]),retain_graph=True) J[0]=x.grad # 因为梯度是累加的,所以需要清除对x的梯度 x.grad = torch.zeros_like(x.grad) # 生成y2对x的梯度 y.backward(torch.Tensor([[0,1]])) J[1]=x.grad # 雅可比矩阵的值 print(J)
tensor([[4., 3.], [2., 6.]])
更多相关内容 -
关于PyTorch 自动求导机制详解
2020-09-18 19:39:51今天小编就为大家分享一篇关于PyTorch 自动求导机制详解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧 -
pytorch自动求导
2022-02-12 16:58:141. params = torch.tensor([1.0, 0.0], requires_grad=True) ...如果这些函数是可微的(大多数PyTorch张量运算都是可微的),则导数的值将自动存储在参数张量的 grad 属性中。 你可以将包含任意数量的张量的1.求导
params = torch.tensor([1.0, 0.0], requires_grad=True)
注意到了张量构造函数的 require_grad = True 吗?这个参数告诉PyTorch需要追踪在 params 上进行运算而产生的所有张量。换句话说,任何以 params 为祖先的张量都可以访问从 params 到该张量所调用的函数链。如果这些函数是可微的(大多数PyTorch张量运算都是可微的),则导数的值将自动存储在参数张量的 grad 属性中。
你可以将包含任意数量的张量的 require_grad 设置为 True 以及组合任何函数。在这种情况下,
PyTorch会在沿着整个函数链(即计算图)计算损失的导数,并在这些张量(即计算图的叶节点)的grad 属性中将这些导数值累积(accumulate)起来警告:PyTorch的新手(以及很多经验丰富的人)经常忽视的事情:是积累(accumulate)而不是存储(store)。
为防止这种情况发生,你需要在每次迭代时将梯度显式清零。可以使用就地方法 zero_ 轻松地做到这一点:
if params.grad is not None: params.grad.zero_()# 这可以在调用backward之前在循环中的任何时候完成
如果不使用 params.grad.zero_()会出现梯度累积的效果
第一次求导
第二次求导
求和的梯度为1,但是梯度不归0就累计了
请注意,更新参数时,你还执行了奇怪的 .detach().requires_grad_() 。要了解原因,请考虑一下你构建的计算图。为了避免重复使用变量名,我们重构 params 参数更新行: p1 = (p0 * lr *
p0.grad) 。这里 p0 是用于初始化模型的随机权重, p0.grad 是通过损失函数根据 p0 和训练数据计算出来的。2.优化
每个优化器都有两个方法: zero_grad 和 step 。前者将构造时传递给优化器的所有参数的 grad 属性归零;后者根据特定优化器实施的优化策略更新这些参数的值。
设置损失函数:均方差损失函数
def loss_fn(t_p, t_c): squared_diffs = (t_p - t_c)**2 return squared_diffs.mean()
现在创建参数并实例化一个梯度下降优化器:t_p = model(t_u, *params) loss = loss_fn(t_p, t_c) loss.backward() optimizer.step() params
调用 step 后 params 的值就会更新,无需亲自更新它!调用 step 发生的事情是:优化器通过将
params 减去 learning_rate 与 grad 的乘积来更新的 params ,这与之前手动编写的更新过程完全相同。params = (params - learning_rate *params.grad).detach().requires_grad_()
以下就是准备循环的代码,需要在正确的位置(在调用 backward 之前)插入额外的 zero_grad :
params = torch.tensor([1.0, 0.0], requires_grad=True) learning_rate = 1e-2 optimizer = optim.SGD([params], lr=learning_rate) t_p = model(t_un, *params) loss = loss_fn(t_p, t_c) optimizer.zero_grad() # 此调用可以在循环中更早的位置 loss.backward() optimizer.step() params
for循环开始
def training_loop(n_epochs, learning_rate, params, t_u, t_c): for epoch in range(1, n_epochs + 1): if params.grad is not None: params.grad.zero_() # 这可以在调用backward之前在循环中的任何时候完成 t_p = model(t_u, *params) loss = loss_fn(t_p, t_c) loss.backward() params = (params - learning_rate *params.grad).detach().requires_grad_() if epoch % 500 == 0: print('Epoch %d, Loss %f' % (epoch, float(loss))) return params
开始训练
t_un = 0.1 * t_u training_loop( n_epochs = 5000, learning_rate = 1e-2, params = torch.tensor([1.0, 0.0], requires_grad=True), t_u = t_un, t_c = t_c)
优化器设置
params = torch.tensor([1.0, 0.0], requires_grad=True) learning_rate = 1e-5 optimizer = optim.SGD([params], lr=learning_rate)
-
pytorch自动求导机制
2022-01-24 00:01:21为了计算梯度,pytorch提供了内置的求导机制 torch.autograd,它支持对任意计算图的自动梯度计算。 计算图是由节点和边组成的,其中的一些节点是数据,一些是数据之间的运算 计算图实际上就是变量之间的关系 tensor...Torch.autograd
在训练神经网络时,我们最常用的算法就是反向传播(BP)。
参数的更新依靠的就是loss function针对给定参数的梯度。为了计算梯度,pytorch提供了内置的求导机制 torch.autograd,它支持对任意计算图的自动梯度计算。
- 计算图是由节点和边组成的,其中的一些节点是数据,一些是数据之间的运算
- 计算图实际上就是变量之间的关系
- tensor 和 function 互相连接生成的一个有向无环图
Tensors,Function,计算图
考虑最简单的例子,一个一层的神经网络
-
input:x
-
parameters:w,b
import torch x = torch.ones(5) # input tensor y = torch.zeros(3) # expected output w = torch.randn(5, 3, requires_grad=True) b = torch.randn(3, requires_grad=True) z = torch.matmul(x, w)+b #x 和 w 矩阵相乘,再加上 bias b loss = torch.nn.functional.binary_cross_entropy_with_logits(z, y)
其计算图如下所示
- 我们可以在创建tensor时设置 requires_grad = True 来支持梯度计算
- 也可以后续使用 x.requires_grad_(True) 来设置
我们对 tensor 应用的来构建计算图的函数,实际上是 Function 类的一个对象。
-
该对象知道如何在前向传播中实施函数
-
也知道如何在反向传播中计算梯度
-
对反向传播函数的引用存储在tensor的 grad_fn 属性中
print('Gradient function for z =', z.grad_fn) print('Gradient function for loss =', loss.grad_fn) >>Gradient function for z = <AddBackward0 object at 0x000002A040C867F0> >>Gradient function for loss = <BinaryCrossEntropyWithLogitsBackward object at 0x000002A040C867F0>
计算梯度
为了更新参数,我们需要计算 loss function 关于参数的梯度。
-
我们使用 loss.backward() 来计算梯度
-
使用 w.grad,b.grad 来检索梯度值
loss.backward() print(w.grad) print(b.grad) >>tensor([[0.2832, 0.0843, 0.3005], [0.2832, 0.0843, 0.3005], [0.2832, 0.0843, 0.3005], [0.2832, 0.0843, 0.3005], [0.2832, 0.0843, 0.3005]]) >>tensor([0.2832, 0.0843, 0.3005])
-
note:
-
我们仅能获得计算图叶子节点的 grad 属性,并且需要这些节点设置 requires_grad = True。对于图中的其他节点,梯度是不可获取的
-
由于性能原因,在给定的计算图中,我们仅能使用 backward() 计算梯度一次(每次backward之后,计算图会被释放,但叶子节点的梯度不会被释放。叶子节点就是参数,不包括一些运算产生的中间变量。比如 x,w是叶子节点,但是 h = wx,h就不是叶子节点)。如果需要多次计算,我们需要设置 backward 的 retain_graph = True,这样的话求得的最终梯度是几次梯度之和
x = torch.ones((1, 4), dtype=torch.float32, requires_grad=True) y = x ** 2 z = y * 4 loss1 = z.mean() loss2 = z.sum() loss1.backward(retain_graph=True) loss2.backward() #如果上面没有设置 retain_graph = True,这里会报错 print(x.grad)
-
禁用梯度跟踪
有时,我们只想对数据应用模型(forward过程),而不考虑模型的更新,这时我们可以不使用梯度跟踪
-
将计算代码包围在 torch.no_grad() 块中
z = torch.matmul(x, w)+b print(z.requires_grad) >>True with torch.no_grad(): z = torch.matmul(x, w)+b print(z.requires_grad) >>False
-
另一种方式是使用 detach()
z = torch.matmul(x, w)+b z_det = z.detach() print(z_det.requires_grad) >>False
禁用梯度跟踪的一些原因
- fine-tune一个预训练的模型时,我们需要冻结模型的一些参数
- 当进行前向传播时,加速计算。对不追踪梯度的tensor进行计算会更高效
计算图的扩展
从概念上讲,autograd在由Function对象组成的有向无环图(DAG)中保存数据(张量)和所有执行的操作(以及产生的新张量)的记录。在DAG中,叶子是 input tensors,根是 output tensors,通过从根到叶跟踪这个图,可以使用链式法则自动计算梯度
在前向传播中,autograd同时做两件事
- 运行请求的操作来计算结果张量
- 在DAG中保持操作的梯度函数
反向传播过程开始,当 .backward() 在 DAG 根上被使用时
- 从每个 .grad_fn 中计算梯度
- 将它们累加到各个 tensor 的 .grad 属性中
- 利用链式法则,一直传播到叶子 tensor
DAGs 在 pytorch 中是动态的。值得注意的是,图是从头创建的。在每次.backward()调用之后autograd开始填充一个新的图。这正是允许我们在模型中使用控制流语句的原因,如果需要的话,我们可以在每次迭代中改变 shape(tensor的属性),size(tensor的方法) 和 operations(加减乘除等运算过程)
Tensor梯度和雅克比乘法
在很多情况下,我们loss function的结果是一个标量,我们以此来计算参数的梯度。但是,有些时候,我们的 loss 是 tensor。在这种情况下,pytorch 允许我们计算所谓的雅克比乘法,而不是梯度
-
如果是标量对向量求导(scalar对tensor求导),那么就可以保证上面的计算图的根节点只有一个,此时不用引入grad_tensors参数,直接调用backward函数即可
-
如果是(向量)矩阵对(向量)矩阵求导(tensor对tensor求导),实际上是先求出Jacobian矩阵中每一个元素的梯度值(每一个元素的梯度值的求解过程对应上面的计算图的求解方法),然后将这个Jacobian矩阵与grad_tensors参数对应的矩阵相乘,得到最终的结果。v中的每个值代表该位置对应输出产生的梯度的权重。因此 v 的大小与输出 y 相同
-
当y 和 x 都是向量时,雅克比点乘 v T ⋅ J v^T \cdot J vT⋅J 是矩阵乘法, v = ( v 1 , … , v m ) v = (v_1,\dots,v_m) v=(v1,…,vm) , v v v 是 backward函数的参数,与 y 的大小一致。 v v v 中的每个量代表该位置对应 y 元素产生梯度的权重
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 ** 2 + 2 * x2 + x3 y[1] = x1 + x2 ** 3 + x3 ** 2 y[2] = 2 * x1 + x2 ** 2 + x3 ** 3 v = torch.tensor([3, 2, 1], dtype=torch.float) y.backward(v) print(x1.grad) >>tensor(10.) print(x2.grad) >>tensor(34.) print(x3.grad) >>tensor(42.)
-
v ∘ J = [ 3 ∗ 2 x 1 + 2 ∗ 1 + 1 ∗ 2 , 3 ∗ 2 + 2 ∗ 3 x 2 2 + 1 ∗ 2 x 2 , 3 ∗ 1 + 2 ∗ 2 x 3 + 1 ∗ 3 x 3 2 ] = [ 10 , 34 , 42 ] v \circ J=\left[3 * 2 x_{1}+2 * 1+1 * 2,3 * 2+2 * 3 x_{2}^{2}+1 * 2 x_{2}, 3 * 1+2 * 2 x_{3}+1 * 3 x_{3}^{2}\right]=[10,34,42] v∘J=[3∗2x1+2∗1+1∗2,3∗2+2∗3x22+1∗2x2,3∗1+2∗2x3+1∗3x32]=[10,34,42]可以理解为 $v \circ J = 3 * [2x_1\,2\,1] + 2 * [1\,3x_2^2\,2x_3] + 1 * [2\,2x_2\,3x_3^2]$ - 第一项是 $y_1$ 产生的梯度,系数为 3 - 第二项是 $y_2$ 产生的梯度,系数为 2 - 第三项是 $y_3$ 产生的梯度,系数为 1
-
矩阵对矩阵,对应位置相乘(点乘,也是加权求和)v的大小与输出相同,每个值代表对应位置产生梯度的权重,然后相加
- 输出是 3 * 3 的,一共 9 个输出
- 每个输出对参数都有一个梯度矩阵,总的梯度是九个矩阵的加权
inp = torch.tensor([[1., 0, 0], [0, 1, 0], [0, 0, 1], [1, 1, 1], [1, 1, 1]]) w = torch.ones(3, 5, requires_grad=True) out = torch.matmul(w, inp) print(out) >>tensor([[3., 3., 3.], [3., 3., 3.], [3., 3., 3.]], grad_fn=<MmBackward>) x = torch.tensor([[1, 0, 0], [0, 0, 0], [0, 0, 0]]) #只考虑第一个输出产生的梯度,该梯度是一个矩阵 out.backward(x, retain_graph=True) print("First call\n", w.grad) >> tensor([[1., 0., 0., 1., 1.], [0., 0., 0., 0., 0.], [0., 0., 0., 0., 0.]]) w.grad.zero_() x = torch.ones(3, 3) out.backward(x) # 所有输出产生的梯度权重都是 1,总的梯度为 9 个矩阵之和 print("First call\n", w.grad) >> tensor([[1., 1., 1., 3., 3.], [1., 1., 1., 3., 3.], [1., 1., 1., 3., 3.]])
-
输出是标量的时候,v 相当于 tensor(1.0) ,可以省略
-
PyTorch 自动求导机制【附示例代码】
2021-05-19 18:34:37Pytorch 自动求导机制 文章目录Pytorch 自动求导机制1. 简单的自动求导(输出是1维)2. 多维数组的自动求导(输出是多维)3. 多次自动求导 自动求导是 PyTorch 中非常重要的特性,能够让我们避免手动去计算非常复杂...Pytorch 自动求导机制
自动求导是 PyTorch 中非常重要的特性,能够让我们避免手动去计算非常复杂的导数,这能够极大地减少了我们构建模型的时间。每个Tensor都有个标志:
requires_grad
,它都允许从梯度计算中精细地排除子图,并可以提高效率。requires_grad=True
要求梯度requires_grad=False
不要求梯度什么是排除子图?
答: 排除没必要的梯度计算。requires_grad
如果有一个单一的输入操作需要梯度,它的输出也需要梯度。相反,只有所有输入都不需要梯度,输出才不需要。如果其中所有的变量都不需要梯度进行,后向计算不会在子图中执行。
步骤:
- 导入Variable并指定
requires_grad=True
, y.backward()
x.grad
1. 简单的自动求导(输出是1维)
import torch from torch.autograd import Variable x = torch.Tensor([2]) # tensor 变成Variable x = Variable(x,requires_grad=True)
y=x**3
y.backward() print(x.grad)
tensor([12.])
例2: z = ( x + 2 ) 2 + 3 z = (x + 2)^2 + 3 z=(x+2)2+3, z z z对 x x x进行求导。
x = Variable(torch.Tensor([2]), requires_grad=True) y = x + 2 z = y ** 2 + 3 print(z)
tensor([19.], grad_fn=<AddBackward0>)
# 使用自动求导 z.backward() print(x.grad)
tensor([8.])
例3:更复杂的例子
x = Variable(torch.randn(5, 10), requires_grad=True) y = Variable(torch.randn(5, 5), requires_grad=True) w = Variable(torch.randn(10, 5), requires_grad=True) out = torch.mean(y - torch.mm(x, w)) # torch.matmul 是做矩阵乘法或者的使用torch.mm() print(out) out.backward()
tensor(-0.8683, grad_fn=<MeanBackward0>)
# 得到 x 的梯度 print(x.grad)
tensor([[ 0.0479, 0.0470, 0.0192, 0.1219, 0.2274, -0.1751, 0.0755, 0.0062, -0.1455, 0.0992], [ 0.0479, 0.0470, 0.0192, 0.1219, 0.2274, -0.1751, 0.0755, 0.0062, -0.1455, 0.0992], [ 0.0479, 0.0470, 0.0192, 0.1219, 0.2274, -0.1751, 0.0755, 0.0062, -0.1455, 0.0992], [ 0.0479, 0.0470, 0.0192, 0.1219, 0.2274, -0.1751, 0.0755, 0.0062, -0.1455, 0.0992], [ 0.0479, 0.0470, 0.0192, 0.1219, 0.2274, -0.1751, 0.0755, 0.0062, -0.1455, 0.0992]])
# 得到 y 的的梯度 print(y.grad)
tensor([[0.0400, 0.0400, 0.0400, 0.0400, 0.0400], [0.0400, 0.0400, 0.0400, 0.0400, 0.0400], [0.0400, 0.0400, 0.0400, 0.0400, 0.0400], [0.0400, 0.0400, 0.0400, 0.0400, 0.0400], [0.0400, 0.0400, 0.0400, 0.0400, 0.0400]])
# 得到 w 的梯度 print(w.grad)
tensor([[0.0219, 0.0219, 0.0219, 0.0219, 0.0219], [0.0160, 0.0160, 0.0160, 0.0160, 0.0160], [0.0847, 0.0847, 0.0847, 0.0847, 0.0847], [0.1473, 0.1473, 0.1473, 0.1473, 0.1473], [0.0931, 0.0931, 0.0931, 0.0931, 0.0931], [0.0924, 0.0924, 0.0924, 0.0924, 0.0924], [0.1073, 0.1073, 0.1073, 0.1073, 0.1073], [0.0393, 0.0393, 0.0393, 0.0393, 0.0393], [0.0226, 0.0226, 0.0226, 0.0226, 0.0226], [0.0419, 0.0419, 0.0419, 0.0419, 0.0419]])
2. 多维数组的自动求导(输出是多维)
例4:多维数组的自动求导机制
m = Variable(torch.FloatTensor([[2, 3]]), requires_grad=True) # 构建一个 1 x 2 的矩阵 n = Variable(torch.zeros(1, 2)) # 构建一个相同大小的 0 矩阵 print(m) print(n)
tensor([[2., 3.]], requires_grad=True) tensor([[0., 0.]])
# 通过 m 中的值计算新的 n 中的值 n[0, 0] = m[0, 0] ** 2 n[0, 1] = m[0, 1] ** 3 print(n)
tensor([[ 4., 27.]], grad_fn=<CopySlices>)
import numpy as np a =np.array([[1,2,3],[4,5,6]]) a[1,1]==a[1][1] #True # 只有二维数组或矩阵,tensor才能这样取数
True
n = ( n 0 , n 1 ) = ( m 0 2 , m 1 3 ) = ( 2 2 , 3 3 ) n = (n_0,\ n_1) = (m_0^2,\ m_1^3) = (2^2,\ 3^3) n=(n0, n1)=(m02, m13)=(22, 33),对 n n n进行反向传播,也就是 n n n对 m m m的导数。
∂ n ∂ m = ∂ ( n 0 , n 1 ) ∂ ( m 0 , m 1 ) \frac{\partial n}{\partial m} = \frac{\partial (n_0,\ n_1)}{\partial (m_0,\ m_1)} ∂m∂n=∂(m0, m1)∂(n0, n1)在 PyTorch 中,如果要调用自动求导,需要往
backward()
中传入一个参数,这个参数的形状和 n 一样大,比如是 ( w 0 , w 1 ) (w_0,\ w_1) (w0, w1),那么自动求导的结果就是:
∂ n ∂ m 0 = w 0 ∂ n 0 ∂ m 0 + w 1 ∂ n 1 ∂ m 0 \frac{\partial n}{\partial m_0} = w_0 \frac{\partial n_0}{\partial m_0} + w_1 \frac{\partial n_1}{\partial m_0} ∂m0∂n=w0∂m0∂n0+w1∂m0∂n1
∂ n ∂ m 1 = w 0 ∂ n 0 ∂ m 1 + w 1 ∂ n 1 ∂ m 1 \frac{\partial n}{\partial m_1} = w_0 \frac{\partial n_0}{\partial m_1} + w_1 \frac{\partial n_1}{\partial m_1} ∂m1∂n=w0∂m1∂n0+w1∂m1∂n1n.backward(torch.ones_like(n)) # 将 (w0, w1) 取成 (1, 1) print(m.grad)
tensor([[ 4., 27.]])
关键:为什么要
torch.ones_like(n)
?【向量求导】
n不是一个标量,是一个向量,需要传入一个维数一样的1向量,使得梯度是每个分量的求梯度的和。验证:
∂ n ∂ m 0 = w 0 ∂ n 0 ∂ m 0 + w 1 ∂ n 1 ∂ m 0 = 2 m 0 + 0 = 2 × 2 = 4 \frac{\partial n}{\partial m_0} = w_0 \frac{\partial n_0}{\partial m_0} + w_1 \frac{\partial n_1}{\partial m_0} = 2 m_0 + 0 = 2 \times 2 = 4 ∂m0∂n=w0∂m0∂n0+w1∂m0∂n1=2m0+0=2×2=4
∂ n ∂ m 1 = w 0 ∂ n 0 ∂ m 1 + w 1 ∂ n 1 ∂ m 1 = 0 + 3 m 1 2 = 3 × 3 2 = 27 \frac{\partial n}{\partial m_1} = w_0 \frac{\partial n_0}{\partial m_1} + w_1 \frac{\partial n_1}{\partial m_1} = 0 + 3 m_1^2 = 3 \times 3^2 = 27 ∂m1∂n=w0∂m1∂n0+w1∂m1∂n1=0+3m12=3×32=273. 多次自动求导
通过调用
backward
我们可以进行一次自动求导,如果我们再调用一次backward
,会发现程序报错,没有办法再做一次。这是因为 PyTorch 默认做完一次自动求导之后,计算图就被丢弃了,所以两次自动求导需要手动设置一个东西,我们通过下面的小例子来说明。x = Variable(torch.FloatTensor([3]), requires_grad=True) y = x * 2 + x ** 2 + 3 print(y)
tensor([18.], grad_fn=<AddBackward0>)
设置
retain_graph
为True
来保留计算图y.backward(retain_graph=True) print(x.grad)
tensor([8.])
y.backward() # 再做一次自动求导,这次不保留计算图 print(x.grad)
tensor([16.])
注意:这个张量的所有梯度将会自动累加到
.grad
属性.y.backward() # 第三次做自动求导,这次不保留计算图 print(x.grad)# 报错
练习
x = [ x 0 x 1 ] = [ 2 3 ] x = \left[ \begin{matrix} x_0 \ x_1 \end{matrix} \right] = \left[ \begin{matrix} 2 \ 3 \end{matrix} \right] x=[x0 x1]=[2 3]k = ( k 0 , k 1 ) = ( x 0 2 + 3 x 1 , 2 x 0 + x 1 2 ) k = (k_0,\ k_1) = (x_0^2 + 3 x_1,\ 2 x_0 + x_1^2) k=(k0, k1)=(x02+3x1, 2x0+x12)
求:
j = [ ∂ k 0 ∂ x 0 ∂ k 0 ∂ x 1 ∂ k 1 ∂ x 0 ∂ k 1 ∂ x 1 ] j = \left[ \begin{matrix} \frac{\partial k_0}{\partial x_0} & \frac{\partial k_0}{\partial x_1} \ \frac{\partial k_1}{\partial x_0} & \frac{\partial k_1}{\partial x_1} \end{matrix} \right] j=[∂x0∂k0∂x1∂k0 ∂x0∂k1∂x1∂k1]x = Variable(torch.Tensor([2,3]),requires_grad=True)
k=Variable(torch.zeros(2)) k[0]=x[0]**2+3*x[1] k[1]=2*x[0]+x[1]**2 print(k)
tensor([13., 13.], grad_fn=<CopySlices>)
j = torch.zeros(2, 2) k.backward(torch.FloatTensor([1, 0]), retain_graph=True) j[0] = x.grad.data x.grad.data.zero_() # 归零之前求得的梯度 k.backward(torch.FloatTensor([0, 1])) j[1] = x.grad.data print(j)
tensor([[4., 3.], [2., 6.]])
k.backward(torch.ones_like(k),retain_graph=True) print(x.grad)
参考:自动求导机制
- 导入Variable并指定
-
用numpy、PyTorch自动求导、torch.nn库实现两层神经网络
2021-12-31 20:25:22用numpy、PyTorch自动求导、torch.nn库实现两层神经网络,实现从手动求导到自动求导再到模型的一步步深入。 -
Pytorch自动求导
2021-06-04 22:43:42使用Pytorch自动求导 autograd 是Pytorch框架中一个重要的包,它为张量上的所有操作提供自动求导功能。其运算机制稍后给出。 首先需要理解的是Pytorch自动求导中的一个重要概念——计算图。 -
基于pytorch自动求导机制的牛顿迭代法实现
2022-01-02 23:00:20基于pytorch自动求导机制的牛顿迭代法实现框架,求解的函数可自行替换为任意函数 -
pytorch自动求导Autograd系列教程(一)
2019-04-19 15:22:56前言:构建深度学习模型的基本流程就是:...由损失函数求导的过程,称为“反向传播”,求导是件辛苦事儿,所以自动求导基本上是各种深度学习框架的基本功能和最重要的功能之一,PyTorch也不例外,后面有时间会写一下... -
pytorch自动求导机制——代码实现——(一看就懂系列!!!)
2021-03-02 21:15:18这里写自定义目录标题pytorch自动求导机制——代码实现代码实现 pytorch自动求导机制——代码实现 代码实现 import torch import torch.nn as nn import numpy as np #########————————定义模型————... -
Pytorch 自动求导、梯度下降和反向传播
2020-04-09 18:38:33人工智能AI:Keras PyTorch MXNet TensorFlow PaddlePaddle 深度学习实战(不定时更新) 梯度下降和反向传播 目标 知道什么是梯度下降 知道什么是反向传播 1. 梯度是什么? 梯度:是一个向量,导数+变化最快的... -
PyTorch 自动求导机制
2020-12-22 17:56:10通过从根到叶跟踪该图,您可以使用链式规则自动计算梯度。 在内部,autograd 将该图表示为Function对象(真正的表达式)的图,可以将其apply()编辑以计算评估图的结果。 在计算前向通过时,autograd 同时执行请求的... -
pytorch自动求导,自定义损失函数
2018-05-17 19:55:39动态建图为什么可以反向自动求导?我对于这个问题一直存在疑惑,但是去网上有没有找到好的解释,于是,没办法,自己实验吧。首先,我们做如下定义:a = torch.randn(2)b = torch.randn(2)c = torch.randn(2,requires... -
使用PyTorch自动求导机制编写多元牛顿迭代法(PyTorch向量对向量求导)
2022-02-07 21:58:15使用PyTorch自动求导机制编写多元牛顿迭代法(PyTorch向量对向量求导) -
PyTorch自动求导机制
2019-08-27 16:05:29沿着从根结点到叶结点的路径,利用链式法则就可以自动计算所有的梯度。 就内部而言, autograd 将此图表示为关于 Function 对象(实际上是表达式)的图, Function 可用于计算评估图的结果。当执行前向传播时, ... -
Pytorch 自动求导
2018-11-28 20:20:27https://blog.csdn.net/qjk19940101/article/details/79557204 ... https://github.com/Roc-J/Pytorch_projects/blob/master/Neural_Networks/linear-regr... -
Pytorch 自动求导 autograd,backward 详解
2021-09-15 17:28:53梯度 Autograd: 自动求导机制 PyTorch中,所有神经网络的核心是autograd 包。autograd 包为张量上的所有操作提供了自动求导机制。它是一个在运行时定义(define-by-run)...