Notes sur le format Python Pickle

Contexte

Je souhaite échanger des données binaires entre Python et C / C ++ pour l'apprentissage automatique et les courses tardives. Je veux terminer avec uniquement les fonctions standard de Python. Pour le texte, il existe JSON et le format de texte numpy (csv), mais les binaires ne sont pas faciles à utiliser du côté C ++.

Envisagez la sérialisation de Pickle.

https://docs.python.org/ja/3/library/pickle.html

Il semble que l'endianité soit également prise en considération.

information

Le site qui expliquait brièvement le format de sérialisation de Pickle lui-même n'était pas non plus en anglais: Cry: (Une fois que vous le savez, ce n'est pas ce format compliqué, donc ce n'est peut-être pas suffisant pour l'expliquer ...)

Cependant, heureusement, PyTorch JIT prend en charge la sérialisation avec son propre chargeur Pickle C ++ pour implémenter TorchScript (langage de script de type Python), et le code est utile.

https://github.com/pytorch/pytorch/blob/master/torch/csrc/jit/docs/serialization.md

https://github.com/pytorch/pytorch/blob/master/torch/csrc/jit/serialization/pickler.h

Vous pouvez également analyser les données avec Pickletools en Python.

https://docs.python.org/ja/3.6/library/pickletools.html

format

Version du protocole

Pickle a plusieurs versions de protocole.3 est la valeur par défaut dans Python3, mais lorsqu'il est sérialisé dans proto 3 dans Python3, il ne peut pas être lu dans Python2.

Si vous utilisez principalement des données numériques et que vous ne manipulez pas des données qui ne sont pas très étranges, est-ce que proto 2 est recommandé? (TorchScript ne prend en charge que le proto 2)

L'en-tête sera de 2 octets de «0x80» (PROTO, 1 octet) et le numéro de version (1 octet).

Essayons de sérialiser 1.

import pickle
import io

a = 1 

f = io.BytesIO()
b = pickle.dump(a, f)

w = open("bora.p", "wb")
w.write(f.getbuffer())
$ od -tx1c bora.p
0000000  80  03  4b  01  2e
        200 003   K 001   .
0000005

«K» est «BININT1» . (2e) est STOP. Fin des données.

En regardant unpicker.cpp dans pytorch jit,

    case PickleOpCode::BININT1: {
      uint8_t value = read<uint8_t>();
      stack_.emplace_back(int64_t(value));
    } break;

Comme c'est le cas, vous pouvez voir que BININT1 est une valeur de type int qui peut être sérialisée avec 1 octet.

Essayez les données du tableau.

import pickle
import io

a = [1, 2] 

f = io.BytesIO()
b = pickle.dump(a, f, protocol=2)

w = open("bora.p", "wb")
w.write(f.getbuffer())

Maintenant, vidons-le avec pickletools.

