KEN’S CAT LOG
▣

Mein selbst gebautes LLM mit 300 Millionen Parametern quantisiert: Geschwindigkeit und Genauigkeit auf der CPU verglichen

Das im vorigen Artikel erstellte Modell mit 320 Millionen Parametern für kroatisches RAG. Da es auf der CPU laufen soll, habe ich es diesmal quantisiert, um zu testen, wie klein und schnell es werden kann – und wie stark die Genauigkeit darunter leidet.

Vorweg das Fazit.

  • Q6_K ist genau richtig. Ein Fünftel der Größe (254 MB), etwa viermal schnellere Generierung und die gleiche Genauigkeit wie das ursprüngliche Modell
  • Q2_K ist kaputt. Schnell, aber selbst bei Fragen mit Antwort behauptet es plötzlich: „Es gibt keine Antwort.“
  • Überraschenderweise war Q3_K_M genauer als Q4_K_M

Was ich gemacht habe

Das in PyTorch erstellte Modell habe ich in ein Format (GGUF) konvertiert, das sich mit llama.cpp, einem häufig für CPU-Inferenz verwendeten Tool, ausführen lässt, und in acht Genauigkeitsstufen gespeichert.

  1. Das Modell im gleichen Format wie Qwen3 exportiert und in GGUF konvertiert
  2. Da der Tokenizer selbst gebaut ist, verweigerte llama.cpp die Konvertierung mit dem Hinweis auf einen „unbekannten Tokenizer“. Beim Vergleich der Regeln zur Worttrennung stellte sich heraus, dass sie Zeichen für Zeichen mit Qwen2 übereinstimmen, daher habe ich es als Qwen2 registriert und so durchgelassen
  3. Überprüft, ob die Konvertierung korrekt ist. Die Tokenisierung von 160 Texten stimmte vollständig mit der PyTorch-Version überein. Bei 20 Fragen ließ ich jeweils das Wort mit der höchsten Wahrscheinlichkeit erzeugen; auch die Ausgaben waren bei allen 20 Fragen vollständig identisch
  4. Mit llama-quantize F16 bis Q2_K erstellt

Daher enthält F32 in der Tabelle denselben Inhalt wie die PyTorch-Version. Das ist die Referenz für den Vergleich.

Messmethode

  • Die CPU ist ein i7-13700K. Es wurde keine GPU verwendet
  • Einstellungen von llama.cpp: 8 Threads für die Generierung, 16 Threads zum Einlesen der Texte (das war in meinen Tests am schnellsten)
  • Für 100 Evaluierungsfragen × 4 Arten von Kontexten ließ ich jedes Modell jeweils 400-mal antworten
    • Absatz: Ein Absatz von etwa 100 Tokens, der die Antwort enthält
    • Langer Text: Ein Text mit etwa 3.000 Tokens, der die Antwort enthält (auch irrelevante Artikel sind beigemischt)
    • Keine Antwort (Absatz/langer Text): Texte, die keine Antwort enthalten. Eine Verweigerung zählt als richtig
  • Im vorigen Artikel wurden 300 Fragen gemessen, daher weichen die Zahlen leicht ab

Ergebnisse: Genauigkeit

Format Größe Absatz korrekt Langer Text korrekt Langer Text verweigert Erfunden (keine Antwort: Absatz/langer Text) Genauigkeit nach Abgleich
F32 (Referenz) 1,233MB 77% 53% 30% 1% / 9% 80.9%
F16 617MB 77% 53% 30% 1% / 9% 80.9%
Q8_0 329MB 76% 53% 30% 1% / 8% 81.9%
Q6_K 254MB 79% 54% 30% 2% / 9% 81.3%
Q5_K_M 223MB 72% 45% 41% 1% / 6% 84.4%
Q4_K_M 193MB 68% 37% 46% 1% / 7% 75.9%
Q3_K_M 160MB 72% 48% 38% 1% / 8% 80.4%
Q2_K 129MB 39% 11% 78% 4% / 7% 72.2%

