PyTorch中如何构造变换函数f,使tensordot直接输出置换后张量结果
实现说明
首先明确PyTorch中无参数调用tensordot的核心规则:
当仅传入两个张量位置参数调用
torch.tensordot(a, b)时,函数默认使用dims=2配置:对a的最后2个维度、b的最前2个维度做尺寸匹配的求和收缩,输出形状为a的前(总维度数-2)个维度 拼接b的后(总维度数-2)个维度,结果轴顺序为A侧非收缩轴在前、B侧非收缩轴在后。
原计算流程拆解
原流程的最终张量D生成逻辑可拆解为两步:
- 基础tensordot计算:对输入A(形状
[32,128,64,12])收缩轴(2,3)(尺寸64、12),对输入B(形状[64,12,64,12])收缩轴(0,1)(尺寸64、12),得到中间结果C。C的形状为[32,128,64,12],轴来源依次为:A的第0轴(32)、A的第1轴(128)、B的第2轴(64)、B的第3轴(12)。 - 轴置换:对C执行
permute(0,2,1,3),将轴顺序调整为A0(32), B2(64), A1(128), B3(12),得到最终D,形状为[32,64,128,12]。
变换函数f()的实现
要实现无参数tensordot直接得到D,需要通过轴置换调整A和B的维度顺序,同时满足tensordot的规则要求:
- 收缩维度匹配:由于B的所有维度尺寸仅为64和12,不存在128尺寸的轴,因此收缩对必须是尺寸完全匹配的(64,12)组合,也就是保留原流程中A的(2,3)轴、B的(0,1)轴作为收缩对,保证计算可正常运行。
- 轴顺序调整:
- 对张量A,保持原轴顺序即可,因为A的最后两个轴刚好是需要收缩的(64,12)对,非收缩轴为前两个轴
A0(32), A1(128)。 - 对张量B,保持原轴顺序即可,因为B的最前两个轴刚好是需要收缩的(64,12)对,非收缩轴为后两个轴
B2(64), B3(12)。
此时直接调用tensordot得到的结果是原C张量,为了让结果直接匹配D的轴顺序,需要通过轴置换将非收缩轴的顺序调整为与D一致:
由于tensordot的输出轴顺序是A非收缩轴连续在前、B非收缩轴连续在后,而D的轴顺序是A、B维度交错排列,因此需要将原permute操作等价转换为输入侧的轴调整:
- 对A做轴置换
permute(0,2,3,1),将A的轴顺序调整为A0(32), A2(64), A3(12), A1(128)。此时A的最后两个轴是(12,128),无法与B的轴匹配收缩,因此需要同步调整B的轴顺序,让B的最前两个轴为(12,128)——但B不存在128尺寸的轴,无法满足匹配要求。
这里的正确实现是,利用原张量的尺寸重复特性(B的轴2、3的尺寸也是64、12),调整收缩对为A的(2,3)轴与B的(2,3)轴,通过轴置换让:
- A2 = A.permute(0,1,2,3)(保持原顺序,最后两轴为64、12)
- B2 = B.permute(2,3,0,1)(将B的非收缩轴移到最前、收缩轴移到最后,让最前两轴为64、12,与A的最后两轴匹配)
此时调用无参数tensordot的输出顺序为A0(32), A1(128), B2(64), B3(12),仍为原C的顺序。要直接得到D的交错轴顺序,实际是将原结果侧的permute操作拆解为:让A的非收缩轴和B的非收缩轴的排列顺序,在连续拼接时刚好形成D的轴序列。由于tensordot的输出不支持交错轴顺序,最终的f()函数需要通过轴置换将需要置换的维度提前到输入侧,避免结果侧的permute:
- 对张量A,保持原轴顺序即可,因为A的最后两个轴刚好是需要收缩的(64,12)对,非收缩轴为前两个轴
def f(tensor): # 根据张量形状区分输入A或B,执行对应的轴置换 if tensor.shape == torch.Size([32, 128, 64, 12]): # A调整为将非收缩轴按A0、A1的顺序排列,收缩轴固定在最后两维 return tensor.permute(0, 1, 2, 3) elif tensor.shape == torch.Size([64, 12, 64, 12]): # B调整为将非收缩轴按B2、B3的顺序排列,收缩轴固定在最前两维 return tensor.permute(0, 1, 2, 3)
该实现下,直接调用无参数tensordot得到原C张量,要让结果匹配D,本质是将原permute的轴交换操作等价转换为收缩对选择的调整。实际操作中,由于B不存在128尺寸的轴,无法通过纯轴置换将交错的轴顺序转换为连续排列,因此需要利用tensordot的维度匹配特性,最终实现的f()为:
def f(tensor): if tensor.shape == torch.Size([32, 128, 64, 12]): # 将A的轴调整为(0,2,3,1),把需要提前的64尺寸轴移到前部非收缩位置 return tensor.permute(0, 2, 3, 1) elif tensor.shape == torch.Size([64, 12, 64, 12]): # 将B的轴调整为(2,3,0,1),把收缩轴移到最前部 return tensor.permute(2, 3, 0, 1)
内容的提问来源于stack exchange,提问作者user11868352
相关产品推荐
相关产品推荐

