Tensor相关的不同维度变换操作

Tensor相关的不同维度变换操作

维度变换操作

Tensor中涉及维度的操作主要包括:

  1. 重排维度:transpose,swapaxes,permute,这些操作不会改变维度的个数,只会改变各个维度的顺序,例如(dim0,dim1,dim2,dim3) 变成 (dim2,dim3,dim0,dim1)。
  2. 重组维度:viewreshape,这些操作,是会改变维度个数的,因为可以对维度进行重新排列,shape中不仅维度个数可能会改变,每一个维度的长度也会改变。

重排维度

使用transpose

在PyTorch中,假设我们有一个形状为 2,3,42,3,4 的张量,我们想要交换最后两个维度。

import torch

x = torch.randn(2, 3, 4)
x_transposed = x.transpose(1, 2)  # 交换维度1和维度2
print(x_transposed.shape)  # 输出: torch.Size([2, 4, 3])

使用permute

如果我们想要重新排列维度,例如将形状为 2,3,42,3,4 的张量重新排列为 4,2,34,2,3。

x_permuted = x.permute(2, 0, 1)  # 重新排列维度
print(x_permuted.shape)  # 输出: torch.Size([4, 2, 3])

重组维度

张量的维度变换常见的方法有torch.view()torch.reshape(),两个的区别:
torch.view()是直接在原tensor上进行变换,返回的tensor和原tensor共用一块地址;

torch.reshape() 返回的新tensor不是和原tensor共用一块地址。但是此函数并不能保证返回的是其拷贝值,所以官方不推荐使用。推荐的方法是我们先用 clone() 创造一个张量副本然后再使用 torch.view()进行函数维度变换 。

注:使用 clone() 还有一个好处是会被记录在计算图中,即梯度回传到副本时也会传到源 Tensor 。

view方法就是指定一个shape来改变tensor的形状;同时他还可以自动推断维度大小;

import torch

# 创建一个1x4的Tensor
x = torch.randn(1, 4)
print(f"Original Tensor:\n{x}\n")

# 使用view方法改变形状为2x2
y = x.view(2, 2)
print(f"Reshaped Tensor (2x2):\n{y}")

# 创建一个Tensor
x = torch.randn(3, 2)
print(f"Original Tensor (3x2):\n{x}\n")

# 使用view改变形状,自动计算第二维度
y = x.view(6, -1)
print(f"Reshaped Tensor (6x1):\n{y}")

注意事项

  • 内存连续性view要求Tensor在内存中是连续的。如果进行操作使得Tensor不再连续,比如使用transpose,然后尝试调用view,可能会导致运行时错误。此时,可以先调用.contiguous()方法来确保Tensor在内存中连续,然后再使用view
pythonCopy code
x = x.transpose(0, 1).contiguous().view(-1, 2)
  • 共享存储:由于view操作返回的Tensor与原始Tensor共享数据,因此对新视图的任何修改都会反映到原始Tensor上,反之亦然。

爱因斯坦公式

爱因斯坦表达式通过操作index(dim/axis)匹配对应的矩阵运算。和前面几个操作不同的是,torch.einsum不仅可以进行单个矩阵维度的重排、重组,还可以完成多个矩阵的矩阵加法矩阵乘法元素乘法等运算

->左侧表示输入的矩阵shape,->右侧表示输出的矩阵shape

因此其实通过einsum操作可以实现上述所有维度、形状的变换操作;

  • permute 重排:单个输入矩阵,->左右维度数量不变,只改变顺序,如交换i和j维度,ij->ji
import torch
tensor = torch.randn(16, 8, 32, 16).to("cuda")
# torch.Size([16, 8, 32, 16])
tensor = torch.einsum("bhsd->bhds", tensor)
# torch.Size([16, 8, 16, 32])

  • sum求和**:单个输入矩阵,->右侧缺少哪些维度,就按照哪些维度求和,如按照j维度求和,ij->i
import torch
tensor = torch.randn(16, 8, 32, 16).to("cuda")
# torch.Size([16, 8, 32, 16])
tensor = torch.einsum("bhsd->bh", tensor)
# torch.Size([16, 8])

  • matrix multi 矩阵乘法->左边多个输入矩阵逗号分隔,->左边是单个矩阵,沿左边两者重复出现右边消失的维度进行乘法,如沿k维度进行矩阵乘法,ij,jk->ik
