在入门篇中我们学会了如何创建张量、进行基本的运算。但你是否想过为什么view()改变形状非常快而reshape()有时会变慢为什么transpose()转置后的张量在内存中其实是“乱序”的torch.matmul到底是如何处理高维张量的今天我们将深入 PyTorch 的底层通过存储和步幅的视角彻底看懂张量的本质。实际上一个张量背后包含三个关键概念Storage存储 Stride步长 Shape形状理解这三者再结合矩阵乘法你会对神经网络的计算过程有更深刻的认识。一、什么是Storage存储Storage 是张量数据在内存中的一维连续存储空间。比如一个二维张量[1234] \begin{bmatrix} 1 2 \\ 3 4 \end{bmatrix}[13​24​]在内存中其实就是[1,2,3,4] [1,2,3,4][1,2,3,4]也就是说所有张量本质都是一段线性内存 解释方式二、什么是Stride步长Stride 决定了“当你在某一维移动一步内存地址跳多少”例如示例2×2矩阵[1234] \begin{bmatrix} 1 2 \\ 3 4 \end{bmatrix}[13​24​]shape (2,2) # 形状stride (2,1) # 步长解释行移动跳 2 个元素列移动跳 1 个元素所以访问规则是indexi×stride0j×stride1 \mathrm{i n d e x}i \times s t r i d e_{0}j \times s t r i d e_{1}indexi×stride0​j×stride1​为了理解这个公式我们需要把张量想象成一栋公寓楼。Storage 内存整栋楼的所有房间被拉成一条长走廊门牌号从 0 到 N 依次排列Tensor 张量这栋楼有 3 层行每层有 4 个房间列目标 寻找住在第 i 层第 j 号房间的人数据公式解读物理门牌号(层数i×每层房间数)房间号j \mathrm{物理门牌号}(层数i \times 每层房间数)房间号j \物理门牌号(层数i×每层房间数)房间号jstride0s t r i d e_{0}stride0​行的步幅就是“每层有多少个房间”。如果你想下一层楼你得在长走廊里跨过这一整层的房间数。stride0s t r i d e_{0}stride0​列的步幅就是“房间之间的间距”通常是 1因为同一层的房间是挨着的。为什么 Stride 很重要因为它允许我们不复制数据实现 reshape实现 transpose转置高效切片slice例如转置[1234]→[1324] \begin{bmatrix} 1 2 \\ 3 4 \end{bmatrix} → \begin{bmatrix} 1 3 \\ 2 4 \end{bmatrix}[13​24​]→[12​34​]Storage物理内存[1, 2, 3, 4]数据是连续存放的Shape形状 2 行 2 列Stride 步幅 21stride[0] 2往下走一行比如从1到3需要跨过2个元素。stride[1] 1往右走一列比如从1到2需要跨过1个元素。进行转置操作时PyTorch没有把数据拿出来重新排成1, 3, 2, 4。它只做了两件事修改 Shape把形状从(2, 2)变成(2, 2)虽然数字没变但逻辑变了。交换 Stride把步幅从(2, 1)变成(1, 2)。三、矩阵乘法的本质理解了 Stride我们再来看矩阵乘法。在 PyTorch 中矩阵乘法不仅仅是数学公式它还涉及到维度的广播和内存布局。1. 核心规则内积矩阵乘法 CA×BCA×B的核心是A 的行 与 B 的列 做点积。前提条件A 的最后一维长度必须等于 B 的倒数第二维长度。A:(N,K)A:(N,K)A:(N,K)B:(K,M)B:(K,M)B:(K,M)Result:(N,M)Result:(N,M)Result:(N,M)2.torch.mmvstorch.matmul我们在基础篇提到过它们的区别现在用底层视角再看一遍torch.mm它是严格的。它只接受 2D 张量效率极高但不支持广播。torch.matmul它是智能的。如果输入是 1D它会自动补维度向量点积。如果输入是 3D它会广播。3. 高维矩阵乘法批量矩阵乘法以 3 维张量相乘作为例子(Batch, N, K) (Batch, K, M)PyTorch 的处理逻辑是广播批次维度就像广播加法一样对齐前面的维度。锁定最后两维对最后两维执行标准的矩阵乘法。# 模拟一个 Batch Size 为 2 的数据Atorch.randn(2,3,4)# 2个矩阵3行4列Btorch.randn(2,4,5)# 2个矩阵4行5列Ctorch.matmul(A,B)print(C.shape)# torch.Size([2, 3, 5])四、进阶练习题为了巩固今天的内容请尝试解答以下问题可以先思考再看答案。练习题 1计算 Stride有一个张量x形状为(2, 3, 4)它是连续存储的。请问x.stride()的输出是什么答案(12, 4, 1)解析这是一个 3维 张量。第2维最内层移动 1 步内存跳过 1 个元素 →1。第1维中间层移动 1 步跳过一整个“行”长度为4 →4。第0维最外层移动 1 步跳过一整个“块”3行4列→ 3 * 4 12。练习题 2转置后的 Stride接上题如果对x执行x.transpose(0, 2)新的 Stride 是多少答案(1, 4, 12)解析transpose(0, 2)交换了第 0 维和第 2 维的索引。原来的 Stride 是(12, 4, 1)。交换位置后Stride 变为(1, 4, 12)。注意此时张量在内存中变得不连续。练习题 3矩阵乘法维度代码torch.randn(10, 5) torch.randn(5,)的输出形状是什么答案torch.Size([10])解析左边是矩阵(10, 5)。右边是向量(5,)。根据matmul规则向量被视为列向量(5, 1)。运算(10, 5) (5, 1) - (10, 1)。特殊规则如果最后结果是(N, 1)这种单列矩阵matmul会自动压缩掉最后的 1变成(10,)的向量。练习题 4连续性问题xtorch.randn(3,4)yx.transpose(0,1)zy.view(12)# 这里会发生什么答案报错RuntimeError: view size is not compatible with input tensors size and stride...解析y是转置后的张量其 Stride 为(1, 3)假设原 Stride 为(4, 1)它是非连续的。view()要求底层数据必须是连续的以便重新解释形状。修正使用z y.contiguous().view(12)。本文章仅用于个人深度学习技术学习、知识点梳理与交流探讨聚焦 PyTorch 张量的存储Storage、步幅Stride及矩阵乘法底层逻辑所有内容均为个人学习过程中的理解与总结不用于任何商业用途、盈利活动及正式教学场景。因本人对 PyTorch 底层原理的认知仍在逐步提升中文章内容包括知识点解读、代码示例、练习题解析等可能存在疏漏、错误或表述不严谨之处不代表 PyTorch 官方技术标准。在此恳请各位学习者理性看待若发现文中错误、有不同理解或补充建议欢迎留言交流、批评指正共同探讨、共同进步一起夯实 PyTorch 学习基础。