Qwen3 en local : estimer la mémoire des poids et du cache KV

Illustration d'une puce entourée de blocs de mémoire et d'une file de jetons.

Pour choisir un Qwen local, comptez la mémoire du contexte en plus des poids du modèle. Passer les poids à quatre bits peut réduire leur place, mais le cache utilisé pendant la génération peut rester en seize bits. Un petit modèle avec une longue conversation peut donc occuper beaucoup plus que la taille annoncée de ses poids.

Ce dossier compare trois variantes précises : Qwen/Qwen3-0.6B, Qwen/Qwen3-4B et Qwen/Qwen3-8B. Fiches et configurations vérifiées le 20 octobre 2026. Il s’agit d’un exemple de dimensionnement de cette famille Qwen3, pas d’un classement de toutes les versions Qwen ni d’une annonce du dernier modèle.

Les variantes comparées et leurs licences

Dépôt officiel Paramètres annoncés Couches Têtes KV Licence indiquée
Qwen3-0.6B 0,6 milliard 28 8 Apache-2.0
Qwen3-4B 4,0 milliards 36 8 Apache-2.0
Qwen3-8B 8,2 milliards 36 8 Apache-2.0

Les trois configurations utilisées déclarent une dimension de tête de 128. Le nombre 8B du nom n’est pas le nombre arrondi retenu par sa fiche, qui indique 8,2 milliards : le calcul ci-dessous suit cette dernière valeur. Les nombres de paramètres restent arrondis, donc les résultats sont des approximations.

Ces variantes produisent du texte. Ne les confondez pas avec des modèles d’embeddings ou des variantes de vision. Pour une distribution quantifiée fournie par un autre dépôt, vérifiez sa provenance, sa révision et ses propres fichiers de licence ; ce tableau porte uniquement sur les trois dépôts liés.

Deux termes à estimer séparément

Pour les poids, l’approximation est paramètres × bits par poids / 8 octets. Elle suppose que tous les poids utilisent ce format et ignore les métadonnées de quantification et les éventuelles couches conservées dans une précision supérieure.

Pour un cache KV dense de cette architecture, nous calculons 2 × couches × têtes KV × dimension de tête × tokens × octets par élément × batch. Le facteur deux compte clés et valeurs. Les tokens incluent le prompt et la génération déjà présente dans le cache. La documentation Transformers sur les stratégies de cache explique que son stockage dépend aussi de la stratégie choisie, avec des compromis de mémoire et de vitesse.

Nous divisons par 1024³ pour obtenir des GiB. Un GiB vaut environ 1,074 Go décimal. Hypothèses communes : une seule séquence, cache en seize bits, aucune économie par cache glissant, partagé ou quantifié.

Tableau de mémoire théorique

Variante Poids en 16 bits Poids idéalisés en 4 bits Cache à 4 096 tokens Cache à 32 768 tokens
0.6B 1,12 GiB 0,28 GiB 0,44 GiB 3,50 GiB
4B 7,45 GiB 1,86 GiB 0,56 GiB 4,50 GiB
8B, calcul sur 8,2 milliards 15,27 GiB 3,82 GiB 0,56 GiB 4,50 GiB
Estimation partielle : poids à quatre bits et cache à seize bits pour 4 096 ou 32 768 tokens.
Calcul théorique exécuté en Python ; aucune consommation RAM ou VRAM n’a été mesurée sur ces modèles.

Pour 8,2 milliards de paramètres à quatre bits et 32 768 tokens, les deux termes totalisent déjà environ 8,32 GiB. À 4 096 tokens, ils totalisent environ 4,38 GiB. Ce second chiffre n’est pas une promesse que le modèle fonctionnera sur une carte de cette capacité : il manque encore d’autres allocations.

Pourquoi ce total ne prédit pas la mémoire de votre machine

Le moteur d’inférence, ses buffers de travail, les activations temporaires, les métadonnées de quantification et les autres programmes consomment aussi de la mémoire. Le chargement peut avoir un pic différent de la génération. Répartir le calcul entre RAM et VRAM change l’emplacement des allocations et peut changer fortement la vitesse.

Une quantification du cache ou un moteur utilisant une autre stratégie invaliderait certaines hypothèses de notre tableau. Un cache préalloué peut réserver davantage que les tokens effectivement utilisés. Mesurez donc le processus réel après avoir fixé moteur, format, longueur et nombre de conversations simultanées.

Choisir selon l’usage : un essai en trois étapes

  1. Formulez votre tâche. Par exemple, extraire trois champs d’un court texte français avec un format JSON strict. Préparez aussi quelques entrées ambiguës et des cas où le modèle doit dire qu’une information manque.
  2. Écartez les configurations incompatibles avec le budget. Calculez poids et cache, puis gardez une marge pour le reste du processus. Commencer avec un contexte court et une seule requête simplifie le diagnostic. Le calculateur ne transforme pas la mémoire disponible en garantie d’exécution.
  3. Comparez la qualité observée et la latence. Utilisez les mêmes entrées pour les variantes que votre machine peut charger. Notez format valide, informations correctes, omissions, durée et pic mémoire. Un plus gros modèle n’est utile que si ce gain compte pour votre usage.

