2 AutoGrad
2.1 autograd基础操作
-
计算梯度:
z.backward()
沿着计算图的每一条边计算梯度,直到叶子节点为止
-
查看梯度:
x.grad
∂ z ∂ x \frac{\partial z}{\partial x} ∂x∂z
注意: -
默认参数
backward(retain_graph=False)
下,计算图graph会被清空,如需保留,设置retain_graph=True
-
如果多次调用
backward()
函数(训练的时候多次循环),梯度会不断累加,所以在下一次求梯度之前,要使用net.zero_grad()
将梯度清零,或者x.grad.zero_()
也可以 -
默认
requires_grad=False
,而且tensor类型必须是float
需要求梯度:
x = torch.tensor([2,2],requires_grad=True,dtype = torch.float)
def f(a):
return sum(a**2)
x = torch.tensor([2,2],requires_grad=True,dtype = torch.float)
y = x+1
z = f(y)
print(x,y,z)
if x.grad is not None:
x.grad.zero_() # 梯度清零
z.backward()
print(x.grad) # tensor([6., 6.])
x.grad.zero_()
print(x.grad) # tensor([0., 0.])
y.backward() # 会报错,因为y不是一个标量,只能对标量进行求导
# Error: grad can be implicitly created only for scalar outputs
# 真的想要求梯度,可以在调⽤ backward 时需要传⼊⼀个和 y 同形的权重向量进⾏加权求和得到⼀个标量。
v = torch.tensor([2.,1.])
if x.grad is not None:
x.grad.zero_()
y.backward(v)
print(x.grad) # tensor([2., 1.])
2.2 自定义autograd
通过继承torch.autograd.Function
可以定义自己的函数,并实现相应的梯度计算,
需要实现forward()
和backward()
-
属性(成员变量)
saved_tensors
: 传给forward()
的参数,在backward()
中会用到。
needs_input_grad
:长度为 :attr:num_inputs
的bool
元组,表示输出是否需要梯度。可以用于优化反向过程的缓存。
num_inputs
: 传给函数:func:forward
的参数的数量。
num_outputs
: 函数 :func:forward
返回的值的数目。
requires_grad
: 布尔值,表示函数 :func:backward
是否永远不会被调用。 -
成员函数
forward()
forward
()可以有任意多个输入、任意多个输出,但是输入和输出必须是Variable。(官方给的例子中有只传入tensor作为参数的例子)
backward()
backward()
的输入和输出的个数就是forward()
函数的输出和输入的个数。其中,backward()
输入表示关于forward()
输出的梯度(计算图中上一节点的梯度),backward()
的输出表示关于forward()
的输入的梯度。在输入不需要梯度时(通过查看needs_input_grad
参数)或者不可导时,可以返回None
。
from torch.autograd import Function as Function
class Exp(Function):
@staticmethod
def forward(ctx,x):
result = x.exp()
ctx.save_for_backward(result) # ctx可以理解为用于梯度缓存
return result
@staticmethod
def backward(ctx,grad_output): # backward() 就是在定义怎么对forward()求梯度
result, = ctx.save_tensors
return grad_output * result
my_exp = Exp.apply # convert to function
x = torch.tensor([1.,2.],requires_grad = True)
y = my_exp(x)
print(y) # tensor([2.7183, 7.3891], grad_fn=<ExpBackward>)
print(x.exp())
y.sum().backward()
print(x.grad) # tensor([2.7183, 7.3891])
class my_flip(Function):
@staticmethod
def forward(ctx,x):
return x
@staticmethod
def backward(ctx,grad_output): # 求导的结果是-1,用在链式法则里面,就是整体梯度反转
return -1.0*grad_output
flip=my_flip.apply
x = torch.tensor([1.,2.],requires_grad=True)
xx = my_exp(x)
y = flip(xx)
print(y) # tensor([2.7183, 7.3891], grad_fn=<my_flipBackward>)
z = y.sum()
print(z) # tensor(10.1073, grad_fn=<SumBackward0>)
z.backward()
print(x.grad) # tensor([-2.7183, -7.3891])