- 前言
- 一、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完成了同样的功能但不是原地 *** 作,使用这样的方法能够节省内存。
.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
在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]])
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!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)