„Genauigkeit nach Abgleich“ bezeichnet den Anteil korrekter Antworten, nachdem ein Programm überprüft hat, ob die Zitate des Modells tatsächlich im Text vorkommen; wenn nicht, wurde die Antwort durch „keine Antwort“ ersetzt.

  • F16, Q8_0 und Q6_K sind fast identisch mit der Referenz. Es gibt nur Unterschiede von ein bis zwei Fragen
  • Ab Q5_K_M nehmen nicht so sehr die Fehler zu, sondern die Verweigerungen. Es wirkt, als würde das Modell bei sinkender Sicherheit einfach ablehnen – genau die Eigenschaft, die ich ihm im vorigen Artikel beigebracht habe. Erfundene Antworten nehmen nicht zu
  • Q4_K_M ist schlechter als Q3_K_M. Bei langen Texten 37% gegenüber 48%. Bei 100 Fragen gibt es zwar etwas Schwankung, aber 11 Prozentpunkte sind viel. Den Grund habe ich noch nicht untersucht
  • Q2_K verweigert bei 78% der langen Texte. Das ist bereits ein anderes Modell

Ergebnisse: Geschwindigkeit

Format Generierungsgeschwindigkeit Durchschnittszeit pro Durchlauf
PyTorch fp32 (Referenz) Absatz 14.6 · langer Text 6.7 tok/s —
F32 18.7 tok/s 2.76 Sekunden
F16 35.9 tok/s 2.08 Sekunden
Q8_0 59.0 tok/s 1.93 Sekunden
Q6_K 73.1 tok/s 1.33 Sekunden
Q5_K_M 81.2 tok/s 1.26 Sekunden
Q4_K_M 89.1 tok/s 1.10 Sekunden
Q3_K_M 103.4 tok/s 1.12 Sekunden
Q2_K 119.9 tok/s 1.07 Sekunden

Die Zahlen für llama.cpp sind Durchschnitte aus 400 Durchläufen (Absätze, lange Texte und Fälle ohne Antwort zusammengefasst). Die PyTorch-Zahlen stammen aus den Messungen im vorigen Artikel.

  • Die Generierungsgeschwindigkeit ist nahezu umgekehrt proportional zur Größe. Auf der CPU bestimmt eher die Geschwindigkeit, mit der Gewichte aus dem Speicher gelesen werden, als die Rechenleistung die Geschwindigkeit – je kleiner, desto schneller
  • Selbst bei gleichem F32 ist llama.cpp schneller als PyTorch
  • Ab Q6_K sinkt die Zeit pro Durchlauf jedoch kaum noch. Bei dieser Länge dominiert die Zeit zum anfänglichen Einlesen des Textes

Video: Dieselbe Frage zu einem langen Text auf der CPU lösen lassen

Ich gab dem Modell einen Text mit etwa 3.200 Tokens und fragte: „Wann wurde Need for Speed Heat veröffentlicht?“ Die richtige Antwort ist der 8. November 2019.

Da die Aufnahme lief und gleichzeitig der Browser den Bildschirm zeichnete, sind die Geschwindigkeiten im Video niedriger als in der Tabelle oben. Es ist zum direkten Vergleichen nebeneinander gedacht.

Format Generierungsgeschwindigkeit im Video Bis zum ersten Zeichen Gesamt Ergebnis
PyTorch fp32 5.5 tok/s 4.8 Sekunden 18.5 Sekunden korrekt
F32 14.2 tok/s 2.8 Sekunden 8.1 Sekunden korrekt
F16 25.9 tok/s 3.5 Sekunden 6.4 Sekunden korrekt
Q8_0 38.2 tok/s 4.0 Sekunden 5.9 Sekunden korrekt
Q6_K 41.6 tok/s 2.6 Sekunden 4.4 Sekunden korrekt
Q5_K_M 46.3 tok/s 2.6 Sekunden 4.3 Sekunden korrekt
Q4_K_M 47.2 tok/s 2.3 Sekunden 4.7 Sekunden korrekt (Antwort länger)
Q3_K_M 56.7 tok/s 2.5 Sekunden 4.5 Sekunden korrekt (Antwort länger)
Q2_K 61.3 tok/s 2.7 Sekunden 2.8 Sekunden verweigert

