为何同来源张量A、B元素相同但均值存在微小差异?
问题:张量拼接转置后均值计算的微小差异原因及torch.cat()机制
问题描述
将形状为(10,2,1)的3D列表转换为张量列表后,通过两种不同流程得到形状一致的张量A与B:
- 对每个张量转置后沿axis=0拼接得到A;
- 对张量列表沿axis=1拼接后转置得到B。
验证显示A与B元素完全一致,但沿axis=0计算均值时存在微小差异(差值为5.9605e-08),现询问该现象原因及torch.cat()的工作机制。
操作步骤及代码
- 将3D列表tmp转为张量列表:
In [7]: tensor_list = [torch.tensor(x) for x in tmp] ...: tensor_list Out[7]: [tensor([[0.3472], [0.6528]]), tensor([[0.5513], [0.4487]]), ...]
- 生成张量A、B:
In [11]: A = torch.cat([x.T for x in tensor_list], dim=0) ...: A.shape Out[11]: torch.Size([10, 2]) In [12]: B = torch.cat(tensor_list, dim=1).T ...: B.shape Out[12]: torch.Size([10, 2])
- 验证一致性:
In [13]: (A - B).abs().sum() Out[13]: tensor(0.) In [14]: A == B Out[14]: tensor([[True, True], ...])
- 均值差异验证:
In [15]: A.mean(dim=0) - B.mean(dim=0) Out[15]: tensor([5.9605e-08, 0.0000e+00])
环境信息
- OS: Ubuntu 20.04.5 LTS
- Python: Python 3.8.10
- torch: 2.0.1
- CUDA: 11.7
- NVIDIA-SMI 515.86.01
解答
1. 均值差异的原因
这个微小差异是单精度浮点数的精度误差导致的,核心和张量内存存储顺序、均值计算的累加路径有关:
- 尽管A和B的元素值表面完全一致,但两者的内存存储布局不同:
- 生成A时,先将每个(2,1)张量转置为(1,2),再沿dim=0拼接,最终A的内存是按行连续存储(每个转置后的小张量作为一行依次排列)。
- 生成B时,先将所有(2,1)张量沿dim=1拼接成(2,10)张量,再转置得到(10,2),此时B的内存布局是原列转置后的顺序,和A的存储顺序不匹配。
- 单精度浮点数(
float32,PyTorch默认张量类型)的有效精度约为7位十进制数,累加计算均值时,不同的累加顺序会触发不同的舍入操作,最终产生可忽略的微小误差。你看到的5.9605e-08正好是float32的最小可表示精度之一(约为2^-25),完全符合该类型的误差范围。
2. torch.cat()的工作机制
torch.cat()是PyTorch用于张量拼接的核心函数,核心逻辑如下:
- 输入约束:所有待拼接张量必须在除拼接维度(由
dim参数指定)之外的其他维度上形状完全一致,否则会抛出维度不匹配的错误。 - 内存处理逻辑:
- 函数优先尝试直接基于现有内存创建新的张量视图(避免内存拷贝),如果现有内存无法形成连续的视图,则会将所有元素复制到新的连续内存块中。
- 比如生成B时,沿dim=1拼接(2,1)张量,得到的(2,10)张量是连续内存(每个原张量的列依次衔接);生成A时,每个转置后的(1,2)张量沿dim=0拼接,得到的(10,2)张量也是连续内存,但元素的存储顺序和B完全不同。
- 拼接逻辑:遍历所有输入张量,按指定维度将元素依次衔接,最终输出的张量在拼接维度上的大小等于所有输入张量对应维度大小之和,其余维度保持不变。
内容的提问来源于stack exchange,提问作者coco
相关产品推荐
相关产品推荐

