自动求导 & Variable对象(torch.autograde.Variable)
前言:
def to_var(self, x, volatile=False): #torch.Size([1024, 118]) if torch.cuda.is_available(): x = x.cuda() return Variable(x, volatile=volatile)
class torch.autograd.Variable:为什么要引入Variable?首先回答为什么引入Tensor。仅仅使用numpy也可以实现前向反向传播,但numpy不支持GPU运算。而Pytorch的Tensor提供多种操作运算,此外Tensor支持GPU。问来了,两三个网络可以推公式写反向传播,当网络很复杂的时需要自动化。autograd可以帮助我们,当利用autograd时,前向传播会定义一个计算图,图中的节点就是Tensor。图中的变就是函数。当我们将Tensor塞(Variable(tensor,....))到Variable时,Variable就变成了节点。若x为一个Variable,那么x.data即为Tensor,x.grad也为一个Variable。那么x.grad.data就为梯度的值。总结:Pytorch Variable与Pytorch Tensor有着相同的API,Tensor上的所有操作几乎都可用在Variable上。两者不同之处在于利用Variable定义一个计算图,可以实现自动求导。
重要属性如下:
requires_grad:
指定要不要更新这个参数(通过梯度(迭代)来更新),对于不需要更新的参数,可以把它设定为False,可以加快运算。Variable默认是不需要求导的,即requires_grad属性默认为False,如果某一个节点requires_grad被设为True,那么所有依赖它的节点requires_grad都为True。
用户在手动定义Variable时,参数requires默认值是False。而在Module中的层在定义时,相关的Variable的requires_grad参数默认是True。在计算图中,如果有一个输入的requires_grad是True,那么输出的requires_grad也是True。只有在所有输入的requires_grad都为False时,输出的requires_grad才为False。
Valatile:
指定需不需要保留记录用的参数。指定参数为True代表不需要记录(即还要接着更新),可以加快运算。如果有一个参数的volatile是True,则它的requires_grad一定是False。简单来说,对于需要更新的Variable记得将requires_grad设成True,当只需要得到结果而不需要更新的Variable可以将volatile设成True加快运算速度。
Variable的volatile属性默认为False,如果某一个Variable的volatile属性被设为True,那么所有依赖它的节点的volatile属性都为True。volatile属性为True的节点不会求导,volatile的优先级比requires_grad的高。
当有一个输入的volatile=True时,那么输出的volatile=True。volatile=True推荐在模型推理过程(测试)中使用,这时只需要令输入的volatile=True,保证用最小的内存来执行推理,不会保存任何中间状态。在使用volatile=True的时候,变量是不存储creator属性的,这样减少内存的使用。
torch.autograde.Variable是Autograde的核心类,它封装了Tensor,并整合了反向传播相关实现
Variable包含三个属性:
data:存储了Tensor,是本体数据
grad:保存了data的梯度,是个Variable而非Tensor,与data形状一致
grad_fn:指向Function对象,用于反向传播的梯度计算之用
1、data
import torch from torch.autograd import Variable x = Variable(torch.ones(2, 2), requires_grad=True) print(x) print("---------") print(x.data) 输出: tensor([[ 1., 1.], [ 1., 1.]]) --------- tensor([[ 1., 1.], [ 1., 1.]])
2、梯度求解
构建一个简单的方程:y=x[0,0] + x[0,1]+ x[1,0]+ x[1,1],variable的运算结果也是Variable,但是,中间结果(Variable类型)反向传播中不会被求导。
这和Tensorflow不太一致,Tensorflow中中间运算结果的数据结构是Tensor,
y=x.sum() print(y)#tensor(4.)
可以查看目标函数的.grad_fn方法,它用来求梯度,
y.grad_fn # grad_fn:指向Function对象,用于反向传播的梯度计算之用 print(y.grad_fn) # <SumBackward0 object at 0x00000000021CB908> y.backward() # 反向传播 x.grad # Variable的梯度保存在Variable.grad中 print(x.grad) #tensor([[ 1., 1.], [ 1., 1.]])
grad属性保存在Variable中,新的梯度下来会进行累加,可以看到再次求导结果会变成了2
y.backward() x.grad # 可以看到变量梯度是累加的 print(x.grad) #tensor([[ 2., 2.], [ 2., 2.]])
所以要有归0的操作:
print(x.grad.data.zero_()) #tensor([[ 0., 0.], [ 0., 0.]])
汇总:
1 x = Variable(torch.ones(2, 2), requires_grad=True) 2 print(x) # 1 3 print("---------") 4 print(x.data) # 1 5 y = x.sum() 6 print(y) # tensor(4.) 7 8 y.backward() # 反向传播 9 x.grad # Variable的梯度保存在Variable.grad中 10 print(x.grad) # 1 11 12 y.backward() 13 x.grad # 可以看到变量梯度是累加的 14 print(x.grad) # 2 15 16 x.grad.data.zero_() 17 print(x.grad) # 0
对比Variable和Tensor的接口,相差无两:
import torch from torch.autograd import Variable # Variable和Tensor的接口近乎一致,可以无缝切换 x = Variable(torch.ones(2, 2)) y = torch.cos(x) # 传入Variable x_tensor_cos = torch.cos(x.data) # 传入Tensor print(y) print(x_tensor_cos) #tensor([[ 0.5403, 0.5403], [ 0.5403, 0.5403]]) tensor([[ 0.5403, 0.5403], [ 0.5403, 0.5403]])
3、自动求导
torch.qutograd包提供Tensor所有操作的自动求导。
3.1、数据结构介绍
autograd.Variable是这个包最核心的类。它包装一个Tensor,并且几乎支持所有的定义在其上的操作。一旦完成你的运算,你可以调用.backward()来自动计算出所有的梯度,Variable有三个属性:
访问原始的tensor使用属性.data;
关于这一Variable的梯度则集中于.grad;
.creator反映了创建者,标识了是否由用户使用.Variable直接创建(None)。
'''求导数''' x = Variable(torch.ones(2, 2), requires_grad=True) y = x + 2 # AttributeError: 'Tensor' object has no attribute 'creator' # print(x.creator) #None,用户直接创建没有creater属性 print(y.creator) # <torch.autograd._functions.basic_ops.AddConstant object at 0x7fb9b4d4b208>
3.2、求导运算
如果你想要进行求导计算,你可以在Variable上调用.backward()。
- 如果Variable是一个标量(例如它包含一个单元素数据),你无需对backward指定任何参数
'''求导数''' x = Variable(torch.ones(2, 2), requires_grad=True) y = x + 2 z = y*y*3 o = z.mean() o.backward() print(x) print(y) print(z) print(x.grad) # 输出对o对x求倒结果 print(y.grad) # y不是自动求导变量requires_grad没设为True #tensor([[ 1., 1.], [ 1., 1.]]) tensor([[ 3., 3.], [ 3., 3.]]) tensor([[ 27., 27.], [ 27., 27.]]) tensor([[ 4.5000, 4.5000], [ 4.5000, 4.5000]]) None Process finished with exit code 0
- 如果它有更多的元素(矢量),你需要指定一个和tensor的形状匹配的grad_output参数(y在指定方向投影对x的导数)
'''求导数''' x = torch.randn(3) x = Variable(x, requires_grad=True) y = x * 2 gradients = torch.FloatTensor([0.5, 0.5, 1]) y.backward(gradients) # 沿着某方向的梯度 print(x.grad) #tensor([ 1., 1., 2.])
参考:
参考3:Pytorch细节记录
参考4: