介绍
.view() 方法,这个方法是张量形状变换的核心工具,用于在不改变张量数据的前提下,调整张量的维度结构(如将一维张量转为二维、将四维特征图展平为二维等)。
作用
返回一个与原张量共享内存的新视图(新形状),张量的 总元素数必须保持不变(即形状的乘积相等),数据本身不复制、不修改,仅改变维度的解读方式。
关键特征
新张量与原张量共享内存,修改其中一个,另一个也会同步变化。
语法
Tensor.view(*shape) → Tensor
参数:
*shape:指定新的形状,可以是单个整数、多个整数,或包含-1(自动计算该维度的大小,且只能有一个-1);
对比 .reshape()
.reshape() 是 .view() 的升级版,功能类似,但 .view() 仅支持连续内存的张量,.reshape() 可自动处理非连续张量(必要时复制数据)。
例子
通过不同场景的示例,演示 .view() 的核心用法:
import torch
# 原张量:shape [2, 3](总元素数=6)
x = torch.tensor([[1, 2, 3],
[4, 5, 6]])
print("原张量形状:", x.shape) # torch.Size([2, 3])
示例1:基础形状变换(总元素数不变)
转为 [3, 2]
x_view1 = x.view(3, 2)
print("\nview(3, 2) 结果:")
print(x_view1)
# 输出:
# tensor([[1, 2],
# [3, 4],
# [5, 6]])
示例2:使用-1自动计算维度(常用!)
# -1 表示“自动计算该维度的大小”,仅能出现一次
x_view2 = x.view(-1) # 展平为一维(6个元素)
print("\nview(-1) 结果(展平):", x_view2) # tensor([1, 2, 3, 4, 5, 6])
x_view3 = x.view(2, -1) # 固定第一维为2,第二维自动算(3)
print("view(2, -1) 形状:", x_view3.shape) # torch.Size([2, 3])
x_view4 = x.view(-1, 3) # 固定第二维为3,第一维自动算(2)
print("view(-1, 3) 形状:", x_view4.shape) # torch.Size([2, 3])
示例3:高维张量变换(如CNN特征图展平)
# 模拟CNN输出:[batch_size=2, channel=3, height=4, width=4](总元素=2*3*4*4=96)
feat = torch.randn(2, 3, 4, 4)
# 展平为 [2, 3*4*4](batch保留,特征展平)
feat_flat = feat.view(2, -1)
print("\n高维张量view后形状:", feat_flat.shape) # torch.Size([2, 48])
示例4:共享内存特性(修改新视图,原张量也变)
x_view1[0, 0] = 100 # 修改view后的张量
print("\n修改view张量后,原张量:")
print(x) # 原张量[0,0]也变为100!
# 输出:
# tensor([[100, 2, 3],
# [ 4, 5, 6]])
使用场景
.view() 是深度学习中高频使用的操作,典型场景包括:
全连接层前的特征展平
CNN模型中,卷积层输出的四维张量([batch, channel, h, w])需展平为二维([batch, channel*h*w])才能输入全连接层:
class CNN(nn.Module):
def __init__(self):
super().__init__()
self.conv = nn.Conv2d(3, 16, 3) # 输出[batch,16,30,30](假设输入224x224)
self.fc = nn.Linear(16*30*30, 10) # 全连接层需要一维特征
def forward(self, x):
x = self.conv(x)
# 展平:保留batch维度,其余展平
x = x.view(x.size(0), -1) # x.size(0) = batch_size,-1自动计算16*30*30
x = self.fc(x)
return x
调整批量数据形状
将一维的批量数据(如 [batch*seq_len, dim])恢复为二维([batch, seq_len, dim]):
# 输入:[8, 768](batch=2, seq_len=4,总长度=8)
x = torch.randn(8, 768)
# 恢复为 [2, 4, 768]
x_reshaped = x.view(2, 4, 768)
维度压缩/扩展
临时调整维度以适配层的输入要求(如将 [batch, dim] 转为 [batch, 1, dim] 适配卷积层):
x = torch.randn(32, 64) # [batch=32, dim=64]
# 转为 [32, 1, 64](适配Conv1d的输入格式)
x_expand = x.view(32, 1, 64)
注意事项
总元素数必须相等
新形状的乘积必须等于原张量的总元素数,否则报错。例如:
x = torch.randn(2, 3)
x.view(4, 2) # 报错!2*3=6 ≠ 4*2=8
仅支持连续内存张量
如果张量经过转置(.t())、切片等操作后内存不连续,.view() 会报错,此时需用 .contiguous() 先整理内存,或直接用 .reshape():
x = torch.randn(2, 3).t() # 转置后内存不连续
# x.view(6) # 报错!
x_contig = x.contiguous()
x_contig.view(6) # 正常
# 更简单:用reshape
x.reshape(6) # 自动处理非连续内存
共享内存的风险
修改 .view() 返回的张量会同步修改原张量,若需独立张量,需用 .clone() 复制:
x = torch.tensor([1,2,3])
x_view = x.view(3,1).clone() # 复制后不再共享内存
x_view[0,0] = 100
print(x) # 仍为[1,2,3],不受影响
-1的使用限制
.view() 中 -1 只能出现一次,用于自动计算维度,多次出现会报错:
x.view(-1, -1) # 报错!只能有一个-1
总结
.view()核心是共享内存的形状变换,总元素数必须不变,常用-1自动计算维度;- 典型场景:CNN特征展平、批量数据形状调整、维度适配;
- 注意内存连续性问题(报错时用
.contiguous()或.reshape()),以及共享内存的修改风险。