PyTorch:view() 与 reshape() 区别详解

本文详细对比了PyTorch中view与reshape的功能差异,解析了它们在连续性和内存管理方面的特性。view适用于满足连续性的tensor,不改变原始数据,而reshape则更为灵活,可在不满足连续性条件下使用。

总之,两者都是用来重塑tensor的shape的。view只适合对满足连续性条件(contiguous)的tensor进行操作,而reshape同时还可以对不满足连续性条件的tensor进行操作,具有更好的鲁棒性。view能干的reshape都能干,如果view不能干就可以用reshape来处理。别看目录挺多,但内容很细呀~其实原理并不难啦~我们开始吧~

(2021.03.30更新:感谢评论区提出该博客尚存问题及解决方法的各位小伙伴~我已针对这些问题对博客的内容做了修正。修正的主要内容为修改查询存储区地址的方式,由id()换为data_ptr()

目录

一、PyTorch中tensor的存储方式

1、PyTorch张量存储的底层原理

2、PyTorch张量的步长(stride)属性

二、对“视图(view)”字眼的理解

三、view() 和reshape() 的比较

1、对 torch.Tensor.view() 的理解

2、对 torch.reshape() 的理解

四、总结


一、PyTorch中tensor的存储方式

想要深入理解view与reshape的区别,首先要理解一些有关PyTorch张量存储的底层原理,比如tensor的头信息区(Tensor)和存储区 (Storage)以及tensor的步长Stride。不用慌,这部分的原理其实很简单的(^-^)!

1、PyTorch张量存储的底层原理

tensor数据采用头信息区(Tensor)和存储区 (Storage)分开存储的形式,如图1所示。变量名以及其存储的数据是分为两个区域分别存储的。比如,我们定义并初始化一个tensor,tensor名为A,A的形状size、步长stride、数据的索引等信息都存储在头信息区,而A所存储的真实数据则存储在存储区。另外,如果我们对A进行截取、转置或修改等操作后赋值给B,则B的数据共享A的存储区,存储区的数据数量没变,变化的只是B的头信息区对数据的索引方式。如果听说过浅拷贝和深拷贝的话,很容易明白这种方式其实就是浅拷贝

图1 Torch中Tensor的存储结构

举个例子:

import torch
a = torch.arange(5)  # 初始化张量 a 为 [0, 1, 2, 3, 4]
b = a[2:]            # 截取张量a的部分值并赋值给b,b其实只是改变了a对数据的索引方式
print('a:', a)
print('b:', b)
print('ptr of storage of a:', a.storage().data_ptr())  # 打印a的存储区地址
print('ptr of storage of b:', b.storage().data_ptr())  # 打印b的存储区地址,可以发现两者是共用存储区

print('==================================================================')

b[1] = 0    # 修改b中索引为1,即a中索引为3的数据为0
print('a:', a)
print('b:', b)
print('ptr of storage of a:', a.storage().data_ptr())  # 打印a的存储区地址,可以发现a的相应位置的值也跟着改变,说明两者是共用存储区
print('ptr of storage of b:', b.storage().data_ptr())  # 打印b的存储区地址


'''   运行结果   '''
a: tensor([0, 1, 2, 3, 4])
b: tensor([2, 3, 4])
ptr of storage of a: 2862826251264
ptr of storage of b: 2862826251264
==================================================================
a: tensor([0, 1, 2, 0, 4])
b: tensor([2, 0, 4])
ptr of storage of a: 2862826251264
ptr of storage of b: 2862826251264

2、PyTorch张量的步长(stride)属性

torch的tensor也是有步长属性的,说起stride属性是不是很耳熟?是的,卷积神经网络中卷积核对特征图的卷积操作也是有stride属性的,但这两个stride可完全不是一个意思哦。tensor的步长可以理解为从索引中的一个维度跨到下一个维度中间的跨度。为方便理解,就直接用图1说明了,您细细品(^-^):

图2 对张量的stride属性的理解

举个例子:

import torch
a = torch.arange(6).reshape(2, 3)  # 初始化张量 a
b = torch.arange(6).view(3, 2)     # 初始化张量 b
print('a:', a)
print('stride of a:', a.stride())  # 打印a的stride
print('b:', b)
<think>我们正在讨论PyTorch中的viewreshape方法的区别。根据引用[1],我们知道view主要用于改变张量的形状和调整维度,但要求原始张量的内存是连续的。而reshapeview功能类似,但可以处理非连续内存,更灵活。下面我将详细解释两者的区别,并提供使用场景和代码示例。###1.基本概念在PyTorch中,`view`和`reshape`都用于改变张量的形状(维度),但它们在内存连续性要求上有区别:-**view**:要求张量的内存是连续的(即`tensor.is_contiguous()`为True)。如果原始张量不连续,调用`view`会报错。-**reshape**:会先尝试返回原始数据的view(如果内存连续),如果不连续,则会创建新的连续张量并返回(相当于先调用`contiguous()`再`view()`)。引用[4]强调了形状变换的基本规则:原张量和新张量的总元素数必须相等,即:$$\text{dim}_1\times\text{dim}_2\times\dots\times\text{dim}_n=\text{new\_dim}_1\times\text{new\_dim}_2\times\dots\times\text{new\_dim}_m$$###2.主要区别|特性|view|reshape||----------------|-------------------------------|------------------------------||**内存连续性**|必须连续,否则报错|自动处理非连续情况||**灵活性**|较低,要求严格|较高,适应性强||**性能**|通常不复制数据(零拷贝)|非连续时复制数据(有开销)|###3.使用场景-**优先使用view**:当确定张量是连续的,且需要避免数据复制时(例如在训练循环中,对张量进行形状调整)。-**使用reshape**:当不确定张量是否连续,或者需要代码更健壮(处理任意情况)时。###4.代码示例####示例1:连续张量下的使用```pythonimporttorch#创建一个连续张量x=torch.arange(24)#1D张量,24个元素#使用view改变形状x_view=x.view(4,6)#形状变为[4,6]#使用reshape改变形状x_reshape=x.reshape(4,6)#同样[4,6]#检查是否相同print(torch.all(x_view==x_reshape))#输出:tensor(True)```####示例2:非连续张量下的使用```python#创建非连续张量:转置操作会使得张量不连续y=torch.arange(24).view(4,6)#初始连续y_transposed=y.t()#转置后形状为[6,4],且不连续#尝试用view会报错try:y_transposed.view(24)#错误:非连续张量不能直接viewexceptRuntimeErrorase:print(e)#输出:viewsizeisnotcompatiblewithinputtensor'ssizeandstride...#使用reshape处理非连续张量z=y_transposed.reshape(24)#成功,因为reshape会自动处理print(z.shape)#输出:torch.Size([24])```###5.如何选择?-如果能够保证张量连续(例如在模型前向传播过程中,没有进行转置等操作),优先使用`view`以获得潜在的性能优势。-在不确定内存布局或需要通用代码时,使用`reshape`更安全。###总结`view`和`reshape`在功能上相似,但`view`有内存连续性要求,而`reshape`会自动处理。在连续内存下,两者性能相同;在非连续时,`reshape`会进行数据复制,因此略有开销。根据实际情况选择合适的方法即可。§§相关问题§§1.PyTorch中如何检查张量是否连续?2.如何使一个非连续张量变得连续?3.除了viewreshapePyTorch中还有哪些方法可以改变张量形状?4.在神经网络训练中,改变张量形状的最佳实践是什么?</think> ### PyTorch中`view``reshape`的区别详解 #### 一、核心区别 1. **内存连续性要求**: - `view()` 要求原始张量在**内存中连续存储**(`is_contiguous() == True`)。若原始张量不连续,调用会报错 - `reshape()` 自动处理内存连续性:若连续则直接修改视图,若不连续则**复制数据创建新张量** *引用来源:[^1]* 2. **灵活性差异**: ```python # 非连续张量示例(如转置操作后) x = torch.randn(3, 4).t() # 转置后不连续 # view() 会报错 try: x.view(12) except RuntimeError: print("view需要连续内存") # reshape() 可正常运行 y = x.reshape(12) # 自动复制数据 ``` #### 二、使用场景对比 | **方法** | 适用场景 | 避免使用的场景 | |----------|----------|----------------| | `view()` | 1. 连续内存下的形状变更<br>2. 需零拷贝的高效操作<br>3. 固定维度调整(如CNN特征图) | 非连续数据<br>维度不匹配 | | `reshape()` | 1. 任意内存状态的数据<br>2. 不确定连续性的通用代码<br>3. 跨设备数据处理 | 高频循环中(可能触发复制) | #### 三、关键特性总结 1. **形状一致性规则**(两者均需满足): $$ \text{原形状元素总数} = \prod_{i=1}^n \text{dim}_i = \prod_{j=1}^m \text{new\_dim}_j $$ *引用来源:[^4]* 2. **特殊维度-1**(自动推导): ```python x = torch.rand(2, 3, 4) # 24元素 y = x.reshape(2, -1) # 自动推导为(2,12) ``` #### 四、典型代码示例 ```python # 连续内存处理(两者等效) cont_tensor = torch.arange(24) # 连续内存 a = cont_tensor.view(4, 6) # ✅ b = cont_tensor.reshape(3, 8) # ✅ # 非连续数据处理 non_cont = torch.rand(4, 3).t() # 转置后不连续 c = non_cont.reshape(12) # ✅ 自动复制数据 d = non_cont.view(12) # ❌ RuntimeError ``` #### 五、性能优化建议 1. 优先用`view()`处理连续张量(零拷贝) 2. 需要时主动连续化: ```python x = x.contiguous().view(...) # 显示连续化再view ``` 3. 在训练循环中避免不必要的`reshape()`复制操作 > **总结**:`view`是轻量级视图操作,`reshape`是更安全的复制操作。理解内存连续性机制是合理选用的关键[^1]。
评论 80
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

地球被支点撬走啦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值