Tensor含义Tensor张量可以看作是一个多维数组它是标量、向量和矩阵向更高维度的扩展。张量维度数学等价物实例说明PyTorch创建示例0维​标量 (Scalar)​单个数值如损失值tensor(3.1416)1维​向量 (Vector)​一维数组如特征向量tensor([1, 2, 3])2维​矩阵 (Matrix)​二维数组如全连接层权重tensor([[1, 2], [3, 4]])3维及以上​高阶张量​如RGB图像3, 224, 224、图像批次16, 3, 224, 224[32, 100, 8, 64]BL HD- (B, H, L, D)这个形状的意义是32 个样本batch每个样本有 100 个序列位置每个位置的特征被拆分为 8 个注意力头每个头的特征维度是 64。Tensor的关键属性数据类型dtype指定张量中常见的数据类型如torch.float32、torch.float64、torch.int64、torch.bool等设备device表明张量当前存储在何处是cpu还是cuda:0GPU等形状shape一个元组表示张量在每个维度上的大小。是否需要梯度requires_grad一个布尔值指示是否需要为张量计算梯度。Tensor常见操作创建Tensor1. numpy转tensorimport numpy as np import torch numpy_array np.array([1, 1, 1]) tensor torch.from_numpy(numpy_array) print(tensor) tensor([1, 1, 1]) 2. 列表转tensorimport torch torch.tensor([[1, 2, 3], [4, 5, 6]])3. 利用大写接受以shape创建import torch torch.Tensor(3, 2) tensor([[2.1439e-34, 0.0000e00], [2.7678e-04, 4.5817e-41], [4.4842e-44, 0.0000e00]]) torch.empty(3, 2) tensor([[3.8401e-34, 0.0000e00], [3.8398e-34, 0.0000e00], [1.1210e-43, 0.0000e00]])4. 随机创建tensorimport torch torch.rand(2, 3) tensor([[0.3263, 0.2546, 0.9332], [0.3767, 0.1792, 0.3704]])tensor的切片start:end:step如果要存一张RGB的minist28*28图片image torch.rand(4, 3, 28, 28) image[1] # 获取第2张图片 image[0, 0].shape # 获取第1张图片第0个通道的图片形状 image[0, 0, 2, 4] # 返回像素灰度值的标量 # ...tensor的索引查找index_select/masked_select/takex1 np.array([[1,0,0,1],[1,1,0,1],[0,1,1,0],[0,1,1,1],[1,1,0,0]]) x2 np.array([[1,0,1],[1,1,1]]) x1 torch.from_numpy(x1).long() x2 torch.from_numpy(x2).long()tensor的拼接与拆分拼接cat拼接torch.cat(torsors, dim)import torch a torch.rand(4, 3, 18, 18) b torch.rand(5, 3, 18, 18) c torch.rand(4, 2, 18, 18) torch.cat([a, b], dim0) # 拼接得到(9, 3, 18, 18)的数据 torch.cat([a, c], dim1) # 拼接得到(4, 5, 18, 18)的数据stack拼接 import torch a torch.rand(4, 3, 18, 18) b torch.rand(4, 3, 18, 18) torch.stack([a, b], dim0).shape torch.Size([2, 4, 3, 18, 18]) 拆分split拆分 按尺寸拆分torch.split() a torch.rand(4, 4) b a.split(1, dim0) # 对第0维按照1个一份进行拆分拆分得到4个形状为(1, 4)的张量 a tensor([[0.3690, 0.0144, 0.2122, 0.7564], [0.1500, 0.1695, 0.3577, 0.3648], [0.7477, 0.8034, 0.0912, 0.0237], [0.2286, 0.0323, 0.5477, 0.3168]]) b (tensor([[0.3690, 0.0144, 0.2122, 0.7564]]), tensor([[0.1500, 0.1695, 0.3577, 0.3648]]), tensor([[0.7477, 0.8034, 0.0912, 0.0237]]), tensor([[0.2286, 0.0323, 0.5477, 0.3168]])) c a.split(2, dim0) a tensor([[0.8824, 0.4966, 0.5721, 0.3487], [0.3847, 0.2010, 0.5711, 0.4622], [0.7004, 0.3376, 0.7784, 0.9710], [0.9798, 0.1327, 0.9444, 0.1308]]) c (tensor([[0.8824, 0.4966, 0.5721, 0.3487], [0.3847, 0.2010, 0.5711, 0.4622]]), tensor([[0.7004, 0.3376, 0.7784, 0.9710], [0.9798, 0.1327, 0.9444, 0.1308]])) # 不同长度进行拆分(4, 4) 拆成 1 4和 3, 4 d, f a.split([1, 3], dim0) a tensor([[0.8824, 0.4966, 0.5721, 0.3487], [0.3847, 0.2010, 0.5711, 0.4622], [0.7004, 0.3376, 0.7784, 0.9710], [0.9798, 0.1327, 0.9444, 0.1308]]) d tensor([[0.8824, 0.4966, 0.5721, 0.3487]]) f tensor([[0.3847, 0.2010, 0.5711, 0.4622], [0.7004, 0.3376, 0.7784, 0.9710], [0.9798, 0.1327, 0.9444, 0.1308]])chunk拆分按块数拆分 a torch.rand(4, 4) a.chunk(4, dim0) # 将张量依第0维拆分成4个1 3 18 18的张量等效于a.split(1, dim0) (tensor([[0.7528, 0.9029, 0.5109, 0.1446]]), tensor([[0.7029, 0.9443, 0.9600, 0.4517]]), tensor([[0.2038, 0.8926, 0.0444, 0.0144]]), tensor([[0.5201, 0.1761, 0.5112, 0.8281]])) a tensor([[0.7528, 0.9029, 0.5109, 0.1446], [0.7029, 0.9443, 0.9600, 0.4517], [0.2038, 0.8926, 0.0444, 0.0144], [0.5201, 0.1761, 0.5112, 0.8281]])那么chunk和split有啥区别在torch中chunk和split都用于拆分张量他们的核心区别在于指定的拆分方式不同chunk指定的是想要得到的块数而split指定的是每块的尺寸特性torch.chunktorch.split核心逻辑​按块数拆分指定希望将张量在某个维度上分成几块。按尺寸拆分指定拆分后每块在该维度上的大小。关键参数​chunks(整数)目标块数。split_size_or_sections(整数或整数列表)每块的尺寸。尺寸计算​每块尺寸 总尺寸 / 块数 (尽可能平均)。尺寸由用户直接指定。整除问题​若不能整除前几块尺寸会稍大最后一块包含余下部分。实际块数可能小于指定值。若按整数拆分且不能整除最后一块尺寸会较小。实际块数由总尺寸和指定尺寸决定。灵活性​相对固定目标是均分。更灵活可通过整数列表精确控制每一块的大小。典型场景​需要将张量等分成N份例如数据并行、分批处理。1. 需要每块具有固定尺寸。2. 需要不规则拆分例如按特定比例分割数据集。重塑view在 PyTorch 中torch.Tensor.view()方法用于重塑张量Tensor的形状它返回一个与原张量数据共享内存的新张量即修改新张量会影响原张量。其核心作用是在不改变数据的情况下调整张量的维度和大小。基本用法view(*shape) - Tensor参数shape是一个整数序列表示新张量的形状。可以包含-1表示该维度的大小由其他维度自动计算。返回值重塑后的新张量与原张量共享数据。 x torch.rand(6) x tensor([0.9957, 0.2430, 0.0249, 0.4692, 0.8152, 0.2445]) x.shape torch.Size([6]) x.view(2, 3) tensor([[0.9957, 0.2430, 0.0249], [0.4692, 0.8152, 0.2445]]) x tensor([0.9957, 0.2430, 0.0249, 0.4692, 0.8152, 0.2445]) y x.view(2, -1) y tensor([[0.9957, 0.2430, 0.0249], [0.4692, 0.8152, 0.2445]]) x tensor([0.9957, 0.2430, 0.0249, 0.4692, 0.8152, 0.2445]) y[0] 1 y tensor([[1.9957, 1.2430, 1.0249], [0.4692, 0.8152, 0.2445]]) x tensor([1.9957, 1.2430, 1.0249, 0.4692, 0.8152, 0.2445]) 在 PyTorch 中view()和reshape()都是用于重塑张量形状的核心方法但二者在内存连续性要求、数据内存共享策略、适用场景上有显著区别。下面从核心差异、使用场景、底层实现三个维度详细拆解。一、核心区别对比特性view()reshape()内存连续性要求要求原张量是连续的内存中按顺序存储无间隔否则直接调用会报错。无连续性要求自动处理非连续张量内部会判断是否需要转换为连续张量。内存共享策略始终与原张量共享内存修改新张量会影响原张量。可能共享内存也可能创建数据副本1. 若原张量连续行为与view()一致共享内存2. 若原张量非连续会创建新的连续张量副本不共享内存。功能灵活性相对受限仅适用于连续张量。更灵活是view()的超集兼容所有重塑场景。底层实现直接修改张量的维度元信息stride/shape无数据拷贝效率极高。内部逻辑1. 若张量连续 → 调用view()2. 若张量非连续 → 调用contiguous()view()会拷贝数据。二、具体使用场景与示例1. 当张量是连续的时二者行为一致此时reshape()等价于view()均共享内存无数据拷贝。python运行import torch # 创建连续张量默认创建的张量都是连续的 x torch.tensor([[1, 2, 3], [4, 5, 6]]) # shape: (2, 3)is_contiguous() → True # view() 重塑 y_view x.view(3, 2) # reshape() 重塑 y_reshape x.reshape(3, 2) # 修改新张量原张量也会被改变共享内存 y_view[0, 0] 100 print(x) # 输出: tensor([[100, 2, 3], [ 4, 5, 6]]) y_reshape[1, 1] 200 print(x) # 输出: tensor([[100, 2, 3], [ 4, 5, 200]])2. 当张量是非连续的时二者行为不同张量经过transpose()/permute()等操作后会变成非连续内存存储顺序与维度顺序不一致此时view()会报错而reshape()能正常工作。python运行# 转置张量转置后张量变为非连续 x torch.tensor([[1, 2, 3], [4, 5, 6]]) x_trans x.transpose(0, 1) # shape: (3, 2)is_contiguous() → False # 直接调用 view() 会报错 try: x_trans.view(6) except RuntimeError as e: print(view() 报错, e) # 输出view size is not compatible with input tensors size and stride # reshape() 可正常工作内部创建了数据副本 x_reshape x_trans.reshape(6) x_reshape[0] 100 print(x) # 输出: tensor([[1, 2, 3], [4, 5, 6]])原张量未被修改因为是副本 # 若要让 view() 工作需先调用 contiguous() 转换为连续张量 x_trans_cont x_trans.contiguous() x_view x_trans_cont.view(6) x_view[0] 100 print(x) # 输出: tensor([[100, 2, 3], [4, 5, 6]])共享内存原张量被修改三、如何选择优先用reshape()如果你不确定张量是否连续比如经过转置、维度交换后reshape()更安全无需手动处理连续性。对代码简洁性要求高不想关注内存细节时。用view()的场景你明确知道张量是连续的比如刚创建的张量、经过contiguous()后的张量且需要极致的效率无数据拷贝。需强制共享内存修改新张量必须影响原张量时。四、补充reshape()与clone()的配合如果希望重塑后的张量与原张量完全独立不共享内存可在reshape()后调用clone()x torch.tensor([[1, 2, 3], [4, 5, 6]]) x_reshape x.reshape(6).clone() x_reshape[0] 100 print(x) # 输出: tensor([[1, 2, 3], [4, 5, 6]])原张量无变化总结view()轻量级重塑仅适用于连续张量始终共享内存效率高但有使用限制。reshape()通用型重塑兼容所有张量灵活安全是日常开发的首选。简单记reshape()是view()的 “增强版”解决了view()对非连续张量的限制代价是在非连续场景下会产生数据副本。tensor的运算乘法#tensor直接相*,其结果为element wise,矩阵乘法为torch.matmul或者 #高维tensor矩阵相乘实际上是对多个二维矩阵进行并行运算 atorch.rand(4,3,28,64) btorch.rand(4,3,64,32) ab#结果得到的形状为(4,3,28,32) a torch.rand(2, 2) b torch.rand(2, 3) a tensor([[0.9662, 0.4762], [0.6365, 0.2129]]) b tensor([[0.6583, 0.9811, 0.2332], [0.5010, 0.9111, 0.3308]]) a b tensor([[0.8746, 1.3818, 0.3828], [0.5257, 0.8185, 0.2189]]) a torch.rand(4, 16, 28, 28) b torch.rand(4, 16, 28, 32) (a b).shape torch.Size([4, 16, 28, 32])平方/开方 a torch.full([2, 2], 2) # 创建一个(2, 2)的全2矩阵 a tensor([[2, 2], [2, 2]]) a.pow(2) # a的每个元素都平方等价于 a ** 2 tensor([[4, 4], [4, 4]]) a ** 2 tensor([[4, 4], [4, 4]]) #开方 a.sqrt()#平方根 a**0.5#等价于上一句 #exp,log a.torch.exp(torch.ones(2,2)) torch.log(a) #近似 #floor()向下取整ceil()向上取整round()四舍五入。 #取整取小数 #trunc()取整frac()取小数 #clamp取范围 atorch.tensor([[3,5],[6,8]]) a.clamp(6)#得到[[6,6],[6,8]],小于6的都变为6 a.clamp(5,6)#得到[[5,5],[6,6]],小于下限变为下限大于上限变为上限。爱因斯坦标示库代码操作概览操作类别代码示例功能描述关键参数说明1. 转置​rearrange(x, b c h w - b h c w)交换维度c通道和h高度的顺序。模式字符串直观表示维度重排。2. 变形​rearrange(x, b c h w - (b c) h w)将批次和通道维度合并改变张量形状而不改变数据顺序。括号()表示合并维度。3. 图像分块​rearrange(x, b c (h1 p1) (w1 p2) - b c (h1 w1) (p1 p2), p12, p22)将图像分割成不重叠的小块例如2x2的patch。(h1 p1)表示将原高度维度拆分为两个新维度。4. 池化/规约​reduce(x, b c h w - b c, max)对高度和宽度维度进行最大化操作实现全局最大池化。模式定义输出形状max指定规约操作。5. 堆叠张量列表​rearrange(tensor_list, n b c h w - n b c h w)将张量列表堆叠成一个新的张量增加一个批次维度。对张量列表进行操作。6. 扩维与复制​rearrange(x, b c h w - b c h w 1)在张量末尾增加一个大小为1的维度。类似torch.unsqueeze。repeat(out1, b c h w 1 - b c h w 2)在新增的维度上将数据复制2次。类似torch.tile。repeat(x, b c h w - b c (2 h) (2 w))​在空间维度上进行上采样将图像尺寸放大2倍。​(2 h)表示将原高度维度数据按特定模式重复。import torch from einops import rearrange, reduce, repeat x torch.randn(2,3,4,4) # 4D Tensor: bs*c*h*w ## 1. 转置 out1 x.transpose(1,2) out2 rearrange(x, b c h w - b h c w) ## 2. 变形 out1 x.reshape(6,4,4) out2 rearrange(x, b c h w - (b c) h w) out3 rearrange(out2, (b c) h w - b c h w, b2) ## 3. image2patch out1 rearrange(x, b c (h1 p1) (w1 p2) - b c (h1 w1) (p1 p2), p12, p22) # p1,p2是patch的高度和宽度 out2 rearrange(out1, b c n a - b n (a c)) # shape:[batchsize, num_patch, patch_depth] ## 4. 求平均池化 out1 reduce(x, b c h w - b c h, mean) # mean, min, max, sum, prod out2 reduce(x, b c h w - b c h 1, sum) # keep dimension out3 reduce(x, b c h w - b c, max) ## 5. 堆叠张量 tensor_list [x, x, x] out1 rearrange(tensor_list, n b c h w - n b c h w) ## 6. 扩维与复制 out1 rearrange(x, b c h w - b c h w 1) # 类似 torch.unsqueeze out2 repeat(out1, b c h w 1 - b c h w 2) # 类似 torch.tile out3 repeat(x, b c h w - b c (2 h) (2 w))为什么使用einops从您提供的代码对比和上面的讲解可以看出einops的核心优势在于代码的可读性和意图的清晰表达。声明式语法你只需要描述“你想让张量变成什么样子”如- b c (2 h) (2 w)而不需要一步步指挥程序“如何去做”比如先unsqueeze再tile。统一的API无论是转置、重塑、切片、重复还是规约操作都可以通过rearrange、repeat、reduce这几个函数和统一的模式字符串来完成大大降低了记忆负担。内置维度检查einops会自动验证变换模式的合理性有效减少因维度计算错误导致的Bug参考资料https://cloud.tencent.com/developer/article/2345313https://cloud.baidu.com/article/2995479https://datawhalechina.github.io/thorough-pytorch/%E7%AC%AC%E4%BA%8C%E7%AB%A0/2.1%20%E5%BC%A0%E9%87%8F.html