【问题标题】:What is the difference between detach, clone and deepcopy in Pytorch tensors in detail?Pytorch 张量中的分离、克隆和深拷贝有什么区别?
【发布时间】:2020-10-07 18:35:04
【问题描述】:

在使用 Pytorch 时,我一直在努力理解 .clone().detach()copy.deepcopy 之间的区别。特别是 Pytorch 张量。

我尝试写下所有关于它们的差异和用例的问题,但很快就不知所措,并意识到也许拥有 Pytorch 张量的 4 个主要属性会更好地阐明使用哪个来解决每个小问题。我意识到需要跟踪的 4 个主要属性是:

  1. 如果有一个张量的新指针/引用
  2. 如果有一个新的张量对象实例(因此这个新实例很可能有它自己的元数据,如require_grads、形状、is_leaf 等)
  3. 如果它为张量数据分配了新内存(即,如果这个新张量是不同张量的视图)
  4. 它是否正在跟踪操作历史记录(或者即使它正在跟踪一个全新的操作历史记录,或者在深拷贝的情况下是相同的旧历史记录)

根据从 Pytorch 论坛和文档中挖掘出来的内容,这是我目前在张量上使用时的区别:

克隆

对于克隆:

x_cloned = x.clone()

我相信这就是它根据主要 4 个属性的行为方式:

  1. 克隆的x_cloned 有它自己的python 引用/指向新对象的指针
  2. 它创建了自己的新张量对象实例(带有单独的元数据)
  3. 它已为x_new 分配了一个新内存,其数据与x 相同
  4. 它正在跟踪原始操作历史,此外还包括此clone 操作.grad_fn=<CloneBackward>

据我所知,它的主要用途似乎是创建事物的副本,以便inplace_ 操作是安全的。此外,再加上 .detach.detach().clone() (顺便说一句“更好”的顺序),它创建了一个全新的张量,该张量已与旧历史分离,从而阻止梯度流过该路径。

分离

x_detached = x.detach()
  1. 创建一个新的 python 引用(当然,唯一没有这样做的是x_new = x)。我相信这个可以使用id
  2. 它创建了自己的新张量对象实例(带有单独的元数据)
  3. 没有x_detached分配了一个与x相同的数据的新内存
  4. 它切断了梯度的历史,不允许它流过它。我认为将其视为没有历史的全新张量是正确的。

我相信我所知道的唯一明智的用途是在将.clone().detach().clone() 结合使用时,使用自己的内存创建新副本。否则,我不确定它有什么用。由于它指向原始数据,因此执行就地操作可能存在潜在危险(因为它会更改旧数据,但旧数据的更改在较早的计算图中为 autograd 所知)。

copy.deepcopy

x_deepcopy = copy.deepcopy(x)
  1. 如果有一个张量的新指针/引用
  2. 它使用自己的元数据创建一个新的张量实例(所有元数据都应指向深层副本,因此如果按照我希望的那样实现新对象)。
  3. 它为张量数据分配了自己的内存
  4. 如果它真的是一个深拷贝,我会期待一个深拷贝的历史。所以它应该对历史进行深度复制。虽然这看起来确实很昂贵,但至少在语义上与深拷贝应该是一致的。

我真的没有看到这方面的用例。我认为任何试图使用它的人真的意味着 1) .detach().clone() 或只是 2) .clone() 本身,这取决于一个人是想用 1 停止梯度流到较早的图形,还是只想用新的图形复制数据记忆 2)。

因此,这是我目前了解差异的最佳方式,而不是询问可能使用它们的所有不同场景。

所以这是对的吗?有没有人发现任何需要纠正的重大缺陷?

我自己担心的是我赋予深度复制的语义,想知道深度复制历史是否正确。

我认为每个用例的常见用例列表会很棒。


资源

这些是我阅读并参与得出此问题结论的所有资源:

【问题讨论】:

标签: python machine-learning pytorch


【解决方案1】:

注意:自发布此问题以来,这些功能的行为和文档页面已更新。


torch.clone()

复制张量,同时在 autograd 图中保持链接。如果你想使用,例如将张量复制为神经网络中的操作(例如,将中级表示传递给两个不同的头以计算不同的损失):

返回输入的副本。

注意:这个函数是可微的,所以梯度会从这个运算的结果流回输入。要创建一个没有 autograd 关系的张量来输入,请参阅detach()

torch.tensor.detach()

返回没有 autograd 历史的原始张量视图。如果您想在不影响计算图的情况下操纵张量的值(未到位)(例如,在正向传递的中途报告值),则使用此方法。

返回一个新的张量,与当前图表分离。

结果永远不需要渐变。

此方法也会影响正向模式 AD 梯度,结果将永远不会有正向模式 AD 梯度。

注意:返回的张量与原始张量共享相同的存储空间。将看到对其中任何一个的就地修改,并可能触发正确性检查中的错误。 1

copy.deepcopy

deepcopy 是来自copy 库的通用 python 函数,它复制现有对象(如果对象本身包含对象,则递归)。

当您希望复制的底层对象是可变的(或包含可变的)并且容易受到镜像更改的影响时,使用此方法(与更常见的分配相反):

Python 中的赋值语句不会复制对象,它们会在目标和对象之间创建绑定。对于可变集合或包含可变项的集合,有时需要一个副本,以便可以更改一个副本而不更改另一个副本。

如您所说,在 PyTorch 设置中,如果您希望张量对象的新副本在完全不同的设置中使用,而对其父级没有任何关系或影响,您应该使用 .detach().clone()


  1. 重要提示:以前,原地大小/步幅/存储更改(例如resize_/resize_as_/set_/transpose_)到返回的张量也会更新原始张量。现在,这些就地更改将不再更新原始张量,而是会触发错误。对于稀疏张量:对返回的张量进行就地索引/值更改(例如zero_/copy_/add_)将不再更新原始张量,而是会触发错误。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-09-16
    • 2011-09-05
    • 2011-03-24
    • 2013-04-28
    • 1970-01-01
    • 1970-01-01
    • 2023-04-05
    • 2013-06-19
    相关资源
    最近更新 更多