Python:向量化 lambda 函数数组的计算
Python: Vectorizing evaluations of arrays of lambda functions
您将如何向量化 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 代码吗?
当然,简单的答案是您无法使用 numpy(或就此而言使用标准 Python)轻易地做到这一点。据我所知,Numpy 实际上并没有对大多数操作本身进行矢量化:它使用像 BLAS/ATLAS/etc 这样的库来处理某些情况。即使它这样做了,它也会在特定情况下在 C 中这样做:它当然不能矢量化 Python 函数执行。
如果你想在其中涉及多处理,是可能的,但这取决于你的情况。您的单个函数应用程序是否耗时,使得它们可以逐个发送,或者您是否需要大量快速函数执行,在这种情况下,您可能希望将它们分批发送到每个进程?
一般来说,由于基础设计不佳(例如,全局解释器锁),标准 Python 很难像您希望的那样实现轻量级并行化。有明显更重的方法,如多处理模块或 Ipython.parallel,但这些方法需要一些工作才能使用。
好的伙计们,我有一个答案:numpy 的 vectorize。
不过请阅读编辑过的部分。你会发现 python 实际上为你优化了代码,这实际上违背了在这种情况下使用 numpy 数组的目的。 (但使用 numpy 数组不会降低性能。)
最后的测试真正表明 python 列表是尽可能高效的,因此这个矢量化过程是不必要的。这就是为什么我没有将这个问题标记为 "best answer".
设置代码:
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))
"unvectorized"版本:
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
随机测试
只是为了看看 b运行ch 预测是如何发挥作用的。从我所看到的,它并没有真正改变太多。矢量化通常仍然获胜。
设置代码。
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 数组
我 运行 这个测试是为了看看当我选择不使用 "optimized" numpy 数组时发生了什么,我收到了一些非常令人惊讶的结果。
设置代码几乎相同,只是我选择不使用 numpy 数组。我也只针对 "random" 案例进行此测试。
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)
和 "unvectorized" 版本:
%timeit evaluate(array1, array2)
100 loops, best of 3: 4.93 ms per loop
你可以看到这实际上是非常令人惊讶的,因为这几乎与我在涉及矢量化 evaluate
.
的 运行dom 测试用例中收到的基准相同
%timeit vec_evaluate(array1, array2)
10 loops, best of 3: 19.8 ms per loop
同样,如果您在使用 vec_evaluate
之前将它们更改为 numpy 数组,您将获得相同的 4.5 毫秒基准。
您将如何向量化 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 代码吗?
当然,简单的答案是您无法使用 numpy(或就此而言使用标准 Python)轻易地做到这一点。据我所知,Numpy 实际上并没有对大多数操作本身进行矢量化:它使用像 BLAS/ATLAS/etc 这样的库来处理某些情况。即使它这样做了,它也会在特定情况下在 C 中这样做:它当然不能矢量化 Python 函数执行。
如果你想在其中涉及多处理,是可能的,但这取决于你的情况。您的单个函数应用程序是否耗时,使得它们可以逐个发送,或者您是否需要大量快速函数执行,在这种情况下,您可能希望将它们分批发送到每个进程?
一般来说,由于基础设计不佳(例如,全局解释器锁),标准 Python 很难像您希望的那样实现轻量级并行化。有明显更重的方法,如多处理模块或 Ipython.parallel,但这些方法需要一些工作才能使用。
好的伙计们,我有一个答案:numpy 的 vectorize。
不过请阅读编辑过的部分。你会发现 python 实际上为你优化了代码,这实际上违背了在这种情况下使用 numpy 数组的目的。 (但使用 numpy 数组不会降低性能。)
最后的测试真正表明 python 列表是尽可能高效的,因此这个矢量化过程是不必要的。这就是为什么我没有将这个问题标记为 "best answer".
设置代码:
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))
"unvectorized"版本:
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
随机测试
只是为了看看 b运行ch 预测是如何发挥作用的。从我所看到的,它并没有真正改变太多。矢量化通常仍然获胜。
设置代码。
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 数组
我 运行 这个测试是为了看看当我选择不使用 "optimized" numpy 数组时发生了什么,我收到了一些非常令人惊讶的结果。
设置代码几乎相同,只是我选择不使用 numpy 数组。我也只针对 "random" 案例进行此测试。
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)
和 "unvectorized" 版本:
%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 毫秒基准。