引言
pytorch所有内置的函数都提供了自动求导功能,所以大部分的时候,我们只需要写出前向传播过程,然后调用tensor.backward()方法,就可以求出所有varible的grad。
例子一:自带函数的自动求导
# coding:utf-8
import torch
from torch.autograd import Variable
x = Variable(torch.Tensor([2.0]), requires_grad=True)
a = torch.tensor([2.0]) # 不需要梯度
a = Variable(torch.Tensor([2.0]), requires_grad=True) # a也需要梯度
z = (x + a) ** 2 + 3
print(z)
print(u'求导之前x的梯度:', x.grad)
z.backward()
print(u'求导之后x的梯度:', x.grad)
print(a.grad)
tensor([ 19.])
求导之前x的梯度: None
求导之后x的梯度: tensor([ 8.])
tensor([ 8.])
解释:
- 需要梯度的量必需声明为Variable,而且requires_grad属性等于True
- 使用z.backward()的时候,z中至少有一个Variable变量,这句会求出所有需要梯度的Variable的梯度,但是整个流程用idle是没法跟踪的,在Variable._execution_engine.run_backward( tensors, grad_tensors, retain_graph, create_graph, allow_unreachable=True) # allow_unreachable flag就断了。
- 这里的前向运算是 (x + a) ** 2 + 3,并没用torch.add, torch.pow, 我想应该是重构的结果
例子二:手动实现求导
采用numpy手动实现(x+2) ** 2 + 3 对x的求导
具体代码
# coding:utf-8
import torch
from torch.autograd import Function,Variable
import numpy as np
class BadFFTFunction(Function):
def forward(self, input):
numpy_input = input.detach().numpy()
# ctx.save_for_backward(numpy_input) # ctx.save_for_backward只能存Variable
self.input = numpy_input
result = np.power(np.add(numpy_input, 2), 2) + 3 # (x+2) ** 2 + 3
return input.new(result) # 和input一样类型的tensor
def backward(self, grad_output): # 用numpy实现手动求导
input = self.input
result = np.dot(np.add(input, 2), 2)
return grad_output.new(result) * grad_output
# since this layer does not have any parameters, we can
# simply declare this as a function, rather than as an nn.Module class
def incorrect_fft(input):
return BadFFTFunction()(input)
x = Variable(torch.Tensor([2.0]), requires_grad=True)
result = incorrect_fft(x)
result.backward(torch.ones(result.size()) * 0.01)
print(x.grad)
解释:
- ctx.save_for_backward只能存Variable,所以这里用self.input来存numpy
- 自己写的操作要继承torch.autograd.Function,然后重写forwark和backward
- result.backward(torch.ones(result.size()) * 0.01)反向传播的时候会将Variable的grid放缩一定的倍数