web-dev-qa-db-fra.com

Pickle dump fichier énorme sans erreur de mémoire

J'ai un programme où j'ajuste essentiellement la probabilité de certaines choses en fonction de ce qui est déjà connu. Mon fichier de données est déjà enregistré en tant que pickle objet Dictionnaire dans Dictionary.txt.

Le problème est qu'à chaque fois que j'exécute le programme, il extrait le Dictionary.txt, Le transforme en objet dictionnaire, le modifie et le remplace Dictionary.txt. Ceci est assez gourmand en mémoire car le Dictionary.txt Fait 123 Mo. Quand je vide, je reçois le MemoryError, tout semble bien quand je le tire dedans ..

  • Existe-t-il une meilleure façon (plus efficace) d'effectuer les modifications? (Peut-être sans avoir à écraser le fichier entier à chaque fois)

  • Existe-t-il un moyen d'invoquer la récupération de place (via le module gc)? (Je l'ai déjà activé automatiquement via gc.enable())

  • Je sais qu'en plus de readlines(), vous pouvez lire ligne par ligne. Existe-t-il un moyen de modifier le dictionnaire de manière incrémentielle ligne par ligne lorsque j'ai déjà un fichier d'objet de dictionnaire entièrement rempli dans le programme.

  • D'autres solutions?

Merci pour votre temps.

23
user2543682

J'avais le même problème. J'utilise joblib et le travail a été fait. Au cas où quelqu'un voudrait connaître d'autres possibilités.

enregistrer le modèle sur le disque

from sklearn.externals import joblib
filename = 'finalized_model.sav'
joblib.dump(model, filename)  

un peu plus tard ... charger le modèle à partir du disque

loaded_model = joblib.load(filename)
result = loaded_model.score(X_test, Y_test) 

print(result)
15
Ch HaXam