Von F32 bis Q5_K_M sind sowohl Antwort als auch Zitat Zeichen für Zeichen identisch mit der PyTorch-Version. Bei Q4_K_M und Q3_K_M wurden die Antwortsätze länger, aber die Zitate waren korrekt. Q2_K verweigerte mit „Der Text enthält keine Antwort“, obwohl eine Antwort vorhanden war.

PyTorch fp32 (das ursprüngliche Modell)

PyTorch fp32 auf der CPU bei der Beantwortung einer Frage mit langem Kontext再生

llama.cpp F32 (1,233MB)

llama.cpp F32 auf der CPU bei der Beantwortung einer Frage mit langem Kontext再生

llama.cpp F16 (617MB)

llama.cpp F16 auf der CPU bei der Beantwortung einer Frage mit langem Kontext再生

llama.cpp Q8_0 (329MB)

llama.cpp Q8_0 auf der CPU bei der Beantwortung einer Frage mit langem Kontext再生

llama.cpp Q6_K (254MB)

llama.cpp Q6_K auf der CPU bei der Beantwortung einer Frage mit langem Kontext再生

llama.cpp Q5_K_M (223MB)

llama.cpp Q5_K_M auf der CPU bei der Beantwortung einer Frage mit langem Kontext再生

llama.cpp Q4_K_M (193MB)

llama.cpp Q4_K_M auf der CPU bei der Beantwortung einer Frage mit langem Kontext再生

llama.cpp Q3_K_M (160MB)

llama.cpp Q3_K_M auf der CPU bei der Beantwortung einer Frage mit langem Kontext再生

llama.cpp Q2_K (129MB)

llama.cpp Q2_K auf der CPU bei der Beantwortung einer Frage mit langem Kontext再生

Fazit

  • Wenn dieses Modell auf der CPU laufen soll, dann Q6_K. Die Genauigkeit bleibt unverändert, die Größe sinkt auf ein Fünftel und die Generierung ist etwa fünfmal so schnell wie mit PyTorch. Es gibt keinen Grund zu zögern
  • Für zusätzliche Sicherheit Q8_0. Fast identisch mit dem Original bei 329 MB. Das ist immer noch klein genug
  • Kleine Modelle mit 300 Millionen Parametern sind anfällig für Quantisierung. Bei großen Modellen heißt es, dass selbst Q4 kaum Einbußen verursacht, hier war der Verlust ab Q5 jedoch deutlich sichtbar. Das Modell hat von Anfang an wenig Spielraum, daher wirkt sich jede Kürzung aus
  • Dass es eher verweigert als halluziniert, ist erfreulich. Für RAG ist eine Verweigerung sicherer als mehr erfundene Antworten. Der Effekt des Verstärkungslernens, mit dem ich dem Modell im vorigen Artikel das Verweigern beigebracht habe, zeigt sich auch hier
  • Bei kleinen Modellen schrumpfen die Geschwindigkeitsunterschiede. Ab Q6_K bleibt trotz schnellerer Generierung die Zeit zum Einlesen der Texte bestehen, weshalb sich das subjektiv kaum verändert. Auch im Video liegen Q6_K bis Q3_K_M alle bei etwa vier Sekunden
  • Warum Q4_K_M gegen Q3_K_M verloren hat, bleibt unklar. Ich möchte das mit mehr Fragen sowie einer Untersuchung, welche Schichten wie quantisiert werden, weiter analysieren