如何将np.einsum分解为np.dot操作以提升张量计算效率?
高效实现4维张量与系数的求和运算
你的需求是计算4维张量与对应系数的乘积求和,替代嵌套循环和默认np.einsum的低效实现,下面提供几种高效方案:
1. 优化版np.einsum(简洁且高效)
默认np.einsum用纯Python解析表达式,但开启optimize=True后,numpy会自动将表达式转换为底层BLAS优化的点积/矩阵乘法操作,既保留代码简洁性,又大幅提升速度:
import numpy as np D = np.arange(4).reshape(2, 2) i, j, k, l = 1, 0, 1, 1 tensor_S = np.arange(16).reshape(2, 2, 2, 2) # 开启优化的einsum SUM_ijkl_opt = np.einsum('a,b,c,d,abcd', D[i], D[j], D[k], D[l], tensor_S, optimize=True) print(SUM_ijkl_opt) # 输出1325,与原结果一致
2. 分步张量点积(手动拆解为BLAS操作)
你的求和本质是 ( \sum_{a,b,c,d} D_i(a) \cdot D_j(b) \cdot D_k(c) \cdot D_l(d) \cdot S(a,b,c,d) ),可以通过np.tensordot逐步对每个维度做求和点积,tensordot直接调用BLAS库,性能拉满:
# 第一步:对a维度求和 temp = np.tensordot(tensor_S, D[i], axes=([0], [0])) # 第二步:对b维度求和 temp = np.tensordot(temp, D[j], axes=([0], [0])) # 第三步:对c维度求和 temp = np.tensordot(temp, D[k], axes=([0], [0])) # 第四步:对d维度求和,得到标量结果 SUM_ijkl_dot = np.dot(temp, D[l]) print(SUM_ijkl_dot) # 输出1325,与原结果一致
也可以写成链式调用:
SUM_ijkl_chain = np.tensordot( np.tensordot( np.tensordot(tensor_S, D[i], axes=([0], [0])), D[j], axes=([0], [0]) ), D[k], axes=([0], [0]) ).dot(D[l])
3. Reshape + 矩阵乘法(灵活适配不同维度)
如果需要更灵活的维度操作,可以将张量reshape为矩阵,再通过np.dot逐步收缩维度:
# 把4维张量reshape为(2, 8),第一个维度对应a,剩下的b/c/d合并为一维 temp = tensor_S.reshape(2, -1).T.dot(D[i]) # 再reshape为(2,4),对应b维度和c/d合并维度 temp = temp.reshape(2, -1).T.dot(D[j]) # reshape为(2,2),对应c和d维度 temp = temp.reshape(2, -1).T.dot(D[k]) # 最后和D[l]点积得到标量 SUM_ijkl_reshape = temp.dot(D[l]) print(SUM_ijkl_reshape) # 输出1325,与原结果一致
性能建议
- 优先使用优化版einsum,兼顾简洁性和性能,numpy会自动选择最优运算路径;
- 若需要更精细的控制,选择
tensordot,它专门针对张量收缩做了优化; - 对于超大规模张量,可考虑使用
cuPy替代numpy,利用GPU加速计算。
内容的提问来源于stack exchange,提问作者youmu
相关产品推荐
相关产品推荐

