Pythonマルチプロセッシングライブラリには、複数の引数をサポートするpool.mapの変種がありますか?
text = "test"
def harvester(text, case):
X = case[0]
text+ str(X)
if __== '__main__':
pool = multiprocessing.Pool(processes=6)
case = RAW_DATASET
pool.map(harvester(text,case),case, 1)
pool.close()
pool.join()
これに対する答えはバージョンと状況に依存します。最近のバージョンのPython(3.3以降)に対する最も一般的な回答は、最初に J.F. Sebastian によって説明されました。1 Pool.starmap
メソッドを使用します。これは一連の引数タプルを受け付けます。それは自動的に各タプルから引数を解凍し、与えられた関数に渡します:
import multiprocessing
from itertools import product
def merge_names(a, b):
return '{} & {}'.format(a, b)
if __== '__main__':
names = ['Brown', 'Wilson', 'Bartlett', 'Rivera', 'Molloy', 'Opie']
with multiprocessing.Pool(processes=3) as pool:
results = pool.starmap(merge_names, product(names, repeat=2))
print(results)
# Output: ['Brown & Brown', 'Brown & Wilson', 'Brown & Bartlett', ...
以前のバージョンのPythonでは、引数を明示的に解凍するためのヘルパー関数を書く必要があります。 with
を使いたい場合は、Pool
をコンテキストマネージャに変えるためのラッパーを書く必要があります。 ( muon に感謝します)
import multiprocessing
from itertools import product
from contextlib import contextmanager
def merge_names(a, b):
return '{} & {}'.format(a, b)
def merge_names_unpack(args):
return merge_names(*args)
@contextmanager
def poolcontext(*args, **kwargs):
pool = multiprocessing.Pool(*args, **kwargs)
yield pool
pool.terminate()
if __== '__main__':
names = ['Brown', 'Wilson', 'Bartlett', 'Rivera', 'Molloy', 'Opie']
with poolcontext(processes=3) as pool:
results = pool.map(merge_names_unpack, product(names, repeat=2))
print(results)
# Output: ['Brown & Brown', 'Brown & Wilson', 'Brown & Bartlett', ...
もっと単純なケースでは、2番目の引数を固定して、partial
を使用することもできますが、これはPython 2.7以降でのみです。
import multiprocessing
from functools import partial
from contextlib import contextmanager
@contextmanager
def poolcontext(*args, **kwargs):
pool = multiprocessing.Pool(*args, **kwargs)
yield pool
pool.terminate()
def merge_names(a, b):
return '{} & {}'.format(a, b)
if __== '__main__':
names = ['Brown', 'Wilson', 'Bartlett', 'Rivera', 'Molloy', 'Opie']
with poolcontext(processes=3) as pool:
results = pool.map(partial(merge_names, b='Sons'), names)
print(results)
# Output: ['Brown & Sons', 'Wilson & Sons', 'Bartlett & Sons', ...
これの多くは彼の答えに触発されたものであり、おそらく代わりに受け入れられたはずです。しかし、これは一番上で立ち往生しているので、それは将来の読者のためにそれを改良するのが最善のようでした。
複数の引数をサポートするpool.mapの亜種はありますか?
Python 3.3には pool.starmap()
メソッドが含まれています :
#!/usr/bin/env python3
from functools import partial
from itertools import repeat
from multiprocessing import Pool, freeze_support
def func(a, b):
return a + b
def main():
a_args = [1,2,3]
second_arg = 1
with Pool() as pool:
L = pool.starmap(func, [(1, 1), (2, 1), (3, 1)])
M = pool.starmap(func, Zip(a_args, repeat(second_arg)))
N = pool.map(partial(func, b=second_arg), a_args)
assert L == M == N
if __name__=="__main__":
freeze_support()
main()
古いバージョンの場合
#!/usr/bin/env python2
import itertools
from multiprocessing import Pool, freeze_support
def func(a, b):
print a, b
def func_star(a_b):
"""Convert `f([1,2])` to `f(1,2)` call."""
return func(*a_b)
def main():
pool = Pool()
a_args = [1,2,3]
second_arg = 1
pool.map(func_star, itertools.izip(a_args, itertools.repeat(second_arg)))
if __name__=="__main__":
freeze_support()
main()
1 1
2 1
3 1
ここで itertools.izip()
と itertools.repeat()
がどのように使われているかに注目してください。
@unutbuで言及されたバグのため Python 2.6では functools.partial()
または同様の機能を使用することはできないので、単純なラッパー関数func_star()
は明示的に定義されるべきです。 回避策uptimebox
が推奨するものも参照してください。
私は以下が良くなると思います
def multi_run_wrapper(args):
return add(*args)
def add(x,y):
return x+y
if __== "__main__":
from multiprocessing import Pool
pool = Pool(4)
results = pool.map(multi_run_wrapper,[(1,2),(2,3),(3,4)])
print results
出力
[3, 5, 7]
Python 3.3+ をpool.starmap():
と共に使用する
from multiprocessing.dummy import Pool as ThreadPool
def write(i, x):
print(i, "---", x)
a = ["1","2","3"]
b = ["4","5","6"]
pool = ThreadPool(2)
pool.starmap(write, Zip(a,b))
pool.close()
pool.join()
結果:
1 --- 4
2 --- 5
3 --- 6
あなたが好きならば、あなたはZip()より多くの引数を使うこともできます:Zip(a,b,c,d,e)
引数として定数値を渡したい場合は、import itertools
を使用してからZip(itertools.repeat(constant), a)
を使用する必要があります。
JF Sebastian answerでitertoolsについて学んだので、私はそれをさらに一歩進めて、parmap
とmap
関数をpython-2.7とpython-3.2(そしてそれ以降)にも提供するstarmap
パッケージを書くことにしました。 任意の数の 位置引数を取ることができます。
Installation
pip install parmap
並列化する方法:
import parmap
# If you want to do:
y = [myfunction(x, argument1, argument2) for x in mylist]
# In parallel:
y = parmap.map(myfunction, mylist, argument1, argument2)
# If you want to do:
z = [myfunction(x, y, argument1, argument2) for (x,y) in mylist]
# In parallel:
z = parmap.starmap(myfunction, mylist, argument1, argument2)
# If you want to do:
listx = [1, 2, 3, 4, 5, 6]
listy = [2, 3, 4, 5, 6, 7]
param = 3.14
param2 = 42
listz = []
for (x, y) in Zip(listx, listy):
listz.append(myfunction(x, y, param1, param2))
# In parallel:
listz = parmap.starmap(myfunction, Zip(listx, listy), param1, param2)
ParmapをPyPIと githubリポジトリ にアップロードしました。
例として、質問は以下のように答えられます:
import parmap
def harvester(case, text):
X = case[0]
text+ str(X)
if __== "__main__":
case = RAW_DATASET # assuming this is an iterable
parmap.map(harvester, case, "test", chunksize=1)
pathos (注:githubでバージョンを使用)と呼ばれるmultiprocessing
のフォークがあります。これはstarmap
を必要としません - map関数はpythonのmap用のAPIを反映しています。 。 pathos
を使うと、__main__
ブロックで動かなくなる代わりに、通常はインタプリタでマルチプロセッシングを行うこともできます。 Pathosは、いくらかの穏やかな更新の後のリリースが予定されています - 大部分はpython 3.xへの変換です。
Python 2.7.5 (default, Sep 30 2013, 20:15:49)
[GCC 4.2.1 (Apple Inc. build 5566)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> def func(a,b):
... print a,b
...
>>>
>>> from pathos.multiprocessing import ProcessingPool
>>> pool = ProcessingPool(nodes=4)
>>> pool.map(func, [1,2,3], [1,1,1])
1 1
2 1
3 1
[None, None, None]
>>>
>>> # also can pickle stuff like lambdas
>>> result = pool.map(lambda x: x**2, range(10))
>>> result
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>>
>>> # also does asynchronous map
>>> result = pool.amap(pow, [1,2,3], [4,5,6])
>>> result.get()
[1, 32, 729]
>>>
>>> # or can return a map iterator
>>> result = pool.imap(pow, [1,2,3], [4,5,6])
>>> result
<processing.pool.IMapIterator object at 0x110c2ffd0>
>>> list(result)
[1, 32, 729]
新しい関数ごとにラッパーを書かないようにするために、次の2つの関数を使用できます。
import itertools
from multiprocessing import Pool
def universal_worker(input_pair):
function, args = input_pair
return function(*args)
def pool_args(function, *args):
return Zip(itertools.repeat(function), Zip(*args))
次のように、引数arg_0
、arg_1
、およびarg_2
のリストとともに関数function
を使用します。
pool = Pool(n_core)
list_model = pool.map(universal_worker, pool_args(function, arg_0, arg_1, arg_2)
pool.close()
pool.join()
Python2のためのより良い解決策:
from multiprocessing import Pool
def func((i, (a, b))):
print i, a, b
return a + b
pool = Pool(3)
pool.map(func, [(0,(1,2)), (1,(2,3)), (2,(3, 4))])
2 3 4
1 2 3
0 1 2
でる[]:
[3、5、7]
もう1つの簡単な方法は、関数のパラメータをTupleにラップしてから、渡す必要があるパラメータをTupleにもラップすることです。大量のデータを扱う場合、これはおそらく理想的ではありません。私はそれが各タプルのためにコピーを作るだろうと思います。
from multiprocessing import Pool
def f((a,b,c,d)):
print a,b,c,d
return a + b + c +d
if __== '__main__':
p = Pool(10)
data = [(i+0,i+1,i+2,i+3) for i in xrange(10)]
print(p.map(f, data))
p.close()
p.join()
出力をランダムな順序で出力します。
0 1 2 3
1 2 3 4
2 3 4 5
3 4 5 6
4 5 6 7
5 6 7 8
7 8 9 10
6 7 8 9
8 9 10 11
9 10 11 12
[6, 10, 14, 18, 22, 26, 30, 34, 38, 42]
もっと良い方法は ラッパー関数 を手で書く代わりに decorator を使うことです。特にマップする関数がたくさんある場合、decoratorはすべての関数のラッパーを書くことを避けて時間を節約します。通常、装飾された関数は選択できませんが、それを回避するためにfunctools
を使うことができます。より多くの議論が見つかります ここ 。
ここでの例
def unpack_args(func):
from functools import wraps
@wraps(func)
def wrapper(args):
if isinstance(args, dict):
return func(**args)
else:
return func(*args)
return wrapper
@unpack_args
def func(x, y):
return x + y
それからあなたはzipされた引数でそれをマッピングすることができます
np, xlist, ylist = 2, range(10), range(10)
pool = Pool(np)
res = pool.map(func, Zip(xlist, ylist))
pool.close()
pool.join()
もちろん、他の答えで述べたように、Python 3(> = 3.3)では常に Pool.starmap
を使うことができます。
def f1(args):
a, b, c = args[0] , args[1] , args[2]
return a+b+c
if __== "__main__":
import multiprocessing
pool = multiprocessing.Pool(4)
result1 = pool.map(f1, [ [1,2,3] ])
print(result1)
もう1つの方法は、リストのリストを1引数のルーチンに渡すことです。
import os
from multiprocessing import Pool
def task(args):
print "PID =", os.getpid(), ", arg1 =", args[0], ", arg2 =", args[1]
pool = Pool()
pool.map(task, [
[1,2],
[3,4],
[5,6],
[7,8]
])
自分の好きな方法で引数のリストリストを作成することができます。
Python 3.4.4から、multiprocessing.get_context()を使用して、複数のstartメソッドを使用するためのコンテキストオブジェクトを取得できます。
import multiprocessing as mp
def foo(q, h, w):
q.put(h + ' ' + w)
print(h + ' ' + w)
if __== '__main__':
ctx = mp.get_context('spawn')
q = ctx.Queue()
p = ctx.Process(target=foo, args=(q,'hello', 'world'))
p.start()
print(q.get())
p.join()
または単に交換するだけです
pool.map(harvester(text,case),case, 1)
によって:
pool.apply_async(harvester(text,case),case, 1)
ここには多くの答えがありますが、どのバージョンでも動作するPython 2/3互換コードを提供しているようには見えません。あなたのコードを ちょうど動作させたい にしたい場合、これはどちらのPythonバージョンでも動作します。
# For python 2/3 compatibility, define pool context manager
# to support the 'with' statement in Python 2
if sys.version_info[0] == 2:
from contextlib import contextmanager
@contextmanager
def multiprocessing_context(*args, **kwargs):
pool = multiprocessing.Pool(*args, **kwargs)
yield pool
pool.terminate()
else:
multiprocessing_context = multiprocessing.Pool
それ以降は、通常のPython 3の方法でマルチプロセッシングを使用できます。例えば:
def _function_to_run_for_each(x):
return x.lower()
with multiprocessing_context(processes=3) as pool:
results = pool.map(_function_to_run_for_each, ['Bob', 'Sue', 'Tim']) print(results)
python 2またはPython 3で動作します。
text = "test"
def unpack(args):
return args[0](*args[1:])
def harvester(text, case):
X = case[0]
text+ str(X)
if __== '__main__':
pool = multiprocessing.Pool(processes=6)
case = RAW_DATASET
# args is a list of tuples
# with the function to execute as the first item in each Tuple
args = [(harvester, text, c) for c in case]
# doing it this way, we can pass any function
# and we don't need to define a wrapper for each different function
# if we need to use more than one
pool.map(unpack, args)
pool.close()
pool.join()
公式文書では、反復可能な引数を1つだけサポートしていると述べています。そのような場合にはapply_asyncを使用するのが好きです。あなたの場合、私はするでしょう:
from multiprocessing import Process, Pool, Manager
text = "test"
def harvester(text, case, q = None):
X = case[0]
res = text+ str(X)
if q:
q.put(res)
return res
def block_until(q, results_queue, until_counter=0):
i = 0
while i < until_counter:
results_queue.put(q.get())
i+=1
if __== '__main__':
pool = multiprocessing.Pool(processes=6)
case = RAW_DATASET
m = Manager()
q = m.Queue()
results_queue = m.Queue() # when it completes results will reside in this queue
blocking_process = Process(block_until, (q, results_queue, len(case)))
blocking_process.start()
for c in case:
try:
res = pool.apply_async(harvester, (text, case, q = None))
res.get(timeout=0.1)
except:
pass
blocking_process.join()
これは、 pool.imap forkで使用される1引数関数に複数の引数を渡すために使用するルーチンの例です。
from multiprocessing import Pool
# Wrapper of the function to map:
class makefun:
def __init__(self, var2):
self.var2 = var2
def fun(self, i):
var2 = self.var2
return var1[i] + var2
# Couple of variables for the example:
var1 = [1, 2, 3, 5, 6, 7, 8]
var2 = [9, 10, 11, 12]
# Open the pool:
pool = Pool(processes=2)
# Wrapper loop
for j in range(len(var2)):
# Obtain the function to map
pool_fun = makefun(var2[j]).fun
# Fork loop
for i, value in enumerate(pool.imap(pool_fun, range(len(var1))), 0):
print(var1[i], '+' ,var2[j], '=', value)
# Close the pool
pool.close()