36

IPythonの並列環境を使用しようとしていますが、これまでのところ、見栄えは良いですが、問題が発生しています。ライブラリで定義された関数があるとしましょう

def func(a,b):
   ...

これは、aの1つの値とbの値の束を評価するときに使用します。

[func(myA, b) for b in myLongList]

明らかに、実際の関数はもっと複雑ですが、問題の本質は、複数のパラメーターを必要とすることであり、そのうちの1つだけにマップしたいと思います。問題は、map、@dview.parallelなどがすべての引数にマップされることです。

それで、func(myA、myLongList)の答えを取得したいとしましょう。これを行うための明白な方法は、functools.partialまたはちょうど同じようにカレーすることです

dview.map_sync(lambda b: func(myA, b),   myLongList)

ただし、これはリモートマシンでは正しく機能しません。その理由は、ラムダ式を選択すると、myAの値が含まれず、代わりに、リモートマシンのローカルスコープからのmyAの値が使用されるためです。クロージャがピクルスになると、クロージャが閉じる変数はピクルスになりません。

これを実際に機能させると考える2つの方法は、すべての引数のリストを手動で作成し、すべての引数に対してマップを機能させることです。

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」のようなものは存在しません。私はおそらくそれをどのように実装するかについての考えを持っています...これはサポートが存在するはずの非常に基本的な操作のように感じるので、何かが足りないかどうかを確認したいと思います。

4

5 に答える 5

15

私はバトゥの答えを少し改善することができます(これは良いものだと思いますが、おそらくこれらのオプションを使用する理由をそれほど詳しく文書化していません)。ipython のドキュメントも現在、この点に関して非常に不十分です。したがって、関数は次の形式になります。

def myfxn(a,b,c,d):
  ....
  return z

mylibというファイルに保存されます。実行中にb、c、およびdが同じであるとしましょう。したがって、ラムダ関数を記述して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(...)、負荷分散された方法で実際の計算を実行します)。

于 2013-07-24T23:45:25.123 に答える
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']
于 2012-10-13T04:33:53.777 に答える
0

これを行うエレガントな方法は、部分関数を使用することです。

foo の最初の引数を myArg にしたいことがわかっている場合は、次のようにして新しい関数 bar を作成できます。

from functools import partial
bar = partial(foo, myARg)

bar(otherArg)その後戻りますfoo(myArg,otherArg)

于 2012-09-20T19:21:16.397 に答える