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.
| Étape | Fichier .tflite | APK compressé |
|---|---|---|
int8 QAT (module 4) | 3,7 Mo | 3,3 Mo |
int8 QAT + 60 % élagage | 3,7 Mo | 2,1 Mo |
int8 QAT + 80 % élagage | 3,7 Mo | 1,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é cible | Exactitude typique | Perte 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 :
- Affiner le modèle
float32sur le domaine (fil rouge : PlantVillage). - Élaguer avec planification progressive (module 5), en gardant
float32. - Optionnellement, regrouper les poids, encore en
float32. - Appliquer le QAT (module 4) pour préparer la quantification
int8. - Convertir en
.tfliteint8complet.
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.
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
| Variante | Taille APK | Latence Nokia G21 | Exactitude |
|---|---|---|---|
float32 (module 2) | 12 Mo | 240 ms | 96,3 % |
int8 QAT (module 4) | 3,3 Mo | 145 ms | 96,2 % |
int8 QAT + élagage 60 % | 2,1 Mo | 145 ms | 95,7 % |
int8 QAT + élagage 80 % | 1,4 Mo | 145 ms | 94,4 % |
int8 QAT + élagage 60 % + regroupement 32 | 1,7 Mo | 145 ms | 95,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
.tflitene baisse pas, seule la compression APK/IPA en profite. - La planification progressive (par exemple
PolynomialDecayde 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.