You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NumPy中批量轴为何前置?切换约定需考量性能问题

关于NumPy批量轴约定的疑问与建议

问题背景

我开发的所有包都将后置轴用作批量轴,这在我看来更符合直觉,但NumPy始终把前置轴作为批量轴。现在我考虑切换到NumPy的约定,只为让NumPy用户更容易理解我的代码,想问问有没有什么建议?

性能对比测试

从性能角度来看,切换到前置批量轴可能是个糟糕的选择,以下是测试代码及结果:

import numpy as np

np.random.seed(6512)
a = np.random.rand(50000, 8, 3, 3)

np.random.seed(85742)
b = np.random.rand(50000, 8, 3, 3)

c = a @ b
# 每次循环耗时19.8毫秒±543微秒(7次运行,每次100循环的均值±标准差)

d = np.einsum("...ik,...kj->...ij", a, b)
# 每次循环耗时84.1毫秒±2.4毫秒(7次运行,每次10循环的均值±标准差)

# 现在使用后置轴(确保转置后的数据为C连续数组)
A = np.ascontiguousarray(np.transpose(a, [2, 3, 0, 1])) # A_ijab
B = np.ascontiguousarray(np.transpose(b, [2, 3, 0, 1])) # B_ijab

C = (B.T @ A.T).T # (C^T)_baji = B_bajk A_baki -> C_ijab
# 每次循环耗时16.9毫秒±1.82毫秒(7次运行,每次100循环的均值±标准差)

D = np.einsum("ik...,kj...->ij...", A, B)
# 每次循环耗时17.2毫秒±842微秒(7次运行,每次100循环的均值±标准差)

assert np.allclose(c, d)
assert np.allclose(C, D)
assert np.allclose(np.transpose(D, [2, 3, 0, 1]), d)
assert np.allclose(np.transpose(C, [2, 3, 0, 1]), c)

更复杂的einsum运算测试

# 交叉并矢积
# ----------------------

E = np.einsum("ik...,jl...->ijkl...", A, B)
# 每次循环耗时76.5毫秒±2.22毫秒(7次运行,每次10循环的均值±标准差)

e = np.einsum("...ik,...jl->...ijkl", a, b)
# 每次循环耗时207毫秒±3.29毫秒(7次运行,每次10循环的均值±标准差)

assert np.allclose(np.transpose(E, [4, 5, 0, 1, 2, 3]), e)

核心建议

  • 优先权衡性能与兼容性:如果你的包性能敏感(比如涉及大量矩阵运算、einsum操作),从测试结果看后置批量轴的性能优势明显,强行切换会导致运行效率大幅下降,这时候兼容性的代价可能过高。
  • 提供转换接口:不需要完全切换约定,而是给用户提供批量轴前后置转换的便捷函数,既保留自己的设计直觉,也让NumPy用户能轻松适配你的包。
  • 文档明确约定:在包的文档中清晰标注你使用的批量轴位置,给出示例代码说明如何与NumPy的前置轴风格交互,降低用户的理解成本。
  • 考虑生态适配:如果你的包需要深度集成NumPy、PyTorch等主流库(它们大多使用前置批量轴),长期来看统一约定能减少用户的转换代码,但前提是性能损失在可接受范围内。

内容的提问来源于stack exchange,提问作者adtzlr

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 05:37:18