深入理解张量在内存中的物理布局,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) 来解读它。

关键概念:步幅 (Strides)

步幅 (Stride) 是理解张量布局的核心。它定义了:为了在某个维度上移动到下一个元素,你需要跳过多少个物理内存位置。

向量示例 (1D)

对于一维向量,步幅通常为 1

// 逻辑: vector[i]
// 物理: pointer + i * stride
int offset = i * 1;

矩阵示例 (2D)