Faire tourner un réseau de neurones sans jamais voir les données
De « c'est quoi chiffrer » à « faire un ResNet sur des données chiffrées ». Dix concepts, dix animations.

Le problème de départ
Les meilleurs modèles d'IA vivent dans le cloud (le MLaaS). Pour en profiter, il faut leur envoyer ses données. Mais certaines, on aimerait les garder pour soi.

On envoie les données… en clair
Ce modèle dans le cloud est génial… mais pour l'utiliser, je dois lui envoyer mes données.



Le risque, en chiffres
des fuites de données impliquent des données stockées dans le cloud.
durée moyenne d'exposition des données avant l'endiguement de la fuite.
coût moyen mondial d'une violation de données (en dollars américains), en baisse de 9 % sur un an grâce à une identification et un confinement plus rapides.
Source : IBM, Cost of a Data Breach 2025.
Il faut donc calculer sans jamais exposer les données. C'est l'enjeu du PPML (Privacy-Preserving ML).
On calcule sur du chiffré
Mes données médicales… en clair, chez moi. Pas question de les livrer telles quelles.



L'idée magique : l'homomorphisme
Homomorphisme = une opération sur les chiffrés correspond à la même opération sur les clairs. Faire le calcul « par le bas » (chiffrer, combiner, déchiffrer) donne exactement le même résultat que « par le haut », le diagramme commute.
Deux nombres bien à moi : a = 5 et b = 7.

