python、pytorch中的常见的浅拷贝、深拷贝问题总结

文章目录

  • 前言
  • 一、python中的浅拷贝,深拷贝
    • 1. 赋值操作原理
    • 2. copy()
    • 3. deepcopy()
  • 二、pytorch中的深拷贝、浅拷贝
    • 1. inplace = True
    • 2. .Tensor、.tensor、.from_numpy、.as_tensor的区别
    • 3. .detach()和.clone()
    • 4. contiguous函数


前言

本文将介绍在python编程过程中遇到的各种赋值、浅拷贝、深拷贝之间的差异,同时介绍pytorch中的浅拷贝、深拷贝操作


提示:以下是本篇文章正文内容,下面案例可供参考

一、python中的浅拷贝,深拷贝

1. 赋值操作原理

python中a = something 的赋值操作准确的应该理解为,给存储something建立一个索引a(即存储地址),a通过访问something的存储内容,获得something的值。下面举例说明,因为python独特的机制会出现的奇怪显示。首先我们都知道’='赋值是浅拷贝的一种。按理说,当a=b,,如果b原始的值发生改变,那么a也会发生改变,但是现实却不一定都是这样:

#情况1:
a = [1,4,7]
b = a
a = [2,3,5]
print(b)
#情况2:
a = [1,4,7]
b = a
a[0],a[1],a[2] = 4, 5, 6
print('b is :'b)
结果: [1,4,7] [4,5,6] 原理解释:在情况一中,当使用b=a之后,b和a都指向[1,4,7]的存储内容,其实际标签即[1,4,7]的存储地址。当使用a = [2,3,5]之后,a指向[2,3,5],获得对应的存储地址,并更新了以前的地址。此时b的内容仍然为[1,4,7]的存储空间,故a获得的新值没有赋给b。在情况二,a,b都指向[1,4,7]的存储空间,此时a[0],a[1],a[2] = 4,5,6改变了存储空间的值,但是a,b此时的存储地址没变,随着存储值的更新,a,b从存储空间索引出的值也一样改变了。第二种是经典的使用赋值方法的浅拷贝方法

2. copy()

copy():copy是python中常见的一个函数,它也是属于浅拷贝的一种,但是在复制的过程中存在两种情况:
第一种:当复制的对象中无复杂子对象的时候。原来值的改变不会影响浅复制的值,同时原来值的id和浅拷贝值的id不一致
第二种:当复制的对象中存在复杂子对象的时候。如果改变其中复杂子对象的值,浅复制的值也会改变。改变其他值,则不会影响浅复制的值
主要原因是在copy()中将复杂子类使用一个公共镜像储存起来,当镜像改变了之后另一个使用镜像已经被改变了

import copy
a = [[1,2],1,4]
b = copy.copy(a)
#改变复杂子对象的值
a[0][0] = 0
print(a)
print(b)
#改变非复杂子对象的值
a[2] = 0
print(a)
print(b)
结果: [[0,2],1,4] [[0,2],1,4] [[0,2],1,0] [[0,2],1,4]

3. deepcopy()

即将被复制对象完全再复制一遍,作为独立的新个体单独存在

from copy import deepcopy
a = [1,2,3,4]
b = deepcopy(a)
a[0] = 2
print(a)
print(b)

结果:
[2,2,3,4]
[1,2,3,4]


二、pytorch中的深拷贝、浅拷贝

1. inplace = True

inplace =True的意思是进行原地操作。
例如:x = x + 5,对x就是一个原地操作,y= x+5, x= y完成了同样的功能但不是原地操作,使用这样的方法能够节省内存。

2. .Tensor、.tensor、.from_numpy、.as_tensor的区别

.Tensor和.tensor是深拷贝,在内存中创建一个额外的数据副本,不共享内存,所以不受数组改变的影响。.from_numpy和as_tensor是浅拷贝,在内存中共享数据。

import numpy as np
import torch
a  = np.array([0,1,2,3])
a1 = torch.from_numpy(a)
a2 = torch.Tensor(a)
a3 = torch.tensor(a)
a4 = torch.as_tensor(a)
print("before changed:")
print(a1)
print(a2)
print(a3)
print(a4)
a[0] = 3
a[1] = 2
a[2] = 1
a[3] = 0
print("changed:")
print(a1)
print(a2)
print(a3)
print(a4)
结果: before changed: tensor([0, 1, 2, 3]) tensor([0., 1., 2., 3.]) tensor([0, 1, 2, 3]) tensor([0, 1, 2, 3]) changed: tensor([3, 2, 1, 0]) tensor([0., 1., 2., 3.]) tensor([0, 1, 2, 3]) tensor([3, 2, 1, 0])

3. .detach()和.clone()

.clone()是深拷贝,开辟新的存储地址而不是引用来保存旧的tensor,在梯度会传的时候clone()充当中间变量,会将梯度传给源张量进行叠加,但是本身不保存其grad,值为None。
.detach是浅拷贝,新的tensor会脱离计算图,不会牵扯梯度计算。

import torch

x= torch.tensor([2., 4.], requires_grad=True)
clone_x = x.clone()
detach_x = x.detach()
clone_detach_x = x.clone().detach()

y = 2*x + 10
y.backward(torch.FloatTensor([1,1]))

print(x.grad)
print(clone_x.requires_grad)
print(clone_x.grad)
print(detach_x.requires_grad)
print(clone_detach_x.requires_grad)

结果:
tensor([2., 2.])
True
None
False
False

4. contiguous函数

在pytorch中,很多操作都用到了浅拷贝的思路,只是重新定义下标与元素的对应关系。例如在使用transpose()进行转置操作时,pytorch不会创建转置后的,新的tensor,仅仅修改tensor中的一些属性(元数据)。转置和原数据的内存是共享的。
不使用contiguous()之前

import torch
x = torch.randn(3, 2)
y = torch.transpose(x, 0, 1)
print("before")
print("x:", x)
print("y:", y)

print("after:")
y[0, 0] = 11
print("x:", x)
print("y:", y)

结果:
before
x: tensor([[-0.4466, 0.5280],
[ 0.6272, -0.3708],
[ 0.2064, 0.3755]])
y: tensor([[-0.4466, 0.6272, 0.2064],
[ 0.5280, -0.3708, 0.3755]])
after:
x: tensor([[11.0000, 0.5280],
[ 0.6272, -0.3708],
[ 0.2064, 0.3755]])
y: tensor([[11.0000, 0.6272, 0.2064],
[ 0.5280, -0.3708, 0.3755]])

使用contiguous()之后
import torch
x = torch.randn(3, 2)
y = torch.transpose(x, 0, 1).contiguous()
print("before")
print("x:", x)
print("y:", y)

print("after:")
y[0, 0] = 11
print("x:", x)
print("y:", y)

结果:
before
x: tensor([[-0.4466, 0.5280],
[ 0.6272, -0.3708],
[ 0.2064, 0.3755]])
y: tensor([[-0.4466, 0.6272, 0.2064],
[ 0.5280, -0.3708, 0.3755]])
after:
x: tensor([[11.0000, 0.5280],
[ 0.6272, -0.3708],
[ 0.2064, 0.3755]])
y: tensor([[11.0000, 0.6272, 0.2064],
[ 0.5280, -0.3708, 0.3755]])


欢迎喜欢的朋友点赞,谢谢!best wishes!

你可能感兴趣的:(python基础语法,pytorch,python,pytorch)