pytorch梯度计算

张量

pytorch定义张量:

import torch

# 利用 torch.empty() 初始化指定大小的张量,如果不指定值的话,内容为随机值
# 传入的参数为想创建的张量大小
x = torch.empty(1)  # scalar,大小为 1*1 的张量
print(x.size())
x = torch.empty(3)  # vector, 1D,大小为 1*3 的张量
print(x.size())
x = torch.empty(2, 3)  # matrix, 2D,大小为 2*3 的张量
print(x.size())
x = torch.empty(2, 2, 3)  # tensor, 3D,大小为 2*2*3 的张量
print(x.size())

# 输出结果
# torch.Size([1])
# torch.Size([3])
# torch.Size([2, 3])
# torch.Size([2, 2, 3])
  • torch.rand(size):随机初始化值在 0-1 之间的张量
  • torch.zeros(size): 初始化全为 1 的张量
  • torch.ones(size):初始化全为0的张量
  • torch.view(size) :重塑,意思就是将原张量的形状进行变换,即元素总个数不变的情况下改变行数和列数,使用类似于 numpy.reshape

当 x 的大小为 12 × 23 × 32 12\times23\times32 12×23×32 ,而我们想把 x 转为 2 × m 2\times m 2×m 的大小时,我们就必须手动算出 12 × 23 × 32 12\times23\times32 12×23×32 的值,然后除以 2,进而得到 m 的值。为了避免这种情况,我们可以将 m 所在位置赋为 -1。计算机看到 -1 时,会自动使用 12 × 23 × 32 ÷ 2 12\times23\times32 ÷ 2 12×23×32÷2 的值来替换 -1:

x = torch.randn(12, 23, 32)
y = x.view(2, -1)  # 将需要计算的那个维度直接用 -1 表示 12*23*32/2 的值
x.size(), y.size()

# 输出结果
# (torch.Size([12, 23, 32]), torch.Size([2, 4416]))

梯度

我们一般可以使用 torch.autograd.backward() 来自动计算变量的梯度,该函数会对指定的变量进行偏导的求取。为了辨别函数中哪些变量需要求偏导,哪些不需要求偏导,我们一般会在定义张量时,加上 requires_grad=True,表示该变量可以求偏导。

梯度计算

以下述方程为例子求导:
z = 1 n ∑ i = 1 n 3 y i 2 z = \frac{1}{n}\sum_{i=1}^n 3y_i^2 z=n1i=1n3yi2

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(21.5671, grad_fn=<MeanBackward0>)
# <MeanBackward0 object at 0x7f8af040f2d0>

我们也可以使用 z.backward() 求取梯度,该张量的梯度结果会被放在所对应变量的 grad 属性中:

z.backward()
print(x.grad)  # dz/dx

print(2*(x+2)) # 比较直接计算的结果

# 输出结果
# tensor([1.9913, 6.1642, 5.0839])
# tensor([1.9913, 6.1642, 5.0839], grad_fn=<MulBackward0>)

简单的说, torch.autograd.backward 就是使用链式法则对变量的偏导进行了求解。该函数有一个参数 grad_variables,该参数相当于给原梯度进行了一个加权。

停止梯度

如果我们不需要某些张量的梯度计算,我们就可以使用下面三种方法告诉计算机停止梯度的计算:

  • 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)  # 此时不可偏导了,求取梯度的函数不存在了
  • x.detach()
a = torch.randn(2, 2, requires_grad=True)
b = a.detach()
print(a.requires_grad)
print(b.requires_grad)
  • 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)

梯度清空

多次运行该函数,该函数会将计算得到的梯度累加起来:

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.])

我们可以使用 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.])

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

optimizer = torch.optim.SGD([x], lr=0.1)
optimizer.step()
optimizer.zero_grad()
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

凡心curry

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

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

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

打赏作者

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

抵扣说明:

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

余额充值