本文介绍: 在pytorch中只有很少几个操作是**不改变tensor内容本身的**,大多数操作**不进行数拷贝数据的改变,变的是原数据**。例如:narrow()、view()、expand()和transpose()等。

pytorch contiguous

1 官方中英文doc

torch.Tensor.contiguous (Python method, in torch.Tensor)
torch.Tensor.is_contiguous (Python method, in torch.Tensor)

1.1 contiguous() → Tensor

Returns a contiguous tensor containing the same data as self tensor. If self tensor is contiguous, this function returns the self tensor.

1.2 contiguous() → Tensor

返回一个内存连续的有相同数据tensor,如果原tensor内存连续,则返回tensor

2 pytorch contiguous的使用

contiguous一般与transpose,permute,view搭配使用使用transpose或permute进行维度变换后,调用contiguous,然后方可使用view维度进行变形(如:tensor_var.contiguous().view() ),

示例如下:

x = torch.Tensor(2,3)
y = x.permute(1,0)         # permute:二维tensor的维度变换,此处功能相当于转置transpose
y.view(-1)                 # 报错view使用前需调用contiguous()函数
y = x.permute(1,0).contiguous()
y.view(-1)                 # 不报错

具体原因有两种说法:

view()、permute()和contiguous() 具体用法和区别

1. view()

view() 变换维度,把原先tensor中的数据按行优先顺序排成一维数据然后按照输入参数组合成其他维度的tensor。

import torch
a=torch.Tensor([[[1,2,3],[4,5,6]]]) # torch.Size([1, 2, 3])
print(a.view(3,2))                  # torch.Size([3, 2])

输出

tensor([[1., 2.],
        [3., 4.],
        [5., 6.]])

2. permute()

permute() 利用索引将tensor中的维度进行调换
b=a.permute(2,0,1) permute里的参数对应的是张量a的维度索引,利用索引来对内部数据调换。
a.permute(2,0,1):把 a 的最后一个维度放到最前面。

import torch
inputs = torch.Tensor([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]])
print("inputs:", inputs, inputs.shape)

输出

inputs: tensor([[[ 1.,  2.,  3.],
		         [ 4.,  5.,  6.]],
		
		        [[ 7.,  8.,  9.],
		         [10., 11., 12.]]]) 
 torch.Size([2, 2, 3])
 
outputs tensor([[[ 1.,  4.],
		         [ 2.,  5.],
		         [ 3.,  6.]],
		
		        [[ 7., 10.],
		         [ 8., 11.],
		         [ 9., 12.]]]) 
 torch.Size([2, 3, 2])

3. contiguous()

pytorch中只有很少几个操作是不改变tensor内容本身的,大多数操作不进行数拷贝和数据的改变,变的是原数据。例如:narrow()、view()、expand()和transpose()等。

例如使用transpose()进行转置操作时,pytorch并不会创建新的、转置后的tensor,而是修改了tensor中的一些属性(也就是原数据),使得此时的offsetstride是与转置tensor相对应的。转置的tensor和原tensor的内存是共享

举个栗子:

import torch

x = torch.randn(3, 2)
y = torch.transpose(x, 0, 1)

print("修改前:")
print("x:", x)
print("y:", y)

y[0, 0] = 11
print("n修改后:")
print("x:", x)
print("y:", y)

运行结果

原数据:

x: tensor([ [ 0.5287,  0.8814],
	        [ 1.2752,  1.9832],
	        [ 1.9872, -1.2719]])
y: tensor([[ 0.5287,  1.2752,  1.9872],
       	   [ 0.8814,  1.9832, -1.2719]])

定义后:

x: tensor([[11.0000,  0.8814],
           [ 1.2752,  1.9832],
           [ 1.9872, -1.2719]])
y: tensor([[11.0000,  1.2752,  1.9872],
           [ 0.8814,  1.9832, -1.2719]])

重点!重点!重点!!

可以看到,我们重新定义了 y 元素值的同时,x元素的值也发生了变化。因此tensor中数据还是在内存中一块区域里,只是布局的问题!为什么这么说:因为,**y里面数据布局的方式和从头开始创建一个常规的tensor布局的方式是不一样的。**这个可能只是python中之前常用的浅拷贝,y还是指向x变量所处的位置,只是说记录了transpose这个变化的布局。

如果想要断开这两个变量之间的依赖(x本身是contiguous的),就要使用contiguous()针对x进行变化,感觉上就是我们认为的深拷贝

调用contiguous()时,会强制拷贝一份tensor,让它的布局和从头创建的一模一样,但是两个tensor完全没有联系。还是上面的栗子:

import torch

x = torch.randn(3, 2)
y = torch.transpose(x, 0, 1).contiguous()
print("原数据:")
print("x:", x)
print("y:", y)

y[0, 0] = 11
print("n重定义后:")
print("x:", x)
print("y:", y)

运行结果

原数据:

x: tensor([[ 2.5966, -0.7265],
           [ 0.0200,  0.5726],
           [-0.8865, -0.4727]])
y: tensor([[ 2.5966,  0.0200, -0.8865],
           [-0.7265,  0.5726, -0.4727]])

定义后:

x: tensor([[ 2.5966, -0.7265],
           [ 0.0200,  0.5726],
           [-0.8865, -0.4727]])
y: tensor([[11.0000,  0.0200, -0.8865],
           [-0.7265,  0.5726, -0.4727]])

可以看到对y使用了contiguous()后,改变y的值,对x没有任何影响

参考:
https://zhuanlan.zhihu.com/p/545769141
https://zhuanlan.zhihu.com/p/64376950

原文地址:https://blog.csdn.net/weixin_42046845/article/details/134667338

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。

如若转载,请注明出处:http://www.7code.cn/show_4903.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注