27. Sérialisation Avancée

pickle, json avancé, msgpack, parquet, hdf5, zarr, protobuf, PyYAML, toml, performance, sécurité.


27.1 pickle — sérialisation Python native

import pickle
 
data = {"nom": "Alice", "scores": [1, 2, 3], "obj": lambda x: x * 2}
 
# Sérialiser → bytes
octets = pickle.dumps(data)
 
# Déserialiser
restauré = pickle.loads(octets)
 
# Fichier
with open("data.pkl", "wb") as f:
    pickle.dump(data, f)
 
with open("data.pkl", "rb") as f:
    restauré = pickle.load(f)

Protocoles

pickle.dumps(data, protocol=5)   # 5 = protocol par défaut (3.8+)
pickle.DEFAULT_PROTOCOL          # 5
 
# Protocoles: 0 (texte), 1-2 (anciens binaires), 3-4 (binaires modernes),
#              5 (3.8+, buffers out-of-band)

Pickle et classes personnalisées

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
 
    def __reduce__(self):
        """Contrôle la sérialisation pickle."""
        return (self.__class__, (self.x, self.y))
 
p = Point(1, 2)
restauré = pickle.loads(pickle.dumps(p))

⚠️ Sécurité

Ne jamais dépickler des données non fiables — exécution de code arbitraire.

import pickletools
 
# Vérifier qu'un pickle est sûr (analyse statique)
with open("data.pkl", "rb") as f:
    pickletools.dis(f)  # désassembler les opcodes pickle

Alternatives sécurisées : json, msgpack, protobuf, pyarrow.serialize (déprécié).

27.2 json avancé

Sérialiseurs personnalisés

import json
from datetime import datetime
from pathlib import Path
import numpy as np
 
class JSONEncoderCustom(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime):
            return obj.isoformat()
        if isinstance(obj, Path):
            return str(obj)
        if isinstance(obj, np.ndarray):
            return obj.tolist()
        if isinstance(obj, np.integer):
            return int(obj)
        if isinstance(obj, np.floating):
            return float(obj)
        return super().default(obj)
 
with open("data.json", "w") as f:
    json.dump({"date": datetime.now(), "path": Path("/tmp")}, f, cls=JSONEncoderCustom)

json.dumps formaté

# Compact
json.dumps(data, separators=(",", ":"))   # pas d'espaces
 
# Formaté
json.dumps(data, indent=2, sort_keys=True, ensure_ascii=False)

Itération sur grands JSON

import ijson  # pip install ijson
 
with open("énorme.json", "rb") as f:
    for item in ijson.items(f, "item"):
        traiter(item)  # streaming, pas de chargement complet

27.3 msgpack — binaire compact et rapide

pip install msgpack
import msgpack
 
data = {"nom": "Alice", "âge": 30, "scores": [1.0, 2.0, 3.0]}
 
# Sérialisation
packed = msgpack.packb(data)
print(len(packed))  # plus petit que JSON
 
# Déserialisation
restauré = msgpack.unpackb(packed)

Comparaison taille :

import json, msgpack, pickle
 
data = {"a": list(range(1000)), "b": {"nested": "data" * 100}}
 
len(json.dumps(data))       # ~1200 octets
len(msgpack.packb(data))    # ~1000 octets (plus compact)
len(pickle.dumps(data))     # ~2100 octets

27.4 parquet — format colonnaire

pip install pyarrow  # ou fastparquet
import pandas as pd
import numpy as np
 
# Création
df = pd.DataFrame({
    "a": np.random.rand(1000),
    "b": np.random.randint(0, 100, 1000),
    "c": ["chat", "chien"] * 500,
})
 
# Écriture
df.to_parquet("data.parquet")
 
# Lecture
df = pd.read_parquet("data.parquet")
 
# Avec pyarrow directement
import pyarrow as pa
import pyarrow.parquet as pq
 
table = pa.Table.from_pandas(df)
pq.write_table(table, "data.parquet")
table = pq.read_table("data.parquet")

Avantages : compression élevée, lecture sélective de colonnes, adapté au ML.

27.5 hdf5 — hiérarchique, scientifique

pip install h5py
import h5py
import numpy as np
 
# Écriture
with h5py.File("données.h5", "w") as f:
    f.create_dataset("train/images", data=np.random.rand(100, 32, 32, 3))
    f.create_dataset("train/labels", data=np.random.randint(0, 10, 100))
 
    # Attributs (métadonnées)
    f.attrs["description"] = "Dataset d'entraînement"
    f.attrs["version"] = 1
 
    # Groupes hiérarchiques
    grp = f.create_group("config")
    grp.attrs["learning_rate"] = 0.001
    grp.create_dataset("paramètres", data=np.array([1, 2, 3]))
 
# Lecture
with h5py.File("données.h5", "r") as f:
    images = f["train/images"][:]     # chargement complet
    images = f["train/images"][0:32]  # chargement partiel (slicing)
    print(f.attrs["description"])

Lecture partielle (essentiel pour gros datasets)

with h5py.File("gros.h5", "r") as f:
    ds = f["data"]
    # On peut slicer sans charger en mémoire
    batch = ds[100:200]  # ne charge que 100 éléments

27.6 zarr — arrays chunkés, cloud-native

pip install zarr
import zarr
import numpy as np
 
# Stockage local
z = zarr.open_array("data.zarr", mode="w", shape=(1000, 1000), dtype="f4",
                     chunks=(100, 100))
z[:] = np.random.rand(1000, 1000)
 
# Lecture
z = zarr.open_array("data.zarr", mode="r")
print(z[0:10, 0:10])  # ne charge que les chunks nécessaires
 
# Stockage cloud (S3, GCS)
store = zarr.ABSStore("bucket", prefix="data")
z = zarr.open_array(store, mode="r")

Avantages : chunked, compression, parallélisable, compatible cloud.

27.7 PyYAML — configuration humaine

pip install pyyaml
# config.yaml
model:
  name: resnet20
  hidden_size: 256
  dropout: 0.1
training:
  batch_size: 64
  learning_rate: 0.001
  epochs: 100
import yaml
 
with open("config.yaml") as f:
    config = yaml.safe_load(f)  # safe_load (pas yaml.load)
 
print(config["model"]["name"])
print(config["training"]["learning_rate"])

27.8 toml — configuration (pyproject.toml)

import tomllib  # stdlib 3.11+
 
with open("config.toml", "rb") as f:
    config = tomllib.load(f)
 
# Avant 3.11 : pip install tomli

27.9 Tableau comparatif

FormatBinaire ?TailleLecture partielleTypéUsage
pickleOuiMoyenneNonPython uniquementInterne, cache
jsonNonGrandeNonFaibleAPIs, config
msgpackOuiPetiteNonFaibleRéseau, stockage compact
parquetOuiTrès petiteOui (colonnes)FortData science, pandas
hdf5OuiPetiteOui (slicing)MoyenDatasets ML, scientifique
zarrOuiPetiteOui (chunks)FortDatasets cloud, distribué
yamlNonGrandeNonFaibleConfiguration humaine
tomlNonGrandeNonFaiblepyproject.toml

27.10 Recommandations pour la thèse

  • Configuration : YAML ou TOML
  • Checkpoints de modèles : pickle (PyTorch .pt / .pth) ou HDF5
  • Datasets : HDF5 (petits) ou zarr (gros, cloud)
  • Échange de données : msgpack (binaire) ou JSON (texte)
  • DataFrame : parquet (lecture/écriture rapide, compression)
  • Sauvegarde de résultats d’expériences : JSON pour la lisibilité, msgpack pour la compacité

🔗 ← Retour au cours · ← précédent · Suivant →