Je suis l'auteur d'un package appelé klepto (et également l'auteur de dill). klepto est conçu pour stocker et récupérer des objets de manière très simple, et fournit une interface de dictionnaire simple pour les bases de données, le cache mémoire et le stockage sur disque. Ci-dessous, je montre le stockage de gros objets dans une "archive de répertoire", qui est un répertoire de système de fichiers avec un fichier par entrée. Je choisis de sérialiser les objets (c'est plus lent, mais utilise dill, donc vous pouvez stocker presque n'importe quel objet), et je choisis un cache. L'utilisation d'un cache mémoire me permet d'avoir un accès rapide à l'archive du répertoire, sans avoir à avoir toute l'archive en mémoire. L'interaction avec une base de données ou un fichier peut être lente, mais l'interaction avec la mémoire est rapide… et vous pouvez remplir le cache mémoire comme vous le souhaitez à partir de l'archive.

>>> import klepto
>>> d = klepto.archives.dir_archive('stuff', cached=True, serialized=True)
>>> d
dir_archive('stuff', {}, cached=True)
>>> import numpy
>>> # add three entries to the memory cache
>>> d['big1'] = numpy.arange(1000)
>>> d['big2'] = numpy.arange(1000)
>>> d['big3'] = numpy.arange(1000)
>>> # dump from memory cache to the on-disk archive
>>> d.dump()
>>> # clear the memory cache
>>> d.clear()
>>> d
dir_archive('stuff', {}, cached=True)
>>> # only load one entry to the cache from the archive
>>> d.load('big1')
>>> d['big1'][-3:]
array([997, 998, 999])
>>> 

klepto fournit un accès rapide et flexible à de grandes quantités de stockage, et si l'archive permet un accès parallèle (par exemple certaines bases de données), vous pouvez lire les résultats en parallèle. Il est également facile de partager les résultats dans différents processus parallèles ou sur différentes machines. Ici, je crée une deuxième instance d'archive, pointant vers la même archive de répertoire. Il est facile de passer des clés entre les deux objets et ne fonctionne pas différemment d'un processus différent.

>>> f = klepto.archives.dir_archive('stuff', cached=True, serialized=True)
>>> f
dir_archive('stuff', {}, cached=True)
>>> # add some small objects to the first cache  
>>> d['small1'] = lambda x:x**2
>>> d['small2'] = (1,2,3)
>>> # dump the objects to the archive
>>> d.dump()
>>> # load one of the small objects to the second cache
>>> f.load('small2')
>>> f       
dir_archive('stuff', {'small2': (1, 2, 3)}, cached=True)

Vous pouvez également choisir parmi différents niveaux de compression de fichiers, et si vous souhaitez que les fichiers soient mappés en mémoire. Il existe de nombreuses options différentes, à la fois pour les backends de fichiers et les backends de base de données. L'interface est cependant identique.

En ce qui concerne vos autres questions sur la récupération de place et la modification de parties du dictionnaire, les deux sont possibles avec klepto, car vous pouvez charger et supprimer individuellement des objets du cache mémoire, vider, charger et synchroniser avec l'archive backend, ou l'une des autres méthodes de dictionnaire.

Voir un tutoriel plus long ici: https://github.com/mmckerns/tlkklp

Obtenez klepto ici: https://github.com/uqfoundation

13
Mike McKerns

J'ai eu une erreur de mémoire et l'ai résolue en utilisant le protocole = 2:

cPickle.dump(obj, file, protocol=2)

3
denfromufa

Que dis-tu de ça?

import cPickle as pickle
p = pickle.Pickler(open("temp.p","wb")) 
p.fast = True 
p.dump(d) # d could be your dictionary or any file
2
richie

Avez-vous essayé d'utiliser le streaming cornichon: https://code.google.com/p/streaming-pickle/

Je viens de résoudre une erreur de mémoire similaire en passant au cornichon en continu.

2
Chris Wheadon

Si votre clé et vos valeurs sont des chaînes, vous pouvez utiliser l'un des moteurs de stockage de valeurs-clés persistants intégrés disponibles dans la bibliothèque standard Python. Exemple de la anydbm documentation du module:

import anydbm

# Open database, creating it if necessary.
db = anydbm.open('cache', 'c')

# Record some values
db['www.python.org'] = 'Python Website'
db['www.cnn.com'] = 'Cable News Network'

# Loop through contents.  Other dictionary methods
# such as .keys(), .values() also work.
for k, v in db.iteritems():
    print k, '\t', v

# Storing a non-string key or value will raise an exception (most
# likely a TypeError).
db['www.yahoo.com'] = 4

# Close when done.
db.close()
2
Imran

J'ai récemment eu ce problème. Après avoir essayé cpickle avec ASCII et le protocole binaire 2, j'ai constaté que mon SVM de sci-kit learn entraîné sur plus de 20 Go de données ne marchait pas en raison d'une erreur de mémoire. Cependant, l'aneth package semblait résoudre le problème. Dill ne créera pas de nombreuses améliorations pour un dictionnaire mais peut aider à la diffusion en continu. Il est destiné à diffuser des octets décapés sur un réseau.

import dill

with open(path,'wb') as fp:
    dill.dump(outpath,fp)
    dill.load(fp)

Si l'efficacité est un problème, essayez de charger/enregistrer dans une base de données. Dans ce cas, votre solution de stockage peut être un problème. À 123 Mo Pandas devrait être bien. Cependant, si la machine a une mémoire limitée, SQL offre des opérations de sacs rapides et optimisées sur les données, généralement avec un support multithread. Mon svm de noyau poly enregistré.

1
Andrew Scott Evans

Cela peut sembler trivial, mais essayez d'utiliser le 64 bits Python si vous ne l'êtes pas.

1
lyron

Aucune des réponses ci-dessus n'a fonctionné pour moi. J'ai fini par utiliser Hickle, qui remplace directement les cornichons à base de HDF5. Au lieu de l'enregistrer dans un cornichon, il enregistre les données dans un fichier HDF5. L'API est identique pour la plupart des cas d'utilisation et elle possède des fonctionnalités vraiment intéressantes telles que la compression.

pip install hickle

Exemple:

# Create a numpy array of data
array_obj = np.ones(32768, dtype='float32')

# Dump to file
hkl.dump(array_obj, 'test.hkl', mode='w')

# Load data
array_hkl = hkl.load('test.hkl')
1
gidim