Le bruit, héros et méchant
La sécurité repose sur un problème dur, RLWE (réseaux euclidiens) : on cache le message dans une équation, puis on ajoute un petit bruit aléatoire. Retrouver le message revient à résoudre un problème réputé NP-difficile, infaisable sans la clé, même pour un ordinateur quantique.
Ce bruit qui nous protège a pourtant un revers : on enchaîne les opérations (chacune s'applique au résultat de la précédente), et à chaque étape le bruit s'accumule. Vas-y, empile les opérations et regarde la jauge :


Chaque opération s'ajoute à la chaîne et consomme du budget de bruit. La × en coûte bien plus que la +, d'où le nombre limité d'opérations avant le seuil.
🔑 Tout le design d'un calcul HE, c'est un budget de bruit à ne pas dépasser, et une × coûte bien plus cher qu'une +.
« Et quand ça déborde ? » Deux issues : dimensionner large, ou un « reset » du bruit. On y revient après un détour par les réseaux de neurones.
Quel schéma pour le machine learning ?
Avant de choisir, un petit rappel : de quoi est fait un réseau de neurones, et quelles opérations il faut savoir exécuter pour faire une inférence.
Rappel express
Un réseau, c'est un empilement de couches, et chaque neurone fait deux choses
La somme pondérée w·x + b : des × (poids × entrées) et des + (l'accumulation). C'est tout Conv et Dense/FC.
Une fonction non-linéaire f (ReLU, sigmoïde…) appliquée à chaque valeur.
L'inférence, c'est empiler ces couches et pousser l'entrée à travers (forward pass). Deux ingrédients seulement : des sommes pondérées (×, +) et des activations. Retiens bien ce partage, il décide de tout côté HE.
Or les poids et les activations sont des nombres réels
Il faut donc un schéma qui gère la virgule. Parmi la famille des schémas HE :
CKKS = « le flottant chiffré ». Il gère nativement les réels et tolère de petites erreurs (comme du flottant bruié), et un réseau tolère ces erreurs. Mariage parfait : c'est le langage du ML sur HE.
Le chiffré CKKS et ses 3 contraintes
En CKKS, un nombre est représenté comme ceci : pas un simple nombre, mais une paire de polynômes qui portent le message × une échelle Δ (plus un compteur ℓ dont on comprendra le rôle un peu plus loin). Déroule l'animation :
Un chiffré A propre : 2 composantes (c0, c1), échelle Δ, niveau ℓ = 4.

Mais il manque un champ crucial à ce chiffré : le bruit. Zoomons dessus.

Le chiffré bruité entre dans la machine, on rejoue son déchiffrement à l'aveugle, le cadenas 🔒 ne s'ouvre jamais, et il ressort rafraîchi, à bas bruit.
L'intérêt : sans bootstrapping, la profondeur est plafonnée (leveled). Avec, elle est illimitée, mais un seul bootstrap coûte plus cher que 160 multiplications : c'est l'opération la plus lente de tout HE.
Et voilà d'où vient le niveau ℓ
Puisqu'on ne peut pas laisser le bruit franchir ce seuil, on ne peut enchaîner qu'un nombre limité de multiplications avant de devoir le remettre à zéro (bootstrapping). Ce compte à rebours d'opérations, fixé au départ, c'est le niveau ℓ.
Après chaque × chiffré, le rescaling remet l'échelle Δ et le bruit sous contrôle, mais consomme un cran de ℓ. À zéro, plus aucune × possible sans bootstrapper.
Donc, faire calculer ce chiffré impose 3 contraintes
1. Profondeur multiplicative. Cette longueur de chaîne que tu empilais tout à l'heure, on l'appelle la profondeur. Ce qui compte : les × en chaîne, pas leur nombre total. Chaque couche du réseau en consomme.
2. Les niveaux. Le budget ℓ qu'on vient de voir : chaque × chiffré en consomme un ; à zéro, on s'arrête ou on bootstrappe.
3. La maintenance. Relinéarisation + rescaling après chaque ×, exactement ce qu'on a vu sur la structure ci-dessus.

Le packing SIMD : un chiffré = tout un vecteur
Retour au partage de tout à l'heure : un neurone, c'est une somme pondérée w·x + b, puis une activation. Cette somme pondérée, c'est l'essentiel du calcul d'un réseau. Et sous chiffrement, elle coûte une fortune : une image, c'est des milliers de valeurs, donc des milliers de × et de +… sur des chiffrés CKKS énormes (un gros polynôme chacun). À raison d'un nombre par chiffré, HE serait inutilisable.
6 chiffrés énormes · 6 opérations pour 6 malheureux nombres. Multiplie par les milliers de valeurs d'une vraie couche : impraticable.
1 chiffré · 1 opération traite toutes les valeurs en parallèle (SIMD). La somme pondérée redevient abordable.

Une même instruction s'applique aux 8 slots d'un coup (SIMD). Pour sommer entre slots (produit scalaire, convolution), il faut les faire tourner.
🔑 Le packing, c'est ce qui rend la somme pondérée praticable : une seule × par les poids (en clair) traite tout le vecteur d'un coup, puis les rotations replient les slots les uns sur les autres pour en faire la somme. C'est le rotate-and-sum.
Voilà tes données x, chiffrées. Et voilà w, les poids du neurone : eux sont en clair, le serveur les connaît.

Le produit scalaire w·x, c'est donc une multiplication puis une somme repliée par rotations. Et pour n valeurs, il ne faut que log₂(n) rotations : 8192 slots se replient en 13 tours. C'est ça, le rotate-and-sum, et c'est ce qui rend une couche linéaire bon marché sous chiffrement.
Voilà pourquoi, plus loin, les couches linéaires seront « bon marché » : toute une couche tient en une poignée d'opérations packées. Reste à savoir tout ce que HE sait faire, et surtout ce qu'il ne sait pas.
Ce qu'on peut… et ce qu'on ne peut PAS faire
En CKKS, HE ne dispose que d'une toute petite boîte à outils. La grosse carte défile toute seule, clique sur une opération pour voir son coût, ou pourquoi elle est impossible.
Activations
ReLU, sigmoïde, tanh, softmax, exp… ne sont pas des polynômes. HE ne fait que + et ×.
C'est le point de bascule : un réseau, ce n'est pas que des × et des +. Il y a des activations, et là, HE ne sait pas faire directement.
Le défi central : les non-linéarités
Le cœur du sujet. HE ne fait que + et × → donc seulement des polynômes. Or ReLU, sigmoïde, max-pooling ne sont pas des polynômes. La parade : les remplacer par un polynôme qui leur ressemble sur l'intervalle utile, joue avec le degré et le point de test :

Monte le degré : la précision grimpe… mais le coût en profondeur aussi (d ≈ 2ⁿ ⇒ n niveaux). C'est le compromis du domaine.
🔑 Rendre une couche non-linéaire « exécutable » = trouver le meilleur compromis entre précision de l'approximation et profondeur consommée, sans laisser les activations sortir de l'intervalle où le polynôme diverge.
Assembler un réseau chiffré
L'objectif du début : faire tourner un modèle sur tes données privées sans jamais les exposer. Bonne nouvelle : on n'entraîne pas sous HE. Seule l'inférence passe sous chiffrement.
Le réseau est entraîné normalement, sur des données en clair. On en ressort des poids.
sans chiffrementOn rend le modèle « HE-compatible » : ReLU → polynômes, packing, budget de niveaux et bootstraps.
une fois, avant de déployerLe client chiffre son entrée, le serveur fait le forward pass sur du chiffré, et renvoie un résultat que seul le client déchiffre.
à chaque requêteL'inférence chiffrée, déroulée
Le réseau est déjà entraîné, en clair. J'y envoie mon image, chiffrée : elle entre par la couche d'entrée 🔒.

Les 9 notions à retenir
Le parcours complet, d'un coup d'œil. Clique sur une notion pour la revoir.
- 01/09
Le problème
Calculer dans le cloud sans jamais exposer les données : le défi du PPML.
Revoir - 02/09
Homomorphisme
L'opération traverse le chiffrement : même résultat dans le clair et dans le chiffré.
Revoir - 03/09
Le bruit
Il protège le message, mais s'accumule à chaque opération et fixe un budget strict.
Revoir - 04/09
CKKS
Le schéma « flottant chiffré » : gère nativement les réels, langage naturel du ML.
Revoir - 05/09
Les 3 contraintes
Profondeur multiplicative, niveaux disponibles, maintenance (relin + rescaling).
Revoir - 06/09
Packing SIMD
Un chiffré encode tout un vecteur ; les rotations recombinent les slots.
Revoir - 07/09
La boîte à outils
Seulement + et × disponibles ; toute autre opération doit devenir un polynôme.
Revoir - 08/09
Les non-linéarités
Le défi central : approcher ReLU par des polynômes sans exploser la profondeur.
Revoir - 09/09
Assembler un réseau
Linéaires bon marché, activations goulot, bootstrapping pour aller plus profond.
Revoir
🎉 Et voilà, tu sais maintenant comment un réseau entraîné en clair peut tourner sur des données que le serveur ne voit jamais.

Merci d'avoir suivi cette aventure jusqu'au bout 🎉
J'espère que ce voyage au cœur du chiffrement homomorphe vous a plu. N'hésitez pas à partager vos opinions, questions ou retours, ça compte vraiment.
Et à très vite pour la prochaine aventure ! 🚀