栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

pytorch 梯度累加_pytorch 梯度?

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

pytorch 梯度累加_pytorch 梯度?

  本文讲解梯度的定义和求解方式,然后引入 PyTorch 中的相关函数,完成张量的梯度定义、梯度计算、梯度清空以及关闭梯度等操作。

梯度的求解

一、梯度计算

1.requires_grad2.backward3.复合函数求导 二、停止张量的梯度计算

1.x.requires_grad_(False)2.x.detach()3. with torch.no_grad() 三、梯度的清空

1.变量梯度清零x.grad.zero_()2.优化器梯度清零optimizer.zero_grad()

import torch
一、梯度计算
1.requires_grad

  张量梯度的计算 在一元函数中,某点的梯度表示的就是某点的导数。在多元函数中某点的梯度表示的是,由每个自变量所对应的偏导值所组成的向量。如 f ( x , y , z ) f(x,y,z) f(x,y,z) 的梯度向量就是:
( ∂ f / ∂ x , ∂ f / ∂ y , ∂ f / ∂ z ) (partial f/ partial x,partial f/ partial y,partial f/ partial z) (∂f/∂x,∂f/∂y,∂f/∂z)
  梯度的方向就是函数值上升最快的方向。我们一般可以使用torch.autograd.backward() 来自动计算变量的梯度,该函数会对指定的变量进行偏导的求取。为了辨别函数中哪些变量需要求偏导,哪些不需要求偏导,我们一般会在定义张量时,加上requires_grad=True,表示该变量可以求偏导。

x=torch.randn(1,requires_grad=True)
y=torch.randn(1)
z=torch.randn(1)
f1=2*x+y
f2=y+z
# 查看变量是否存在求梯度函数
print(f1.grad_fn)
print(f2.grad_fn)

输出结果如下:


None

  从结果可以看出, x x x 被定义成可以求偏导的变量,因此,它所对应的变量 f 1 f1 f1 就是可求导的(通过 torch.grad_fn 查看)。


2.backward

  接下来让我利用 f1.backward() 求取 f 1 f1 f1 的梯度(即所有变量的偏导),然后利用 x.grad 展示 ∂ f / ∂ x partial f / partial x ∂f/∂x的值。

f1.backward()
print(x.grad)  # df1/dx

输出结果如下:

tensor([2.])

3.复合函数求导

  当然除了上面简单的一元函数求偏导外,我们还可以使用上面的方法来求取复合函数的偏导:

x=torch.randn(3,requires_grad=True)#x中存了三个变量,x1 x2 x3
y=x+2
z=y*y*3
z=z.mean()
print(z)
print(z.grad_fn)
tensor(11.7241, grad_fn=)

  根据上面代码可知,我们定义了一个 z 关于变量 x 的多元复合函数,如下:
y = x + 2 y=x+2 y=x+2
z = 1 n ∑ i = 1 n 3 y i 2 z=frac {1}{n} sum_{i=1}^n3y_i^2 z=n1​i=1∑n​3yi2​
我们动手计算以下 z z z关于 x x x的偏导数,首先将 z z z进行展开:
z = 1 n ∑ i = 1 n = 1 3 ( 3 y 1 2 + 3 y 2 2 + 3 y 3 2 ) z=frac {1}{n}sum_{i=1}^n=frac {1}{3}(3y_1^2+3y_2^2+3y_3^2) z=n1​i=1∑n​=31​(3y12​+3y22​+3y32​)
特别的,我们计算 z z z关于 x 1 x_1 x1​的偏导, ∂ z ∂ x 1 = ∂ z ∂ y 1 ⋅ ∂ y 1 ∂ x 1 frac {partial z}{partial x_1}=frac {partial z}{partial y_1} cdot frac {partial y_1}{partial x_1} ∂x1​∂z​=∂y1​∂z​⋅∂x1​∂y1​​。
首先计算 ∂ z ∂ y 1 frac {partial z}{partial y_1} ∂y1​∂z​:
∂ z ∂ y 1 = 1 3 ∗ 3 ∗ 2 ∗ y 1 = 2 y 1 frac {partial z}{partial y_1}=frac {1}{3}*3*2*y_1=2y_1 ∂y1​∂z​=31​∗3∗2∗y1​=2y1​
接着计算 ∂ y 1 ∂ x 1 frac {partial y_1}{partial x_1} ∂x1​∂y1​​为:
∂ y 1 ∂ x 1 = 1 frac {partial y_1}{partial x_1}=1 ∂x1​∂y1​​=1
所以最终 ∂ z ∂ x 1 frac {partial z}{partial x_1} ∂x1​∂z​为:
∂ z ∂ x 1 = ∂ z ∂ y 1 ⋅ ∂ y 1 ∂ x 1 = 2 y 1 = 2 ( x 1 + 2 ) frac {partial z}{partial x_1}=frac {partial z}{partial y_1} cdot frac {partial y_1}{partial x_1}=2y_1=2(x_1+2) ∂x1​∂z​=∂y1​∂z​⋅∂x1​∂y1​​=2y1​=2(x1​+2)

  我们也可以使用 z.backward()求取梯度,该张量的梯度结果会被放在所对应变量的 grad 属性中。下面我们比较一下通过 z.backward()求取梯度和我们上面推导出的结果是否一样。

