BitNet / Ternary / Binary — Les LLMs 1-bit#

TL;DRBitNet est une architecture Transformer révolutionnaire où les poids sont binaires {-1, +1} (BitNet b1) ou ternaires {-1, 0, +1} (BitNet b1.58). Au lieu de multiplier, on additionne ou soustrait les activations — éliminant totalement les multiplications matricielles. BitNet b1.58 matche les Transformers FP16/BF16 en qualité tout en divisant la mémoire par ~10×. C'est potentiellement l'avenir de l'inférence LLM.


Le concept radical : pourquoi 1.58 bits ?#

Le constat#

Un LLM classique stocke chaque poids en FP16 ou BF16 : 16 bits par paramètre. Pour un modèle de 70B, cela représente 140 GB de mémoire. La quantification PTQ (GPTQ, AWQ) descend à 4-bit (17.5 GB), mais en deçà, la qualité s'effondre.

L'idée de BitNet : et si on entraînait un modèle from scratch avec des poids qui ne peuvent valoir que -1, 0, ou +1 ?

Format Bits/poids Mémoire (70B model)
FP32 (legacy) 32 280 GB
FP16 / BF16 (std) 16 140 GB
INT8 (quantif.) 8 70 GB
INT4 (GPTQ/AWQ) 4 35 GB
INT2 (QuIP#/AQLM) 2 17.5 GB
Binary {-1,+1} 1 8.75 GB
Ternary {-1,0,+1} 1.58 13.8 GB
(log₂(3) ≈ 1.585)

Pourquoi 1.58 et pas 1 ou 2 ?#

flowchart LR subgraph BIN["Binaire {-1, +1} (1-bit)"] B1["Deux valeurs possibles"] B2["Problème : pas de zéro → chaque poids a un effet"] B3["Réseau trop rigide, perte de capacité"] end subgraph TER["Ternaire {-1, 0, +1} (1.58-bit)"] T1["Trois valeurs possibles, log₂(3) = 1.585 bits"] T2["Le zéro permet la SPARSITÉ naturelle"] T3["Beaucoup de poids = 0 → pruning implicite"] T4["✅ Meilleur compromis expressivité / compression"] end subgraph I2["INT2 {-1.5, -0.5, +0.5, +1.5} (2-bit)"] I1["Quatre valeurs, mais multiplications nécessaires"] I2["Moins radical que le ternaire"] end

L'analogie pour néophyte#

Imagine que chaque poids d'un réseau de neurones est normalement un nombre à virgule complexe (0.0372...). BitNet dit : remplaçons chaque nombre par un simple signe : positif (+), négatif (−), ou zéro (rien). Au lieu de faire des multiplications compliquées, on ne fait qu'additionner ou soustraire les valeurs d'entrée. C'est comme remplacer une calculatrice scientifique par un compteur d'étiquettes : beaucoup plus rapide, beaucoup moins d'énergie — et étonnamment, ça marche aussi bien !


Papier source#

Élément BitNet (b1) BitNet b1.58
Titre BitNet: Scaling 1-bit Transformers for Large Language Models The Era of 1-bit LLMs: All Large Language Models are in 1.58 Bits
Auteurs Hongyu Wang, Shuming Ma, Li Dong, et al. (Microsoft Research) Shuming Ma, Hongyu Wang, Lingxiao Ma, et al. (Microsoft Research)
Date Octobre 2023 Février 2024
Publication JMLR 2025 (vol. 26)
arXiv 2310.11453 2402.17764

Mécanisme technique#

BitNet b1 (binaire) : la fondation#

Chaque poids de la couche BitLinear (remplaçant nn.Linear) vaut exactement +1 ou -1.

flowchart LR subgraph CLASSIC["Couche linéaire classique"] Y1["Y = X × W
(W contient des valeurs FP16)"] W1["W = [ 0.037, -0.142, 0.091, 0.218, ... ]"] OP1["→ Multiplication matricielle standard (GEMM)
→ Coûteux en compute et en énergie"] end
flowchart LR subgraph BITLINEAR["BitLinear (BitNet b1)"] Y2["Y = X × W_ternary"] W2["W = [ +1, -1, +1, +1, ... ]"] OP2["→ Chaque « multiplication » devient ADDITION ou SOUSTRACTION
→ w_ij = +1 : X_j est additionné
→ w_ij = -1 : X_j est soustrait
→ Pas de multiplication du tout !"] end

BitNet b1.58 (ternaire) : l'amélioration décisive#

Chaque poids vaut -1, 0, ou +1. L'ajout du zéro permet la sparsité.

flowchart LR subgraph TER["BitLinear ternaire (BitNet b1.58)"] W["W = [ +1, 0, +1, -1, ... ]
[ 0, +1, -1, 0, ... ]"] OP["→ w_ij = +1 : X_j additionné
→ w_ij = -1 : X_j soustrait
→ w_ij = 0 : X_j ignoré (gratuit !)"] RES["→ ~30-40% de zéros typiquement → sparsité implicite
→ Encore moins de compute que le binaire pur"] end

La matrice ternaire en détail#

block-beta columns 1 t["MATRICE DE POIDS TERNAIRE BitNet b1.58"] m["Exemple 5×8 :
R0: +1, 0, -1, +1, 0, 0, +1, -1
R1: 0, +1, +1, 0, -1, +1, 0, 0
R2: -1, 0, 0, +1, +1, 0, -1, +1
R3: +1, -1, 0, 0, 0, +1, +1, 0
R4: 0, +1, -1, +1, 0, -1, 0, +1"] d["Stockage : chaque poids = 2 bits (log₂(3) arrondi)
Sparsité : ~35% de zéros → compute réduit
Pas de multiplication : additions/soustractions uniquement"]

Comment entraîner un réseau ternaire ?#

Le défi : l'opération round() n'est pas différentiable. Comment backpropagé à travers une quantification ?

Réponse : Straight-Through Estimator (STE)

flowchart TD subgraph FWD["FORWARD PASS (inférence simulée)"] WC1["w_continu = valeur apprise (FP16, paramètre du modèle)"] WT1["w_ternary = round(w_continu / β)
où β = moyenne absolue des poids (|w|̄)"] RES1["Résultat : w_ternary ∈ {-1, 0, +1}
L'activation passe à travers w_ternary (additions/soustractions)"] WC1 --> WT1 --> RES1 end subgraph BWD["BACKWARD PASS (gradients)"] G1["Le gradient « passe à travers » la fonction round comme si elle était l'identité (STE)"] G2["∂L/∂w_continu ≈ ∂L/∂w_ternary"] G3["+ Clipping : gradient = 0 si |w_continu| > 1
(pour éviter que les poids ne dérivent trop loin)"] G4["→ w_continu est mis à jour par SGD/Adam
→ À la convergence, la plupart des w_continu ≈ -1, 0, ou +1"] G1 --> G2 --> G3 --> G4 end

Architecture complète BitNet#

flowchart TD IN["INPUT TOKENS (INT8)"] LN["LayerNorm (absorbe le scaling) → INT8 activation"] IN --> LN LN --> BLQ["BitLinear (Q projection)
W ∈ {-1,0,+1} × X (INT8) = additions"] LN --> BLK["BitLinear (K projection)
W ∈ {-1,0,+1} × X (INT8) = additions"] LN --> BLV["BitLinear (V projection)
W ∈ {-1,0,+1} × X (INT8) = additions"] BLQ --> ATT["Attention (INT8)"] BLK --> ATT BLV --> ATT ATT --> BLFFN["BitLinear (FFN)
W ∈ {-1,0,+1} × activations = additions"] BLFFN --> OUT["OUTPUT"]

Performance comparée à FP16#

Le résultat choc de BitNet b1.58#

BitNet b1.58 matche ou surpasse les Transformers FP16/BF16 à taille de modèle équivalente :

Modèle FP16 Transformer BitNet b1.58 Résultat
3B ~10.5 ~10.5 ✅ ÉQUIVALENT
4B ~9.8 ~9.8 ✅ ÉQUIVALENT

b1.58 matche FP16 en perplexité ET en zero-shot tasks.

Avantages systémiques de BitNet b1.58#

Métrique FP16 BitNet b1.58
Bits par poids 16 1.58 (10× moins)
Mémoire modèle 100% ~10%
Type d'opération Multiply Add/Subtract seulement
Énergie/op ~100% ~5-10%
Latence 100% nettement réduite
Throughput 100% nettement supérieur
Qualité (PPL) référence équivalente ✅
Entraînement standard from scratch requis

Scaling law de BitNet#

xychart-beta title "Scaling law : Qualité (PPL) vs Taille du modèle" x-axis "Taille du modèle (params)" [1, 2, 3, 4, 5] y-axis "Perplexité (plus bas = mieux)" 8 --> 12 line [11.5, 10.8, 10.2, 9.8, 9.5] line [11.6, 10.9, 10.3, 9.8, 9.5]

BitNet b1.58 (ligne 2) suit la même courbe de scaling que FP16 Transformer (ligne 1) — les deux courbes convergent à mesure que la taille augmente.

BitNet b1.58 suit la même loi de scaling que les Transformers classiques : doubler la taille du modèle réduit la perplexité de la même manière. C'est ce qui rend cette approche crédible à grande échelle.


Pourquoi BitNet est potentiellement l'avenir de l'inférence LLM#

1. Élimination des multiplications#

flowchart LR subgraph GEMM_FP16["GEMM classique (FP16)"] F1["Pour chaque élément de Y :
y_i = Σ (x_j × w_ij)"] F2["N multiplications"] F3["❌ Les multiplications coûtent cher
en énergie et en silicium"] end subgraph BITNET_TER["BitNet ternaire"] T1["Pour chaque élément de Y :
y_i = Σ (±x_j) + skip(0)"] T2["N additions/soustractions"] T3["✅ Les additions sont ~10× moins
énergétiques et en silicium"] end

2. Latence et throughput#

Sans multiplications, le bottleneck devient purement la bande passante mémoire pour charger les activations. Les poids ternaires sont si petits qu'ils tiennent entièrement en cache.

3. Nouveaux paradigmes hardware#

BitNet ouvre la voie à des chips spécialisés conçus pour les opérations ternaires :
- Pas besoin de FPU (floating-point unit)
- Pas besoin de multipliers
- Unités arithmétiques simplifiées → plus de cœurs sur le même silicium
- Consommation énergétique drastiquement réduite

flowchart LR subgraph GPU["GPU actuel"] TC["Tensor Cores FP16 (multipliers)"] TG["Grande surface · Haute énergie"] end subgraph CHIP["Chip ternaire idéal"] TAU["Ternary ALUs (adders only)"] CG["Petite surface · Basse énergie"] end GPU -.->|"→ Un chip ternaire pourrait théoriquement
avoir 10× plus d'unités de calcul
pour la même surface de silicium"| CHIP

Le défi majeur : entraînement from scratch#

block-beta columns 1 t["QU'EST-CE QUI EMPÊCHE BITNET D'ÊTRE PARTOUT ?"] p1["1. ENTRAÎNEMENT FROM SCRATCH OBLIGATOIRE\n• Impossible de prendre Llama/GPT et convertir en ternaire\n• Le modèle doit être entraîné depuis zéro avec QAT\n• Coût d'entraînement équivalent à un LLM classique\n• Seul Microsoft Research l'a fait (modèles petits)"] p2["2. PAS DE SUPPORT HARDWARE TERNAIRE\n• Les GPU actuels ne sont pas optimisés pour le ternaire\n• Le ternaire est packé en 2-bit et déquantifié → overhead\n• Le vrai speedup nécessite un hardware dédié"] p3["3. ÉCOSYSTÈME IMMATURE\n• Frameworks (PyTorch, vLLM) non optimisés\n• Pas de modèles pré-entraînés à grande échelle\n• Tooling (tokenizers, serving) à construire"] p4["4. SEULEMENT LES POIDS SONT TERNAIRES\n• Les activations restent en INT8\n• Le KV cache reste en pleine précision\n→ L'économie mémoire totale est < 10× pratique"]

BitNet b1.58 2B4T : premier modèle open-source#

Élément Détail
Nom BitNet b1.58 2B4T
Taille 2 milliards de paramètres
Poids Ternaires {-1, 0, +1}
Source Microsoft Research
arXiv 2504.12285
Hugging Face microsoft/bitnet-b1.58-2B-4T

C'est le premier modèle BitNet open-weight entraîné à grande échelle, permettant à la communauté de tester et développer l'écosystème.


BitNet vs quantification PTQ classique#

Aspect PTQ (GPTQ/AWQ) BitNet b1.58
Quand ? Après training Pendant training
Modèle source Existe (FP16) From scratch
Bits/poids 2-8 1.58
Qualité à 2-bit Dégradée Équivalente à FP16
Opérations Déquant + GEMM Additions seulement
Hardware requis GPU standard Idéalement dédié
Maturité Production ✅ Recherche ⚠️
Flexibilité Convertir n'importe quel modèle Ré-entraîner obligatoirement

Exemple pratique#

Utiliser BitNet b1.58 2B4T#

# Installer le runtime BitNet
pip install bitnet

# Ou cloner le repository officiel
git clone https://github.com/microsoft/BitNet.git
cd BitNet
pip install -e .
from bitnet import BitNetModel
from transformers import AutoTokenizer

# Charger le modèle ternaire
model = BitNetModel.from_pretrained("microsoft/bitnet-b1.58-2B-4T")
tokenizer = AutoTokenizer.from_pretrained("microsoft/bitnet-b1.58-2B-4T")

# Inférence
inputs = tokenizer("Explique le concept de quantification ternaire :", return_tensors="pt")
outputs = model.generate(**inputs, max_new_tokens=200)
print(tokenizer.decode(outputs[0]))

Inférence optimisée avec le runtime bitnet.cpp#

# Compiler le runtime C++ optimisé
cd bitnet
mkdir build && cd build
cmake ..
make -j

# Inférence CPU avec kernels ternaires optimisés
./bin/bitnet \
    -m models/bitnet-b1.58-2B-4T \
    -p "Que sont les LLMs 1-bit ?" \
    -n 200

Aperçu de l'entraînement d'un BitLinear#

import torch
import torch.nn as nn

class BitLinear(nn.Linear):
    """Couche linéaire ternaire BitNet b1.58"""

    def __init__(self, in_features, out_features, bias=False):
        super().__init__(in_features, out_features, bias=bias)

    def forward(self, x):
        # Quantification ternaire des poids avec STE
        w = self.weight  # poids continus (appris)

        # β = moyenne absolue des poids
        beta = w.abs().mean()

        # Quantification : round(w / β) → {-1, 0, +1}
        w_quant = torch.clamp(
            torch.round(w / (beta + 1e-8)),
            min=-1, max=1
        )

        # STE : en backward, le gradient passe à travers
        # (torch.detach() pour le forward, identité pour le backward)
        w_ste = w_quant.detach() + w - w.detach()

        # Multiplication = additions (le hardware le fera nativement)
        return torch.nn.functional.linear(x, w_ste, self.bias)

TernaryLM et autres implémentations#

Implémentation Source Notes
BitNet b1.58 2B4T arXiv:2504.12285 Modèle officiel Microsoft, 2B params
bitnet.cpp github.com/microsoft/BitNet Runtime C/C++ optimisé CPU/GPU
TernaryLM arXiv:2602.07374 Implémentation alternative, entraînement
BitNet (b1) arXiv:2310.11453 Version binaire originale (moins performante)

Résumé : BitNet en 5 points#

  1. Poids ternaires {-1, 0, +1} = 1.58 bits/poids → mémoire ÷ 10
  2. Pas de multiplications → additions seulement → latence et énergie réduites
  3. Qualité équivalente à FP16 (même loi de scaling)
  4. Mais : entraînement from scratch obligatoire + hardware non optimisé
  5. Potentiel énorme si le hardware ternaire émerge → pourrait redéfinir l'inférence LLM

Références#

ia llm quantification bitnet ternary binary 1-bit 1.58-bit microsoft qat