Aller au contenu principal

La quantification ternaire en pratique : Bonsai 2 27B en 5,95 Go

Avancé

Le 17 septembre 2026, PrismML a publié Ternary Bonsai 2 27B : un modèle de 27 milliards de paramètres dont les poids ne sont que -1, 0 et +1, compressés dans un fichier de 5,95 Go, publié sous licence Apache 2.0. Le dépôt GGUF sur Hugging Face a franchi les 2,5 millions de téléchargements en cinq jours. Le titre était partout le même — un modèle 27B qui tient sur un ordinateur portable.

Le titre est vrai. Presque tout ce que les gens en déduisent ne l'est pas.

Le llama.cpp standard chargera le fichier et produira du charabia fluide sans jamais lever d'erreur. Ollama et LM Studio le refusent purement et simplement. Le fichier de 5,95 Go nécessite environ 12 Go de VRAM avec les réglages par défaut. La plus grosse version de 7,21 Go est plus rapide sur un GPU de datacenter et plus lente sur une 4090. Et le fameux « 98,2 % de la précision complète » est une moyenne sur quatorze benchmarks qui cache un effondrement de 6 points en raisonnement multimodal et, dans un test indépendant, un échec complet sur les langues à faibles ressources.

Cette page constitue la couche pratique : ce que la quantification ternaire apporte réellement, comment faire tourner ce modèle sans corrompre silencieusement vos résultats, et comment lire honnêtement un chiffre de rétention après compression. La technique survit au modèle — le même raisonnement s'applique à chaque sortie de modèle en basse précision qui suivra.

What you'll learn
  • Comprendre pourquoi les poids ternaires font 1,75 bit en pratique, et non les 1,58 bit promis par la théorie
  • Reconnaître le mode d'échec silencieux : les chargeurs standard ignorent la rotation de Hadamard et n'affichent jamais d'erreur
  • Choisir entre PTQ1_0 et PQ2_0 selon le point de croisement bande-passante/dépaquetage, pas selon la taille du fichier
  • Budgéter la mémoire réelle : 5,95 Go de poids, ~64 KiB par token de cache, et pourquoi les réglages par défaut dépassent 12 Go
  • Lire correctement « 98,2 % de rétention » — quelles capacités survivent à la compression et lesquelles disparaissent discrètement

Ce qu'est réellement la quantification ternaire​

Un modèle normal stocke chaque poids sous forme de flottant 16 bits. Un modèle ternaire stocke chaque poids comme l'une de trois valeurs — -1, 0, +1 — avec une seule échelle FP16 partagée par groupe de poids. La reconstruction est w = scale_g * t, où t est le trit.

Trois états nécessitent log₂(3) ≈ 1,58 bit. C'est le chiffre que la littérature de recherche utilise, depuis BitNet b1.58 de Microsoft. Ce n'est pas le chiffre qu'on obtient sur disque.

Bonsai 2 27B est publié à 1,75 bit par poids pour sa version la plus dense. Les 0,17 bit supplémentaires par poids sont la partie que personne n'explique :

  • Les échelles de groupe. Une échelle FP16 par groupe de 128 poids représente 16 bits amortis sur 128 poids — 0,125 bit par poids avant même le reste.
  • La perte d'empaquetage. Les trits ne se divisent pas proprement en octets. L'empaquetage dense récupère la majeure partie, mais pas la totalité, de la densité théorique.
  • Les composants non quantifiés. Les embeddings, les projections de sortie et les couches de normalisation restent en précision plus élevée. Sur un modèle 27B, ils représentent une petite fraction des paramètres mais une fraction notable des octets.

Le ratio réel n'est donc pas 16/1,58 ≈ 10×. C'est 53,8 Go → 5,95 Go, soit environ 9× — exactement ce que rapporte la fiche du modèle.

Pourquoi le ternaire est rapide, et ce n'est pas une histoire de calcul

L'intuition la plus répandue est que la multiplication ternaire est bon marché : multiplier par -1, 0 ou +1 revient à un changement de signe, un saut, ou rien du tout. C'est vrai, mais ce n'est pas de là que vient le gain sur du matériel réel.

