En bref
PolarQuant est une méthode de compression du cache KV publiée par Google Research. Elle permet de diviser par 4× la mémoire occupée par le cache clés-valeurs à l'inférence, sans réentraîner le modèle et avec une perte de qualité minime.
Son approche est différente de la quantification classique (int8/int4 avec scale + zero_point) : au lieu de stocker des valeurs numériques arrondies avec des paramètres de calibration par bloc, PolarQuant convertit les vecteurs en angles + rayons (coordonnées polaires), puis quantifie les angles — qui se trouvent être très prévisibles après un pré-traitement adapté. Résultat : pas besoin de stocker des constantes de quantification coûteuses en mémoire.
PolarQuant fait partie d'une suite plus large (TurboQuant, QJL) que Google Research a présentée en 2026, ciblant la compression "extrême" du cache KV pour les contextes longs.
Le problème que cela résout
Dans un Transformer auto-régressif, chaque nouveau token généré nécessite de recalculer l'attention sur tous les tokens précédents. Pour éviter de tout recalculer à chaque pas, on stocke les clés (K) et valeurs (V) dans un cache. Ce cache grossit linéairement avec la longueur du contexte et devient rapidement le principal goulet d'étranglement mémoire quand on travaille avec des contextes longs (32K, 128K tokens…).
La quantification classique (type int4/int8) réduit bien la taille du cache, mais elle introduit un surcoût souvent sous-estimé : pour chaque bloc de données quantifiées, il faut stocker un scale et parfois un zero_point en pleine précision. Google Research estime que ce surcoût peut ajouter 1 à 2 bits par valeur quantifiée selon les schémas — ce qui réduit le gain réel de compression.
Comment ça marche
PolarQuant se décompose en trois étapes, qui s'appliquent aux vecteurs K et V avant leur stockage dans le cache :
1. Préconditionnement (rotation aléatoire)
On multiplie chaque vecteur par une matrice de rotation orthogonale partagée (la même pour toutes les couches et têtes d'attention). L'effet : les coordonnées du vecteur sont "mélangées" de façon à ce qu'aucune dimension ne concentre toute l'énergie. Les produits scalaires sont préservés exactement.
2. Transformation polaire récursive
Le vecteur préconditionné est converti en coordonnées polaires de façon récursive : on prend les coordonnées par paires, on calcule un angle et un rayon pour chaque paire, puis on recommence avec les rayons. Après 4 niveaux (le réglage retenu), on obtient beaucoup d'angles et très peu de rayons (15/16 d'angles, 1/16 de rayons).
L'astuce centrale : après le préconditionnement, ces angles sont très concentrés autour de valeurs prévisibles (typiquement autour de 45°). Plus la dimension est grande, plus cette concentration est forte. On peut donc les encoder avec très peu de bits.
3. Quantification des angles via codebooks
Les angles sont quantifiés indépendamment via des codebooks (tables de correspondance), construits soit "en ligne" pendant le prefill, soit "hors ligne" (pré-calculés une fois pour toutes). En pratique, l'implémentation utilise 4 bits pour le premier niveau d'angles et 2 bits pour les suivants, soit environ 3,875 bits par coordonnée — contre 16 bits en FP16.
À la lecture (quand l'attention a besoin des K/V), on reconstruit les vecteurs en sens inverse : codebook → angles → cos/sin → rayons → rotation inverse.
Implémentation pratique
L'implémentation décrite dans l'article repose sur PyTorch avec des kernels CUDA custom pour les opérations critiques (déquantification pendant le calcul d'attention). Les valeurs sont empaquetées dans des torch.uint8. Les expériences sont réalisées sur un seul GPU RTX A6000 (48 GB).
Codebooks en ligne vs hors ligne — c'est le choix pratique le plus important :
Le mode en ligne construit les codebooks par k-means sur les angles observés pendant le prefill de chaque prompt. Qualité légèrement meilleure, mais prefill 3 à 4× plus lent.
Le mode hors ligne utilise des codebooks pré-calculés et réutilisables. Le prefill retrouve une vitesse normale, avec une perte de qualité négligeable — parce que la distribution des angles est très stable après préconditionnement.
Benchmarks
Qualité — LongBench (Llama-3.1-8B-Instruct)
| Méthode | Moyenne | SQA | MQA | Résumé | Few-shot | Synthétique | Code |
|---|---|---|---|---|---|---|---|
| Exact (16 bits) | 45.71 | 45.32 | 26.69 | 68.62 | 59.25 | 46.17 | 48.63 |
| SnapKV | 38.23 | 42.61 | 19.07 | 64.65 | 59.60 | 43.28 | 44.57 |
| HeadKV | 39.45 | 42.69 | 19.77 | 68.07 | 59.48 | 42.60 | 45.34 |
| PyramidKV | 36.80 | 41.54 | 18.91 | 64.88 | 59.68 | 42.38 | 44.03 |
| StreamingLLM | 25.68 | 35.79 | 20.90 | 56.91 | 58.81 | 32.07 | 38.36 |
| KIVI | 43.38 | 37.81 | 27.44 | 68.60 | 58.67 | 44.29 | 46.70 |
| PolarQuant | 44.03 | 44.34 | 27.32 | 68.68 | 59.82 | 44.46 | 48.11 |
| PolarQuant-R (offline) | 44.71 | 44.72 | 26.43 | 68.58 | 60.08 | 45.20 | 48.29 |
| PolarQuant-R (online) | 45.45 | 45.13 | 26.42 | 68.54 | 59.57 | 45.13 | 48.37 |
Ce qu'il faut retenir : PolarQuant-R (online) est quasiment au niveau du FP16 (45.45 vs 45.71). Les méthodes par éviction de tokens (SnapKV, PyramidKV, StreamingLLM) perdent nettement plus en qualité. KIVI (quantification 2 bits) fait un bon score moyen mais chute sur certaines tâches (SQA).
Latence — prefill + génération (entrée 16K tokens, génération 1K tokens)
| Méthode | Prefill (s) | Génération (s) |
|---|---|---|
| Exact (16 bits) | 2.934 | 38.374 |
| SnapKV | 3.438 | 34.053 |
| PyramidKV | 3.428 | 32.732 |
| HeadKV | 3.300 | 34.401 |
| KIVI | 3.590 | 49.564 |
| PolarQuant (online) | 11.633 | 44.448 |
| PolarQuant-R (offline) | 3.364 | 44.097 |
Ce qu'il faut retenir : le mode online a un prefill très lent (~4× le FP16) à cause du clustering. Le mode offline ramène le prefill au niveau du FP16. En génération, PolarQuant est ~14% plus rapide que KIVI. Les méthodes par éviction restent les plus rapides en génération (elles gardent moins de tokens en cache).
PolarQuant vs les alternatives
| Approche | Principe | Compression | Points forts | Points faibles |
|---|---|---|---|---|
| PolarQuant | Rotation + polaire + codebooks | >4,2× | Proche du FP16 en qualité, pas de paramètres par bloc | Kernels CUDA custom, pas de repo public, prefill coûteux en mode online |
| KIVI | Quantification 2 bits asymétrique | ~2,6× | Implémentation "hardware-friendly", simple | Qualité variable selon les tâches |
| SnapKV / PyramidKV / HeadKV | Éviction/sélection de tokens | ~4× | Rapide en génération | Perte de qualité notable, surtout sur tâches multi-docs |
| StreamingLLM | Fenêtre glissante + tokens initiaux | Variable | Très simple | Forte dégradation sur tâches longues |
| Quantification affine standard | scale + zero_point par bloc | Variable (int8/int4) | Très compatible matériel | Surcoût des paramètres de quantification |
Limites et points d'attention
Côté pratique :
Le prefill en mode codebook online est un vrai problème de latence (11.6s vs 2.9s en FP16). Le mode offline le résout, mais avec un léger compromis qualité. La dimension des têtes d'attention doit être une puissance de 2 (ce qui est le cas de la plupart des architectures courantes, mais pas toutes). Pas de repo public, pas d'intégration framework — pour l'instant c'est un résultat de recherche, pas un outil prêt à l'emploi.
Côté robustesse :
La méthode fonctionne d'autant mieux que la dimension est grande (les angles se concentrent davantage). Sur de petites dimensions de tête, les codebooks fixes pourraient être moins efficaces. Si les vecteurs K/V ont une distribution très inhabituelle, le préconditionnement aide mais ne garantit rien.