Aller au contenu principal

Module 5 — Élagage, sparsité et réduction de taille

Après les modules 3 et 4, le fil rouge tourne en int8 avec 96,2 % d'exactitude et un fichier de 3,7 Mo. C'est déjà excellent, mais deux scénarios exigent d'aller plus loin : la cible finale est une montre connectée avec 1 Mo de stockage libre pour le modèle, ou le magasin d'applications refuse toute mise à jour au-delà d'une taille précise.

L'élagage (pruning) répond à ces cas. Il repose sur une observation solide : dans un réseau entraîné, une part significative des poids est proche de zéro et contribue à peine aux prédictions. Les mettre exactement à zéro produit une matrice creuse qui se compresse très bien, avec une perte d'exactitude qu'on peut contrôler.

Le piège immédiat : élaguer ne réduit pas la taille du .tflite

Ce point mérite d'être placé au premier plan, parce qu'il est contre-intuitif et responsable de la moitié des questions posées par les équipes qui découvrent l'élagage.

Un modèle dont 60 % des poids ont été mis à zéro produit un .tflite strictement de la même taille que le modèle non élagué. La raison est simple : le format TFLite stocke chaque poids dans un tableau dense float32 ou int8, sans mécanisme de représentation creuse. Un poids à zéro occupe exactement les 4 ou 1 octet(s) d'un poids non nul.

Le gain n'apparaît qu'après compression du fichier (gzip, zip, ou la compression appliquée par le magasin d'applications à l'APK/IPA). Une suite de zéros se compresse beaucoup mieux qu'une suite de flottants aléatoires, et c'est là — et là seulement — que le fichier livré à l'utilisateur rétrécit.

ÉtapeFichier .tfliteAPK compressé
int8 QAT (module 4)3,7 Mo3,3 Mo
int8 QAT + 60 % élagage3,7 Mo2,1 Mo
int8 QAT + 80 % élagage3,7 Mo1,4 Mo

C'est le chiffre de la troisième colonne qui compte pour l'utilisateur ; le .tflite isolé n'est un indicateur pertinent qu'après compression.

Élagage par magnitude, avec planification

TFMOT propose la même mécanique que le QAT : une fonction qui enveloppe le modèle et ajoute des opérations d'élagage à l'entraînement. La stratégie par défaut — l'élagage par magnitude — met à zéro les poids dont la valeur absolue est la plus faible.

import tensorflow_model_optimization as tfmot

modele = tf.keras.models.load_model("plantvillage_mobilenetv2.keras")

pas_par_epoque = len(donnees_entrainement)
planning = tfmot.sparsity.keras.PolynomialDecay(
initial_sparsity=0.20,
final_sparsity=0.60,
begin_step=0,
end_step=pas_par_epoque * 4,
)

modele_elague = tfmot.sparsity.keras.prune_low_magnitude(
modele, pruning_schedule=planning,
)

modele_elague.compile(
optimizer=tf.keras.optimizers.Adam(learning_rate=1e-5),
loss="sparse_categorical_crossentropy",
metrics=["accuracy"],
)

modele_elague.fit(
donnees_entrainement,
epochs=5,
validation_data=donnees_validation,
callbacks=[tfmot.sparsity.keras.UpdatePruningStep()],
)

modele_elague = tfmot.sparsity.keras.strip_pruning(modele_elague)

Trois étapes essentielles :

  • La planification progressive : commencer à 20 % de sparsité et monter jusqu'à 60 % en quatre époques. Un saut brutal de 0 à 60 % détruit l'exactitude ; la montée progressive laisse au réseau le temps de compenser la disparition de chaque poids.
  • Le rappel UpdatePruningStep : sans lui, la planification n'est jamais appliquée et les poids restent intacts.
  • strip_pruning à la fin : retire les enveloppes d'entraînement pour produire un modèle propre, prêt à être converti.

Le taux d'apprentissage reste bas (1e-5) parce que l'élagage se fait sur un modèle déjà entraîné. Après cette phase, une conversion int8 complète donne le fichier final.

Combien élaguer avant de perdre trop d'exactitude ?

L'exactitude ne dégrade pas linéairement avec le pourcentage de sparsité. Sur MobileNetV2, une règle empirique tient :

Sparsité cibleExactitude typiquePerte contre float32
40 %96,1 %0,2 point
60 %95,8 %0,5 point
75 %95,0 %1,3 point
85 %92,4 %3,9 point