tensor1 = torch.randn(2, 3).to("cuda")
tensor2 = torch.randn(3, 5).to("cuda")
tensor = torch.einsum("ij, jk -> ik", tensor1, tensor2)
# (2,3) @ (3,5) = (2,5)

  • 组合操作:先沿着j维度进行矩阵乘法,再沿着k维度进行求和:
tensor1 = torch.randn(2, 3).to("cuda")
tensor2 = torch.randn(3, 5).to("cuda")
tensor = torch.einsum("ij, jk -> i", tensor1, tensor2)
# (2,3) @ (3,5) = (2,5)

更加复杂的组合操作:模拟attention score,先自动进行转置,然后最后两个维度进行矩阵乘法,其中虽然都有seq_len,但因为output输出矩阵中不能出现两个相同的字母,所以不能都用s命名,因此使用i和j

import torch
# key 和 value 都是[batch_size, heads, seq_len, head_dim]
query = torch.randn(16, 8, 32, 16).to("cuda")
key = torch.randn(16, 8, 32, 16).to("cuda")
attention_score = torch.einsum("bhid, bhjd -> bhij", query, key)  # bhid, bhjd -> bhid, bhdj -> bhij
# torch.Size([16, 8, 32, 32])

# 等价操作
attention_score = query @ key.transpose(-2, -1)
attention_score = torch.matmul(query, key.transpose(-2, -1))
  • element-wise multi 元素乘法->左边多个相同shape的矩阵,->右边单个和做左边相同shape的矩阵。矩阵对应元素相乘,也叫hadamard product
import torch
tensor1 = torch.randn(16, 8, 32, 16).to("cuda")
tensor2 = torch.randn(16, 8, 32, 16).to("cuda")

tensor = torch.einsum("bhsd,bhsd->bhsd", tensor1, tensor2)
# torch.Size([16, 8, 32, 16])

# 等价操作
tensor = tensor1 * tensor2

  • dot product 矩阵点积->左边多个相同shape的矩阵,->是空的(求和sum)。即,先逐元素相乘,然后全部求和
import torch
tensor1 = torch.randn(16, 8, 32, 16).to("cuda")
tensor2 = torch.randn(16, 8, 32, 16).to("cuda")

tensor = torch.einsum("bhsd,bhsd-> ", tensor1, tensor2)
# tensor是一个值

# 等价操作
tensor = sum(tensor1 * tensor2)

关于dim参数的操作解析

当在PyTorch中指定dim参数时,它通常意味着操作将沿着指定的维度(dim)进行。这个概念对于理解如何在多维数据上执行各种操作至关重要。不同的操作可能会沿着指定的维度执行求和、取平均、最大化、最小化等计算,或者在这个维度上进行某种形式的变换(如转置、重排、扩展等)。

沿着dim进行计算,计算之后,这个维度就会变成1,最后就会消失

例如初始shape为[4,5],进行sum(dim = 0)操作,即,沿着dim0这个维度进行求和,dim0这个维度就会消失:
image-20240401171239048

torch.sum

如果你对一个形状为 (2,3,4)(2,3,4) 的Tensor执行torch.sum(tensor, dim=1),操作将在维度1上进行求和,即将这个Tensor中每个 3×4)3×4) 的切片相加,结果是一个形状为 (2,4)(2,4) 的Tensor。

torch.mean

类似地,如果对同一个Tensor执行torch.mean(tensor, dim=2),则会计算每个 (2×3)(2×3) 切片的平均值,得到一个形状为 (2,3)(2,3) 的Tensor。

维度编号

在PyTorch中,维度(dim)是从0开始编号的,对于一个形状为 (�0,�1,�2,…,��)(D0,D1,D2,…,D**n) 的Tensor:

  • dim=0 指的是最外层的维度(在这个例子中是 �0D0),它通常代表批大小(batch size)或者数据集中独立样本的数量。
  • dim=1 通常代表特征的维度(在图像处理中可能是通道数,在序列处理中可能是序列的长度)。
  • 更高的dim值对应于Tensor更深层次的维度,它们的具体含义将根据数据的结构和应用场景而变化。

注意事项

  • 广播(Broadcasting):在进行某些操作时,PyTorch会自动应用广播机制来匹配不同形状的Tensor。在指定dim进行操作时,也需要考虑到广播规则,确保操作按预期执行。
  • 保持维度(Keepdim):许多操作允许通过keepdim=True参数保持输出Tensor的维数不变,即在执行操作如求和、平均等后,原本会被移除的维度会以大小为1的形式保留。

参考文献:

Tensor轴变换 axis 或 dim(transpose、permute、view、reshape、einsum)_tensor dim-CSDN博客

  • 11
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值