Pour une première manipulation, la variante 0.6B réduit le volume de poids à charger ; cela ne valide pas sa qualité pour votre tâche. Pour du code, testez les programmes produits dans un environnement isolé sur des cas attendus. Pour un long document, commencez par vérifier si tout le document doit réellement rester dans une seule requête.

Calculateur Python et CSV gratuits

Télécharger le calculateur, les 18 estimations CSV et les cinq tests — ZIP gratuit. Le programme calcule seulement les formules et une figure : il ne télécharge pas les modèles et n’appelle aucune API. Installez les dépendances du README, puis lancez python memoire_qwen.py et python test_exemple.py.

"""Estimation théorique, sans charger de modèle ni mesurer de matériel."""
import csv, json, math
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt

OUT = Path(__file__).resolve().parent
GIB = 1024 ** 3
# Paramètres arrondis des fiches officielles ; architecture des config.json.
MODELES = [
    {"nom": "Qwen3-0.6B", "parametres_milliards": .6, "couches": 28, "tetes_kv": 8, "dimension_tete": 128},
    {"nom": "Qwen3-4B", "parametres_milliards": 4., "couches": 36, "tetes_kv": 8, "dimension_tete": 128},
    {"nom": "Qwen3-8B", "parametres_milliards": 8.2, "couches": 36, "tetes_kv": 8, "dimension_tete": 128},
]


def estimer(modele, tokens, bits_poids=16, octets_cache=2, batch=1):
    valeurs = [tokens, bits_poids, octets_cache, batch, modele["parametres_milliards"],
               modele["couches"], modele["tetes_kv"], modele["dimension_tete"]]
    if any(not math.isfinite(v) or v <= 0 for v in valeurs):
        raise ValueError("Les dimensions et tailles doivent être finies et positives.")
    poids = modele["parametres_milliards"] * 1e9 * bits_poids / 8 / GIB
    cache = (2 * modele["couches"] * modele["tetes_kv"] * modele["dimension_tete"]
             * tokens * octets_cache * batch / GIB)
    return {"poids_GiB": poids, "cache_KV_GiB": cache, "somme_partielle_GiB": poids + cache}


def experience():
    lignes = []
    for m in MODELES:
        for bits in [16, 4]:
            for tokens in [4096, 16384, 32768]:
                lignes.append({"modele": m["nom"], "bits_poids": bits, "tokens": tokens,
                               **estimer(m, tokens, bits)})
    with (OUT / "estimations.csv").open("w", newline="", encoding="utf-8") as f:
        writer = csv.DictWriter(f, fieldnames=list(lignes[0]))
        writer.writeheader()
        writer.writerows(lignes)
    # Hypothèse 4 bits uniquement pour les poids ; cache conservé en 16 bits.
    fig, ax = plt.subplots(figsize=(10, 5), layout="constrained")
    labels, poids, caches = [], [], []
    for m in MODELES:
        for tokens in [4096, 32768]:
            r = estimer(m, tokens, 4)
            labels.append(m["nom"] + "\n" + str(tokens) + " tokens")
            poids.append(r["poids_GiB"])
            caches.append(r["cache_KV_GiB"])
    ax.bar(labels, poids, label="Poids idéalisés à 4 bits", color="#167c80")
    ax.bar(labels, caches, bottom=poids, label="Cache KV à 16 bits", color="#e0a13b")
    ax.set(ylabel="GiB théoriques", title="Poids + cache KV : estimation partielle, pas un besoin RAM mesuré")
    ax.tick_params(axis="x", labelsize=8)
    ax.legend()
    fig.savefig(OUT / "figure.png", dpi=160)
    plt.close(fig)
    return {"date_verification_fiches": "2026-10-20", "batch": 1, "octets_cache": 2,
            "mesure_materielle": False, "estimations": lignes}


if __name__ == "__main__":
    results = experience()
    (OUT / "resultats.json").write_text(json.dumps(results, indent=2), encoding="utf-8")
    print(json.dumps(results, indent=2))

Vérifier les paramètres d’une autre variante

Relevez son nombre de paramètres, ses couches, ses têtes KV et sa dimension de tête dans les fichiers officiels. Ne remplacez pas les têtes KV par le nombre de têtes de requête : avec une attention groupée, ce ne sont pas nécessairement les mêmes nombres. Pour les trois modèles présentés, les configurations sont accessibles ici : 0.6B, 4B et 8B.

Si l’architecture change, réexaminez la formule du cache avant de réutiliser le script. Pour travailler ensuite vos tableaux d’essais, retrouvez les exercices CSV gratuits et les cas pratiques Python.

About Salah YAHIAOUI

Salah YAHIAOUI publie sur The Pingouin des tutoriels Python et des décryptages sur l’intelligence artificielle. Les cas pratiques récents précisent leurs données, leurs tests et leurs limites. À propos, méthode éditoriale et contact.

View all posts by Salah YAHIAOUI →