Le coude apparaît vers 75 % ; au-delà, la perte s'accélère. Pour le fil rouge, viser 60 % de sparsité donne un APK à 2,1 Mo pour 0,5 point d'exactitude perdue, ce qui reste très acceptable. Descendre à 1,4 Mo à 80 % ne se justifie que si le stockage est réellement critique.

Regroupement des poids : une seconde source de compressibilité

Le regroupement (weight clustering) est complémentaire de l'élagage. Il force chaque poids à prendre l'une de K valeurs — typiquement K = 32 ou K = 16 — en groupant les poids proches, à la manière d'un k-means. Un tableau de flottants dont il n'existe que 32 valeurs distinctes se compresse beaucoup mieux qu'un tableau avec 262 144 valeurs uniques.

regroupe = tfmot.clustering.keras.cluster_weights(
modele,
number_of_clusters=32,
cluster_centroids_init=tfmot.clustering.keras.CentroidInitialization.KMEANS_PLUS_PLUS,
)
regroupe.compile(optimizer="adam", loss="sparse_categorical_crossentropy", metrics=["accuracy"])
regroupe.fit(donnees_entrainement, epochs=3, validation_data=donnees_validation)
regroupe = tfmot.clustering.keras.strip_clustering(regroupe)

Combiné à l'élagage à 60 % et à l'int8, le fil rouge descend à 1,7 Mo compressé, pour une exactitude à 95,4 %. Le gain est réel, mais plus modeste que celui de l'élagage seul, et le coût de mise en œuvre — une troisième phase d'entraînement — le rend inutile pour la plupart des applications mobiles standard. Il devient pertinent sur les cibles micro-contrôleurs (voir cours dédié) où le kilooctet compte vraiment.

L'ordre d'application n'est pas neutre

Une équipe qui débute empile souvent les techniques dans un ordre malheureux : d'abord la quantification, puis l'élagage sur le modèle quantifié. Cela produit rarement de bons résultats.

L'ordre qui marche est le suivant :

  1. Affiner le modèle float32 sur le domaine (fil rouge : PlantVillage).
  2. Élaguer avec planification progressive (module 5), en gardant float32.
  3. Optionnellement, regrouper les poids, encore en float32.
  4. Appliquer le QAT (module 4) pour préparer la quantification int8.
  5. Convertir en .tflite int8 complet.

Cet ordre laisse chaque étape s'appuyer sur la précédente sans que la suivante n'annule ses effets. Un modèle élagué à 60 %, puis QAT, puis converti, atteint 95,7 % d'exactitude sur le fil rouge, contre 94,8 % si on quantifie d'abord et qu'on élague ensuite.

La sparsité ne se traduit en vitesse que sur du matériel dédié

Les processeurs mobiles standard ignorent la sparsité : ils exécutent 95,7 % × 0 comme n'importe quelle multiplication. Seuls certains délégués (XNNPACK avec sparsité activée, EdgeTPU récents) exploitent réellement les zéros pour accélérer les convolutions. L'élagage réduit donc la taille avant tout, et la latence de manière optionnelle.

Le tableau de compromis après le module 5

VarianteTaille APKLatence Nokia G21Exactitude
float32 (module 2)12 Mo240 ms96,3 %
int8 QAT (module 4)3,3 Mo145 ms96,2 %
int8 QAT + élagage 60 %2,1 Mo145 ms95,7 %
int8 QAT + élagage 80 %1,4 Mo145 ms94,4 %
int8 QAT + élagage 60 % + regroupement 321,7 Mo145 ms95,4 %

La variante à 2,1 Mo, 95,7 %, 145 ms est celle qui sera retenue pour l'application finale du module 10, sauf contrainte de stockage plus forte. Elle divise le fichier d'origine par six sans dégrader significativement l'expérience utilisateur.

En résumé

  • L'élagage met à zéro les poids de faible magnitude ; la taille du .tflite ne baisse pas, seule la compression APK/IPA en profite.
  • La planification progressive (par exemple PolynomialDecay de 20 à 60 %) est indispensable ; un saut brutal détruit l'exactitude.
  • Le regroupement des poids complète l'élagage pour les cibles très contraintes ; sur mobile standard, l'élagage seul suffit.
  • L'ordre correct est : affiner → élaguer → (regrouper) → QAT → convertir ; l'inverser réduit sensiblement l'exactitude finale.

Module suivant : l'interprète TFLite et les délégués matériels, qui transforment ces 145 ms sur processeur en 30 ms sur accélérateur.