La génération de tokens est limitée par la bande passante mémoire, pas par le calcul. Chaque token généré nécessite de faire transiter toute la matrice de poids depuis la mémoire. Réduire les octets de 9× réduit le coût dominant de 9×. L'arithmétique n'a jamais été le goulot d'étranglement.

Cela a une conséquence pratique : un CPU ou un GPU sans support natif du ternaire doit déquantifier vers un format normal pour utiliser ses unités de multiplication-accumulation existantes, ce qui annule le gain arithmétique tout en conservant le gain de bande passante. C'est pourquoi le format d'empaquetage choisi change la performance dans une direction qui paraît contre-intuitive — voir plus bas.

Le piège : llama.cpp standard charge le fichier et ment​

C'est la chose la plus importante de cette page, et elle n'apparaît dans presque aucune couverture de la sortie du modèle.

Les poids de Bonsai 2 intègrent une rotation de Hadamard par blocs repliée dans le stockage. Une transformation inverse correspondante doit être appliquée aux activations à l'exécution. La rotation est une technique standard pour la quantification en basse précision — elle répartit les amplitudes aberrantes sur un bloc pour que la grille ternaire ait moins à perdre — mais cela signifie que les poids stockés ne sont pas directement utilisables par un chargeur qui l'ignore.

Un build llama.cpp standard l'ignore. Selon la version, il rejettera les types de tenseurs ou, pire, chargera le fichier avec succès et générera une sortie fluide, confiante et totalement fausse. Il n'y a ni erreur, ni avertissement, ni indicateur de qualité dégradée. La fiche du modèle le dit clairement : llama.cpp standard ne fera pas tourner ces fichiers, et les builds standards produisent du charabia car la transformation d'activation de Hadamard manque.

Les outils en aval héritent du problème de différentes manières :

RuntimeComportement avec Bonsai 2 27B GGUF
Le fork llama.cpp de PrismML (branche prism)Fonctionne — le seul chemin GGUF local aujourd'hui
llama.cpp standardRejette les types de tenseurs, ou charge et produit du charabia
OllamaL'import échoue avec une erreur tensor size overflow
LM StudioRejette les types de tenseurs ggml 142 et 143
MLX (Apple)Un build mlx-2bit séparé est requis ; empreinte plus grande

Les échecs d'Ollama et de LM Studio sont l'issue sûre. Le succès silencieux de llama.cpp standard est le danger, car un script de benchmark ou un harnais d'évaluation enregistrera joyeusement le charabia comme un résultat.

Si vous évaluez un modèle en basse précision, vérifiez d'abord le chargeur

Exécutez une invite à réponse connue avant de lancer quoi que ce soit d'autre. Si un modèle qui obtient 95+ sur HumanEval ne peut pas répondre à « combien font 2+2 » de façon cohérente, vous avez un problème de chargeur, pas un problème de modèle. Cela s'applique à tout format de quantification exotique, pas seulement à celui-ci.

Le faire tourner​

Guided walkthrough1 of 6
  1. git clone https://github.com/PrismML-Eng/llama.cpp && cd llama.cpp, puis cmake -B build -DGGML_CUDA=ON && cmake --build build -j pour NVIDIA. Sur Apple Silicon, retirez le flag CUDA — Metal est activé par défaut. Sur AMD, utilisez -DGGML_HIP=ON. La branche par défaut est prism ; le README déconseille explicitement de compiler depuis prism-v6 et de mélanger les bibliothèques de ce fork avec une installation llama.cpp standard sur la même machine.

Minimal de bout en bout : compiler, récupérer, servir, vérifier

# 1. Fork only — stock llama.cpp will silently produce garbage
git clone https://github.com/PrismML-Eng/llama.cpp && cd llama.cpp
cmake -B build -DGGML_CUDA=ON && cmake --build build -j

# 2. Fetch (PQ2_0 = 7.21 GB, faster on big GPUs; PTQ1_0 = 5.95 GB, fits 8 GB cards)
hf download prism-ml/Ternary-Bonsai-2-27B-gguf \
Ternary-Bonsai-2-27B-PQ2_0.gguf --local-dir .