z.backward()
print(x.grad)  # dz/dx
print(2*(x+2)) # 比较直接计算的结果

输出结果如下:

tensor([4.7038, 3.6106, 3.4256])
tensor([4.7038, 3.6106, 3.4256], grad_fn=)

  上面结果为函数 z z z的梯度向量,即函数 z z z分别关于 x 1 , x 2 , x 3 x_1,x_2,x_3 x1​,x2​,x3​ 的偏导数。
  简单的说, torch.autograd.backward 就是使用链式法则对变量的偏导进行了求解。该函数有一个参数 grad_variables,该参数相当于给原梯度进行了一个加权。

如果使用函数 k.backward(p) 则得到的的变量 x.grad 的值为:
x ⋅ g r a d = p ⋅ k x xcdot grad=pcdot frac {k}{x} x⋅grad=p⋅xk​

x = torch.randn(3, requires_grad=True)

k = x * 2
for _ in range(10):
    k = k * 2

print(k)
print(k.shape)
p = torch.tensor([0.1, 1.0, 0.0001], dtype=torch.float32)

k.backward(p)
print(x.grad)

输出结果如下:

tensor([-2075.6987,   782.0361,   593.5969], grad_fn=)
torch.Size([3])
tensor([2.0480e+02, 2.0480e+03, 2.0480e-01])

二、停止张量的梯度计算

  如果我们不需要某些张量的梯度计算,我们就可以使用下面三种方法告诉计算机停止梯度的计算:
x.requires_grad_(False) x.detach() with torch.no_grad():


1.x.requires_grad_(False)

  就地更改现有标志:

a = torch.randn(2, 2, requires_grad=True)
b = ((a * 3) / (a - 1))
print(b.grad_fn)  # 此时可偏导,求取梯度的函数存在
a.requires_grad_(False)
b = ((a * 3) / (a - 1))
print(b.grad_fn)  # 此时不可偏导了,求取梯度的函数不存在了

输出结果如下:


None

2.x.detach()

  获取具有相同内容但不能进行梯度计算的新张量:

a = torch.randn(2, 2, requires_grad=True)
b = a.detach()
print(a.requires_grad)
print(b.requires_grad)

输出结果如下:

True
False

3. with torch.no_grad()

  作用域中定义的都是不进行梯度计算的张量。

a = torch.randn(2, 2, requires_grad=True)
print((a ** 2).requires_grad)
with torch.no_grad():  # 该作用域下定义的都是不进行梯度计算的张量
    print((a ** 2).requires_grad)

输出结果如下:

True
False

三、梯度的清空
1.变量梯度清零x.grad.zero_()

  在 PyTorch 中,如果我们利用 torch.autograd.backward求取张量的梯度时。但是,如果我们多次运行该函数,该函数会将计算得到的梯度累加起来,如下所示:

x=torch.ones(4,requires_grad=True)
y=(2*x+1).sum()
z=(2*x).sum()
y.backward()
print("第一次偏导:", x.grad)  # dy/dx
z.backward()
print("第二次偏导:", x.grad)  # dy/dx+dz/dx

输出结果如下:

第一次偏导: tensor([2., 2., 2., 2.])
第二次偏导: tensor([4., 4., 4., 4.])

  从上面的结果可以看到,如果我们对张量 y y y和 z z z分别求梯度,那么它们关于 x x x的偏导都会被放入 x ⋅ g r a d xcdot grad x⋅grad中,形成累加的局面。
  我们可以使用 x.grad.zero_() 清空梯度。

x = torch.ones(4, requires_grad=True)
y = (2*x+1).sum()
z = (2*x).sum()
y.backward()
print("第一次偏导:", x.grad)  # dy/dx
x.grad.zero_()
z.backward()
print("第二次偏导:", x.grad)  # dz/dx

输出结果如下:

第一次偏导: tensor([2., 2., 2., 2.])
第二次偏导: tensor([2., 2., 2., 2.])

  这个性质是非常重要的,特别是在后面我们将要学到的梯度下降算法之中。

  因为我们训练模型时需要循环求梯度,如果这时梯度一直叠加,那么我们求出来的结果就没有意义。因此,可以使用上面方法对张量的偏导进行清空。


2.优化器梯度清零optimizer.zero_grad()

  除了张量中存在梯度清空函数,优化器中也存在这样的函数:zero_grad()。

optimizer = torch.optim.SGD([x], lr=0.1)
optimizer.step()
optimizer.zero_grad()
optimizer

输出结果如下:

SGD (
Parameter Group 0
    dampening: 0
    lr: 0.1
    momentum: 0
    nesterov: False
    weight_decay: 0
)

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/786686.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号