自动求导 & 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)

本文转载至自动求导 & Variable对象(torch.autograde.Variable) - 小吴的日常 - 博客园

介绍

class torch.autograd.Variable

        为什么要引入Variable?首先回答为什么引入Tensor

        仅仅使用numpy也可以实现前向反向传播,但numpy不支持GPU运算。而PytorchTensor提供多种操作运算,此外Tensor支持GPU

        问来了,两三个网络可以推公式写反向传播,当网络很复杂的时需要自动化。autograd可以帮助我们,当利用autograd时,前向传播会定义一个计算图,图中的节点就是Tensor。图中的变就是函数。当我们将Tensor塞(Variable(tensor,....))到Variable时,Variable就变成了节点。若x为一个Variable,那么x.data即为Tensorx.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.])

  • 5
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值