# 3. Serve — always set -c explicitly, never leave it at 0
./build/bin/llama-server -m Ternary-Bonsai-2-27B-PQ2_0.gguf \
--alias bonsai-2-27b -ngl 99 -fa on -c 32768 \
--host 127.0.0.1 --port 8080

# 4. Verify the loader BEFORE trusting any output
curl -s http://127.0.0.1:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model":"bonsai-2-27b","messages":[{"role":"user","content":"What is 2+2? Answer with one number."}]}'
# Coherent "4" => loader is correct. Fluent nonsense => wrong build, stop here.

PTQ1_0 contre PQ2_0 : le croisement qui semble contre-intuitif​

Deux versions des mêmes poids. PTQ1_0 empaquette les trits densément à 1,75 bit. PQ2_0 donne à chaque trit son propre slot de 2 bits à 2,13 bits — 21 % de disque en plus, mais le dépaquetage est bien moins coûteux car les slots sont alignés sur les octets.

Débit de génération mesuré, en tokens par seconde :

GPUPQ2_0 (7,21 Go)PTQ1_0 (5,95 Go)Gagnant
RTX 5090129,9120,5PQ2_0
RTX 409081,291,1PTQ1_0
L4 (24 Go)29,832,1PTQ1_0
M5 Pro (Metal)28,1——

Le schéma se généralise : PTQ1_0 l'emporte sur les GPU de génération Ada et d'entrée de gamme, PQ2_0 l'emporte sur H100, A100 et Blackwell.

C'est l'inverse de la règle habituelle en matière de quantification, où le plus petit est toujours le plus rapide. La raison est l'arbitrage bande-passante/dépaquetage évoqué plus haut. Sur une carte limitée par la bande passante, déplacer 21 % d'octets en moins domine et l'empaquetage dense l'emporte. Sur une carte dotée d'une bande passante mémoire énorme, la bande passante cesse d'être la contrainte et le coût de manipulation de bits du dépaquetage dense devient le goulot d'étranglement à la place — le format plus lourd et aligné sur les octets tourne alors plus vite.

Le traitement des invites raconte la même histoire, en plus marqué : sur une RTX 5090, PQ2_0 traite les invites à 3 893 tok/s contre 1 805 pour PTQ1_0 — plus du double, car le prefill est limité par le calcul, là où le coût du dépaquetage frappe le plus fort.

Règle empirique : en dessous de 12 Go de VRAM, vous n'avez pas le choix, prenez PTQ1_0. Au-dessus, prenez PQ2_0, sauf sur une 4090 ou une L4, où PTQ1_0 l'emporte mesurablement.

La taille du fichier n'est pas le besoin en mémoire​

Trois chiffres de « taille » différents circulent pour ce modèle — 5,9 Go, 7,2 Go et 8,6 Go — et tous les trois sont défendables, car ils mesurent des choses différentes. Voici le budget réel :

  • Poids : 5,95 Go (PTQ1_0) ou 7,21 Go (PQ2_0).
  • Projecteur de vision : +629 Mo (Q8_0) ou +931 Mo (BF16), si vous avez besoin des images.
  • Cache d'attention : environ 64 KiB par token. Soit ~0,5 Go à 8K de contexte, ~2 Go à 32K, et ~16 Go pour la fenêtre complète de 262K.
  • Slots parallèles : llama.cpp alloue le cache par slot. Les réglages par défaut en allouent plusieurs.

Un test indépendant a mesuré un peu plus de 12 Go de VRAM avec les réglages par défaut, retombant à environ 8 Go lorsque le serveur était restreint à un seul slot parallèle. L'empreinte disque qu'ils ont mesurée — environ 7 Go — correspond au build PQ2_0, ce qui est en partie à l'origine de la confusion sur les chiffres.

Marge de contexte pratique par carte, avec PQ2_0 sauf mention contraire :

VRAMBuildContexte utilisable
8 GoPTQ1_0~16K tokens (~6K avec la vision)
12 GoPQ2_0~56K tokens
16 GoPQ2_0~118K tokens
24 GoPQ2_0~240K tokens
32 Go+PQ2_0Fenêtre complète de 262K

