【发布时间】:2023-01-20 03:24:48
【问题描述】:
在时间关键的代码片段中,我需要沿张量的不同轴应用一个函数并对结果求和。一个奇特的特征是张量 (ns_test) 的轴数可以很大。我想出了两个实现,我将当前轴 (moveaxis) 移动到第零个 (h_zero) 或最后一个 (h_last) 位置,应用函数,然后将轴移回。我不确定这是最好的方法。
import numpy as np
import time
def h_last(state, km, ns):
new_state = np.zeros_like(state)
for i in range(ns):
a = np.moveaxis(state, i+1, -1).copy()
for k in range(km):
a[..., k] = (k+0.5) * a[..., k]
new_state += np.moveaxis(a, -1, i+1)
return new_state
def h_zero(state, km, ns):
new_state = np.zeros_like(state)
for i in range(ns):
a = np.moveaxis(state, i+1, 0).copy()
for k in range(km):
a[k, ...] = (k+0.5) * a[k, ...]
new_state += np.moveaxis(a, 0, i+1)
return new_state
# ==================== init ============================
km_test = 4
ns_test = 7
nreps = 100
dims = tuple([ns_test] + [km_test] * ns_test)
y= np.random.rand(*dims)
# =================== first run =============================
tic = time.perf_counter()
for i in range(nreps):
yy = h_last(y, km_test, ns_test)
toc = time.perf_counter()
print(f"Run time h_last {toc - tic:0.4f} seconds")
# =================== second run =============================
tic = time.perf_counter()
for i in range(nreps):
yyy = h_zero(y, km_test, ns_test)
toc = time.perf_counter()
print(f"Run time h_zero {toc - tic:0.4f} seconds")
print(np.linalg.norm(yy-yy))
我有点惊讶第零轴的性能更好(我认为 python 在内部使用 C 顺序进行存储)。但我的主要问题是如何进一步加速代码?我查看了apply_along_axis,但这似乎很慢。
【问题讨论】:
-
为什么要复制
moveaxis结果?moveaxis的全部意义不就是要快,因为它只是一个视图吗? -
@chrslg 因为我在
a[..., k] = (k+0.5) * a[..., k]这一行修改了a并且因为我不想通过这个操作破坏state。 -
是的,我明白了。但这使整个事情变得非常缓慢。因为你为每个轴复制。也许你应该复制
state并尽可能长时间地工作。 -
@chrslg 我考虑过,但无法提出有效的解决方案。
-
因此,简而言之,从“C 顺序/缓存”的角度来看,
a[k,...] *=操作比a[...,k] *=操作快。我想这就是你所看到的。如果我总结一下我对这个性能问题的所有抱怨:如果你没有处理move_axis结果的副本,那么,你使用的假轴位置是什么根本无关紧要。但既然你是,zero比last更好是很正常的,因为重要的是循环的最内层索引是最后一个轴(按 C 顺序)。这里循环的最内层索引出现在numpy的隐式循环中a[k, ...] *= ...
标签: python numpy optimization tensor