如何对lambda函数数组的求值进行矢量化?
以下是了解我所谈论的内容的一个例子。 (即使我使用的是numpy
数组,但我并不仅限于使用numpy
。)
我们说我有以下numpy
数组。
array1 = np.array(["hello", 9])
array2 = np.array([lambda s: s == "hello", lambda num: num < 10])
(您可以将这些类型的对象存储在numpy
中,而不会抛出错误,信不信由你。)我想要的是类似于以下内容。
array2 * array1
# Return np.array([True, True]). PS: An explanation of how to `AND` all of
# booleans together quickly would be nice too.
当然,对于大小为2的数组来说,这似乎是不切实际的,但对于任意大小的数组,我认为这会因为所有低级优化而提高性能。
那么,有谁知道如何编写这种奇怪的python代码?
答案 0 :(得分:2)
当然,简单的答案是你不能轻易地使用numpy(或者使用标准Python)来做到这一点。根据我的知识,Numpy实际上并没有对大多数操作本身进行矢量化:它使用BLAS / ATLAS /等库来执行某些情况。即使它确实如此,它也会在C中针对特定情况这样做:它肯定无法向量化Python函数执行。
如果您想在此处涉及多处理, 是可能的,但这取决于您的情况。您的单个功能应用程序是否耗时,使它们可以一个一个地发送,或者您是否需要大量的快速功能执行,在这种情况下,您可能希望将它们的批量发送到每个进程?
一般来说,由于可能被认为是糟糕的基础设计(例如,全局解释器锁),标准Python很难实现轻量级并行化,因为您希望在这里。有很多重要的方法,比如多处理模块或Ipython.parallel,但这些方法需要一些工作才能使用。
答案 1 :(得分:1)
好吧,伙计们,我有一个答案:numpy的vectorize。
请阅读已编辑的部分。您会发现python实际上为您优化了代码,这实际上违背了在这种情况下使用numpy数组的目的。 (但使用numpy数组不会降低性能。)
最后一个测试确实表明python列表尽可能高效,因此这个向量化过程是不必要的。这就是为什么我没有将这个问题标记为“最佳答案”。
设置代码:
def factory(i): return lambda num: num==i
array1 = list()
for i in range(10000): array1.append(factory(i))
array1 = np.array(array1)
array2 = np.array(xrange(10000))
“未实现”版本:
def evaluate(array1, array2):
return [func(val) for func, val in zip(array1, array2)]
%timeit evaluate(array1, array2)
# 100 loops, best of 3: 10 ms per loop
矢量化版本
def evaluate2(func, b): return func(b)
vec_evaluate = np.vectorize(evaluate2)
vec_evaluate(array1, array2)
# 100 loops, best of 3: 2.65 ms per loop
修改强>
好的,我只是想使用上述测试粘贴更多基准测试,除了测试用例不同。
我做了第三次编辑,显示如果你只是使用python列表会发生什么。长话短说,你实际上不会后悔太多。这个测试用例位于最底层。
仅涉及整数的测试用例
总之,如果n
很小,那么非版本化版本会更好。否则,矢量化是要走的路。
使用n = 30
%timeit evaluate(array1, array2)
# 10000 loops, best of 3: 35.7 µs per loop
%timeit vec_evaluate(array1, array2)
# 10000 loops, best of 3: 27.6 µs per loop
使用n = 7
%timeit evaluate(array1, array2)
100000 loops, best of 3: 9.93 µs per loop
%timeit vec_evaluate(array1, array2)
10000 loops, best of 3: 21.6 µs per loop
涉及字符串的测试用例
矢量化获胜。
设置代码:
def factory(i): return lambda num: str(num)==str(i)
array1 = list()
for i in range(7):
array1.append(factory(i))
array1 = np.array(array1)
array2 = np.array(xrange(7))
使用n = 10000
%timeit evaluate(array1, array2)
10 loops, best of 3: 36.7 ms per loop
%timeit vec_evaluate(array1, array2)
100 loops, best of 3: 6.57 ms per loop
使用n = 7
%timeit evaluate(array1, array2)
10000 loops, best of 3: 28.3 µs per loop
%timeit vec_evaluate(array1, array2)
10000 loops, best of 3: 27.5 µs per loop
随机测试
只是看看分支预测如何发挥作用。从我所看到的,它并没有真正改变太多。矢量化通常仍然胜出。
设置代码。
def factory(i):
if random() < 0.5:
return lambda num: str(num) == str(i)
return lambda num: num == i
n = 10000
%timeit evaluate(array1, array2)
10 loops, best of 3: 25.7 ms per loop
%timeit vec_evaluate(array1, array2)
100 loops, best of 3: 4.67 ms per loop
n = 7
%timeit evaluate(array1, array2)
10000 loops, best of 3: 23.1 µs per loop
%timeit vec_evaluate(array1, array2)
10000 loops, best of 3: 23.1 µs per loop
使用python列表而不是numpy数组
我运行此测试以查看当我选择不使用“优化的”numpy数组时发生了什么,我收到了一些非常令人惊讶的结果。
设置代码几乎相同,只是我选择不使用numpy数组。我也只针对“随机”案例进行此测试。
def factory(i):
if random() < 0.5:
return lambda num: str(num) == str(i)
return lambda num: num == i
array1 = list()
for i in range(10000): array1.append(factory(i))
array2 = range(10000)
和“未经过滤的”版本:
%timeit evaluate(array1, array2)
100 loops, best of 3: 4.93 ms per loop
你可以看到这实际上是非常令人惊讶的,因为这几乎与我通过涉及矢量化evaluate
的随机测试案例收到的基准相同。
%timeit vec_evaluate(array1, array2)
10 loops, best of 3: 19.8 ms per loop
同样,如果在使用vec_evaluate
之前将这些更改为numpy数组,则会得到相同的4.5 ms基准。