L'affirmation « tourne sur un ordinateur portable » survit à tout cela — mais « tourne sur un ordinateur portable avec un GPU de 8 Go et un contexte de 16K » est la version honnête, et c'est un produit sensiblement différent.

Ce que cache le « 98,2 % de rétention »​

PrismML rapporte un agrégat de 84,78 contre 86,32 pour le modèle de base en pleine précision sur quatorze benchmarks — le chiffre de 98,2 %. Les moyennes sont l'endroit où se cachent les dégâts de la compression. La répartition par benchmark :

BenchmarkBonsai 2 27BBase FP16Écart
AIME2695,8394,58+1,25
MATH-50098,8099,80−1,00
LiveCodeBench90,0790,05+0,02
HumanEval+95,1293,29+1,83
MMLU-Redux89,0991,46−2,37
MMMU-Pro75,4981,73−6,24

Trois enseignements à tirer de ce tableau.

Premièrement, les dégâts ne sont pas uniformes — ils sont structurels. Les mathématiques et le code sont pratiquement intacts. Le rappel de connaissances chute (le composite connaissance-et-raisonnement se situe à environ 79,9 contre 85,6 en pleine précision). Le raisonnement multimodal encaisse le vrai coup : MMMU-Pro chute de 6,24 points, et OCR Bench v2 passe de 60,99 à 56,88. La compression coûte de la largeur de faits mémorisés, pas de la compétence procédurale — ce qui est exactement ce à quoi on s'attendrait quand on écrase la précision disponible pour stocker des associations rares.

Deuxièmement, méfiez-vous des résultats positifs. Le fait que le modèle compressé « batte » son propre modèle de base en pleine précision de 1,83 point sur HumanEval+ et de 1,25 sur AIME26 ne prouve pas que la compression aide. Cela prouve que ces benchmarks ont une variance d'une exécution à l'autre de l'ordre de quelques points. Ce qui devrait aussi vous inciter à minimiser les petits écarts négatifs.

Troisièmement, les chiffres du fournisseur lui-même ne concordent pas totalement. Le tableau de benchmarks de la fiche du modèle s'agrège à 84,78, tandis que l'article de lancement de PrismML rapporte un agrégat de 83,9. Les deux sont décrits comme la même rétention de 98,2 %. L'écart est faible et inexpliqué ; nous le rapportons plutôt que d'en choisir un.

Le mode d'échec que les benchmarks n'ont pas détecté

Un test indépendant en conditions réelles a découvert un comportement qu'aucune ligne de benchmark ne prédit : chargé de traduire vers des langues à faibles ressources, dont le tamoul, le modèle est entré dans une boucle répétitive et n'a jamais terminé la tâche — pire que la génération précédente de Bonsai. Le même test a constaté qu'il identifiait correctement un vrai bug de fuseau horaire, puis se convainquait lui-même d'abandonner la bonne conclusion, et échouait à produire un bouton de réinitialisation fonctionnel dans une petite tâche HTML sur trois tours de correction.

C'est la signature caractéristique d'une quantification agressive : les scores agrégés tiennent, tandis que la longue traîne — langues rares, auto-correction sur un horizon long — se dégrade de façons qui ne se manifestent qu'à l'usage. Des retours communautaires décrivent aussi une dégradation du rappel et de la cohérence au-delà d'environ 200K tokens sur la fenêtre annoncée de 262K.

L'histoire de la branche principale, et pourquoi elle compte pour la prochaine sortie​

Il existe un effort parallèle pour intégrer Bonsai ternaire dans llama.cpp en amont, et son état actuel est une leçon utile sur la fragmentation des formats en basse précision.

PrismML a proposé un empaquetage ternaire en groupes de 128 pour les modèles Bonsai plus petits. Le mainteneur de llama.cpp s'y est opposé en faveur d'une taille de groupe de 64, au motif que cela ne coûte que moins de 6 % de mémoire supplémentaire tout en supportant plus de formes de tenseurs et en offrant une meilleure qualité. Q2_0 en groupe-64 est devenu le format officiel et a été fusionné dans plusieurs backends, dont CUDA et Vulkan. PrismML a conservé ses variantes en groupe-128 dans le fork.

