Ich möchte Binärdaten zwischen Python und C / C ++ für maschinelles Lernen und spätes Rennen austauschen. Ich möchte nur die Standardfunktionen von Python verwenden. Für Text gibt es JSON und Numpy Text Format (CSV), aber Binärdateien sind auf der C ++ - Seite nicht einfach zu verwenden.
Betrachten Sie die Pickle-Serialisierung.
https://docs.python.org/ja/3/library/pickle.html
Es scheint, dass auch Endianness berücksichtigt wird.
Die Seite, auf der Pickles Serialisierungsformat selbst kurz erklärt wurde, war ebenfalls nicht in englischer Sprache: Cry: (Sobald Sie es wissen, ist es nicht das komplizierte Format, daher reicht es möglicherweise nicht aus, es zu erklären ...)
Zum Glück unterstützt PyTorch JIT die Serialisierung mit einem eigenen C ++ - Pickle-Loader für die Implementierung von TorchScript (Python-ähnliche Skriptsprache), und der Code ist hilfreich.
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
Sie können die Daten auch mit Pickletools in Python analysieren.
https://docs.python.org/ja/3.6/library/pickletools.html
Es gibt mehrere Protokollversionen von Pickle. In Python3 ist 3 die Standardeinstellung, aber wenn sie in Python3 mit Proto 3 serialisiert wird, kann sie in Python2 nicht gelesen werden.
Wenn Sie hauptsächlich numerische Daten verwenden und keine Daten verarbeiten, die nicht sehr seltsam sind, wird Proto 2 empfohlen? (TorchScript unterstützt nur Proto 2)
Der Header besteht aus 2 Bytes "0x80" (PROTO, 1 Byte) und der Versionsnummer (1 Byte).
Versuchen wir, 1 zu serialisieren.
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" ist "BININT1"
.
(2e) ist STOP
. Datenende.
Mit Blick auf unpicker.cpp in pytorch jit,
case PickleOpCode::BININT1: {
uint8_t value = read<uint8_t>();
stack_.emplace_back(int64_t(value));
} break;
Da dies der Fall ist, können Sie sehen, dass BININT1
ein Wert vom Typ int ist, der mit 1 Byte serialisiert werden kann.
Probieren Sie die Array-Daten aus.
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())
Lassen Sie es uns jetzt mit Pickletools entsorgen.
$ 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
Grundsätzlich handelt es sich um eine Kombination aus Präfix und tatsächlichen Daten. Danach sollten Sie verschiedene Dinge ausprobieren, indem Sie auf pickler.cpp, unpickler.cpp und pickletools.py von pytorch jit verweisen und diese analysieren!
numpy array
Lassen Sie uns das numpy-Array (ndarray) serialisieren.
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
Sie können sehen, dass die Array-Daten als Byte-String um BINUNICODE gespeichert sind. Nach der Analyse des Quellcodes von numpy können Sie die Pickle-Version von numpy array und pytorch tensor (Sie können sich vorstellen, dass es eine ähnliche Struktur wie numpy hat) mit Ihrem eigenen C ++ - Loader laden! (numpy native? NPY / NPZ hat ein etwas prägnantes Format, zum Beispiel kann cnpy https://github.com/rogersce/cnpy lesen und schreiben)
TODO
Recommended Posts