Course
Python Pickle Tutorial: Objekt-Serialisierung
Einführung in die Objektserialisierung
Bist du es leid, deinen Python-Code jedes Mal neu auszuführen, wenn du auf einen zuvor erstellten Datenrahmen, eine Variable oder ein Machine-Learning-Modell zugreifen musst?
Die Objektserialisierung könnte die Lösung sein, nach der du suchst.
Dabei wird eine Datenstruktur im Speicher gespeichert, damit du sie bei Bedarf laden oder übertragen kannst, ohne ihren aktuellen Zustand zu verlieren.
Hier ist ein einfaches Diagramm, das erklärt, wie die Serialisierung funktioniert:
Bild vom Autor
In Python arbeiten wir mit High-Level-Datenstrukturen wie Listen, Tupeln und Mengen. Wenn wir diese Objekte jedoch im Speicher ablegen wollen, müssen sie in eine Folge von Bytes umgewandelt werden, die der Computer verstehen kann. Dieser Prozess wird Serialisierung genannt.
Wenn wir das nächste Mal auf dieselbe Datenstruktur zugreifen wollen, muss diese Bytefolge in einem Prozess, der als Deserialisierung bezeichnet wird, wieder in das High-Level-Objekt umgewandelt werden.
Wir können Formate wie JSON, XML, HDF5 und Pickle für die Serialisierung verwenden. In diesem Tutorium lernen wir die Python Pickle-Bibliothek zur Serialisierung kennen. Wir werden uns mit seinen Einsatzmöglichkeiten beschäftigen und erklären, wann du Pickle anderen Serialisierungsformaten vorziehen solltest.
Schließlich werden wir lernen, wie man die Pickle Python-Bibliothek verwendet, um Listen, Wörterbücher, Pandas-Datenrahmen, Modelle für maschinelles Lernen und mehr zu serialisieren.
Um den Beispielcode in diesem Lernprogramm ganz einfach selbst auszuführen, kannst du eine kostenlose DataLab-Arbeitsmappe erstellen, auf der Python vorinstalliert ist und die alle Codebeispiele enthält. Wenn du mehr über das Laden von Python-Pickled-Dateien erfahren möchtest, schau dir diese praktische DataCamp-Übung an.
Python von Grund auf lernen
Warum brauchen wir die Objektserialisierung?
Bevor wir mit Python Pickle loslegen, wollen wir verstehen, warum die Serialisierung von Objekten so wichtig ist.
Du fragst dich vielleicht, warum wir Datenstrukturen nicht einfach in einer Textdatei speichern und bei Bedarf wieder darauf zugreifen können, anstatt sie zu serialisieren.
Lass uns ein einfaches Beispiel durchgehen, um die Vorteile der Serialisierung zu verstehen.
Hier ist ein verschachteltes Wörterbuch mit Schülerinformationen wie Name, Alter und Geschlecht:
students = {
'Student 1': {
'Name': "Alice", 'Age' :10, 'Grade':4,
},
'Student 2': {
'Name':'Bob', 'Age':11, 'Grade':5
},
'Student 3': {
'Name':'Elena', 'Age':14, 'Grade':8
}
}
Schauen wir uns den Datentyp des Objekts "Schüler" an:
type(students)
dict
Nachdem wir nun bestätigt haben, dass das Schülerobjekt ein Wörterbuch ist, können wir es ohne Serialisierung in eine Textdatei schreiben:
with open('student_info.txt','w') as data:
data.write(str(students))
Da wir nur String-Objekte in Textdateien schreiben können, haben wir das Wörterbuch mit der Funktion str() in einen String umgewandelt. Das bedeutet, dass der ursprüngliche Zustand unseres Wörterbuchs verloren geht.
Lies nun das Wörterbuch, drucke es aus und überprüfe noch einmal die Schrift:
with open("student_info.txt", 'r') as f:
for students in f:
print(students)
type(students)
str
Das verschachtelte Wörterbuch wird jetzt als String ausgegeben und gibt einen Fehler zurück, wenn wir versuchen, auf seine Schlüssel oder Werte zuzugreifen.
Hier kommt die Serialisierung ins Spiel.
Bei komplexeren Datentypen wie Wörterbüchern, Datenrahmen und verschachtelten Listen ermöglicht es die Serialisierung, den ursprünglichen Zustand des Objekts zu erhalten, ohne dass relevante Informationen verloren gehen.
Später in diesem Artikel werden wir lernen, das gleiche Wörterbuch mit Pickle in einer Datei zu speichern und es mit der Bibliothek zu deserialisieren. Mehr über das Verstehen von Python-Wörterbüchern kannst du in einem separaten Tutorial nachlesen.
Einführung in Pickle in Python
Das Pickle-Modul von Python ist ein beliebtes Format, um Datentypen zu serialisieren und zu deserialisieren. Dieses Format ist nativ für Python, d.h. Pickle-Objekte können mit keiner anderen Programmiersprache geladen werden.
Pickle hat im Vergleich zu anderen Serialisierungsformaten seine eigenen Vor- und Nachteile.
Vorteile der Verwendung von Pickle zur Serialisierung von Objekten
- Im Gegensatz zu Serialisierungsformaten wie JSON, die keine Tupel und Datetime-Objekte verarbeiten können, kann Pickle fast jeden gängigen eingebauten Python-Datentyp serialisieren. Außerdem behält es den genauen Zustand des Objekts bei, was JSON nicht kann.
- Pickle ist auch eine gute Wahl, um rekursive Strukturen zu speichern, da es ein Objekt nur einmal schreibt.
- Pickle ermöglicht Flexibilität bei der Deserialisierung von Objekten. Du kannst ganz einfach verschiedene Variablen in einer Pickle-Datei speichern und sie in einer anderen Python-Sitzung wieder laden, um deine Daten genau so wiederherzustellen, wie sie waren, ohne dass du deinen Code bearbeiten musst.
Nachteile der Verwendung von Pickle
- Pickle ist unsicher, weil es bösartige Python-Callables ausführen kann, um Objekte zu konstruieren. Beim Deserialisieren eines Objekts kann Pickle nicht zwischen einem bösartigen Callable und einem nicht-bösartigen unterscheiden. Dadurch können Benutzer während der Deserialisierung beliebigen Code ausführen.
- Wie bereits erwähnt, ist Pickle ein Python-spezifisches Modul und es kann schwierig sein, gepickelte Objekte zu deserialisieren, wenn du eine andere Sprache verwendest.
- Mehrere Benchmarks haben ergeben, dass Pickle langsamer ist und größere serialisierte Werte erzeugt als Formate wie JSON und ApacheThrift.
Speichern und Laden von Objekten mit der Pickle Dump Python-Funktion und der Load-Funktion
Pickle verwendet die folgenden Funktionen zum Serialisieren und Deserialisieren von Python-Objekten:
pickle.dump(obj, file, protocol=None, *, fix_imports=True, buffer_callback=None)
pickle.dumps(obj, protocol=None, *, fix_imports=True, buffer_callback=None)
pickle.load(file, *, fix_imports=True, encoding='ASCII', errors='strict', buffers=None)
pickle.loads(data, /, *, fix_imports=True, encoding=”ASCII”, errors=”strict”, buffers=None)
Die Pickle-Funktionen dump() und dumps() werden verwendet, um ein Objekt zu serialisieren. Der einzige Unterschied zwischen ihnen ist, dass dump() die Daten in eine Datei schreibt, während dumps() sie als Byte-Objekt darstellt.
Ähnlich liest load() gepickte Objekte aus einer Datei, während loads() sie aus einem byteartigen Objekt deserialisiert.
In diesem Lernprogramm werden wir die Funktionen dump() und load() verwenden, um Python-Objekte in eine Datei zu picken und zu entpicken.
Serialisierung von Python-Datenstrukturen mit Pickle
Listen
Erstellen wir zunächst eine einfache Python-Liste:
import pickle
student_names = ['Alice','Bob','Elena','Jane','Kyle']
Jetzt öffnen wir eine Textdatei, schreiben die Liste mit der Funktion dumps() hinein und schließen die Datei:
with open('student_file.pkl', 'wb') as f: # open a text file
pickle.dump(student_names, f) # serialize the list
Zuerst haben wir eine Datei namens "student_file.pkl" erstellt. Die Erweiterung muss nicht .pkl sein. Du kannst die Datei so nennen, wie du willst, und sie wird trotzdem erstellt. Es ist jedoch eine gute Praxis, die Erweiterung .pkl zu verwenden, damit du daran erinnert wirst, dass es sich um eine Pickle-Datei handelt.
Beachte auch, dass wir die Datei im Modus "wb" geöffnet haben. Das bedeutet, dass du die Datei im Binärmodus schreibst, sodass die Daten in einem Byte-Objekt zurückgegeben werden.
Dann verwenden wir die Funktion dump(), um die Liste "student_names" in der Datei zu speichern.
Schließlich kannst du die Datei mit der folgenden Codezeile schließen:
f.close()
Jetzt deserialisieren wir die Datei und drucken die Liste aus:
with open('student_file.pkl', 'rb') as f:
student_names_loaded = pickle.load(f) # deserialize using load()
print(student_names_loaded) # print student names
Die Ausgabe des obigen Codes sollte wie folgt aussehen:
['Alice', 'Bob', 'Elena', 'Jane', 'Kyle']
Beachte, dass wir zum Deserialisieren der Datei den Modus "rb" verwenden müssen, der für "read binary" steht. Dann entpacken wir das Objekt mit der load()-Funktion. Danach können wir die Daten in einer anderen Variablen speichern und nach Belieben verwenden.
Überprüfen wir nun den Datentyp der Liste, die wir gerade entpickt haben:
type(student_names_loaded)
list
Toll! Wir haben den ursprünglichen Zustand und Datentyp dieser Liste beibehalten.
Numpy-Arrays
Jetzt wollen wir versuchen, einen etwas komplexeren Datentyp zu serialisieren und zu deserialisieren - Numpy-Arrays.
Erstellen wir zunächst ein 10 x 10 Array mit Einsen:
import numpy as np
numpy_array = np.ones((10,10)) # 10x10 array
Dann rufen wir wie zuvor die Funktion dump() auf, um dieses Array in eine Datei zu serialisieren:
with open('my_array.pkl','wb') as f:
pickle.dump(numpy_array, f)
Zum Schluss entpacken wir dieses Array und überprüfen seine Form und seinen Datentyp, um sicherzustellen, dass es seinen ursprünglichen Zustand beibehalten hat:
with open('my_array.pkl','rb') as f:
unpickled_array = pickle.load(f)
print('Array shape: '+str(unpickled_array.shape))
print('Data type: '+str(type(unpickled_array)))
Du solltest die folgende Ausgabe erhalten:
Array shape: (10, 10)
Data type: <class 'numpy.ndarray'>
Das ungepickte Objekt ist ein 10X10 Numpy-Array, das die gleiche Form und den gleichen Datentyp hat wie das Objekt, das wir gerade serialisiert haben.
pandas DataFrames
Ein Datenrahmen ist ein Objekt, mit dem Datenwissenschaftler täglich arbeiten. Die beliebteste Art, einen Pandas-Dataframe zu laden und zu speichern, ist, ihn als csv-Datei zu lesen und zu schreiben. Erfahre mehr über das Importieren von Daten in unserem Pandas read_csv()-Tutorial.
Dieser Prozess ist jedoch langsamer als die Serialisierung und kann extrem zeitaufwändig werden, wenn der Datenrahmen groß ist.
Vergleichen wir die Effizienz des Speicherns und Ladens eines Pandas-Datenrahmens mit Pickle und csv, indem wir die jeweils benötigte Zeit vergleichen.
Zuerst erstellen wir einen Pandas-Datenrahmen mit 100.000 Zeilen gefälschter Daten:
import pandas as pd
import numpy as np
# Set random seed
np.random.seed(123)
data = {'Column1': np.random.randint(0, 10, size=100000),
'Column2': np.random.choice(['A', 'B', 'C'], size=100000),
'Column3': np.random.rand(100000)}
# Create Pandas dataframe
df = pd.DataFrame(data)
Berechnen wir nun, wie lange es dauert, diesen Datenrahmen als csv-Datei zu speichern:
import time
start = time.time()
df.to_csv('pandas_dataframe.csv')
end = time.time()
print(end - start)
0.19349145889282227
Wir haben 0,19 Sekunden gebraucht, um einen Pandas-Datenrahmen mit drei Zeilen und 100.000 Spalten in einer csv-Datei zu speichern.
Mal sehen, ob die Verwendung von Pickle die Leistung verbessern kann. Die Pandas-Bibliothek hat eine Methode namens to_pickle(), mit der wir Datenrahmen in nur einer Zeile Code in Pickle-Dateien serialisieren können:
start = time.time()
df.to_pickle("my_pandas_dataframe.pkl")
end = time.time()
print(end - start)
0.0059659481048583984
Wir haben nur 5 Millisekunden gebraucht, um denselben Pandas-Datenrahmen in einer Pickle-Datei zu speichern, was eine erhebliche Leistungssteigerung gegenüber dem Speichern als csv-Datei ist.
Jetzt wollen wir die Datei zurück in Pandas lesen und sehen, ob das Laden einer Pickle-Datei Leistungsvorteile gegenüber dem einfachen Lesen einer csv-Datei bietet:
# Reading the csv file into Pandas:
start1 = time.time()
df_csv = pd.read_csv("my_pandas_dataframe.csv")
end1 = time.time()
print("Time taken to read the csv file: " + str(end1 - start1) + "\n")
# Reading the Pickle file into Pandas:
start2 = time.time()
df_pkl = pd.read_pickle("my_pandas_dataframe.pkl")
end2 = time.time()
print("Time taken to read the Pickle file: " + str(end2 - start2))
Der obige Code sollte die folgende Ausgabe liefern:
Time taken to read the csv file: 0.00677490234375
Time taken to read the Pickle file: 0.0009608268737792969
Es dauerte 6 Millisekunden, um die csv-Datei in Pandas einzulesen, und nur 0,9 Millisekunden, um sie in Pickle einzulesen.
Auch wenn dieser Unterschied gering erscheinen mag, kann die Serialisierung großer Pandas-Dataframes mit Pickle zu einer erheblichen Zeitersparnis führen. Pickle hilft uns auch dabei, den Datentyp jeder Spalte in jedem Fall beizubehalten und nimmt weniger Speicherplatz als eine csv-Datei ein.
Wörterbücher
Zum Schluss lass uns das Wörterbuch serialisieren, das wir im ersten Teil des Tutorials in eine Textdatei geschrieben haben:
students = {
'Student 1': {
'Name': "Alice", 'Age' :10, 'Grade':4,
},
'Student 2': {
'Name':'Bob', 'Age':11, 'Grade':5
},
'Student 3': {
'Name':'Elena', 'Age':14, 'Grade':8
}
}
Als wir das Wörterbuch als Textdatei gespeichert haben, mussten wir es in eine Zeichenkette umwandeln und haben dabei seinen ursprünglichen Zustand verloren.
Jetzt serialisieren wir es mit Pickle und lesen es zurück, um sicherzustellen, dass es immer noch alle Eigenschaften eines Python-Dictionarys enthält:
# serialize the dictionary to a pickle file
with open("student_dict.pkl", "wb") as f:
pickle.dump(students, f)
# deserialize the dictionary and print it out
with open("student_dict.pkl", "rb") as f:
deserialized_dict = pickle.load(f)
print(deserialized_dict)
Du solltest die folgende Ausgabe erhalten:
{'Student 1': {'Name': 'Alice', 'Age': 10, 'Grade': 4}, 'Student 2': {'Name': 'Bob', 'Age': 11, 'Grade': 5}, 'Student 3': {'Name': 'Elena', 'Age': 14, 'Grade': 8}}
Prüfen wir nun den Typ dieser Variablen:
type(deserialized_dict)
dict
Versuchen wir, einige Informationen über den ersten Schüler in diesem Wörterbuch zu finden:
print(
"The first student's name is "
+ deserialized_dict["Student 1"]["Name"]
+ " and she is "
+ (str(deserialized_dict["Student 1"]["Age"]))
+ " years old."
)
The first student's name is Alice and she is 10 years old.
Toll! Das Wörterbuch hat alle seine ursprünglichen Eigenschaften behalten und kann genau wie vor der Serialisierung aufgerufen werden. In unserem Tutorial zum Verstehen von Python-Wörterbüchern erfährst du mehr.
Serialisierung von Machine Learning Modellen mit Pickle
Das Training eines maschinellen Lernmodells ist ein zeitaufwändiger Prozess, der Stunden und manchmal sogar viele Tage dauern kann. Es ist einfach nicht machbar, einen Algorithmus von Grund auf neu zu trainieren, wenn du ihn wiederverwenden oder auf eine andere Umgebung übertragen musst.
Wenn du mehr über Best Practices bei der Entwicklung von Machine Learning Algorithmen erfahren möchtest, kannst du unseren Kurs Designing Machine Learning Workflows in Python besuchen.
Mit Pickle kannst du Modelle für maschinelles Lernen in ihrem bestehenden Zustand serialisieren, damit du sie bei Bedarf wieder verwenden kannst.
In diesem Abschnitt lernen wir, wie man ein Machine Learning-Modell mit Pickle serialisiert.
Dazu generieren wir zunächst einige gefälschte Daten und erstellen ein lineares Regressionsmodell mit der Scikit-Learn-Bibliothek:
from sklearn.linear_model import LinearRegression
from sklearn.datasets import make_regression
# generate regression dataset
X, y = make_regression(n_samples=100, n_features=3, noise=0.1, random_state=1)
# train regression model
linear_model = LinearRegression()
linear_model.fit(X, y)
Lass uns nun einige zusammenfassende Modellparameter ausdrucken:
# summary of the model
print('Model intercept :', linear_model.intercept_)
print('Model coefficients : ', linear_model.coef_)
print('Model score : ', linear_model.score(X, y))
Model intercept : -0.010109549594702116
Model coefficients : [44.18793068 98.97389468 58.17121618]
Model score : 0.9999993081899219
Dann können wir dieses Modell mit der dump()-Funktion von Pickle serialisieren:
with open("linear_regression.pkl", "wb") as f:
pickle.dump(linear_model, f)
Das Modell wird jetzt als Pickle-Datei gespeichert. Deserialisieren wir sie mit der Funktion load():
with open("linear_regression.pkl", "rb") as f:
unpickled_linear_model = pickle.load(f)
Das serialisierte Modell wird nun geladen und in der Variable "unpickled_linear_model" gespeichert. Überprüfe die Parameter dieses Modells, um sicherzustellen, dass es mit dem Modell übereinstimmt, das wir ursprünglich erstellt haben:
# summary of the model
print('Model intercept :', unpickled_linear_model.intercept_)
print('Model coefficients : ', unpickled_linear_model.coef_)
print('Model score : ', unpickled_linear_model.score(X, y))
Du solltest die folgende Ausgabe erhalten:
Model intercept : -0.010109549594702116
Model coefficients : [44.18793068 98.97389468 58.17121618]
Model score : 0.9999993081899219
Toll! Die Parameter des Modells, das wir gerade entpackt haben, sind die gleichen wie die, die wir ursprünglich erstellt haben.
Jetzt können wir dieses Modell verwenden, um Vorhersagen für einen Testdatensatz zu treffen, darauf zu trainieren oder es in eine andere Umgebung zu übertragen.
Die Leistung von Python Pickle bei großen Objekten steigern
Pickle ist ein effizientes Serialisierungsformat, das sich in verschiedenen Benchmarks oft als schneller als JSON, XML und HDF5 erwiesen hat.
Bei extrem großen Datenstrukturen oder riesigen Machine-Learning-Modellen kann Pickle jedoch erheblich langsamer werden, und die Serialisierung kann zu einem Engpass in deinem Arbeitsablauf werden.
Hier sind einige Möglichkeiten, wie du die Zeit, die du zum Speichern und Laden von Pickle-Dateien benötigst, reduzieren kannst:
Verwende das Argument 'PROTOCOL'.
Das Standardprotokoll, das beim Speichern und Laden von Pickle-Dateien verwendet wird, ist derzeit das Protokoll 4, das mit den verschiedenen Python-Versionen am besten kompatibel ist.
Wenn du deinen Arbeitsablauf jedoch beschleunigen möchtest, kannst du das Argument HIGHEST_PROTOCOL verwenden, das das schnellste verfügbare Protokoll von Pickle ist.
Um den Leistungsunterschied zwischen dem kompatiblen Pickle-Protokoll und dem Standardprotokoll zu vergleichen, serialisieren wir zunächst einen Pandas-Datenrahmen mit dem Standardprotokoll. Beachte, dass dies die Protokollversion ist, die Pickle verwendet, wenn kein bestimmtes Protokoll ausdrücklich angegeben ist:
import pickle
import time
import numpy as np
# Set random seed
np.random.seed(100)
data = {'Column1': np.random.randint(0, 10, size=100000),
'Column2': np.random.choice(['A', 'B', 'C'], size=100000),
'Column3': np.random.rand(100000)}
# serialize to a file
start = time.time()
with open("df1.pkl", "wb") as f:
pickle.dump(data, f)
end = time.time()
print(end - start)
0.006001710891723633
Es dauerte etwa 6 Millisekunden, bis wir den Datenrahmen mit dem Standardprotokoll von Pickle serialisiert hatten.
Jetzt lass uns den Datenrahmen mit dem höchsten Protokoll beizen:
start = time.time()
with open("df2.pkl", "wb") as f:
pickle.dump(data, f, protocol=pickle.HIGHEST_PROTOCOL)
end = time.time()
print(end - start)
0.0030384063720703125
Mit dem höchsten Protokoll gelang es uns, den Datenrahmen in der Hälfte der Zeit zu serialisieren.
Verwende "cPickle" statt "Pickle".
Das Modul "cPickle" ist eine schnellere Version von "Pickle", die in C geschrieben ist. Das macht sie schneller als die "Pickle"-Bibliothek, die rein in Python implementiert ist.
Beachte, dass in Python3 "cPickle" in "_pickle" umbenannt wurde, also die Bibliothek, die wir importieren werden.
import _pickle as cPickle
start = time.time()
with open("df3.pkl", "wb") as f:
cPickle.dump(data, f)
end = time.time()
print(end-start)
0.004027366638183594
Die Serialisierung mit "cPickle" dauerte etwa 4 Millisekunden, was eine erhebliche Verbesserung gegenüber dem Python-Modul "Pickle" darstellt.
Serialisiere nur, was du brauchst
Selbst mit Workarounds, die die Serialisierung beschleunigen, kann der Prozess bei großen Objekten immer noch sehr langsam sein.
Um die Leistung zu verbessern, kannst du die Datenstruktur aufteilen und nur die notwendigen Teilmengen serialisieren.
Bei der Arbeit mit Wörterbüchern kannst du zum Beispiel Schlüssel-Wert-Paare angeben, auf die du wieder zugreifen möchtest. Verkleinere die Größe des Wörterbuchs, bevor du es serialisierst, da dies die Komplexität des Objekts reduziert und den Prozess erheblich beschleunigt.
Serialisierung mit Pickle in Python: Nächste Schritte
Herzlichen Glückwunsch! Du hast eine Vielzahl von Themen rund um die Serialisierung in Python und die Verwendung der Pickle-Bibliothek gelernt.
Du solltest jetzt ein solides Verständnis davon haben, was Serialisierung ist, wie du Pickle zur Serialisierung von Python-Datenstrukturen verwendest und wie du die Leistung von Pickle mit verschiedenen Argumenten und Modulen optimieren kannst.
Hier sind einige Schritte, die du unternehmen kannst, um dein Verständnis von Serialisierung zu verbessern und sie zur Verbesserung deiner Data Science-Workflows zu nutzen:
- Lerne die neuen Serialisierungsformate kennen. Auch wenn wir in diesem Lehrgang nur das Pickle-Modul behandelt haben, ist es nicht unbedingt das beste Serialisierungsformat für jedes Szenario. Es lohnt sich, andere Formate wie JSON, XML und HDF5 kennenzulernen, damit du weißt, welches Format du für die verschiedenen Anwendungsfälle wählen solltest.
- Versuche es selbst. Wende die in diesem Tutorial gelehrten Konzepte auf deine Data Science Workflows an. Wenn du das nächste Mal eine neue Datenstruktur erstellst oder die Ausgabe einer Berechnung in einer Variablen speicherst, serialisiere sie für die spätere Verwendung, anstatt deinen gesamten Code immer wieder neu auszuführen.
- Nimm einen Online-Kurs. Um deine Data-Science-Workflows zu optimieren und Aufgaben wie die Serialisierung effizient durchzuführen, ist ein gutes Verständnis der Python-Datenstrukturen und -Objekte unabdingbar. Um dies zu erreichen, kannst du unseren Kurs Datentypen für Data Science in Python besuchen.
Erfahre mehr über Python und maschinelles Lernen
Course
Machine Learning with Tree-Based Models in Python
Course
Unsupervised Learning in Python
Der Blog