Ainsi, « llama.cpp supporte-t-il les modèles ternaires » a deux réponses correctes et opposées :

  • Oui — les builds Q2_0_g64 tournent sur la branche principale sur tous les backends, et le README du fork lui-même les désigne comme le chemin portable.
  • Non, pas pour ce modèle — au moment de la vérification, le dépôt publié Ternary-Bonsai-2-27B-gguf contient F16, PQ2_0, PTQ1_0 et les deux fichiers mmproj. Il n'y a aucun build Q2_0_g64 du 27B. Le fork est le seul chemin.

Deux formats ternaires plus anciens, TQ1_0 et TQ2_0, existent déjà dans ggml mais utilisent une taille de groupe de 256, incompatible avec la façon dont ces modèles sont nativement empaquetés. TQ2_0 est aussi la disposition ternaire fidèle la plus compacte dans ggml, à environ 2,06 bits par poids — et dans la branche principale actuelle, il implémente un produit scalaire CPU uniquement, sans chemin CUDA, Metal ou Vulkan. Voir « support ternaire » dans un changelog vous en apprend très peu sur si votre GPU l'exploitera réellement.

Pour le prochain modèle en basse précision que vous évaluerez, la question à poser n'est pas est-il quantifié mais quelle taille de groupe, quel empaquetage, quels backends ont des noyaux dédiés, et existe-t-il un build portable — car c'est ce qui détermine si vous tournez sur la branche principale ou si vous maintenez le fork de quelqu'un d'autre.

Quand c'est le bon outil​

Vaut le coup quand :

  • Vous avez besoin d'un modèle maths ou code performant sur du matériel qui ne peut pas contenir un 27B en précision supérieure. C'est là que la rétention est quasi parfaite et que la valeur est la plus élevée.
  • Vous êtes limité par la bande passante — un GPU d'ordinateur portable, un boîtier périphérique, une L4 partagée — et que 9× moins d'octets par token fait la différence entre utilisable et non utilisable.
  • L'énergie compte. PrismML rapporte 0,714 mWh par token sur une RTX 4090, qu'ils présentent comme 40 % plus efficace qu'un modèle 8B en pleine précision.
  • La licence Apache 2.0 et l'exécution entièrement locale sont des exigences, pas des préférences.

Ne vaut pas le coup quand :

  • Le travail est riche en connaissances ou multimodal. C'est précisément là que la facture de la compression est la plus salée.
  • Vous avez besoin d'une couverture de langues rares non anglaises. Les preuves indépendantes disponibles ici sont mauvaises, et les benchmarks ne l'ont pas prédit.
  • Vous ne pouvez pas maintenir un runtime d'inférence forké. Ollama et LM Studio ne le chargent pas aujourd'hui, et un build standard échoue silencieusement plutôt que bruyamment.
  • Vous comptiez l'utiliser au-delà d'environ 200K tokens de contexte en vous fiant à la spécification de 262K.
Appuyez sur Entrée ou Espace pour retourner la carte. Utilisez les flèches gauche et droite pour naviguer entre les cartes.Terme affiché.
1 / 10

Testez-vous​

Check yourself

0/5
  1. Vous téléchargez Ternary-Bonsai-2-27B-PTQ1_0.gguf, vous le faites tourner sur un build llama.cpp standard récent, et il génère un texte fluide mais subtilement faux. Quelle est la cause la plus probable ?
  2. Vous avez une RTX 4090 avec 24 Go. Quel build GGUF donne le meilleur débit de génération ?
  3. Votre carte a 8 Go de VRAM. Le fichier de poids fait 5,95 Go. Vous lancez llama-server avec les réglages par défaut et il manque de mémoire. Pourquoi ?
  4. Un collègue cite « 98,2 % de la performance en pleine précision » pour soutenir que le modèle compressé est un remplacement direct pour un pipeline de compréhension de documents. Quelle est l'objection la plus solide ?
  5. Un changelog indique que llama.cpp « supporte la quantification ternaire ». Que devez-vous encore vérifier avant de supposer que votre GPU l'exploitera ?

Sources et lectures complémentaires​

Suivant​