def to_var(self, x, volatile=False):
#torch.Size([1024, 118])
if torch.cuda.is_available():
x = x.cuda()
return Variable(x, volatile=volatile)
本文转载至自动求导 & Variable对象(torch.autograde.Variable) - 小吴的日常 - 博客园
介绍
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.]])
汇总:
x = Variable(torch.ones(2, 2), requires_grad=True)
print(x) # 1
print("---------")
print(x.data) # 1
y = x.sum()
print(y) # tensor(4.)
y.backward() # 反向传播
x.grad # Variable的梯度保存在Variable.grad中
print(x.grad) # 1
y.backward()
x.grad # 可以看到变量梯度是累加的
print(x.grad) # 2
x.grad.data.zero_()
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.])