numpy是在1D数组的垂直堆栈上的Hadmard产品,其循环速度比遍历1D数组列表并在每个数组上执行Hadamard(基于元素的)产品的速度要快得多(这很有意义,无论如何我都对其进行了测试) 。
在这种情况下,我需要在一组numpy数组与另一组numpy数组之间执行Hadamard乘积:
stacked_arrays = np.vstack([1D-arrays...])
stacked_arrays *= np.power(factor, np.arange(1, num_arrays))
但是,我需要执行此操作来更改列表中的每个组件1D数组,并且此操作需要进行很多操作。我知道这听起来像是一个奇怪的功能,但是有没有办法做到这一点而无需循环:
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数组]列表是较大的1D数组列表的子列表。
此较大的列表是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
答案 0 :(得分:1)
让我们分解问题。您希望有一个对所有可变数组的引用列表,但希望能够对它们作为一个块执行操作。
我认为您的做法是落后的。与其尝试将数组打包和解包到单独的缓冲区中,不如将视图维护在单个缓冲区中。
替换当前循环
m = [np.array([1, 2, 4, 5], dtype=np.float64) for _ in range(100)]
具有单个缓冲区,并查看每一行:
buf = np.vstack([np.array([1, 2, 4, 5], dtype=np.float64) for _ in range(100)])
m = list(buf) # check that m[0].base is b
现在您有了一个数组m
的列表,您可以分别修改每个数组。只要您将修改保留在原位并且不重新分配列表元素,所有更改将直接显示在buf
中。同时,您可以在buf
上执行批量计算,只要就地进行,m
就会反映所有更改。
实际上,您甚至不需要m
。注意list(buf)
是如何在块的每一行中创建视图的。您可以轻松地直接索引到缓冲区中。例如,m[3][8]
通常以buf
的形式写为buf[3, 8]
,但是您也可以使用buf
是序列的事实,并写成buf[3][8]
。这种方法效率较低,因为它每次都会创建一个新视图(buf[3]
),但是创建的次数很少。
要提取随机的行子集,还可以使用序列索引。假设您的缓冲区保留了最后M
行,您希望对其进行改组并从中提取N
行的子序列。您可以通过创建索引数组并反复遍历索引来完成此操作:
indices = np.arange(M)
# inside the loop:
np.random.shuffle(indices)
chunk = buf[indices[:N]]
# do your math on `chunk`
只要indices
不变,并且您认为随机播放足够随机,就不需要重新分配M
或对其进行重新排序。