<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Quantization on kenji.blog</title><link>http://kenji.blog/fr/tags/quantization/</link><description>Recent content in Quantization on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>fr</language><copyright>kenjinote</copyright><lastBuildDate>Fri, 11 Sep 2026 00:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/fr/tags/quantization/index.xml" rel="self" type="application/rss+xml"/><item><title>Explication du fonctionnement de la technologie de quantification (GGUF) de llama.cpp</title><link>http://kenji.blog/fr/p/llama-cpp-quantization-gguf/</link><pubDate>Fri, 11 Sep 2026 00:00:00 +0900</pubDate><guid>http://kenji.blog/fr/p/llama-cpp-quantization-gguf/</guid><description>&lt;img src="http://kenji.blog/p/llama-cpp-quantization-gguf/img/eyecatch.jpg" alt="Featured image of post Explication du fonctionnement de la technologie de quantification (GGUF) de llama.cpp" />&lt;h2 id="1-introduction--pourquoi-les-llm-ont-ils-besoin-de-quantification-">1. Introduction : Pourquoi les LLM ont-ils besoin de quantification ?
&lt;/h2>&lt;p>Bien que l&amp;rsquo;évolution récente des grands modèles de langage (LLM : Large Language Models) soit remarquable, en coulisses, l&amp;rsquo;« épuisement des ressources de calcul » et les « goulots d&amp;rsquo;étranglement de la bande passante mémoire » sont devenus de graves problèmes. Par exemple, si l&amp;rsquo;on charge en mémoire un modèle de 70B (70 milliards) de paramètres comme Llama 3 avec une virgule flottante standard de 16 bits (FP16), les paramètres seuls consomment environ 140 Go de VRAM/RAM. Si l&amp;rsquo;on y ajoute le contexte lors de l&amp;rsquo;inférence (cache KV), cela ne peut fonctionner sans regrouper plusieurs GPU haut de gamme pour centres de données (NVIDIA A100 80 Go ou H100 80 Go).&lt;/p>
&lt;p>C&amp;rsquo;est là qu&amp;rsquo;interviennent &lt;strong>llama.cpp&lt;/strong> et sa &lt;strong>technologie de quantification (Quantization)&lt;/strong>, devenus les sauveurs permettant aux développeurs individuels ou aux appareils périphériques (MacBook ou PC de jeu classique) de faire fonctionner des LLM. En particulier, le format de fichier &lt;strong>GGUF (GPT-Generated Unified Format)&lt;/strong> et l&amp;rsquo;algorithme avancé de quantification par blocs appelé &lt;strong>k-quants&lt;/strong> constituent une méthode révolutionnaire qui comprime la taille du modèle à une fraction de l&amp;rsquo;original tout en minimisant la dégradation de la précision du modèle (Perplexité).&lt;/p>
&lt;p>Dans cet article, nous expliquerons en profondeur la quantification dans llama.cpp, depuis les bases mathématiques, les différences avec le format GGML, la structure détaillée du format GGUF, jusqu&amp;rsquo;au mécanisme interne de k-quants.&lt;/p>
&lt;hr>
&lt;h2 id="2-bases-mathématiques-de-la-quantification-quantization">2. Bases mathématiques de la quantification (Quantization)
&lt;/h2>&lt;p>Dans le contexte des LLM, la quantification désigne l&amp;rsquo;opération consistant à mapper des valeurs continues (ou des nombres à virgule flottante de haute précision) vers des valeurs discrètes avec un nombre de bits inférieur (INT8, INT4, INT3, etc.).&lt;/p>
&lt;h3 id="21-formules-de-base-de-la-quantification-linéaire">2.1. Formules de base de la quantification linéaire
&lt;/h3>&lt;p>L&amp;rsquo;approche la plus simple est la quantification linéaire (quantification Min-Max). Soit $W$ le tenseur de poids d&amp;rsquo;origine de haute précision et $W_q$ le tenseur d&amp;rsquo;entiers quantifié.&lt;/p>
$$ W_q = \text{round}\left( \frac{W}{S} \right) + Z $$
&lt;p>Où :&lt;/p>
&lt;ul>
&lt;li>$S$ est le &lt;strong>facteur d&amp;rsquo;échelle (Scale Factor)&lt;/strong>, qui détermine la taille de pas (résolution) de la quantification.&lt;/li>
&lt;li>$Z$ est le &lt;strong>point zéro (Zero-point)&lt;/strong>, une valeur de biais permettant de décaler la valeur entière quantifiée correspondant au nombre réel $0.0$.&lt;/li>
&lt;li>$\text{round}(\cdot)$ est la fonction d&amp;rsquo;arrondi à l&amp;rsquo;entier le plus proche.&lt;/li>
&lt;/ul>
&lt;p>Lors de l&amp;rsquo;inférence, la déquantification (Dequantization) restaure un poids réel approximatif $\tilde{W}$.&lt;/p>
$$ \tilde{W} = S \times (W_q - Z) $$
&lt;h3 id="22-quantification-symétrique-vs-quantification-asymétrique">2.2. Quantification symétrique vs Quantification asymétrique
&lt;/h3>&lt;p>Selon le traitement du point zéro $Z$, on distingue principalement deux méthodes.&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Quantification asymétrique (Asymmetric Quantization)&lt;/strong>
Mappage en utilisant la valeur minimale $W_{\min}$ et la valeur maximale $W_{\max}$ des données.
&lt;/p>
$$ S = \frac{W_{\max} - W_{\min}}{2^b - 1}, \quad Z = \text{round}\left(-\frac{W_{\min}}{S}\right) $$
&lt;p>
Où $b$ est le nombre de bits de quantification (par exemple, pour 4 bits, $2^4-1 = 15$). Étant donné qu&amp;rsquo;il est nécessaire de conserver $Z$, la surcharge en calcul et en mémoire augmente légèrement.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Quantification symétrique (Symmetric Quantization)&lt;/strong>
Mappage centré sur zéro en utilisant la valeur absolue maximale des données ($Z=0$).
&lt;/p>
$$ S = \frac{\max(|W_{\max}|, |W_{\min}|)}{2^{b-1} - 1}, \quad Z = 0 $$
&lt;p>
Les premières quantifications de llama.cpp (comme l&amp;rsquo;ancien Q4_0) adoptaient la quantification symétrique. Sans le terme $Z$, cette méthode présente l&amp;rsquo;avantage d&amp;rsquo;accélérer considérablement le calcul du produit scalaire avec les instructions SIMD.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="3-lévolution-de-ggml-vers-gguf-et-la-structure-des-fichiers">3. L&amp;rsquo;évolution de GGML vers GGUF et la structure des fichiers
&lt;/h2>&lt;p>Pour parler de llama.cpp, il est indispensable de mentionner &lt;strong>GGML&lt;/strong>, une bibliothèque de calcul tensoriel écrite en C++, et &lt;strong>GGUF&lt;/strong>, le format de fichier qui en dérive.&lt;/p>
&lt;h3 id="31-les-défis-de-ggml">3.1. Les défis de GGML
&lt;/h3>&lt;p>Les premières versions de llama.cpp utilisaient le format &lt;code>ggml&lt;/code> (ainsi que ses variantes comme &lt;code>ggjt&lt;/code>). Cependant, elles présentaient les problèmes suivants :&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Manque d&amp;rsquo;extensibilité :&lt;/strong> Les nombres magiques et les hyperparamètres étaient codés en dur avec une longueur et un ordre fixes. Chaque ajout d&amp;rsquo;une nouvelle architecture de modèle (ex : Llama, Falcon, Mixtral, etc.) ou d&amp;rsquo;un nouveau tokenizer entraînait des modifications destructives.&lt;/li>
&lt;li>&lt;strong>Perte de rétrocompatibilité :&lt;/strong> Le format étant fréquemment mis à jour, il arrivait souvent que les anciens fichiers de modèles ne puissent plus être lus par les versions récentes de llama.cpp.&lt;/li>
&lt;/ul>
&lt;h3 id="32-la-naissance-du-format-gguf">3.2. La naissance du format GGUF
&lt;/h3>&lt;p>Introduit en août 2023, &lt;strong>GGUF&lt;/strong> est un format très polyvalent conçu pour résoudre ces problèmes. Sa principale caractéristique est l&amp;rsquo;adoption d&amp;rsquo;une &lt;strong>structure de métadonnées basée sur des paires clé-valeur (Key-Value)&lt;/strong>.&lt;/p>
&lt;p>Le diagramme Mermaid suivant résume la structure d&amp;rsquo;un fichier GGUF.&lt;/p>
&lt;div class="mermaid">graph TD
A["Fichier GGUF"] --> B["En-tête (Magic, Version)"]
A --> C["Métadonnées (Paires Clé-Valeur)"]
A --> D["Infos des Tenseurs (Nom, Forme, Décalage)"]
A --> E["Données des Tenseurs (Charge utile binaire)"]
C --> C1["general.architecture: llama"]
C --> C2["llama.context_length: 4096"]
C --> C3["tokenizer.ggml.tokens: [...]"]
E --> E1["Poids de la Couche 0"]
E --> E2["Poids de la Couche 1"]
E --> E3["..."]&lt;/div>
&lt;p>&lt;strong>Principaux avantages de GGUF :&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Flexibilité :&lt;/strong> Tous les hyperparamètres du modèle, les paramètres RoPE (Rotary Positional Embedding), les données de vocabulaire du tokenizer, etc., sont stockés sous forme de paires clé-valeur nommées. Les clés inconnues étant ignorées, l&amp;rsquo;ajout de nouvelles fonctionnalités est facile.&lt;/li>
&lt;li>&lt;strong>Indépendance vis-à-vis du boutisme (Endianness) :&lt;/strong> Bien que GGUF utilise le little-endian par défaut, il possède un indicateur explicite, le rendant sûr et portable entre différentes architectures.&lt;/li>
&lt;li>&lt;strong>Optimisation pour le mmap (Memory Mapping) :&lt;/strong> Les données des tenseurs sont alignées sur des limites spécifiques (padding) et peuvent être mappées directement depuis le disque vers l&amp;rsquo;espace mémoire via l&amp;rsquo;appel système &lt;code>mmap()&lt;/code> de l&amp;rsquo;OS. Ainsi, le temps d&amp;rsquo;initialisation du chargement du modèle est virtuellement nul.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="4-les-profondeurs-de-k-quants--quantification-avancée-par-blocs">4. Les profondeurs de k-quants : Quantification avancée par blocs
&lt;/h2>&lt;p>Le véritable atout du format GGUF réside dans &lt;strong>k-quants (K-quantization)&lt;/strong>, le mécanisme responsable de la compression des poids du modèle.&lt;/p>
&lt;p>Généralement, les poids d&amp;rsquo;un réseau de neurones suivent une distribution proche de la normale sur l&amp;rsquo;ensemble d&amp;rsquo;une couche, mais il existe des valeurs aberrantes (Outliers) localement. Si l&amp;rsquo;on quantifie les poids d&amp;rsquo;une couche entière avec un facteur d&amp;rsquo;échelle $S$ uniforme, l&amp;rsquo;information des petits poids sera complètement écrasée par les valeurs aberrantes.&lt;/p>
&lt;p>Pour éviter cela, llama.cpp utilise une &lt;strong>quantification par blocs (Block-wise Quantization)&lt;/strong>. Les tenseurs de poids sont divisés en petits blocs (par exemple, 32 éléments ou 256 éléments), et chaque bloc possède son propre facteur d&amp;rsquo;échelle (et son point zéro).&lt;/p>
&lt;h3 id="41-limites-de-la-quantification-ancienne-q4_0-q4_1">4.1. Limites de la quantification ancienne (Q4_0, Q4_1)
&lt;/h3>&lt;p>L&amp;rsquo;ancien format &lt;code>Q4_0&lt;/code> regroupait 32 poids FP16 en un seul bloc et partageait un facteur d&amp;rsquo;échelle FP16.&lt;/p>
&lt;ul>
&lt;li>Taille du bloc : 32&lt;/li>
&lt;li>Mémoire : 1 échelle (16 bits) + 32 poids de 4 bits (128 bits) = 144 bits&lt;/li>
&lt;li>Nombre effectif de bits par élément (bpw : bits per weight) : $144 / 32 = 4,5$ bpw&lt;/li>
&lt;/ul>
&lt;p>Bien que ce soit suffisamment performant, les limites en termes de précision et de taux de compression devenaient visibles. C&amp;rsquo;est là qu&amp;rsquo;est apparu &lt;strong>k-quants&lt;/strong>, avec sa structure hiérarchique plus complexe et plus raffinée.&lt;/p>
&lt;h3 id="42-structure-hiérarchique-des-super-blocs-et-sous-blocs-exemple-de-q4_k_m">4.2. Structure hiérarchique des super-blocs et sous-blocs (Exemple de Q4_K_M)
&lt;/h3>&lt;p>k-quants possède une structure hiérarchique composée d&amp;rsquo;un grand « super-bloc (Super-block) » et de petits « sous-blocs (Sub-block) » à l&amp;rsquo;intérieur. Cela permet de quantifier les métadonnées elles-mêmes (comme les valeurs d&amp;rsquo;échelle) et de réduire le bpw au minimum tout en maintenant la précision.&lt;/p>
&lt;p>Examinons la structure de &lt;strong>Q4_K_M&lt;/strong>, qui est la configuration la plus populaire. Q4_K_M utilise un super-bloc de 256 éléments.&lt;/p>
&lt;div class="mermaid">graph TD
A["Super-bloc (256 poids)"] --> B["Métadonnées d'échelle (FP16/INT8)"]
A --> C["Sous-bloc 0 (32 poids, 4 bits)"]
A --> D["Sous-bloc 1 (32 poids, 4 bits)"]
A --> E["..."]
A --> F["Sous-bloc 7 (32 poids, 4 bits)"]
B --> B1["Super-échelle (FP16)"]
B --> B2["Sous-échelles (8 x 6 bits)"]
B --> B3["Sous-mins (8 x 6 bits)"]&lt;/div>
&lt;p>En C++ (GGML), la structure réelle est conceptuellement définie comme suit :&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Structure conceptuelle de block_q4_K dans llama.cpp
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="cp">#define QK_K 256
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">struct&lt;/span> &lt;span class="nc">block_q4_K&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">uint8_t&lt;/span> &lt;span class="n">d&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">];&lt;/span> &lt;span class="c1">// Super-échelle pour l&amp;#39;ensemble du super-bloc (FP16 x 2, etc.)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">uint8_t&lt;/span> &lt;span class="n">scales&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">12&lt;/span>&lt;span class="p">];&lt;/span> &lt;span class="c1">// Données compressées de l&amp;#39;échelle sur 6 bits et de la valeur minimale (point zéro) sur 6 bits pour les 8 sous-blocs (32 éléments chacun)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">uint8_t&lt;/span> &lt;span class="n">qs&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">QK_K&lt;/span>&lt;span class="o">/&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">];&lt;/span> &lt;span class="c1">// Données de poids quantifiées sur 4 bits (256 éléments / 2 = 128 octets)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;strong>Processus mathématique de déquantification (Dequantization) :&lt;/strong>&lt;/p>
&lt;p>La valeur réelle approximative $\tilde{W}_{i, j}$ de l&amp;rsquo;élément $j$ ($0 \le j &lt; 32$) dans le sous-bloc $i$ ($0 \le i &lt; 8$) est calculée comme suit :&lt;/p>
$$ \tilde{W}_{i, j} = S_{\text{super}} \times s_i \times (w_{i, j} - m_i) $$
&lt;ul>
&lt;li>$S_{\text{super}}$ : Échelle en virgule flottante de l&amp;rsquo;ensemble du super-bloc&lt;/li>
&lt;li>$s_i$ : Échelle quantifiée sur 6 bits pour le sous-bloc $i$&lt;/li>
&lt;li>$m_i$ : Valeur minimale (point zéro) quantifiée sur 6 bits pour le sous-bloc $i$&lt;/li>
&lt;li>$w_{i, j}$ : Poids quantifié sur 4 bits ($0 \dots 15$)&lt;/li>
&lt;/ul>
&lt;p>Grâce à cette structure hiérarchique, l&amp;rsquo;espace mémoire occupé par le facteur d&amp;rsquo;échelle lui-même est drastiquement réduit, tout en maintenant l&amp;rsquo;adaptabilité aux valeurs aberrantes. Q4_K_M atteint environ &lt;strong>4,8 bpw&lt;/strong> au total.&lt;/p>
&lt;h3 id="43-diverses-options-k-quants">4.3. Diverses options k-quants
&lt;/h3>&lt;p>llama.cpp offre de nombreuses variations selon vos besoins. Le suffixe après &amp;ldquo;K&amp;rdquo; (S, M, L) indique la taille.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align:left">Format&lt;/th>
&lt;th style="text-align:center">BPW (Bits per Weight)&lt;/th>
&lt;th style="text-align:left">Aperçu et caractéristiques&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Q2_K&lt;/strong>&lt;/td>
&lt;td style="text-align:center">2,5～3,3&lt;/td>
&lt;td style="text-align:left">Compression extrême. Baisse significative de la précision, destiné aux environnements avec très peu de VRAM.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Q3_K_M&lt;/strong>&lt;/td>
&lt;td style="text-align:center">3,3&lt;/td>
&lt;td style="text-align:left">Standard de la quantification à 3 bits. Se dégrade plus que Q4 mais reste souvent dans une plage acceptable.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Q4_K_M&lt;/strong>&lt;/td>
&lt;td style="text-align:center">4,8&lt;/td>
&lt;td style="text-align:left">&lt;strong>Le compromis idéal recommandé (Sweet spot)&lt;/strong>. Équilibre entre la réduction de taille de moitié et le maintien de la précision.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Q5_K_M&lt;/strong>&lt;/td>
&lt;td style="text-align:center">5,5&lt;/td>
&lt;td style="text-align:left">Pour ceux qui recherchent une plus grande précision. Position intermédiaire entre Q4 et FP16.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Q6_K&lt;/strong>&lt;/td>
&lt;td style="text-align:center">6,6&lt;/td>
&lt;td style="text-align:left">Maintient une Perplexité presque équivalente à FP16, mais la taille du fichier est plus grande.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Q8_0&lt;/strong>&lt;/td>
&lt;td style="text-align:center">8,5&lt;/td>
&lt;td style="text-align:left">Équivalent à INT8. Principalement utilisé pour les tenseurs intermédiaires lors des calculs d&amp;rsquo;inférence, ou seulement dans la dernière couche.&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;em>Remarque : Le BPW réel est moyenné sur l&amp;rsquo;ensemble du modèle car une quantification mixte (Mixed Quantization) est appliquée en fonction du tenseur (par exemple, projection Q/K/V de l&amp;rsquo;Attention vs poids FFN). En interne, des optimisations sont effectuées, comme la quantification des tenseurs importants en Q6 et le reste en Q4.&lt;/em>&lt;/p>
&lt;hr>
&lt;h2 id="5-optimisation-des-performances-lors-de-linférence--simd-et-architecture-cuda">5. Optimisation des performances lors de l&amp;rsquo;inférence : SIMD et architecture CUDA
&lt;/h2>&lt;p>Charger un modèle GGUF en mémoire ne rend pas l&amp;rsquo;inférence plus rapide à lui seul. La majeure partie de l&amp;rsquo;inférence d&amp;rsquo;un LLM est un &amp;ldquo;produit matriciel (Matrix-Vector Multiplication, ou GEMV, ou Matrix-Matrix, GEMM)&amp;rdquo;. La clé de l&amp;rsquo;accélération réside dans le calcul efficace du produit scalaire entre les poids quantifiés et les activations (données d&amp;rsquo;entrée) conservées en FP16 (ou FP32).&lt;/p>
&lt;h3 id="51-utilisation-des-instructions-simd-dans-un-environnement-cpu">5.1. Utilisation des instructions SIMD dans un environnement CPU
&lt;/h3>&lt;p>La raison pour laquelle llama.cpp affiche des vitesses incroyables pour l&amp;rsquo;inférence CPU réside dans l&amp;rsquo;optimisation &lt;strong>SIMD (Single Instruction, Multiple Data)&lt;/strong> au niveau de l&amp;rsquo;assembleur.
Par exemple, sur les processeurs Intel/AMD, les jeux d&amp;rsquo;instructions &lt;strong>AVX2&lt;/strong> et &lt;strong>AVX-512&lt;/strong> sont pleinement utilisés, et sur Apple Silicon, c&amp;rsquo;est &lt;strong>ARM NEON&lt;/strong>.&lt;/p>
&lt;p>Pendant l&amp;rsquo;inférence, $W_q$ n&amp;rsquo;est pas reconverti en FP32 (déquantifié) avant la multiplication.
Les activations sont également quantifiées dynamiquement par bloc (Dynamic Quantization, généralement vers INT8), et des calculs d&amp;rsquo;entiers &lt;strong>INT8 $\times$ INT4&lt;/strong> sont effectués en une seule fois à l&amp;rsquo;aide d&amp;rsquo;instructions spéciales de produit scalaire SIMD (ex : &lt;code>vdpaddd&lt;/code> ou &lt;code>_mm256_madd_epi16&lt;/code>). Le résultat est reconverti en FP32 dans l&amp;rsquo;accumulateur final et multiplié par le facteur d&amp;rsquo;échelle, permettant d&amp;rsquo;atteindre un débit phénoménal.&lt;/p>
&lt;h3 id="52-déchargement-offloading-dans-un-environnement-gpu-cublas--cuda">5.2. Déchargement (Offloading) dans un environnement GPU (cuBLAS / CUDA)
&lt;/h3>&lt;p>Les versions récentes de llama.cpp offrent également un support très robuste pour les GPU NVIDIA (CUBLAS / CUDA), et non seulement pour le CPU.
Il est possible de décharger tout ou partie des couches d&amp;rsquo;un fichier GGUF dans la VRAM (avec l&amp;rsquo;option &lt;code>--n-gpu-layers&lt;/code>).&lt;/p>
&lt;div class="mermaid">sequenceDiagram
participant User
participant CPU_RAM as CPU &amp; RAM (mmap)
participant VRAM as VRAM du GPU
participant Compute as Tensor Cores
User->>CPU_RAM: Charger GGUF (mmap)
CPU_RAM->>VRAM: Décharger les couches (ex: 30/32 couches)
Note over CPU_RAM, VRAM: Les données restent quantifiées dans la VRAM
User->>Compute: Passe avant (Tokens d'entrée)
Compute->>VRAM: Récupérer les poids quantifiés
Compute->>Compute: Déquantification à la volée vers FP16 dans la SRAM
Compute->>Compute: Multiplication matricielle (cuBLAS / Noyaux personnalisés)
Compute->>User: Sortie (Logits)&lt;/div>
&lt;p>Lors des calculs sur GPU, la bande passante de la VRAM (Memory Bandwidth) constitue le plus grand goulot d&amp;rsquo;étranglement. Étant donné que les poids sont compressés avec k-quants, la quantité de données transférées de la VRAM vers les unités de calcul du GPU (SM : Streaming Multiprocessor ou Tensor Cores) est réduite au tiers ou au quart. Dès que les poids atteignent l&amp;rsquo;unité de calcul, ils sont déquantifiés à la volée en FP16, et la multiplication matricielle est exécutée à très haute vitesse grâce aux Tensor Cores.
En d&amp;rsquo;autres termes, la quantification n&amp;rsquo;est &lt;strong>pas effectuée pour &amp;ldquo;réduire la quantité de calculs&amp;rdquo;, mais pour &amp;ldquo;réduire la quantité de données transférées&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;hr>
&lt;h2 id="6-exemples-concrets-du-compromis-entre-utilisation-de-la-mémoire-et-performances">6. Exemples concrets du compromis entre utilisation de la mémoire et performances
&lt;/h2>&lt;p>Prenons le modèle Llama 3 8B comme exemple pour examiner les spécifications requises selon le niveau de quantification GGUF. (Les valeurs sont des estimations approximatives)&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align:left">Modèle / Quantification&lt;/th>
&lt;th style="text-align:left">Taille du fichier&lt;/th>
&lt;th style="text-align:left">VRAM/RAM requise&lt;/th>
&lt;th style="text-align:left">Vitesse d&amp;rsquo;inférence&lt;/th>
&lt;th style="text-align:left">Dégradation (Perplexité)&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Llama-3-8B (FP16)&lt;/strong>&lt;/td>
&lt;td style="text-align:left">Env. 16 Go&lt;/td>
&lt;td style="text-align:left">18 Go ou plus&lt;/td>
&lt;td style="text-align:left">Référence&lt;/td>
&lt;td style="text-align:left">Aucune (Base)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Llama-3-8B (Q8_0)&lt;/strong>&lt;/td>
&lt;td style="text-align:left">Env. 8,5 Go&lt;/td>
&lt;td style="text-align:left">10 Go ou plus&lt;/td>
&lt;td style="text-align:left">Rapide&lt;/td>
&lt;td style="text-align:left">Presque zéro&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Llama-3-8B (Q6_K)&lt;/strong>&lt;/td>
&lt;td style="text-align:left">Env. 6,6 Go&lt;/td>
&lt;td style="text-align:left">8 Go ou plus&lt;/td>
&lt;td style="text-align:left">Très rapide&lt;/td>
&lt;td style="text-align:left">Minime&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Llama-3-8B (Q4_K_M)&lt;/strong>&lt;/td>
&lt;td style="text-align:left">Env. 4,9 Go&lt;/td>
&lt;td style="text-align:left">6,5 Go ou plus&lt;/td>
&lt;td style="text-align:left">La plus rapide / optimale&lt;/td>
&lt;td style="text-align:left">Acceptable / Légère&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Llama-3-8B (Q3_K_M)&lt;/strong>&lt;/td>
&lt;td style="text-align:left">Env. 3,9 Go&lt;/td>
&lt;td style="text-align:left">5,5 Go ou plus&lt;/td>
&lt;td style="text-align:left">La plus rapide&lt;/td>
&lt;td style="text-align:left">Plutôt visible&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">&lt;strong>Llama-3-8B (Q2_K)&lt;/strong>&lt;/td>
&lt;td style="text-align:left">Env. 3,0 Go&lt;/td>
&lt;td style="text-align:left">4,5 Go ou plus&lt;/td>
&lt;td style="text-align:left">Rapide&lt;/td>
&lt;td style="text-align:left">Dégradation évidente&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>Attention (Impact du cache KV) :&lt;/strong>
Lors de l&amp;rsquo;inférence LLM, lorsque la longueur du contexte (nombre de tokens du prompt) augmente, la consommation de mémoire du &lt;strong>cache KV&lt;/strong> (qui stocke les états d&amp;rsquo;Attention passés) explose, en plus de celle des poids du modèle.
Par exemple, pour un contexte de 8192 tokens, le cache KV seul consomme plusieurs Go. Par conséquent, en utilisation réelle, il est nécessaire de conserver une marge (Headroom) de &lt;code>taille du fichier modèle + environ 1,5 Go à 3 Go&lt;/code>. La raison pour laquelle Q4_K_M est recommandé est qu&amp;rsquo;il représente l&amp;rsquo;équilibre parfait permettant de fonctionner en toute sécurité sur un GPU équipé de 8 Go de VRAM (comme la RTX 3060 / 4060), tout en réservant l&amp;rsquo;espace pour ce cache KV.&lt;/p>
&lt;p>Récemment, llama.cpp a ajouté une fonctionnalité permettant de &lt;strong>quantifier le cache KV lui-même en Q8_0 ou Q4_0&lt;/strong>, et les efforts visant à étendre davantage la longueur du contexte sont continus.&lt;/p>
&lt;hr>
&lt;h2 id="7-conclusion">7. Conclusion
&lt;/h2>&lt;p>Dans cet article, nous avons exploré en profondeur le fonctionnement interne du format GGUF et de la technologie de quantification k-quants, qui constituent le cœur de llama.cpp.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>La flexibilité de GGUF :&lt;/strong> Grâce à une structure de métadonnées de type clé-valeur, GGUF a mis en place un écosystème robuste capable de suivre l&amp;rsquo;évolution rapide des LLM (apparition de nouvelles architectures) sans subir de modifications destructives.&lt;/li>
&lt;li>&lt;strong>Compression extrême grâce à k-quants :&lt;/strong> La gestion hiérarchique des facteurs d&amp;rsquo;échelle avec les super-blocs et sous-blocs permet de conserver les informations des valeurs aberrantes tout en atteignant une compression incroyable de 4,8 bits en moyenne par poids (Q4_K_M).&lt;/li>
&lt;li>&lt;strong>Résolution du goulot d&amp;rsquo;étranglement de la mémoire :&lt;/strong> Grâce à des implémentations avancées de noyaux SIMD et CUDA, la déquantification à la volée réduit les transferts depuis la VRAM et améliore considérablement la vitesse d&amp;rsquo;inférence.&lt;/li>
&lt;/ol>
&lt;p>La prouesse technologique de llama.cpp, qui favorise la démocratisation de l&amp;rsquo;IA, dépasse le simple statut d&amp;rsquo;outil et peut sans exagération être considérée comme l&amp;rsquo;un des sommets de l&amp;rsquo;ingénierie logicielle moderne. En comprenant l&amp;rsquo;algorithme de quantification et la structure du format GGUF, vous serez en mesure de choisir le modèle le plus adapté à votre environnement et d&amp;rsquo;effectuer des ajustements de performance avec une plus grande précision.&lt;/p>
&lt;h3 id="liens-utiles">Liens utiles
&lt;/h3>&lt;ul>
&lt;li>&lt;a class="link" href="https://github.com/ggerganov/llama.cpp" target="_blank" rel="noopener"
>Référentiel GitHub de llama.cpp&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://github.com/ggerganov/ggml/blob/master/docs/gguf.md" target="_blank" rel="noopener"
>Spécifications du format GGUF&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://github.com/ggerganov/llama.cpp/pull/1684" target="_blank" rel="noopener"
>Pull Request sur l&amp;rsquo;implémentation de k-quants&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>(Fin)&lt;/p></description></item></channel></rss>