深入理解张量在内存中的物理布局,Strides,View 与 Reshape 的区别,以及梯度追踪机制。
当你第一次看到这个错误信息时,可能会感到困惑:
RuntimeError: view size is not compatible with input tensor's size and stride
或者你可能疑惑过,为什么 PyTorch 代码中经常出现 detach().clone() 这样的组合,而不是单独使用其中一个?
这些问题的根源在于:这里其实混合了三个不同的概念——内存布局、视图操作、以及梯度追踪。理解这些概念的起点是:
一个 PyTorch 张量 = 一块扁平的内存 + 一套解读这块内存的元数据。
这个元数据包括:
让我们用代码验证:
import torch
x = torch.tensor([[1, 2, 3],
[4, 5, 6]])
print(x)
print(f"Shape: {x.shape}") # torch.Size([2, 3])
print(f"Stride: {x.stride()}") # (3, 1)
虽然我们看到的是一个 2×3 的矩阵,但 PyTorch 实际上将数据存储为 [1, 2, 3, 4, 5, 6] 这样一个扁平数组,然后用 shape=(2,3) 和 stride=(3,1) 来解读它。
步幅 (Stride) 是理解张量布局的核心。它定义了:为了在某个维度上移动到下一个元素,你需要跳过多少个物理内存位置。
对于一维向量,步幅通常为 1:
// 逻辑: vector[i]
// 物理: pointer + i * stride
int offset = i * 1;