我正在尝试使用IPython的并行环境,到目前为止,它看起来很棒,但我遇到了问题。假设我有一个在库中定义的函数
def func(a,b):
...
当我想要评估a的一个值和b的一堆值时,我会使用它。
[func(myA, b) for b in myLongList]
显然,真正的功能更复杂,但问题的实质是它需要多个参数,我只想映射其中一个。问题是map,@ dview.parallel等映射了所有参数。
所以我想说我想得到func(myA,myLongList)的答案。显而易见的方法是咖喱,或者是functools.partial,或者只是
dview.map_sync(lambda b: func(myA, b), myLongList)
但是,这在远程计算机上无法正常工作。原因是当lambda表达式被pickle时,myA的值不包括在内,而是使用远程机器上本地作用域的myA值。当闭包被腌制时,它们关闭的变量不会。
我能想到的两种实际工作方式是为每个参数手动构建列表,并对所有参数进行映射工作,
dview.map_sync(func, [myA]*len(myLongList), myLongList)
或者恐怖地使用数据作为函数的默认参数,强制它被腌制:
# Can't use a lambda here b/c lambdas don't use default arguments :(
def parallelFunc(b, myA = myA):
return func(myA, b)
dview.map_sync(parallelFunc, myLongList)
真的,当真正的函数需要很多参数并且更复杂时,这一切似乎都非常扭曲。是否有一些惯用的方法呢?像
这样的东西@parallel(mapOver='b')
def bigLongFn(a, b):
...
但据我所知,没有像'mapOver'这样的东西存在。我可能已经知道如何实现它...这只是一个非常基本的操作,应该存在支持,所以我想检查我是否遗漏了一些东西。
答案 0 :(得分:15)
我可以对batu的答案有所改进(我认为这是一个很好的答案,但也许不会详细记录为什么你使用这些选项)。目前,ipython文档在这一点上也是非常不合适的。所以你的功能是:
def myfxn(a,b,c,d):
....
return z
并存储在名为 mylib 的文件中。假设b,c和d在运行期间是相同的,所以你编写一个lambda函数将它减少为1参数函数。
import mylib
mylamfxn=lambda a:mylib.myfxn(a,b,c,d)
你要运行:
z=dview.map_sync(mylamfxn, iterable_of_a)
在梦想世界中,一切都会神奇地发挥作用。但是,首先你会收到“找不到mylib”的错误,因为ipcluster进程没有加载mylib。如果需要,确保ipcluster进程在其python路径中具有“mylib”并且位于myfxn的正确工作目录中。然后你需要添加到你的python代码:
dview.execute('import mylib')
在每个进程上运行import mylib
命令。如果你再试一次,你会得到一个错误:“全局变量b未定义”,因为虽然变量在你的python会话中,但它们不在ipcluster进程中。但是,python提供了一种将一组变量复制到子进程的方法。继续上面的例子:
mydict=dict(b=b, c=c, d=d)
dview.push(mydict)
现在所有子进程都可以访问b,c和d。然后你可以运行:
z=dview.map_sync(mylamfxn, iterable_of_a)
它现在应该像宣传的那样工作。无论如何,我是python并行计算的新手,并发现这个线程很有用,所以我想我会尝试帮助解释一些让我感到困惑的点......
最终的代码是:
import mylib
#set up parallel processes, start ipcluster from command line prior!
from IPython.parallel import Client
rc=Client()
dview=rc[:]
#...do stuff to get iterable_of_a and b,c,d....
mylamfxn=lambda a:mylib.myfxn(a,b,c,d)
dview.execute('import mylib')
mydict=dict(b=b, c=c, d=d)
dview.push(mydict)
z=dview.map_sync(mylamfxn, iterable_of_a)
这可能是制作python中并行运行的任何令人尴尬的并行代码的最快捷,最简单的方法....
更新您还可以使用dview在没有循环的情况下推送所有数据,然后使用lview(即lview=rc.load_balanced_view(); lview.map(...)
以负载平衡的方式进行实际计算。
答案 1 :(得分:6)
这是我给StackOverflow的第一条消息所以请温柔;)我试图做同样的事情,并提出以下内容。我很确定这不是最有效的方式,但似乎有点工作。 目前有一点需要注意的是,由于某些原因,我只看到两台发动机100%工作,其他发动机几乎闲置......
为了在map中调用多个arg函数,我首先在我的个人parallel.py模块中编写了这个例程:
def map(r,func, args=None, modules=None):
"""
Before you run parallel.map, start your cluster (e.g. ipcluster start -n 4)
map(r,func, args=None, modules=None):
args=dict(arg0=arg0,...)
modules='numpy, scipy'
examples:
func= lambda x: numpy.random.rand()**2.
z=parallel.map(r_[0:1000], func, modules='numpy, numpy.random')
plot(z)
A=ones((1000,1000));
l=range(0,1000)
func=lambda x : A[x,l]**2.
z=parallel.map(r_[0:1000], func, dict(A=A, l=l))
z=array(z)
"""
from IPython.parallel import Client
mec = Client()
mec.clear()
lview=mec.load_balanced_view()
for k in mec.ids:
mec[k].activate()
if args is not None:
mec[k].push(args)
if modules is not None:
mec[k].execute('import '+modules)
z=lview.map(func, r)
out=z.get()
return out
正如您所看到的,该函数采用了args参数,该参数是头节点工作空间中参数的dict。然后将这些参数推送到引擎。此时它们成为本地对象,可以直接在函数中使用。例如,在上面评论中给出的最后一个例子中,使用l engine-local变量对A矩阵进行切片。
我必须说,即使上述功能有效,我也不会百分之百满意。如果我能想出更好的东西,我会在这里发布。
更新:2013年4月11日 我对代码做了一些小改动: - activate语句缺少括号。导致它不能运行。 - 将mec.clear()移动到函数的顶部,而不是结束。 我还注意到,如果我在ipython中运行它,效果最好。例如,如果我使用上面的函数运行脚本为“python ./myparallelrun.py”,我可能会收到错误,但如果我使用“%run ./myparallelrun.py”在ipython中运行它,则不会出错。不知道为什么......
答案 2 :(得分:0)
执行此操作的优雅方法是使用部分功能。
如果你知道你希望foo的第一个参数是myArg,你可以通过
创建一个新的功能栏from functools import partial
bar = partial(foo, myARg)
bar(otherArg)
将返回foo(myArg,otherArg)
答案 3 :(得分:0)
让我们建立:
dview.map_sync(func, [myA]*len(myLongList), myLongList)
也许以下方法可行:
from itertools import izip_longest
dview.map_sync(func, izip_longest(myLongList, [], fillvalue=myA))
示例:
>>> # notice that a is a tuple
... concat = lambda a: '%s %s' % a
>>> mylonglist = range(10)
>>> from itertools import izip_longest
>>> map(concat, izip_longest(mylonglist, [], fillvalue='mississippi'))
['0 mississippi', '1 mississippi', '2 mississippi', '3 mississippi',
'4 mississippi', '5 mississippi', '6 mississippi', '7 mississippi',
'8 mississippi', '9 mississippi']
答案 4 :(得分:0)
我发布Alex S.评论作为回答。这可能是解决这个问题的正确方法:
使用lambda进行部分应用。我知道它看起来很奇怪,但是使用my_f = lambda a,my = other,arguments = go,right = here:f(a,my,arguments,right)是最简单的方法,不会陷入酸洗和推动问题