设置:我有一个函数preprocess(data, predicate)
和一个可能如下所示的谓词列表:
<击> 撞击>
preds = [lambda x: x < 1,
lambda x: x < 2,
lambda x: x < 3,
lambda x: x < 42]
击> <击> 撞击>
编辑:我可能应该更精确,因为我认为1,2,3,42显然是可以识别的例子,但它似乎太隐含了。实际上我正在做一些NLP,data
是单词列表,一个谓词看起来像lambda w: (w.lower() not in stopwords.words('english') and re.search("[a-z]", w.lower()))
。我想测试不同的谓词来评估哪种最佳效果。
这是我真正想做的事情。与每个谓词并行调用preprocess
。
编辑:因为这是一个预处理步骤,所以我需要preprocess
返回的内容继续使用它。
我希望我能做什么,但遗憾的是不能:
pool = Pool(processes=4)
pool.map(lambda p: preprocess(data, p), preds)
据我所知,这是因为传递给pool.map的所有东西都必须是可以腌制的。在this问题中,提出了两个解决方案,其中第一个(接受的答案)似乎不切实际,并且在我正在使用的Python 2.7中似乎没有使用secound,尽管它建议通过{{ 3}}在评论中。
我的问题是pool.map是否是正确的方法,如果是这样的话怎么办?或者我会尝试不同的方法吗?
我知道有很多关于pool.map的问题,即使我花了一些时间搜索我也没找到答案。如果我的代码风格很尴尬,请随时指出。我读到lambda
对某些人看起来很奇怪,我可能应该使用functools.partial。
提前致谢。
答案 0 :(得分:3)
在这种简单的情况下,您只需修改preprocess
函数即可接受threshold
属性。类似的东西:
def preprocess(data, threshold):
def predicate(x):
return x < threshold
return old_preprocess(data, predicate)
现在,在preds
列表中,您可以简单地输入可选择的整数:
preds = [1,2,3,42]
pool = Pool(processes=4)
pool.map(preprocess, zip(data, preds))
您可以使用operator
模块扩展它以选择运算符:
def preprocess(data, pred):
threshold, op = pred
def predicate(x):
return op(x, threshold)
return old_preprocess(data, predicate)
import operator as op
preds = [(1, op.lt), (2, op.gt), (3, op.ge), (42, op.lt)]
pool = Pool(processes=4)
pool.map(preprocess, zip(data, preds))
用任意谓词来扩展它会变得更难。可能最简单的方法是使用marshal
模块,该模块能够将函数的代码转换为bytes
对象并返回。
类似的东西:
real_preds = [marshal.dumps(pred.__code__) for pred in preds]
然后preprocess
应重新构建谓词函数:
import types
def preprocess(data, pred):
pred = types.FunctionType(marshal.loads(pred), globals())
这是最后一个建议的MWE:
>>> from multiprocessing import Pool
>>> import marshal
>>> import types
>>> def preprocess(pred):
... pred = types.FunctionType(marshal.loads(pred), globals())
... return pred(2)
...
>>> preds = [lambda x: x < 1,
... lambda x: x <2,
... lambda x: x < 3,
... lambda x: x < 42]
>>> real_preds = [marshal.dumps(pred.__code__) for pred in preds]
>>> pool = Pool(processes=4)
>>> pool.map(preprocess, real_preds)
[False, False, True, True]
请注意,pool.map
的参数必须是可选的。这意味着无法使用lambda
作为Pool.map
的第一个参数:
>>> pool.map(lambda x: preprocess(x), real_preds)
Exception in thread Thread-5:
Traceback (most recent call last):
File "/usr/lib/python3.3/threading.py", line 639, in _bootstrap_inner
self.run()
File "/usr/lib/python3.3/threading.py", line 596, in run
self._target(*self._args, **self._kwargs)
File "/usr/lib/python3.3/multiprocessing/pool.py", line 351, in _handle_tasks
put(task)
File "/usr/lib/python3.3/multiprocessing/connection.py", line 206, in send
ForkingPickler(buf, pickle.HIGHEST_PROTOCOL).dump(obj)
_pickle.PicklingError: Can't pickle <class 'function'>: attribute lookup builtins.function failed
关于“是Pool.map
正确的工具?我认为它在很大程度上取决于数据的大小。使用多处理会增加相当多的开销,所以即使你”让它工作“也有很高的机会这是不值得的。特别是,在你编辑的问题中,你为谓词设置了一个更“真实世界”的场景:
lambda w: (w.lower() not in stopwords.words('english') and re.search("[a-z]", w.lower()))
我认为这个谓词没有花足够的时间来使Pool.map
值得使用。显然,这取决于w
的大小和要映射的元素数量。
使用此谓词进行非常快速的测试我发现当Pool.map
长度约为35000个字符时,使用w
开始变得更快。如果w
小于1000,那么使用Pool
比普通map
<慢> 15倍(要检查256个字符串。如果字符串为60000则Pool
有点更快。)
请注意,如果w
很长,那么 值得使用def
代替lambda
并避免w.lower()
的双重计算。要么使用普通map
,要么使用Pool.map
。
答案 1 :(得分:2)
您可以使用Pool.map执行此操作,只需组织正确映射的内容即可。地图基本上是这样的:
result = map(function, things)
相当于
result = []
for thing in things:
result.append(function(thing))
或者更简洁地说,
result = [function(thing) for thing in things]
你可以构造你的函数,使它接受一个参数(上限)并进行比较:
def mapme(bound):
p = lambda x : x < bound
return preprocess(data, p)
从那里开始,如果您正在进行并行映射或单线程映射,则无关紧要。只要preprocess
没有副作用,您就可以使用地图。
答案 2 :(得分:0)
如果您正在使用这些函数进行副作用而不需要使用pool.map()
的统一输出,则可以使用os.fork()
进行模拟(至少在类似unix的系统上) )。
您可以尝试这样的事情:
import numpy as np
import os
nprocs=4
funcs=np.array_split(np.array(preds),nprocs)
#Forks the program into nprocs programs, each with a procid from 0 to nprocs-1
procid=0
for x in range(1,nprocs):
if (os.fork()==0):
procid=x
break
map(lambda p: preprocess(data, p), funcs[procid])