Python 数组列表上Hadamard的效率
numpy是一个一维数组垂直堆栈上的Hadamard产品,它比循环遍历一维数组列表并在每个数组上执行Hadamard(元素级)产品要快得多(这很有意义,我无论如何都对它进行了测试) 我需要在一组numpy阵列和另一组numpy阵列之间执行Hadamard乘积:Python 数组列表上Hadamard的效率,python,arrays,numpy,Python,Arrays,Numpy,numpy是一个一维数组垂直堆栈上的Hadamard产品,它比循环遍历一维数组列表并在每个数组上执行Hadamard(元素级)产品要快得多(这很有意义,我无论如何都对它进行了测试) 我需要在一组numpy阵列和另一组numpy阵列之间执行Hadamard乘积: stacked_arrays = np.vstack([1D-arrays...]) stacked_arrays *= np.power(factor, np.arange(1, num_arrays)) 然而,我需要这个操作来改变列
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
创建numpy数组的副本,因此也不能使用map
:
result = map(lambda x, y: x * y, zip([1D..arrays], factors))
由于无法使用lambda
执行*=
,因此将返回numpy数组列表,保留原始数组未经修改
有没有办法让np.vstack
仍然以某种方式引用旧的组件数组,或者有另一种方法可以在对未堆叠的数组进行变异时,在堆叠的
数组之间实现哈达玛积的速度?因为如果不需要进行拆垛(np.split
),可以节省一些时间
时间结果:
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
让我们把这个问题分解一下。您希望有一个对数组的引用列表,这些数组都是可变的,但是您希望能够作为一个块对它们执行操作 我认为你的方法是倒退的。与其尝试将阵列打包和解包到单独的缓冲区中,不如将视图维护到单个缓冲区中 替换当前循环
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`
只要
M
没有变化,并且您相信洗牌足够随机,您就不需要重新分配或重新排序索引。是什么阻止您将2D堆叠数组作为列表索引?“因为您不能使用lambda*=而不是lambda x,y:x*y
(可以是np.multiply
),尝试lambda x,y:np.multiply(x,y,out=x)
。记住,*=
返回self
进行重新分配,就像*
一样。@mad物理学家看到我对你的第一条评论的澄清。但是基于你所说的。我想我会把它作为一个2D堆叠数组,用np.random.shuffle
进行洗牌,并使它充当deque
do2DArray=2DArray[-min(2DArray.shape[0]:,DEQUE_SIZE:,:]
每次追加新行后。问题是,您不需要将数据混洗,只需将索引放入其中。我已经用一些您可能会感兴趣/相关的细节更新了我的答案。我将把循环缓冲区的实现留给您。如果您发布任何相关问题,请告诉我。因此,关于循环,我意识到了一件事ar缓冲区,将2D数组转换为循环缓冲区牺牲了。我见过一些创建Numpy循环缓冲区和的实现。我做了一些测试(如上),比较了列表、deque和np.array的pop和append(实际上是setitem,因为它是循环缓冲区)更新:所以我确实找到了另一个使用numpy的循环缓冲区实现,它声称它比笔记本条目63中的deque
:快。@dylan。为什么要使用append或insert?你拥有的块已经是一个循环缓冲区了。你只需要跟踪哪一行就可以了你正在写信,并且有一个标志会在缓冲区满后触发。在这一点上,它比deque有效得多。@dylan。因为最初的问题并不是很清楚你的目的,我建议你问另一个问题,展示你的研究,关于如何维护缓冲区。他已经在进行大量的范围爬行重新。