在PytorchTensor中,detach、clone和deepcopy之间有什么具体区别?

kr98yfug  于 2023-03-02  发布在  其他
关注(0)|答案(1)|浏览(233)

在使用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).
所以这是我现在理解差异的最好方法,而不是问所有可能使用它们的不同场景。
那么这是正确的吗?有人看到任何需要纠正的重大缺陷吗?
我自己担心的是我给深度复制的语义,并怀疑它是否正确的深度复制历史。
我认为为每一个列出一个通用用例是很好的。
资源
以下是我阅读和参与的所有资源,以得出这个问题的结论:

krcsximq

krcsximq1#

    • 注意:**由于此问题已发布,这些函数的行为和文档页面已更新。

∮ ∮ ∮ ∮
复制Tensor,同时保持autograd图中的链接。例如,如果要将Tensor复制为神经网络中的一个操作(例如,将中间级表示传递到两个不同的头以计算不同的损失),则使用:
返回输入的副本。

    • 注:**此函数可微分,因此梯度将从此操作的结果流回输入。要创建与输入没有自动梯度关系的Tensor,请参见detach()

∮ ∮ ∮ ∮
返回不带自动梯度历史的原始Tensor视图。如果您想要操作Tensor值(不在适当位置)而不影响计算图形(例如,报告正向传递中途的值),则使用此选项。
返回从当前图形分离的新Tensor。
结果将永远不需要梯度。
该方法也影响正向模式AD梯度,并且结果将永远不会具有正向模式AD梯度。

    • 注意:**返回的Tensor与原始Tensor共享相同的存储空间。在它们中的任何一个上的就地修改都将被看到,并可能在正确性检查中触发错误。1

∮ ∮ ∮ ∮
deepcopycopy库中的一个通用python函数,用于复制现有对象(如果对象本身包含对象,则递归复制)。
当您希望复制的底层对象是可变的(或包含可变对象),并且容易受到其中一个对象中所做镜像更改的影响时,可以使用此方法(与更常见的赋值相反):
Python中的赋值语句并不复制对象,而是在目标和对象之间创建绑定。对于可变的或包含可变项的集合,有时需要一个副本,这样就可以更改一个副本而不更改另一个副本。
在PyTorch设置中,如前所述,如果希望在完全不同的设置中使用Tensor对象的新副本,而不与其父对象发生关系或对其产生影响,则应使用.detach().clone()
1.* * 重要说明:**以前,对返回Tensor的大小/跨距/存储进行就地更改(例如resize_/resize_as_/set_/transpose_)也会更新原始Tensor。现在,这些就地更改将不再更新原始Tensor,而是会触发错误。对于稀疏Tensor:对返回Tensor的原地索引/值更改(例如zero_/copy_/add_)将不再更新原始Tensor,而是触发错误。

相关问题