web-dev-qa-db-ja.com

python struct.error: 'i'形式には-2147483648 <= number <= 2147483647が必要です

問題

マルチプロセッシングモジュール(multiprocessing.Pool.starmap()を使用して機能エンジニアリングを行います。ただし、次のようなエラーメッセージが表示されます。このエラーメッセージは、入力データフレーム(train_scala、test、ts)のfraction(frac=0.05)に対して同じコードがスムーズに機能したため、入力のサイズ(2147483647 = 2 ^ 31 − 1?)であると推測されます。データフレームの種類をできる限り小さく変換しますが、良くなりません。

Anacondaのバージョンは4.3.30で、Pythonバージョンは3.6(64ビット)です。システムのメモリサイズは128GBを超え、20コア以上です。任意のポインターを提案しますか?またはこの問題を解決する解決策?この問題がマルチプロセッシングモジュールの大きなデータに起因する場合、Python3でマルチプロセッシングモジュールを使用するにはどのくらい小さいデータを使用すればよいですか?

コード:

from multiprocessing import Pool, cpu_count
from itertools import repeat    
p = Pool(8)
is_train_seq = [True]*len(historyCutoffs)+[False]
config_Zip = Zip(historyCutoffs, repeat(train_scala), repeat(test), repeat(ts), ul_parts_path, repeat(members), is_train_seq)
p.starmap(multiprocess_FE, config_Zip)

エラーメッセージ:

Traceback (most recent call last):
  File "main_1210_FE_scala_multiprocessing.py", line 705, in <module>
    print('----Pool starmap start----')
  File "/home/dmlab/ksedm1/anaconda3/envs/py36/lib/python3.6/multiprocessing/pool.py", line 274, in starmap
    return self._map_async(func, iterable, starmapstar, chunksize).get()
  File "/home/dmlab/ksedm1/anaconda3/envs/py36/lib/python3.6/multiprocessing/pool.py", line 644, in get
    raise self._value
  File "/home/dmlab/ksedm1/anaconda3/envs/py36/lib/python3.6/multiprocessing/pool.py", line 424, in _handle_tasks
    put(task)
  File "/home/dmlab/ksedm1/anaconda3/envs/py36/lib/python3.6/multiprocessing/connection.py", line 206, in send
    self._send_bytes(_ForkingPickler.dumps(obj))
  File "/home/dmlab/ksedm1/anaconda3/envs/py36/lib/python3.6/multiprocessing/connection.py", line 393, in _send_bytes
    header = struct.pack("!i", n)
struct.error: 'i' format requires -2147483648 <= number <= 2147483647

追加情報

  • historyCutoffsは整数のリストです
  • train_scalaはpandas DataFrame(377MB)
  • テストはpandas DataFrame(15MB)
  • tsはpandas DataFrame(547MB)
  • ul_parts_pathはディレクトリのリストです(文字列)
  • is_train_seqはブール値のリストです

追加コード:メソッドmultiprocess_FE

def multiprocess_FE(historyCutoff, train_scala, test, ts, ul_part_path, members, is_train):
    train_dict = {}
    ts_dict = {}
    msno_dict = {}
    ul_dict = {}
    if is_train == True:
        train_dict[historyCutoff] = train_scala[train_scala.historyCutoff == historyCutoff]
    else:
        train_dict[historyCutoff] = test
    msno_dict[historyCutoff] = set(train_dict[historyCutoff].msno)
    print('length of msno is {:d} in cutoff {:d}'.format(len(msno_dict[historyCutoff]), historyCutoff))
    ts_dict[historyCutoff] = ts[(ts.transaction_date <= historyCutoff) & (ts.msno.isin(msno_dict[historyCutoff]))]
    print('length of transaction is {:d} in cutoff {:d}'.format(len(ts_dict[historyCutoff]), historyCutoff))    
    ul_part = pd.read_csv(gzip.open(ul_part_path, mode="rt"))  ##.sample(frac=0.01, replace=False)
    ul_dict[historyCutoff] = ul_part[ul_part.msno.isin(msno_dict[historyCutoff])]
    train_dict[historyCutoff] = enrich_by_features(historyCutoff, train_dict[historyCutoff], ts_dict[historyCutoff], ul_dict[historyCutoff], members, is_train)
14
SUNDONG

プロセス間の通信プロトコルはpicklingを使用し、ピクルスされたデータの前にピクルスされたデータのサイズが付けられます。メソッドでは、すべての引数を一緒にが1つのオブジェクトとしてピクルされます。

Pickleしたときにi structフォーマッタ(4バイトの符号付き整数)に収まるよりも大きいオブジェクトを作成したため、コードが行った仮定が破られました。

代わりに、データフレームの読み込みを子プロセスに委任し、データフレームの読み込みに必要なメタデータのみを送信することができます。合計サイズは1GBに近く、プロセス間でパイプを介して共有するには多すぎるデータです。

プログラミングガイドラインセクション からの引用:

pickle/unpickleよりも継承する方が良い

spawnまたはforkserver開始メソッドを使用する場合、multiprocessingからの多くの型は、子プロセスが使用できるように選択可能にする必要があります。 ただし、一般に、パイプまたはキューを使用して共有オブジェクトを他のプロセスに送信することは避けてください。代わりに、他の場所で作成された共有リソースへのアクセスを必要とするプロセスが祖先プロセスからそれを継承できるように、プログラムを調整する必要があります。

Windowsで実行していない場合にspawnまたはforkserverメソッドを使用すると、データフレームをグローバルとして読み込むことができますbeforeサブプロセスを開始し、その時点で子プロセスは、通常のOSコピーオンライトメモリページ共有メカニズムを介してデータを「継承」します。

13
Martijn Pieters

この問題は最近のPRでpython https://github.com/python/cpython/pull/10305 に修正されました

必要に応じて、pythonとanacondaのリリースを待たずに、この変更をローカルで実行してすぐに動作するようにすることができます。

6
Alex