PyTorch学习笔记4--PyTorch自动微分
PyTorch所有神经网络的核心是autograd自动微分。该 autograd 软件包为 Tensors 上的所有操作提供自动微分。
计算图 = Tensor + Function
PyTorch也是以计算图
为核心进行微分的。这与TensorFlow中是一致的。在计算图中,圆圈/矩形等表示Tensor
,而线条表示计算操作
(TensorFlow里是Ops,PyTorch里是Function,本质相同)。在PyTorch里,Tensor
和Function
互相连接并生成一个非循环图
,它表示和存储了完整的计算历史。 每个张量都有一个.grad_fn
属性,这个属性引用了一个创建该Tensor的Function(除非这个张量是用户手动创建的,即,这个张量的 grad_fn 是 None)。
- 要追踪所有对于某张量的操作,设置其
.requires_grad
为True
即可。 - 要计算对某张量的梯度,得到某Tensor后对其调用 .backward(),这个张量的所有梯度将会自动积累到 .grad 属性。
- 要阻止张量跟踪历史记录,可以调用.detach()方法将其与计算历史记录分离,并禁止跟踪它将来的计算记录。
为了防止跟踪历史记录(和使用内存),可以将代码块包装在with torch.no_grad():中。 在评估模型时特别有用,因为模型可能具有requires_grad = True的可训练参数,但是我们不需要梯度计算。
import torch
#--------------------------------------------
x = torch.ones(2, 2, requires_grad=True)
print(x)
# 新建全1矩阵,跟踪计算记录
# 打印结果为
tensor([[1., 1.],
[1., 1.]], requires_grad=True)
#--------------------------------------------
y = x + 2
print(y)
print(y.grad_fn)
# 对张量x进行了操作,得到y,脑袋里思考这里形成了一个什么样的计算图
# 打印结果为(grad_fn被自动生成了,显示了构建此Tensor的Function是加法)
tensor([[3., 3.],
[3., 3.]], grad_fn=<AddBackward>)
<AddBackward object at 0x00000232535FD860>
#--------------------------------------------
z = y * y * 3
out = z.mean()
print(z, out)
# 打印结果为
tensor([[27., 27.],
[27., 27.]], grad_fn=<MulBackward>) tensor(27., grad_fn=<MeanBackward1>)
#--------------------------------------------
a = torch.randn(2, 2)
a = ((a * 3) / (a - 1))
print(a.requires_grad)
a.requires_grad_(True)
print(a.requires_grad)
b = (a * a).sum()
print(b.grad_fn)
# .requires_grad_( ... ) 可以改变现有张量的 requires_grad属性
# 运行结果为
False
True
<SumBackward0 object at 0x000002325360B438>
以上程序示范了如何使用requires_grad
跟踪Tensor的生成过程,也就是计算图(模型)的生成过程:其中张量x是2x2的全1矩阵,张量y是2x2的全3矩阵,张量z是2x2的全27矩阵,张量out是标量,z的平均值。
因为\(out=\frac{1}{4} \sum_{i}z_{i}\),而\(z_{i} = 3(x_{i} + 2)^{2}\),因此\(\frac{\partial out}{\partial x_{i}} = \frac{3}{2}(x_{i}+2)\),所以\(\frac{\partial out}{\partial x_{i}}=4.5\)
# ...
# 当计算图构建完成,可以开始计算梯度了,下面一行代码自动完成梯度的计算:
out.backward() # out是标量,因此不需要为backward()函数指定参数,相当于out.backward(torch.tensor(1))
print(x.grad) # backward自动计算该Tensor的梯度,x.grad即为到x这里的梯度,指定了自变量为x而非y和z。
# 计算结果为
tensor([[4.5000, 4.5000],
[4.5000, 4.5000]])
以上代码讲了y为标量情况下的求导方法。以下代码介绍y为矢量情况下的求导方法。
x = torch.randn(3, requires_grad=True) #创建一个x, size为(1,3),初始化为正态随机数。
y = x * 2
while y.data.norm() < 1000: #求得 y^(2^(n))>=1000的最小值
y = y * 2
print(y)
# 打印结果为
tensor([-920.6895, -115.7301, -867.6995], grad_fn=<MulBackward>)
gradients = torch.tensor([0.1, 1.0, 0.0001], dtype=torch.float)
y.backward(gradients) #当y为矢量,需要对backward()函数指定参数,该参数指定了求导后的乘因子
print(x.grad)
# 打印结果为
tensor([ 51.2000, 512.0000, 0.0512])
# 如果gradients因子均为1,则结果应为tensor([ 512.0000, 512.0000, 512.0000])
如果.requires_grad=True
但是你又不希望进行autograd
的计算, 那么可以将变量包裹在 with torch.no_grad()
中:
print(x.requires_grad)
print((x ** 2).requires_grad)
with torch.no_grad():
print((x ** 2).requires_grad)
# 打印结果为
True
True
False