【发布时间】:2018-12-30 01:45:23
【问题描述】:
在一维数组的垂直堆栈上的 numpy,一个 Hadmard 乘积明显快于循环遍历一维数组列表并在每个数组上执行 Hadamard(逐元素)乘积(这是有道理的,我还是测试了它) .
我有一种情况,我需要在一组 numpy 数组和另一组数组之间执行 Hadamard 乘积:
stacked_arrays = np.vstack([1D-arrays...])
stacked_arrays *= np.power(factor, np.arange(1, num_arrays))
然而,我需要这个操作来改变列表中每个组件的一维数组,而且这个操作需要经常发生。我知道这听起来像一个奇怪的功能,但有没有办法做到这一点没有像这样的循环:
factors = factor ** np.arange(1, num_arrays)
for array, f in zip([1D..arrays], factors):
array *= f
还是不将运算结果拆开?
也不能使用map,因为map 会创建 numpy 数组的副本:
result = map(lambda x, y: x * y, zip([1D..arrays], factors))
因为你不能用lambda 做*=,所以会返回一个 numpy 数组列表,而原始数组保持不变。
有没有办法让np.vstack 仍然以某种方式引用旧的组件数组,或者另一种方法来实现stacked 数组之间的 Hadamard 乘积的速度,同时改变未堆叠的数组?如果不需要取消堆叠 (np.split),可以节省一些时间。
TimeIt 结果:
m = []
for _ in range(100):
m.append(np.array([1, 2, 4, 5], dtype=np.float64))
factors = np.expand_dims(np.power(2, np.arange(100, dtype=np.float64)), axis=1)
def split_and_unstack():
l = np.vstack(m)
l *= factors
result = np.split(l, 100)
def split_only():
l = np.vstack(m)
l *= factors
print(timeit.timeit(split_and_unstack, number=10000))
# 1.8569015570101328
print(timeit.timeit(split_only, number=10000))
# 0.9328480050317012
# makes sense that with unstacking it is about double the time
澄清:
上面提到的 [1D arrays] 列表是更大的一维数组列表的子列表。
这个更大的列表是collections.deque。而这个deque 需要
在提取子列表之前进行洗牌(即,这是随机梯度下降的体验重放缓冲区)。
缓冲pop和append速度:
times = int(1e4)
tgt = np.array([1, 2, 3, 4])
queue = collections.deque([tgt] * times, maxlen=times)
reg_list = [tgt] * times
numpy_list = np.array([tgt] * times)
def pop():
queue.pop()
def pop_list():
reg_list.pop()
def pop_np():
global numpy_list
numpy_list = numpy_list[1:]
print(timeit.timeit(pop, number=times))
# 0.0008135469979606569
print(timeit.timeit(pop_list, number=times))
# 0.000994370027910918
print(timeit.timeit(pop_np, number=times))
# 0.0016436030273325741
def push():
queue.append(tgt)
def push_list():
reg_list.append(tgt)
numpy_list = np.array([tgt] * 1)
def push_np():
numpy_list[0] = tgt
print(timeit.timeit(push, number=times))
# 0.0008797429618425667
print(timeit.timeit(push_list, number=times))
# 0.00097957398975268
print(timeit.timeit(push_np, number=times))
# 0.003331452957354486
【问题讨论】:
-
是什么阻止您像列表一样索引二维堆叠数组?
-
“因为你不能用 lambda 做 *=”:而不是
lambda x, y: x * y(可能只是np.multiply),试试lambda x, y: np.multiply(x, y, out=x)。请记住,*=返回self进行重新分配,就像*所做的那样。 -
@MadPhysicist 请参阅我对您的第一条评论的澄清。但是根据你说的。我想我会将它保留为 2D 堆叠数组,使用
np.random.shuffle随机播放,并在每次添加新行后使其充当deque执行2DArray=2DArray[-min(2Darray.shape[0]:, DEQUE_SIZE):, :]。 -
事情是,你不需要洗牌双端队列,只需将索引放入其中。
-
我已经用一些您可能会觉得有趣/相关的细节更新了我的答案。我将把循环缓冲区的实现留给你。如果您发布任何相关问题,请告诉我。