PyTorch系列4 --- 自动求导

参考:

##我们将介绍PyTorch中的自动求导机制,自动求导是PyTorch中非常重要的特性,能够让我们避免手动去计算非常复杂的导数,
# 这能够极大地减少我们构建模型的时间,,这也是其前身Torch这个框架不具备的特性
import torch
from torch.autograd import Variable
##简单情况下的自动求导。“简单”体现在计算的结果都是标量,也就是一个数,我们对这个标量进行自动求导。
x = Variable(torch.Tensor([2]),requires_grad = True)
print("x的值:",x.data)
y = x + 2
z = y**2 + 3
print("z = y**2 + 3的值:",z)
#使用自动求导
z.backward()
print("x.grad:",x.grad) #表示z 对x 求导的结果,其中z = (x+2)^2 + 3  所以 z' = 2(x + 2)

运行结果:
在这里插入图片描述

##在看一个复杂点的例子
x = Variable(torch.randn(10,20),requires_grad = True)
y = Variable(torch.randn(10,5),requires_grad = True)
w = Variable(torch.randn(20,5),requires_grad = True)
out = torch.mean(y - torch.matmul(x,w)) # torch.matmul 是做矩阵乘法
out.backward()
#得到 x 的梯度
print("x.grad:",x.grad)
#得到y的梯度
print("y.grad:",y.grad)
#得到 w 的梯度
print("w.grad:",w.grad)
#上面的数学公式就更加复杂,,矩阵乘法之后对两个矩阵对应元素相乘,然后所有元素求平均。。使用PyTorch能够非常容易的得到x, y 和 w 的导数,
# 因为深度学习中充满大量的矩阵运算,所以我们没有办法手动去求这些导数,,有了自动求导可以非常方便的解决网络更新的问题

运行结果:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#复杂情况的自动求导
##对多维数组自动求导机制
m = Variable(torch.FloatTensor([[2,3]]),requires_grad = True) #构建一个1 X 2 的矩阵
n = Variable(torch.zeros(1,2)) #构建一个相同大小的 0 矩阵
print("m:",m)
print("n:",n)
# 通过 m 中的值计算新的 n 中的值
n[0,0] = m[0,0]**2
n[0,1] = m[0,1]**3
print("通过 m 中的值计算新的 n 中的值后n 的值:",n)
# 将上面的式子写成数学公式,可以得到:n = (n0,n1) = (m0^2, m1^3) = (2^2, 3^3)
#下面我们直接对n进行反向求导,,也就是n 对 m 求导
#在PyTorch中,如果要调用自动求导,需要往backward()中传入一个参数,这个参数的形状和n 一样大,比如是(w0, w1),,其实也就是对不同变量分别求导,然后拼成一个向量
n.backward(torch.ones_like(n)) #将(w0, w1) 取成(1,1)
print("m.grad:",m.grad)

运行结果:
在这里插入图片描述

#多次自动求导
#通过调用backward我们可以进行一次自动求导,如果我们再调用一次backward,会发现程序报错,没有办法再做一次。
# 这是因为P有Torch默认做完一次自动求导之后,计算图就被丢弃了,所以两次自动求导需要手动设置一个东西,例子如下:
x = Variable(torch.FloatTensor([3]),requires_grad = True)
print("x:",x)
y = x * 2 + x ** 2 +3
print("y = x * 2 + x ** 2 +3:",y)
#设置 retain_graph 为True 来保留计算图
y.backward(retain_graph = True)
print("第一次自动求导x.grad:",x.grad) #第一次自动求导x.grad: tensor([8.])
y.backward() #再做一次自动求导,这次不保留计算图
print("第二次自动求导x.grad:",x.grad) #第二次自动求导x.grad: tensor([16.])
#可以发现x 的梯度变成了16,因为这里做了两次自动求导,,所以第一次的梯度8 和 第二次的梯度8 加起来得到了16的结果

运行结果:
在这里插入图片描述

#小练习
x = Variable(torch.FloatTensor([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:",k)

j = torch.zeros(2,2)
k.backward(torch.FloatTensor([1,0]),retain_graph = True)
j[0] = x.grad.data
print("j[0]:",j[0])
x.grad.data.zero_() #归零之前求得的梯度
k.backward(torch.FloatTensor([0,1]))
j[1] = x.grad.data
print("j[1]:",j[1])
print("j:",j)

运行结果:
在这里插入图片描述


更多精彩内容