在使用Pytorch时,我一直在努力理解.clone()
、.detach()
和copy.deepcopy
之间的区别,特别是在使用PytorchTensor时。
我试着写我所有的问题,他们的差异和使用情况,并很快被淹没,并意识到也许有4个主要属性的PytorchTensor将澄清更好地使用,通过每一个小问题。4个主要属性,我意识到一个需要跟踪是:
1.如果具有指向Tensor新指针/引用
1.如果具有新Tensor对象示例(且因此很可能该新示例具有其自己的元数据,如require_grads
、shape、is_leaf
等)。
1.如果它已经为Tensor数据分配了新的存储器(即如果这个新Tensor是不同Tensor的视图)
1.它是否跟踪操作历史记录(或者即使它跟踪的是全新的操作历史记录,或者在深层拷贝的情况下跟踪的是相同的旧操作历史记录)
根据从Pytorch论坛和文档中挖掘出来的内容,这是我目前在Tensor上使用时对每种方法的区别:
克隆
对于克隆:
x_cloned = x.clone()
我相信这是它如何根据主要的4个属性表现出来的:
1.克隆的x_cloned
有它自己的python引用/指向新对象的指针
1.它已经创建了自己的新Tensor对象示例(带有单独的元数据)
1.它已经为x_new
分配了一个新的内存,其数据与x
相同
1.它正在跟踪原始操作历史记录,此外还将此clone
操作作为.grad_fn=<CloneBackward>
包括在内
据我所知,其主要用途似乎是创建事物的副本,以便inplace_
操作安全。此外,与.detach
结合为.detach().clone()
(顺便说一句,这样做的"更好"顺序),它创建了一个与旧历史分离的全新Tensor,从而停止通过该路径的梯度流。
分离
x_detached = x.detach()
1.创建了一个新的python引用(当然唯一没有做的是x_new = x
),我相信可以使用id
来做这个
1.它已经创建了自己的新Tensor对象示例(带有单独的元数据)
1.它未为x_detached
分配与x具有相同数据的新内存
1.它切断了梯度的历史,不允许它流过,我认为把它看作是没有历史的,一个全新的Tensor是正确的。
我相信我所知道的唯一合理的用法是当.clone()
作为.detach().clone()
与.clone()
结合时,用它自己的内存创建新的副本。否则,我不确定它的用途。由于它指向原始数据,就地执行操作可能存在潜在危险(因为它改变了旧数据,但在早期计算图中autograd不知道旧数据的变化)。
复制.深拷贝
x_deepcopy = copy.deepcopy(x)
1.如果具有指向Tensor新指针/引用
1.它用自己的元数据创建了一个新的Tensor示例(所有的元数据都应该指向深度副本,所以如果它像人们所期望的那样实现,我希望是新的对象)。
1.它有自己的内存分配给Tensor数据
1.如果它真的是一个深度拷贝,我希望它是一个历史的深度拷贝,所以它应该做一个历史的深度复制,虽然这看起来真的很昂贵,但至少在语义上与深度拷贝应该是什么一致。
我真的没有看到这个的用例,我假设任何人尝试使用这个真的意味着1).detach().clone()
或只是2).clone()
本身,这取决于是否有人想停止梯度流到前面的图形与1或如果他们只是想复制数据与一个新的内存2).
所以这是我现在理解差异的最好方法,而不是问所有可能使用它们的不同场景。
那么这是正确的吗?有人看到任何需要纠正的重大缺陷吗?
我自己担心的是我给深度复制的语义,并怀疑它是否正确的深度复制历史。
我认为为每一个列出一个通用用例是很好的。
资源
以下是我阅读和参与的所有资源,以得出这个问题的结论:
- 到0.4.0 https://pytorch.org/blog/pytorch-0_4_0-migration-guide/的迁移指南
- 关于使用克隆的困惑:https://discuss.pytorch.org/t/confusion-about-using-clone/39673/3
- v0.4.0中的克隆和分离:https://discuss.pytorch.org/t/clone-and-detach-in-v0-4-0/16861/2
- 用于克隆的文档:
- https://pytorch.org/docs/stable/tensors.html#torch.Tensor.clone
- 分离文档(在浏览器中搜索单词detach,没有直接链接):
- https://pytorch.org/docs/stable/tensors.html#torch.Tensor
- detach(). clone()和clone(). detach()之间的区别:https://discuss.pytorch.org/t/difference-between-detach-clone-and-clone-detach/34173
- 为什么我可以在Pytorch with detach中更改Tensor的值,而计算图形却不知道?Why am I able to change the value of a tensor without the computation graph knowing about it in Pytorch with detach?
- 在PytorchTensor中detach,clone和deepcopy的具体区别是什么?在PytorchTensor中detach,clone和deepcopy的具体区别是什么?
- 副本. deepcopy()与克隆()https://discuss.pytorch.org/t/copy-deepcopy-vs-clone/55022/10
1条答案
按热度按时间krcsximq1#
∮ ∮ ∮ ∮
复制Tensor,同时保持autograd图中的链接。例如,如果要将Tensor复制为神经网络中的一个操作(例如,将中间级表示传递到两个不同的头以计算不同的损失),则使用:
返回输入的副本。
detach()
。∮ ∮ ∮ ∮
返回不带自动梯度历史的原始Tensor视图。如果您想要操作Tensor值(不在适当位置)而不影响计算图形(例如,报告正向传递中途的值),则使用此选项。
返回从当前图形分离的新Tensor。
结果将永远不需要梯度。
该方法也影响正向模式AD梯度,并且结果将永远不会具有正向模式AD梯度。
∮ ∮ ∮ ∮
deepcopy
是copy
库中的一个通用python函数,用于复制现有对象(如果对象本身包含对象,则递归复制)。当您希望复制的底层对象是可变的(或包含可变对象),并且容易受到其中一个对象中所做镜像更改的影响时,可以使用此方法(与更常见的赋值相反):
Python中的赋值语句并不复制对象,而是在目标和对象之间创建绑定。对于可变的或包含可变项的集合,有时需要一个副本,这样就可以更改一个副本而不更改另一个副本。
在PyTorch设置中,如前所述,如果希望在完全不同的设置中使用Tensor对象的新副本,而不与其父对象发生关系或对其产生影响,则应使用
.detach().clone()
。1.* * 重要说明:**以前,对返回Tensor的大小/跨距/存储进行就地更改(例如
resize_
/resize_as_
/set_
/transpose_
)也会更新原始Tensor。现在,这些就地更改将不再更新原始Tensor,而是会触发错误。对于稀疏Tensor:对返回Tensor的原地索引/值更改(例如zero_
/copy_
/add_
)将不再更新原始Tensor,而是触发错误。