ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Pytorch随笔--Tensor与Variable

2021-09-22 21:30:38  阅读:177  来源: 互联网

标签:loss tensor -- torch pytorch Pytorch Variable data


前言

该文章是我回过头来看之前的pytorch笔记的一些记录,以自己的思路将一些相关的内容集合起来进行记录,算是对之前阶段学习的总结。

问题描述

刚接触pytorch的时候,一个很常见的操作就是参考别人的代码或者观看别人的教学视频。但随着pytorch的更新,一些特性便会发生变化,这就很明显地会体现在代码上,比如之前的一些代码和现在的代码将会变得不一样,尽管它们实现的功能基本是一样的。尤其是对于训练模型这一部分来说,基本的流程会很相似,但几年前的代码和当前的代码却会有很多地方不同,这对于初学者来说会是一个令人疑惑的点,至少对我来说有过这个困扰。所以下面就是记录一些经过pytorch的更新,而导致不同时间段内的代码不一样的情况,同时也对比分析指出目前应该使用的形式。

Tensor和Variable

Tensor是pytorch中非常重要且常见的数据结构,相较于numpy数组,Tensor能加载到GPU中,从而有效地利用GPU进行加速计算。但是普通的Tensor对于构建神经网络还远远不够,我们需要能够构建计算图的 tensor,这就是 Variable。Variable 是对 tensor 的封装,操作和 tensor 是一样的,但是每个 Variabel都有三个属性,分别是data表示变量中的具体值, grad表示这个变量反向传播的梯度, grad_fn表示是通过什么操作得到这个变量,例如( 加减乘除、卷积、反置卷积)。所以在构建计算图时,需要将tensor封装成Variable变量,这样才能在后续阶段进行反向传播计算梯度。很常见的代码示例如下:

import torch
from torch.autograd import Variable

x = Variable(torch.randn(10, 20), requires_grad=True)
y = Variable(torch.randn(10, 5), requires_grad=True)
w = Variable(torch.randn(20, 5), requires_grad=True)

out = torch.mean(y - torch.matmul(x, w))
out.backward()

注意,以上是pytorch 0.4之前的代码风格。但是在pytorch 0.4版本后,torch.Tensor 和torch.autograd.Variable变成同一个类了。torch.Tensor 能够像之前的Variable一样追踪历史和反向传播,也即不用使用torch.autograd.Variable来封装tensor以使其能够进行反向传播。但是Variable仍能够正常工作,只是返回的依旧是Tensor。目前常用的代码形式如下所示:

import torch

x =torch.randn((10, 20), requires_grad=True)
y = torch.randn((10, 5), requires_grad=True)
w = torch.randn((20, 5), requires_grad=True)

out = torch.mean(y - torch.matmul(x, w))
out.backward()

也就是说使用Variable封装tensor是老版本的使用方式了,虽然不会报错,但属实是可以但没必要。

tensor.data和tensor.detach

在使用pytorch训练网络的过程中,遇到要将GPU上的tensor变量转换成CPU上的numpy数组这种情况是很常见的,比较常见的作法就是使用a.data.cpu().numpy()(具体细节可以参考这里,这不是本文重点),但是这就牵扯出一个安全性问题,使用.data真的安全吗?

首先,我们需要先知道这三个方法的意义:

  • tensor .data 返回和 x 的相同数据 tensor,而且这个新的tensor和原来的tensor是共用数据的,一者改变,另一者也会跟着改变,而且新分离得到的tensor的require s_grad = False, 即不可求导的。
  • tensor .detach() 返回和 x 的相同数据 tensor,而且这个新的tensor和原来的tensor是共用数据的,一者改变,另一者也会跟着改变,而且新分离得到的tensor的require s_grad = False, 即不可求导的。(这一点其实和 tensor.data 是一样的)

由上面的描述会感觉这两者很相似,也没什么不同。但其实这两者会有一个不同的机制,即当y=x.data属性来访问数据时,pytorch不会记录数据是否改变,此时改变了y的值,意味着也要改变x的值,而在自动求导时会使用更改后的值,这会导致错误求导结果。而使用y=x.detach()时,如果了y值,也意味着改变了x值,此时调用x.backword()会报错,这也从侧面表明.detach()方法会记录数据的变化状态。也就是说tensor.data和tensor.detach都是复制计算图中的一份数据,此数据与原数据共享内存,当复制下来的数据因为某些操作变化时,就会影响到计算图梯度的计算,此时tensor.detach会记录下数据的变化状态,当数据有变化时会直接报错已防止计算图梯度被影响,而tensor.data则存在这个隐患,所以使用tensor.data是不安全的,会比较推荐tensor.detach操作。

具体代码示例如下,使用tensor.data分离计算图时,如果复制的值变化了,则梯度的计算会出错,但程序并不会知晓,依旧将其视为正确结果。而使用tensor.detach时,复制的值发生变化就会报错,这样就能避免这种难以发现的错误。

a = torch.tensor([1,2,3.], requires_grad = True)
out = a.sigmoid()

c = out.data
c.zero_()
out.sum().backward()
a.grad  # The result is very, very wrong because `out` changed!
# out: tensor([ 0.,  0.,  0.])

c = out.detach()
c.zero_()
out.sum().backward()  # error
# out: RuntimeError: RuntimeError: one of the variables needed for gradient computation has been modified by an inplace operation

loss.data[0]和loss.item()

在pytorch 0.4之前,广泛的loss使用是:total_loss += loss.data[0], 这是因为loss是variable,其中size为(1,),而在0.4之后,loss是一个0维的向量(标量)。此时就可以可以使用loss.item()从标量中获得Python number,也即获取的是python的基本数据类型。

注意,当不转换为Python number来计算loss的累加值时,程序将会有大量的内存使用,这是因为total_loss += loss.data[0]式子的右边是0维tensor。这样,总损失就会包含loss和梯度历史,这些梯度历史会在大的autograd graph中保存更长时间,带来大量的内存消耗。换句话说,pytorch使用Variable计算的时候,会记录下新产生的Variable的运算符号,在反向传播求导的时候进行使用。如果这里使用loss.data[0]相加,系统会认为这也是计算图的一部分,也就是说网络会一直延伸变大,那么消耗的显存也就越来越多。而使用loss.item()则不会遇到这种问题,所以推荐使用loss.item()来获取每个step的loss。(注意,loss.item()只能针对只有一个元素的tensor使用,如何结果是一个tensor列表,可以使用loss.tolist(),这样获取的依然是Python number)

上述loss积累的式子可以变为:
total_loss += loss.item()

总结

经过上面的讨论,可以知道经过pytorch的不断更新,代码的形式相应的也会有一些变化,总结来看,可以归纳为三点:

  • 不再需要将tensor包装进Variable中,torch.Tensor 和torch.autograd.Variable是同一个类了
  • 推荐使用tensor.detach(),因为其比tensor.data更安全
  • 推荐使用loss.item()获取loss,应为使用loss.data[0]会有显存消耗越来越大的隐患

参考链接

pytorch入门一(Tensor、Variable)_朴素.无恙的博客-CSDN博客

pytorch入门之第一章Variable的理解_YYLin-CSDN博客_pytorch variable

pytorch中的.detach和.data深入详解_MIss-Y的博客-CSDN博客

pytorch学习:loss为什么要加item()_github_38148039的博客-CSDN博客

标签:loss,tensor,--,torch,pytorch,Pytorch,Variable,data
来源: https://blog.csdn.net/qq_61888524/article/details/120421906

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有