--- license: apache-2.0 license_link: https://huggingface.co/Qwen/Qwen3.8-27B/blob/main/LICENSE base_model: - Qwen/Qwen3.8-27B base_model_relation: quantized pipeline_tag: image-text-to-text library_name: llama.cpp language: - de - en tags: - gguf - llama.cpp - mtp - speculative-decoding - qwen3 - multimodal --- # Qwen3.8-27B GGUF — **mit separatem MTP-Draft** GGUF-Konvertierung von [Qwen/Qwen3.8-27B](https://huggingface.co/Qwen/Qwen3.8-27B), gebaut am 14.08.2026 aus den **BF16**-Originalgewichten. Der Grund für dieses Repo ist die Datei **`qwen3.8-27b-MTP-Q8_0.gguf`**: der Multi-Token-Prediction-Kopf als eigenständiges Draft-Modell, direkt nutzbar mit `--spec-type draft-mtp`. Die MTP-Gewichte stecken im Upstream-Checkpoint (`mtp_num_hidden_layers` in der `config.json`), werden von den üblichen GGUF-Repos aber nicht als getrennte Datei ausgeliefert — und ohne getrennte Datei kann `llama-server` sie nicht als Draft laden. **Es bringt Faktor 1,6 bis 2,7.** Gemessen, nicht geschätzt — siehe unten. ## Dateien | Datei | Größe | BPW | Zweck | |---|---|---|---| | `qwen3.8-27b-Q8_0.gguf` | 27,3 GB | 8,50 | Zielmodell — **empfohlen** | | `qwen3.8-27b-Q6_K.gguf` | 21,0 GB | 6,57 | kleinere Alternative | | `qwen3.8-27b-MTP-Q8_0.gguf` | 3,0 GB | — | **MTP-Draft** für spekulatives Dekodieren | | `qwen3.8-27b-mmproj-F16.gguf` | 0,9 GB | — | Vision/Video-Projektor | Die MTP-Tensoren sind aus den Zielmodellen **entfernt** (`--no-mtp`) und liegen ausschließlich in der Draft-Datei. Ziel- und Draft-Datei gehören zusammen. **Welches Zielmodell?** Q8_0 ist das genauere, Q6_K das schnellere. Gemessen ohne Draft, gleiche Einstellungen, 20 Aufgaben: **Q6_K 23,7 t/s gegen Q8_0 19,6 t/s** — Q6_K liegt 21 Prozent vorn, weil beim Erzeugen jedes Tokens alle Gewichte einmal durch den Speicher müssen und 21 GB nun mal schneller gelesen sind als 27 GB. > **Korrektur, 15.08.2026.** Eine frühere Fassung dieser Karte behauptete das > Gegenteil (»Q8_0 ist durchgehend schneller«). Diese Messung verglich Q8_0 > *mit* Draft gegen Q6_K *mit* Draft — dort hängt die Rate an der > Draft-Trefferquote, nicht an der Dequantisierung, und der Vergleich war > damit wertlos. Beim sauberen Vergleich ohne Draft dreht sich das Ergebnis um. ## Verwendung ```bash llama-server \ -m qwen3.8-27b-Q8_0.gguf \ --mmproj qwen3.8-27b-mmproj-F16.gguf \ -md qwen3.8-27b-MTP-Q8_0.gguf \ --spec-type draft-mtp --spec-draft-n-max 3 --spec-draft-ngl 99 \ --ctx-size 97000 --parallel 1 \ --n-gpu-layers 99 --tensor-split 1,1,1 \ --cache-type-k q8_0 --cache-type-v q8_0 \ --flash-attn on --jinja ``` Ohne Spekulation einfach `-md` und die drei `--spec-*`-Schalter weglassen. Ohne Bildeingabe zusätzlich `--mmproj` weglassen. ## Was MTP tatsächlich bringt Beide Läufe mit **identischer** Konfiguration (Q8_0, 97K Kontext, eine Session), einziger Unterschied ist das Draft-Modell. Deutsche Prompts, je 2–3 Durchgänge, ausgewertet wird der Median. Die Zahlen kommen aus `timings.predicted_per_second` bzw. `timings.draft_n` / `draft_n_accepted`, die `llama-server` pro Anfrage selbst meldet. | Textsorte | ohne Draft | **mit Draft** | **Faktor** | Draft-Trefferquote | |---|---|---|---|---| | Wiederholung | 22,9 t/s | **62,0 t/s** | **2,71×** | 85,0 % | | Aufzählung | 23,0 t/s | **61,6 t/s** | **2,68×** | 93,1 % | | Übersetzung | 22,9 t/s | **51,8 t/s** | **2,26×** | 70,9 % | | Struktur / JSON | 23,0 t/s | **47,6 t/s** | **2,07×** | 59,7 % | | Fachtext | 23,0 t/s | **41,7 t/s** | 1,81× | 48,0 % | | Code | 23,0 t/s | **41,1 t/s** | 1,79× | 48,9 % | | Fließtext | 22,9 t/s | **39,5 t/s** | 1,72× | 44,5 % | | Kreativtext | 23,0 t/s | **35,9 t/s** | **1,56×** | 37,4 % | Gesamt über alle Textsorten: **57,3 % Trefferquote** (4.491 von 7.842 Draft-Token). ### Der wichtigste Befund **Ohne Draft ist die Rate völlig flach — 22,9 bis 23,0 t/s, egal was man generiert.** Mit Draft spreizt sie sich um Faktor 1,7 auseinander, weil die Trefferquote massiv an der Textsorte hängt: vorhersagbarer Text wird zu über 90 Prozent korrekt geraten, freier Kreativtext nur zu gut einem Drittel. Wer MTP an einem Aufzählungs-Benchmark misst, kommt auf 2,7× und überschätzt den Nutzen für normale Chatlast deutlich. Der realistische Wert für Fließtext-Dialog liegt bei **rund 1,7×**. ### Was der Draft kostet * **3,0 GB** auf der Platte * **rund 2,7 GB VRAM** (`[spec] estimated memory usage of draft model is 2678.78 MiB` bei 97K Kontext; 1.953 MiB bei 4K) * Ladezeit steigt kaum — 7 bis 27 Sekunden je nach Kontextgröße Deutlich mehr als etwa der Gemma-4-12B-Draft mit 0,5 GB. Wer knapp an VRAM ist, muss das einplanen. ## HumanEval+ — sieben Varianten, ein Ergebnis Alle 164 Aufgaben, alle sieben Varianten, identische Bedingungen. Bewertet wird durch Ausführen: jede gelieferte Funktion läuft gegen den vollständigen EvalPlus-Testblock, der die HumanEval-Originaltests um etwa das Achtzigfache erweitert. ![HumanEval+ je Variante](bench/quoten-hell.png) | Variante | bestanden | Quote | Token/s | 95-%-Intervall | |---|---|---|---|---| | Qwen3.6-27B UD-Q6_K_XL | 149/164 | 90,9 % | 25,1 | ±4,4 | | Qwen3.6-27B Uncensored Q6_K_P | 148/164 | 90,2 % | 26,9 | ±4,5 | | **Qwen3.8-27B Q6_K** | 148/164 | **90,2 %** | **27,9** | ±4,5 | | Qwen3.6-27B Q6_K | 146/164 | 89,0 % | 27,6 | ±4,8 | | **Qwen3.8-27B Q8_0** | 145/164 | **88,4 %** | 19,5 | ±4,9 | | **Qwen3.8-27B BF16** | 145/164 | **88,4 %** | 13,2 | ±4,9 | | Qwen3.6-27B Q5_K_M | 145/164 | 88,4 % | 31,3 | ±4,9 | ### Der eigentliche Befund: die Unterschiede sind keine Die gesamte Spanne beträgt **vier Aufgaben von 164**. Bei ±4,4 bis ±4,9 Punkten Konfidenzintervall überlappen alle sieben Bereiche vollständig — **statistisch ist keine Variante von einer anderen unterscheidbar.** Noch deutlicher wird es beim Blick auf einzelne Aufgaben: * **135 der 164 Aufgaben lösen alle sieben** Varianten * **9 Aufgaben löst keine einzige** (HumanEval/32, 76, 91, 101, 127, 132, 145, 154, 163) * nur **20 Aufgaben** unterscheiden überhaupt — und dort geht es mal so, mal so aus Die scheinbare Rangfolge entsteht ausschließlich aus diesen 20 Wackelkandidaten. Wer daraus ein »Modell A ist besser als B« liest, liest Rauschen. ### Was man trotzdem mitnehmen kann **Quantisierung kostet hier nichts.** BF16 (51 GB), Q8_0 (27 GB) und Q5_K_M (19 GB) landen alle auf **exakt 145 von 164**. Von 51 auf 19 Gigabyte heruntergerechnet — kein messbarer Qualitätsverlust bei Code, aber 13,2 gegen 31,3 Token/s. Die unquantisierte Variante ist für diesen Zweck reine Verschwendung. **Qwen3.8 schlägt Qwen3.6 bei Code nicht.** Die neue Generation liegt im selben Bereich. Wer wegen Code aufrüstet, tut es umsonst; die Verbesserungen liegen woanders (Vision, MTP-Kopf, Kontextlänge). ![Tempo gegen Qualität](bench/tempo-hell.png) ### Einordnung gegen publizierte Werte | Modell | HumanEval+ | |---|---| | Phi-4 Reasoning (Microsoft) | 92,9 % | | **Qwen3.8-27B Q6_K — diese Messung** | **90,2 %** | | Llama 3 405B Instruct | 89 % | | **Qwen3.8-27B Q8_0 — diese Messung** | **88,4 %** | | Granite 3.3 8B Base | 86,1 % | | Kimi K2 Base | 80,3 % | > **Diese Zahlen sind nicht eins zu eins mit den Leaderboards vergleichbar.** > Drei Abweichungen vom offiziellen EvalPlus-Verfahren: die Aufgabe wird im > **Chat-Format** gestellt (»Vervollständige diese Funktion«) statt als reine > Textvervollständigung; **Temperatur 0,2** statt greedy; und der Code wird aus > Markdown-Zäunen geschält. Jeder Punkt kann einige Prozent bewegen, meist nach > oben — Chat-Modelle schneiden im Chat-Format besser ab. Wer vergleichen will, > nimmt die Zahl als Hausmessung, nicht als Leaderboard-Eintrag. Das Fehlerbild ist gesund: bei Q8_0 sind von 19 Fehlschlägen **15 AssertionError**, also echte Logikfehler. Dazu je ein TypeError, SyntaxError, IndexError und eine Zeitüberschreitung — kein Muster, das auf ein kaputtes Testgerüst hindeutet. Das komplette Messgerüst liegt unter `bench/` samt beider Selbsttests: einer prüft das Aufgaben-Harness gegen bekannt richtige *und* bekannt falsche Lösungen, der andere die EvalPlus-Ausführung gegen die mitgelieferten Referenzlösungen (25/25 grün). Ohne diese Prüfung misst ein Benchmark unter Umständen nur die eigenen Denkfehler. ## Durchsatz im Vergleich ![Durchsatz je Variante](bench/durchsatz-hell.png) Sieben 27B-Varianten unter identischen Bedingungen — 16K Kontext, eine Session, kein Draft, `--tensor-split 1,1,1`, KV-Cache q8_0, Temperatur 0,2: | Variante | Größe | Token/s | |---|---|---| | Qwen3.6-27B Q5_K_M | 19 GB | 26,4 | | Qwen3.6-27B Q6_K | 21 GB | 23,9 | | **Qwen3.8-27B Q6_K** | 21 GB | **23,7** | | Qwen3.6-27B Uncensored Q6_K_P | 22 GB | 23,1 | | Qwen3.6-27B UD-Q6_K_XL | 24 GB | 21,1 | | **Qwen3.8-27B Q8_0** | 27 GB | **19,6** | | **Qwen3.8-27B BF16** | 51 GB | **11,5** | Der Zusammenhang ist schlicht: mehr Gigabyte, weniger Token pro Sekunde. Bei der Texterzeugung liest die GPU für **jedes einzelne Token** sämtliche Gewichte einmal durch — Speicherbandbreite ist die Grenze, nicht Rechenleistung. **Die BF16 läuft auf drei RTX 3080 — knapp.** 51,3 GB Gewichte belegen 16,7 / 16,8 / 17,7 GB der drei Karten, macht 51,2 von 60 GB. Mit 16K Kontext bleibt genug für KV-Cache und Puffer. Sie kostet allerdings 40 Prozent Tempo gegenüber Q8_0, ohne dass sich in unserem eigenen Aufgabensatz ein Qualitätsvorsprung gezeigt hätte — beide scheiterten an derselben einen Aufgabe. ## Testaufbau * 3× NVIDIA RTX 3080 20 GB — **Ampere, SM 8.6** * AMD EPYC 7F32, 8 Kerne / 16 Threads * llama.cpp Build **10423** (`a94d563ed`), Container `ghcr.io/ggml-org/llama.cpp:full-cuda` * `--tensor-split 1,1,1`, KV-Cache `q8_0`, `--flash-attn on` * Eine Session (`--parallel 1`), 97.024 Token Kontext * Belegt: 31,2 GB von 60 GB — Modell 27,3 + Draft 2,7 + KV/Puffer Der Kontext ist praktisch gratis: dieselbe Konfiguration mit nur 4K statt 97K lieferte im Rahmen der Messstreuung dieselben Werte (58,8 gegen 60,5 t/s bei Aufzählungen). Man kann den großen Kontext also mitnehmen, ohne Durchsatz zu bezahlen. ## Wie diese Dateien gebaut wurden ```bash # Zielmodell ohne MTP-Tensoren convert_hf_to_gguf.py Qwen3.8-27B/ --outfile tmp-BF16.gguf --outtype bf16 --no-mtp # nur der MTP-Kopf, als Draft convert_hf_to_gguf.py Qwen3.8-27B/ --outfile qwen3.8-27b-MTP-Q8_0.gguf --outtype q8_0 --mtp # Vision/Video-Projektor convert_hf_to_gguf.py Qwen3.8-27B/ --outfile qwen3.8-27b-mmproj-F16.gguf --outtype f16 --mmproj # quantisieren llama-quantize tmp-BF16.gguf qwen3.8-27b-Q8_0.gguf Q8_0 16 llama-quantize tmp-BF16.gguf qwen3.8-27b-Q6_K.gguf Q6_K 16 ``` Die Flags `--mtp` und `--no-mtp` sind neu; ältere llama.cpp-Builds kennen sie nicht. Die Architektur `Qwen3_5ForConditionalGeneration` (Qwen3.8 nutzt sie unverändert weiter) erbt `supports_mtp_export = True` über `Qwen3_5TextModel → _LinearAttentionVReorderBase → Qwen3NextModel → _QwenMtpMixin`. Quelle: `Qwen/Qwen3.8-27B` in **BF16** (51,3 GB, 16,00 BPW), nicht das FP8-Release — also **keine** doppelte Quantisierung. ## Kontextlänge und KV-Cache — ab 48 GB hört die Abwägung auf Auf kleineren Karten zwingt der KV-Cache zu einer Entscheidung: weniger Bits im Cache, dafür mehr Kontext. Gemessen auf **60 GB** (3× RTX 3080) stellt sich diese Frage nicht mehr, weil vorher das Modell selbst deckelt: | KV-Cache | angefordert | tatsächlich belegt | |---|---|---| | q8_0 | 256.000 | 256.000 — läuft | | q8_0 | 384.000+ | läuft, aber jenseits von `n_ctx_train` | | q5_1 | 256.000 | 256.000 — läuft | | q5_1 | 447.488 | abgelehnt | `n_ctx_train` liegt bei Qwen3.8-27B bei **262.144 Token**. Darüber meldet llama.cpp: ``` llama_context: n_ctx_seq (384000) > n_ctx_train (262144) -- possible training context overflow ``` **Mit q8_0 erreicht man auf 60 GB bereits den vollen trainierten Kontext.** Ein Wechsel auf q5_1 oder q4_0 bringt hier keine Kontextlänge mehr, sondern nur freies VRAM — nützlich für mehr parallele Sitzungen, nicht für längere Eingaben. Auf 24 oder 32 GB sieht das anders aus, dort ist die Abwägung real. ## Flash-Attention mit quantisiertem KV-Cache: bitte selbst nachmessen [llama.cpp-Issue #24485](https://github.com/ggml-org/llama.cpp/issues/24485) beschreibt einen **stillen** Rückfall auf CPU-Attention, wenn ein quantisierter KV-Cache mit Flash-Attention kombiniert wird und der Build ohne `GGML_CUDA_FA_ALL_QUANTS=ON` erzeugt wurde. Keine Warnung, aber 25- bis 45-fach langsamerer Prefill. Das Issue wurde als *not planned* geschlossen — das Verhalten bleibt also bestehen. Unser Build ist **nicht** betroffen. Der Test dauert fünf Minuten: derselbe Prompt (8.933 Token), einmal mit q8_0-Cache, einmal mit f16. | KV-Cache | Prefill | Generierung | |---|---|---| | q8_0 | **2.111,7 Tok/s** | 22,8 Tok/s | | f16 | **2.142,7 Tok/s** | 23,0 Tok/s | 1,4 Prozent Abstand — kein Rückfall. **Wichtig: an der Generierungsrate sieht man den Fehler nicht.** Die ist durch die Speicherbandbreite begrenzt und ändert sich kaum; nur die Prefill-Rate deckt das Problem auf. Das dürfte der Grund sein, warum der Fehler in vielen Aufbauten unbemerkt bleibt. Nebenbefund: `--flash-attn off` mit quantisiertem KV-Cache stürzt auf diesem Build ab (Speicherzugriffsfehler), statt sauber abzulehnen. ## Zwei Hinweise für Ampere-Besitzer **FP8 läuft auf Ampere nicht nativ.** SM 8.6 hat keine FP8-Tensorcores, die gibt es erst ab SM 8.9. Qwens FP8-Release lässt sich dort nur dequantisiert fahren — Speicher gespart, Tempo nicht. Für GGUF ist das gegenstandslos, weil `Q8_0` und `Q6_K` reine Integer-Formate mit nativen Ampere-Kernen sind. **vLLM scheitert hier an drei GPUs.** Das Modell hat 24 Attention-Heads, aber nur **4 KV-Heads**. vLLM verlangt, dass `tensor_parallel_size` die KV-Head-Zahl teilt — 4 durch 3 geht nicht auf. Bleiben TP=2 und damit eine ungenutzte Karte. llama.cpp kennt das Problem nicht, weil `--tensor-split` Layer verteilt statt Attention-Köpfe. **Für ungerade GPU-Zahlen ist llama.cpp hier der einzige Weg, alle Karten zu nutzen.** ## Reproduzieren Das Messkript ist beigelegt (`bench_mtp.py`, deutsche Prompts über acht Textsorten, Median aus N Durchgängen): ```bash python3 bench_mtp.py --port 8290 --runs 3 --max-tokens 300 --out ergebnis.md ``` Einzelmessungen schwanken um etwa ±6 Prozent, deshalb Median statt Einzelwert. ## Lizenz Apache 2.0, wie das Basismodell. Alle Rechte am Modell liegen beim Qwen-Team / Alibaba Cloud. Dieses Repo enthält ausschließlich formatkonvertierte Gewichte, kein zusätzliches Training und keine Änderung am Verhalten.