<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>GGUF on kenji.blog</title><link>http://kenji.blog/de/tags/gguf/</link><description>Recent content in GGUF on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>de</language><copyright>kenjinote</copyright><lastBuildDate>Fri, 11 Sep 2026 00:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/de/tags/gguf/index.xml" rel="self" type="application/rss+xml"/><item><title>Erklärung der Funktionsweise der Quantisierungstechnologie (GGUF) in llama.cpp</title><link>http://kenji.blog/de/p/llama-cpp-quantization-gguf/</link><pubDate>Fri, 11 Sep 2026 00:00:00 +0900</pubDate><guid>http://kenji.blog/de/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 Erklärung der Funktionsweise der Quantisierungstechnologie (GGUF) in llama.cpp" />&lt;h2 id="1-einführung-warum-benötigen-llms-quantisierung">1. Einführung: Warum benötigen LLMs Quantisierung?
&lt;/h2>&lt;p>Die Entwicklung von großen Sprachmodellen (LLM: Large Language Models) in den letzten Jahren war bemerkenswert, aber im Hintergrund sind ernsthafte Probleme aufgetreten: &amp;ldquo;Erschöpfung der Rechenressourcen&amp;rdquo; und &amp;ldquo;Engpässe bei der Speicherbandbreite&amp;rdquo;. Wenn beispielsweise ein Modell mit 70B (70 Milliarden) Parametern wie Llama 3 im standardmäßigen 16-Bit-Gleitkommaformat (FP16) in den Speicher geladen wird, verbrauchen allein die Parameter etwa 140 GB VRAM/RAM. Wenn der Kontext (KV-Cache) während der Inferenz hinzugefügt wird, funktioniert dies nicht, ohne mehrere High-End-GPUs für Rechenzentren (wie NVIDIA A100 80GB oder H100 80GB) in einem Cluster zusammenzufassen.&lt;/p>
&lt;p>Als Retter für die Ausführung von LLMs auf Edge-Geräten (wie MacBooks oder gängigen Gaming-PCs) für einzelne Entwickler tauchten &lt;strong>llama.cpp&lt;/strong> und seine Kerntechnologie, die &lt;strong>Quantisierung (Quantization)&lt;/strong>, auf. Insbesondere das Dateiformat &lt;strong>GGUF (GPT-Generated Unified Format)&lt;/strong> und ein fortschrittlicher blockbasierter Quantisierungsalgorithmus namens &lt;strong>k-quants&lt;/strong> sind bahnbrechende Methoden, die die Modellgröße auf einen Bruchteil komprimieren, während die Verschlechterung der Modellgenauigkeit (Perplexity) so gering wie möglich gehalten wird.&lt;/p>
&lt;p>In diesem Artikel werden wir alles vom mathematischen Hintergrund der Quantisierung in llama.cpp über die Unterschiede zum GGML-Format und die detaillierte Struktur des GGUF-Formats bis hin zu den internen Mechanismen von k-quants ausführlich erklären.&lt;/p>
&lt;hr>
&lt;h2 id="2-mathematische-grundlagen-der-quantisierung">2. Mathematische Grundlagen der Quantisierung
&lt;/h2>&lt;p>Im Kontext von LLMs bezieht sich Quantisierung auf den Vorgang, kontinuierliche Werte (oder hochpräzise Gleitkommazahlen) auf diskrete Werte mit einer geringeren Anzahl von Bits (wie INT8, INT4, INT3) abzubilden.&lt;/p>
&lt;h3 id="21-grundlegende-formeln-der-linearen-quantisierung">2.1. Grundlegende Formeln der linearen Quantisierung
&lt;/h3>&lt;p>Der einfachste Ansatz ist die lineare Quantisierung (Min-Max-Quantisierung). Sei der ursprüngliche hochpräzise Gewichtstensor $W$ und der quantisierte Ganzzahl-Tensor $W_q$.&lt;/p>
$$ W_q = \text{round}\left( \frac{W}{S} \right) + Z $$
&lt;p>Hierbei ist:&lt;/p>
&lt;ul>
&lt;li>$S$ der &lt;strong>Skalierungsfaktor (Scale Factor)&lt;/strong>, der die Schrittgröße (Auflösung) der Quantisierung bestimmt.&lt;/li>
&lt;li>$Z$ der &lt;strong>Nullpunkt (Zero-point)&lt;/strong>, ein Bias-Wert, um zu verschieben, welchem ganzzahligen Wert nach der Quantisierung die reelle Zahl $0.0$ entspricht.&lt;/li>
&lt;li>$\text{round}(\cdot)$ die Funktion zur Rundung auf die nächste Ganzzahl.&lt;/li>
&lt;/ul>
&lt;p>Durch inverse Quantisierung (Dequantization) wird während der Inferenz ein ungefähres reelles Gewicht $\tilde{W}$ wiederhergestellt.&lt;/p>
$$ \tilde{W} = S \times (W_q - Z) $$
&lt;h3 id="22-symmetrische-vs-asymmetrische-quantisierung">2.2. Symmetrische vs. asymmetrische Quantisierung
&lt;/h3>&lt;p>Abhängig von der Behandlung des Nullpunkts $Z$ gibt es im Wesentlichen zwei Methoden.&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Asymmetrische Quantisierung (Asymmetric Quantization)&lt;/strong>
Mappt die Daten unter Verwendung des Minimalwerts $W_{\min}$ und des Maximalwerts $W_{\max}$.
&lt;/p>
$$ S = \frac{W_{\max} - W_{\min}}{2^b - 1}, \quad Z = \text{round}\left(-\frac{W_{\min}}{S}\right) $$
&lt;p>
Hier ist $b$ die Anzahl der Quantisierungsbits (z.B. für 4 Bit ist $2^4-1 = 15$). Da $Z$ beibehalten werden muss, erhöht sich der Rechen- und Speicher-Overhead geringfügig.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Symmetrische Quantisierung (Symmetric Quantization)&lt;/strong>
Verwendet den maximalen absoluten Wert der Daten, um sie um Null herum abzubilden ($Z=0$).
&lt;/p>
$$ S = \frac{\max(|W_{\max}|, |W_{\min}|)}{2^{b-1} - 1}, \quad Z = 0 $$
&lt;p>
Frühe Quantisierungen in llama.cpp (wie das Legacy Q4_0) verwendeten eine symmetrische Quantisierung. Da es keinen $Z$-Term gibt, hat dies den Vorteil, dass die Berechnung des inneren Produkts mit SIMD-Befehlen stark beschleunigt wird.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="3-entwicklung-von-ggml-zu-gguf-und-dateistruktur">3. Entwicklung von GGML zu GGUF und Dateistruktur
&lt;/h2>&lt;p>Bei der Diskussion über llama.cpp sind die in C++ geschriebene Tensor-Berechnungsbibliothek &lt;strong>GGML&lt;/strong> und das daraus abgeleitete Dateiformat &lt;strong>GGUF&lt;/strong> unerlässlich.&lt;/p>
&lt;h3 id="31-probleme-mit-ggml">3.1. Probleme mit GGML
&lt;/h3>&lt;p>Frühe Versionen von llama.cpp verwendeten das &lt;code>ggml&lt;/code>-Format (und Varianten wie &lt;code>ggjt&lt;/code>). Diese hatten jedoch die folgenden Probleme:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Fehlende Erweiterbarkeit:&lt;/strong> Magische Zahlen und Hyperparameter waren mit fester Länge und Reihenfolge hartcodiert, was bei jedem Hinzufügen einer neuen Modellarchitektur (z. B. Llama, Falcon, Mixtral usw.) oder eines neuen Tokenizers zu Breaking Changes führte.&lt;/li>
&lt;li>&lt;strong>Verlust der Abwärtskompatibilität:&lt;/strong> Formate wurden häufig aktualisiert, was oft dazu führte, dass ältere Modelldateien in neueren Versionen von llama.cpp nicht mehr geladen werden konnten.&lt;/li>
&lt;/ul>
&lt;h3 id="32-die-geburt-des-gguf-formats">3.2. Die Geburt des GGUF-Formats
&lt;/h3>&lt;p>Das im August 2023 eingeführte &lt;strong>GGUF&lt;/strong> ist ein äußerst vielseitiges Format, das zur Lösung dieser Probleme entwickelt wurde. Sein Hauptmerkmal ist die Einführung einer &lt;strong>Metadatenstruktur basierend auf Schlüssel-Wert-Paaren (Key-Value)&lt;/strong>.&lt;/p>
&lt;p>Das folgende Mermaid-Diagramm ist eine abstrahierte Darstellung der GGUF-Dateistruktur.&lt;/p>
&lt;div class="mermaid">graph TD
A["GGUF-Datei"] --> B["Header (Magic, Version)"]
A --> C["Metadaten (Schlüssel-Wert-Paare)"]
A --> D["Tensor-Info (Name, Shape, Offset)"]
A --> E["Tensor-Daten (Binäre Nutzlast)"]
C --> C1["general.architecture: llama"]
C --> C2["llama.context_length: 4096"]
C --> C3["tokenizer.ggml.tokens: [...]"]
E --> E1["Layer 0 Gewichte"]
E --> E2["Layer 1 Gewichte"]
E --> E3["..."]&lt;/div>
&lt;p>&lt;strong>Hauptvorteile von GGUF:&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Flexibilität:&lt;/strong> Modell-Hyperparameter, RoPE (Rotary Positional Embedding)-Einstellungen, Tokenizer-Vokabulardaten usw. werden alle als benannte Key-Value-Paare gespeichert. Unbekannte Schlüssel werden ignoriert, was das Hinzufügen neuer Funktionen erleichtert.&lt;/li>
&lt;li>&lt;strong>Endian-Unabhängigkeit:&lt;/strong> GGUF verwendet standardmäßig Little-Endian, verfügt jedoch über ein explizites Flag, sodass es sicher portabel zwischen verschiedenen Architekturen ist.&lt;/li>
&lt;li>&lt;strong>Optimierung für mmap (Memory Mapping):&lt;/strong> Tensor-Daten werden an bestimmten Grenzen ausgerichtet (Padding) und können über den &lt;code>mmap()&lt;/code>-Systemaufruf des Betriebssystems direkt von der Festplatte in den Speicherraum abgebildet werden. Dadurch wird die Initialisierungszeit für das Laden des Modells praktisch auf Null reduziert.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="4-die-tiefen-von-k-quants-fortgeschrittene-blockbasierte-quantisierung">4. Die Tiefen von k-quants: Fortgeschrittene blockbasierte Quantisierung
&lt;/h2>&lt;p>Das wahre Herzstück des GGUF-Formats ist der Mechanismus &lt;strong>k-quants (K-quantization)&lt;/strong>, der für die Komprimierung der Modellgewichte verantwortlich ist.&lt;/p>
&lt;p>Die Gewichte eines normalen neuronalen Netzwerks haben eine Form, die über die gesamte Schicht hinweg fast einer Normalverteilung entspricht, aber lokal gibt es Ausreißer (Outliers). Wenn man die Gewichte der gesamten Schicht mit einem einheitlichen Skalierungsfaktor $S$ quantisiert, gehen Informationen über kleine Gewichte aufgrund des Einflusses der Ausreißer vollständig verloren.&lt;/p>
&lt;p>Um dies zu verhindern, führt llama.cpp eine &lt;strong>blockbasierte Quantisierung (Block-wise Quantization)&lt;/strong> durch. Der Gewichtstensor wird in kleine Blöcke (z.B. 32 oder 256 Elemente) unterteilt, und jeder Block hat einen eigenen spezifischen Skalierungsfaktor (und Nullpunkt).&lt;/p>
&lt;h3 id="41-grenzen-der-legacy-quantisierung-q4_0-q4_1">4.1. Grenzen der Legacy-Quantisierung (Q4_0, Q4_1)
&lt;/h3>&lt;p>Das frühe &lt;code>Q4_0&lt;/code> fasste 32 FP16-Gewichte in einem einzigen Block zusammen und teilte einen FP16-Skalierungsfaktor.&lt;/p>
&lt;ul>
&lt;li>Blockgröße: 32&lt;/li>
&lt;li>Speicher: 1 Skala (16 Bit) + 32 x 4-Bit-Gewichte (128 Bit) = 144 Bit&lt;/li>
&lt;li>Effektive Bits pro Gewicht (bpw: bits per weight): $144 / 32 = 4,5$ bpw&lt;/li>
&lt;/ul>
&lt;p>Dies ist bereits gut genug, aber die Grenzen von Genauigkeit und Kompressionsrate wurden sichtbar. Hier kam &lt;strong>k-quants&lt;/strong> mit einer komplexeren und feineren hierarchischen Struktur ins Spiel.&lt;/p>
&lt;h3 id="42-hierarchische-struktur-von-super-blöcken-und-sub-blöcken-beispiel-q4_k_m">4.2. Hierarchische Struktur von Super-Blöcken und Sub-Blöcken (Beispiel: Q4_K_M)
&lt;/h3>&lt;p>k-quants haben eine hierarchische Struktur mit großen &amp;ldquo;Super-Blöcken (Super-block)&amp;rdquo; und kleinen &amp;ldquo;Sub-Blöcken (Sub-block)&amp;rdquo; darin. Dadurch wird auch eine Quantisierung der Metadaten selbst (wie Skalierungswerte) durchgeführt, was die bpw auf das Äußerste senkt und gleichzeitig die Genauigkeit beibehält.&lt;/p>
&lt;p>Schauen wir uns die Struktur von &lt;strong>Q4_K_M&lt;/strong>, der beliebtesten Einstellung, an. Q4_K_M verwendet einen Super-Block mit 256 Elementen.&lt;/p>
&lt;div class="mermaid">graph TD
A["Super-Block (256 Gewichte)"] --> B["Skalierungs-Metadaten (FP16/INT8)"]
A --> C["Sub-Block 0 (32 Gewichte, 4-Bit)"]
A --> D["Sub-Block 1 (32 Gewichte, 4-Bit)"]
A --> E["..."]
A --> F["Sub-Block 7 (32 Gewichte, 4-Bit)"]
B --> B1["Super-Skala (FP16)"]
B --> B2["Sub-Skalen (8 x 6-Bit)"]
B --> B3["Sub-Minima (8 x 6-Bit)"]&lt;/div>
&lt;p>In C++ (GGML) ist die tatsächliche Struktur wie folgt definiert:&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">// Konzeptionelle Struktur von block_q4_K in 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-Skala für den gesamten Super-Block (z. B. FP16 x 2)
&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">// Gepackte Daten: 6-Bit-Skalen und 6-Bit-Minima (Nullpunkte) von 8 Sub-Blöcken (je 32 Elemente)
&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">// Auf 4-Bit quantisierte Gewichtsdaten (256 Elemente / 2 = 128 Bytes)
&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>Mathematischer Prozess der inversen Quantisierung (Dequantization):&lt;/strong>&lt;/p>
&lt;p>Der angenäherte reelle Wert $\tilde{W}_{i, j}$ des Elements $j$ ($0 \le j &lt; 32$) im Sub-Block $i$ ($0 \le i &lt; 8$) wird wie folgt berechnet:&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}}$: Gleitkomma-Skalierung für den gesamten Super-Block&lt;/li>
&lt;li>$s_i$: Quantisierte 6-Bit-Skala für den Sub-Block $i$&lt;/li>
&lt;li>$m_i$: Quantisiertes 6-Bit-Minimum (Nullpunkt) für den Sub-Block $i$&lt;/li>
&lt;li>$w_{i, j}$: Quantisiertes 4-Bit-Gewicht ($0 \dots 15$)&lt;/li>
&lt;/ul>
&lt;p>Durch diese hierarchische Struktur wird die Speicherbelegung durch den Skalierungsfaktor drastisch reduziert, während die Anpassungsfähigkeit an Ausreißer erhalten bleibt. Q4_K_M erreicht insgesamt etwa &lt;strong>4,8 bpw&lt;/strong>.&lt;/p>
&lt;h3 id="43-verschiedene-k-quants-optionen">4.3. Verschiedene k-quants-Optionen
&lt;/h3>&lt;p>llama.cpp bietet je nach Zweck eine Vielzahl von Variationen. Die Suffixe (S, M, L) nach dem &amp;ldquo;K&amp;rdquo; geben die Größe an.&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 pro Gewicht)&lt;/th>
&lt;th style="text-align:left">Übersicht und Eigenschaften&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">Extrem komprimiert. Die Genauigkeit nimmt erheblich ab, aber für Umgebungen mit extrem wenig VRAM gedacht.&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 für 3-Bit-Quantisierung. Schlechter als Q4, liegt aber oft im akzeptablen Bereich.&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>Empfohlener Sweet Spot&lt;/strong>. Halbierung der Modellgröße bei gleichzeitiger Beibehaltung der Genauigkeit.&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">Wenn höhere Genauigkeit erforderlich ist. Zwischenposition zwischen Q4 und 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">Behält fast die gleiche Perplexity wie FP16 bei, hat aber eine größere Dateigröße.&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">Entspricht INT8. Wird hauptsächlich für Zwischentensoren bei der Inferenzberechnung und nur in der letzten Schicht verwendet.&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>※ Die tatsächlichen BPW werden über das gesamte Modell gemittelt, da abhängig vom Tensor des Modells (z. B. Q/K/V-Projektion in der Attention oder FFN-Gewicht) eine gemischte Quantisierung (Mixed Quantization) durchgeführt wird. Wichtige Tensoren werden mit Q6 quantisiert, andere mit Q4, was intern als Optimierung stattfindet.&lt;/p>
&lt;hr>
&lt;h2 id="5-leistungsoptimierung-bei-der-inferenz-simd-und-cuda-architektur">5. Leistungsoptimierung bei der Inferenz: SIMD und CUDA-Architektur
&lt;/h2>&lt;p>Einfach das GGUF-Modell in den Speicher zu laden, macht die Inferenz noch nicht schnell. Der Großteil der LLM-Inferenz besteht aus der &amp;ldquo;Matrix-Vektor-Multiplikation&amp;rdquo; (GEMV oder Matrix-Matrix, GEMM). Der Schlüssel liegt in der Beschleunigung der Multiply-Accumulate-Operationen zwischen den quantisierten Gewichten und den in FP16 (oder FP32) gehaltenen Aktivierungen (Eingabedaten).&lt;/p>
&lt;h3 id="51-nutzung-von-simd-befehlen-in-cpu-umgebungen">5.1. Nutzung von SIMD-Befehlen in CPU-Umgebungen
&lt;/h3>&lt;p>Die erstaunliche Geschwindigkeit von llama.cpp bei der CPU-Inferenz liegt an der &lt;strong>SIMD-Optimierung (Single Instruction, Multiple Data)&lt;/strong> auf Assembler-Ebene.
Beispielsweise nutzt es die &lt;strong>AVX2&lt;/strong>- oder &lt;strong>AVX-512&lt;/strong>-Befehlssätze auf Intel/AMD-CPUs und &lt;strong>ARM NEON&lt;/strong> auf Apple Silicon vollständig aus.&lt;/p>
&lt;p>Während der Inferenz wird $W_q$ nicht explizit zurück in FP32 konvertiert (dequantisiert), bevor die Multiplikation durchgeführt wird.
Auch die Aktivierungsseite wird dynamisch blockweise quantisiert (Dynamic Quantization, normalerweise nach INT8), und die Ganzzahl-Operationen &lt;strong>INT8 $\times$ INT4&lt;/strong> werden auf einmal mithilfe spezieller SIMD-Skalarprodukt-Befehle (z. B. &lt;code>vdpaddd&lt;/code> oder &lt;code>_mm256_madd_epi16&lt;/code>) berechnet. Durch Konvertierung des endgültigen Akkumulators zurück nach FP32 und Multiplikation mit dem Skalierungsfaktor wird ein bemerkenswerter Durchsatz erzielt.&lt;/p>
&lt;h3 id="52-offloading-auf-die-gpu-umgebung-cublas--cuda">5.2. Offloading auf die GPU-Umgebung (cuBLAS / CUDA)
&lt;/h3>&lt;p>Neuere Versionen von llama.cpp unterstützen nicht nur CPUs, sondern bieten auch eine starke Unterstützung für NVIDIA-GPUs (CUBLAS / CUDA).
Es ist möglich, einen Teil oder alle Schichten der GGUF-Datei in den VRAM auszulagern (&lt;code>--n-gpu-layers&lt;/code> Option).&lt;/p>
&lt;div class="mermaid">sequenceDiagram
participant User
participant CPU_RAM as CPU &amp; RAM (mmap)
participant VRAM as GPU VRAM
participant Compute as Tensor Cores
User->>CPU_RAM: GGUF laden (mmap)
CPU_RAM->>VRAM: Schichten auslagern (z.B. 30/32 Schichten)
Note over CPU_RAM, VRAM: Daten bleiben im VRAM quantisiert
User->>Compute: Forward Pass (Eingabe-Tokens)
Compute->>VRAM: Quantisierte Gewichte abrufen
Compute->>Compute: On-the-fly im SRAM zu FP16 dequantisieren
Compute->>Compute: Matrixmultiplikation (cuBLAS / Custom Kernels)
Compute->>User: Ausgabe-Logits&lt;/div>
&lt;p>Bei Berechnungen auf der GPU wird die Speicherbandbreite (Memory Bandwidth) des VRAM zum größten Engpass. Da die Gewichte mit k-quants komprimiert sind, reduziert sich die Datenübertragungsmenge vom VRAM zu den Recheneinheiten der GPU (SM: Streaming Multiprocessor oder Tensor Cores) auf 1/3 bis 1/4. In dem Moment, in dem die Gewichte die Recheneinheit erreichen, werden sie &amp;ldquo;on-the-fly&amp;rdquo; (im laufenden Betrieb) zu FP16 dequantisiert (entpackt), und die Matrixmultiplikation wird mithilfe von Tensor Cores extrem schnell durchgeführt.
Mit anderen Worten: Die Quantisierung erfolgt &lt;strong>nicht &amp;ldquo;um die Rechenmenge zu reduzieren&amp;rdquo;, sondern &amp;ldquo;um die Speicherübertragungsmenge zu reduzieren&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;hr>
&lt;h2 id="6-spezifisches-beispiel-für-den-kompromiss-zwischen-speichernutzung-und-leistung">6. Spezifisches Beispiel für den Kompromiss zwischen Speichernutzung und Leistung
&lt;/h2>&lt;p>Lassen Sie uns anhand des Modells Llama 3 8B die erforderlichen Spezifikationen für verschiedene Quantisierungsstufen in GGUF betrachten. (Die Zahlen sind ungefähre Richtwerte.)&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align:left">Modell / Quantisierung&lt;/th>
&lt;th style="text-align:left">Dateigröße&lt;/th>
&lt;th style="text-align:left">Benötigter VRAM/RAM&lt;/th>
&lt;th style="text-align:left">Inferenzgeschwindigkeit (Schätzung)&lt;/th>
&lt;th style="text-align:left">Perplexity-Verschlechterung&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">Ca. 16 GB&lt;/td>
&lt;td style="text-align:left">18 GB oder mehr&lt;/td>
&lt;td style="text-align:left">Basislinie&lt;/td>
&lt;td style="text-align:left">Keine (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">Ca. 8,5 GB&lt;/td>
&lt;td style="text-align:left">10 GB oder mehr&lt;/td>
&lt;td style="text-align:left">Schnell&lt;/td>
&lt;td style="text-align:left">Fast null&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">Ca. 6,6 GB&lt;/td>
&lt;td style="text-align:left">8 GB oder mehr&lt;/td>
&lt;td style="text-align:left">Sehr schnell&lt;/td>
&lt;td style="text-align:left">Sehr klein&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">Ca. 4,9 GB&lt;/td>
&lt;td style="text-align:left">6,5 GB oder mehr&lt;/td>
&lt;td style="text-align:left">Am schnellsten / Optimal&lt;/td>
&lt;td style="text-align:left">Akzeptabel / Gering&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">Ca. 3,9 GB&lt;/td>
&lt;td style="text-align:left">5,5 GB oder mehr&lt;/td>
&lt;td style="text-align:left">Am schnellsten&lt;/td>
&lt;td style="text-align:left">Etwas auffällig&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">Ca. 3,0 GB&lt;/td>
&lt;td style="text-align:left">4,5 GB oder mehr&lt;/td>
&lt;td style="text-align:left">Schnell&lt;/td>
&lt;td style="text-align:left">Deutliche Verschlechterung&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>Hinweis (Einfluss des KV-Caches):&lt;/strong>
Bei der Inferenz mit LLMs nimmt der Speicherverbrauch des &lt;strong>KV-Caches&lt;/strong>, der die vergangenen Attention-Zustände speichert, bei zunehmender Kontextlänge (Anzahl der Tokens in der Eingabeaufforderung) explosionsartig zu, nicht nur die Gewichte des Modells.
Wenn der Kontext beispielsweise 8192 Token beträgt, verbraucht allein der KV-Cache mehrere GB. Daher muss in der Praxis ein Spielraum (Headroom) von &lt;code>Dateigröße des Modells + ca. 1,5 GB bis 3 GB&lt;/code> vorgesehen werden. Der Grund, warum Q4_K_M empfohlen wird, liegt darin, dass es genau die richtige Balance bietet, um sicher auf einer GPU mit 8 GB VRAM (wie RTX 3060 / 4060) zu laufen, selbst wenn dieser KV-Cache gesichert ist.&lt;/p>
&lt;p>In den neueren Versionen von llama.cpp wurde auch eine Funktion zur &lt;strong>Quantisierung des KV-Caches selbst in Q8_0 oder Q4_0&lt;/strong> hinzugefügt, und es werden kontinuierliche Anstrengungen unternommen, um die Kontextlänge weiter zu erhöhen.&lt;/p>
&lt;hr>
&lt;h2 id="7-zusammenfassung">7. Zusammenfassung
&lt;/h2>&lt;p>In diesem Artikel haben wir uns eingehend mit der internen Struktur des GGUF-Formats, dem Herzstück von llama.cpp, und der k-quants-Quantisierungstechnologie befasst.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Flexibilität von GGUF:&lt;/strong> Durch eine Metadatenstruktur vom Typ Schlüssel-Wert wurde ein robustes Ökosystem aufgebaut, das selbst bei schnellen Entwicklungen von LLMs (wie der Einführung neuer Modellarchitekturen) ohne Breaking Changes mithalten kann.&lt;/li>
&lt;li>&lt;strong>Extreme Komprimierung durch k-quants:&lt;/strong> Durch die hierarchische Verwaltung der Skalierungsfaktoren in Super-Blöcken und Sub-Blöcken wurde eine erstaunliche Komprimierung von durchschnittlich 4,8 Bit pro Gewicht (Q4_K_M) erreicht, während die Informationen der Ausreißer erhalten blieben.&lt;/li>
&lt;li>&lt;strong>Behebung des Engpasses bei der Speicherbandbreite:&lt;/strong> Durch die Reduzierung der VRAM-Übertragungsmenge durch &amp;ldquo;On-the-fly&amp;rdquo;-Dequantisierung und -Berechnung, ermöglicht durch fortschrittliche Kernel-Implementierungen in SIMD und CUDA, wurde die Inferenzgeschwindigkeit drastisch erhöht.&lt;/li>
&lt;/ol>
&lt;p>Die technologischen Fähigkeiten von llama.cpp, das die Demokratisierung der KI vorantreibt, sprengen den Rahmen eines bloßen Werkzeugs, und es ist keine Übertreibung zu sagen, dass es heute einen der Höhepunkte der Softwareentwicklung darstellt. Das Verständnis des Quantisierungsalgorithmus und der Mechanismen des GGUF-Formats wird Ihnen helfen, das optimale Modell für Ihre Umgebung genauer auszuwählen und das Performance-Tuning effizienter durchzuführen.&lt;/p>
&lt;h3 id="referenz-links">Referenz-Links
&lt;/h3>&lt;ul>
&lt;li>&lt;a class="link" href="https://github.com/ggerganov/llama.cpp" target="_blank" rel="noopener"
>llama.cpp GitHub Repository&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"
>GGUF Format Specification&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://github.com/ggerganov/llama.cpp/pull/1684" target="_blank" rel="noopener"
>K-quants Implementation PR&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>(Ende)&lt;/p></description></item></channel></rss>