$ python -m pickletools bora.p 
    0: \x80 PROTO      2
    2: ]    EMPTY_LIST
    3: q    BINPUT     0
    5: (    MARK
    6: K        BININT1    1
    8: K        BININT1    2
   10: e        APPENDS    (MARK at 5)
   11: .    STOP
highest protocol among opcodes = 2

Fondamentalement, c'est une combinaison de préfixe + données réelles, donc après cela, vous devriez essayer différentes choses en vous référant à pickler.cpp, unpickler.cpp et pickletools.py de pytorch jit et l'analyser!

numpy array

Sérialisons le tableau numpy (ndarray).

a = numpy.array([1.0, 2.2, 3.3, 4, 5, 6, 7, 8, 9, 10], dtype=numpy.float32)

f = io.BytesIO()
b = pickle.dump(a, f, protocol=2)

w = open("bora.p", "wb")
w.write(f.getbuffer())
    0: \x80 PROTO      2
    2: c    GLOBAL     'numpy.core.multiarray _reconstruct'
   38: q    BINPUT     0
   40: c    GLOBAL     'numpy ndarray'
   55: q    BINPUT     1
   57: K    BININT1    0
   59: \x85 TUPLE1
   60: q    BINPUT     2
   62: c    GLOBAL     '_codecs encode'
   78: q    BINPUT     3
   80: X    BINUNICODE 'b'
   86: q    BINPUT     4
   88: X    BINUNICODE 'latin1'
   99: q    BINPUT     5
  101: \x86 TUPLE2
  102: q    BINPUT     6
  104: R    REDUCE
  105: q    BINPUT     7
  107: \x87 TUPLE3
  108: q    BINPUT     8
  110: R    REDUCE
  111: q    BINPUT     9
  113: (    MARK
  114: K        BININT1    1
  116: K        BININT1    10
  118: \x85     TUPLE1
  119: q        BINPUT     10
  121: c        GLOBAL     'numpy dtype'
  134: q        BINPUT     11
  136: X        BINUNICODE 'f4'
  143: q        BINPUT     12
  145: K        BININT1    0
  147: K        BININT1    1
  149: \x87     TUPLE3
  150: q        BINPUT     13
  152: R        REDUCE
  153: q        BINPUT     14
  155: (        MARK
  156: K            BININT1    3
  158: X            BINUNICODE '<'
  164: q            BINPUT     15
  166: N            NONE
  167: N            NONE
  168: N            NONE
  169: J            BININT     -1
  174: J            BININT     -1
  179: K            BININT1    0
  181: t            TUPLE      (MARK at 155)
  182: q        BINPUT     16
  184: b        BUILD
  185: \x89     NEWFALSE
  186: h        BINGET     3
  188: X        BINUNICODE '\x00\x00\x80?ÍÌ\x0c@33S@\x00\x00\x80@\x00\x00\xa0@\x00\x00À@\x00\x00à@\x00\x00\x00A\x00\x00\x10A\x00\x00 A'
  240: q        BINPUT     17
  242: h        BINGET     5
  244: \x86     TUPLE2
  245: q        BINPUT     18
  247: R        REDUCE
  248: q        BINPUT     19
  250: t        TUPLE      (MARK at 113)
  251: q    BINPUT     20
  253: b    BUILD
  254: .    STOP
highest protocol among opcodes = 2

Vous pouvez voir que les données du tableau sont stockées sous forme de chaîne d'octets autour de BINUNICODE. Après avoir analysé le code source de numpy, il semble que vous puissiez charger la version pickle du tableau numpy et du tenseur pytorch (vous pouvez imaginer qu'il a une structure similaire à numpy) avec votre propre chargeur C ++! (numpy native? NPY / NPZ est un format un peu concis, par exemple cnpy peut lire et écrire https://github.com/rogersce/cnpy)

TODO

Recommended Posts

Notes sur le format Python Pickle
Mémo de raclage Python
Note d'apprentissage Python_000
Notes d'apprentissage Python
Note d'apprentissage Python_006
notes de python C ++
Note d'apprentissage Python_005
Notes de grammaire Python
Note sur la bibliothèque Python
Format de chaîne Python
notes personnelles python
format en python
mémo pandas python
Note d'apprentissage Python_001
notes d'apprentissage python
Notes d'installation de Python3.4
expansion de variable python, format
notes personnelles en python manquantes
Notes de développement de package Python
notes d'utilisation du décorateur python
Mémo du package d'adresse IP Python
Format d'image en Python
Utilisation méthodique du format [Python]
Premier mémo divers sur Python
Matlab => Notes de migration Python
Remarques sur l'affectation Python3
Notes sur l'utilisation de sous-processus Python
Python essayer / sauf mémo
Notes sur le flacon de framework Python
mémo python utilisant l'opérateur perl-ternaire
Formatez facilement JSON avec Python
Indentation Python et format de chaîne
Mémo d'apprentissage O'Reilly python3 Primer
Remarques sur l'utilisation du test unittest standard Python
Notes Python à oublier bientôt
python * args, ** kwargs Notes d'utilisation
notes python pour l'utilisation de variables spéciales perl
Notes sur l'expression régulière de la théorie Python
Mémo Python Tkinter (pour moi)
Notes d'apprentissage sur l'analyse des données Python
Remarques sur l'installation de Python sur votre Mac
[Python 2/3] Analyser la chaîne de format
Exemple d'utilisation de pickle Python
Formater json avec Vim (avec python)
Obtenez des notes Evernote en Python
Format de chaîne avec l'opérateur Python%
Remarques sur l'installation de Python sur CentOS
Notes sur Python et les types de dictionnaire
Python
Application Python: Traitement des données # 3: Format des données
Notes de grammaire minimales pour écrire Python
Remarques sur l'utilisation de MeCab depuis Python
Formater automatiquement le code Python avec Vim
Gérez les données au format NetCDF avec Python
Gérez le format GDS II avec Python
À propos de Python Pickle (cPickle) et Marshal
Notes personnelles pour le traitement d'images python
Notes personnelles de prétraitement des données Python Pandas