张量
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=1∑n3yi2
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()