web-dev-qa-db-fra.com

python struct.error: le format 'i' nécessite -2147483648 <= nombre <= 2147483647

Problème

Je suis prêt à faire une ingénierie de fonctionnalités en utilisant le module de multiprocessing (multiprocessing.Pool.starmap(). Cependant, il donne un message d'erreur comme suit. Je suppose que ce message d'erreur concerne la taille des entrées (2147483647 = 2 ^ 31 - 1?), Car le même code a fonctionné sans problème pour une fraction(frac=0.05) des trames de données d'entrée (train_scala, test, ts). Je convertis les types de trames de données le plus petit possible, mais cela ne s'améliore pas.

La version anaconda est 4.3.30 et la version Python est 3.6 (64 bits). Et la taille de la mémoire du système dépasse 128 Go avec plus de 20 cœurs. Souhaitez-vous suggérer un pointeur ou une solution pour résoudre ce problème? Si ce problème est dû à des données volumineuses pour un module de multitraitement, combien de données plus petites dois-je utiliser pour utiliser le module de multitraitement sur Python3?

Code:

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)

Message d'erreur:

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

Infos supplémentaires

  • historyCutoffs est une liste d'entiers
  • train_scala est un pandas DataFrame (377MB)
  • le test est un pandas DataFrame (15 Mo)
  • ts est un pandas DataFrame (547MB)
  • ul_parts_path est une liste de répertoires (chaîne)
  • is_train_seq est une liste de booléens

Code supplémentaire: méthode 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

Le protocole de communication entre les processus utilise pickling, et les données picklées sont préfixées avec la taille des données picklées. Pour votre méthode, tous les arguments ensemble sont décapés comme un seul objet.

Vous avez produit un objet qui, lorsqu'il est mariné, est plus grand qu'il ne tient dans un formateur de structure i (un entier signé sur quatre octets), ce qui rompt les hypothèses formulées par le code.

Vous pouvez déléguer la lecture de vos cadres de données au processus enfant à la place, en n'envoyant que les métadonnées nécessaires pour charger le cadre de données. Leur taille combinée est proche de 1 Go, beaucoup trop de données à partager sur un canal entre vos processus.

Citant de la section Consignes de programmation :

Mieux vaut hériter que décaper/décaper

Lorsque vous utilisez les méthodes de démarrage spawn ou forkserver, de nombreux types de multiprocessing doivent être picklables pour que les processus enfants puissent les utiliser. Cependant, il convient généralement d'éviter d'envoyer des objets partagés à d'autres processus à l'aide de canaux ou de files d'attente. Au lieu de cela, vous devez organiser le programme afin qu'un processus qui a besoin d'accéder à une ressource partagée créée ailleurs puisse l'hériter d'un processus ancêtre.

Si vous n'êtes pas en cours d'exécution sur Windows et utilisez les méthodes spawn ou forkserver, vous pouvez charger vos cadres de données en tant que globaux avant démarrer vos sous-processus, à quel moment les processus enfants "hériteront" des données via les mécanismes de partage de page de mémoire de copie sur écriture du système d'exploitation.

13
Martijn Pieters

ce problème a été corrigé dans un PR récent à python https://github.com/python/cpython/pull/10305

si vous le souhaitez, vous pouvez effectuer cette modification localement pour la faire fonctionner immédiatement, sans attendre une version python et anaconda).

6
Alex