<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Inferencja — Inferownia</title><link>https://inferownia.pl/tags/inferencja/</link><description>Inferownia — polski hub wiedzy o lokalnym AI: uruchamianie modeli językowych na własnym sprzęcie, kwantyzacja, inferencja, małe modele, fine-tuning i RAG.</description><language>pl-PL</language><copyright>&#169; 2026 Inferownia</copyright><lastBuildDate>Thu, 09 Jul 2026 00:00:00 +0200</lastBuildDate><atom:link href="https://inferownia.pl/tags/inferencja/index.xml" rel="self" type="application/rss+xml"/><item><title>Mixture of Experts: czemu aktywne parametry to nie wszystkie</title><link>https://inferownia.pl/naukowy/mixture-of-experts/</link><pubDate>Thu, 09 Jul 2026 00:00:00 +0200</pubDate><author>Albert Q. Jiang</author><author>Alexandre Sablayrolles</author><author>Antoine Roux</author><author>Arthur Mensch</author><author>Blanche Savary</author><author>Chris Bamford</author><author>Devendra Singh Chaplot</author><author>Diego de las Casas</author><author>Emma Bou Hanna</author><author>Florian Bressand</author><author>Gianna Lengyel</author><author>Guillaume Bour</author><author>Guillaume Lample</author><author>Lélio Renard Lavaud</author><author>Lucile Saulnier</author><author>Marie-Anne Lachaux</author><author>Pierre Stock</author><author>Sandeep Subramanian</author><author>Sophia Yang</author><author>Szymon Antoniak</author><author>Teven Le Scao</author><author>Théophile Gervet</author><author>Thibaut Lavril</author><author>Thomas Wang</author><author>Timothée Lacroix</author><author>William El Sayed</author><category>inferencja</category><guid>https://inferownia.pl/naukowy/mixture-of-experts/</guid><description>Mixtral 8x7B na token rusza tylko ~13 z 47 miliardów wag, ale do pamięci musisz wcisnąć wszystkie. Rozbieramy, czemu MoE oszczędza obliczenia, a nie ani grama VRAM-u.</description><content:encoded><![CDATA[<p>Osiem razy siedem to pięćdziesiąt sześć. Tyle że model nazywa się „8x7B&quot;, a waży czterdzieści
siedem miliardów parametrów — nie pięćdziesiąt sześć. I zanim zdążysz się z tym pogodzić, ktoś
dorzuca, że aktywnych jest tylko trzynaście. Trzy liczby, z których żadna nie klei się
z sąsiednią. Ściągasz GGUF-a, plik ma dwadzieścia kilka giga w Q4, a gość w komentarzach pisze,
że „chodzi jak siódemka&quot;. No to jak? Duży jak czterdziestka, szybki jak trzynastka,
a nazwany jak pięćdziesiątka szóstka?</p>
<p>To nie literówka i nie marketing. To Mixtral 8x7B<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> — i cała ta trójca liczb bierze
się z jednego pomysłu architektonicznego, który nazywa się <strong>Mixture of Experts</strong>. Jak go
raz zrozumiesz, przestaniesz się dziwić, czemu model „na 13B&quot; i tak każe ci kupić kartę pod
model „na 47B&quot;. Rozłóżmy to na części.</p>
<h2 id="trzy-liczby-jeden-trik">Trzy liczby, jeden trik</h2>
<p>Klasyczny model językowy jest <strong>gęsty</strong> (<em>dense</em>): na każdy token przepycha wszystkie swoje
wagi. Wrzucasz słowo, przechodzi przez każdą warstwę, w każdej warstwie dotyka każdego
neuronu. Prosto, uczciwie, drogo — bo im więcej parametrów, tym więcej roboty na literkę.</p>
<p>Mixtral robi inaczej. Bierze architekturę zwykłego Mistrala 7B, ale w każdej warstwie
podmienia jeden blok — ten feedforwardowy (FFN), czyli tę część, która „przetwarza&quot; token
po tym, jak uwaga (<em>attention</em>) już popatrzyła na kontekst. Zamiast jednego takiego bloku
wstawia <strong>osiem równoległych</strong>. To są właśnie „eksperci&quot;.<sup id="fnref1:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> Osiem kopii tej samej
maszynerii, każda wytrenowana ciut inaczej.</p>
<p>I teraz clou: dla danego tokena <strong>nie odpalają się wszyscy</strong>. Przed blokiem eksperckim stoi
mały <strong>router</strong> (sieć bramkująca, <em>gating network</em>), który patrzy na token i wybiera z ósemki
tylko <strong>dwóch</strong> ekspertów — top-2.<sup id="fnref2:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> Ich wyjścia miesza ważoną sumą (wagi bierze
z softmaxu) i jedzie dalej. Sześciu pozostałych w tej warstwie, dla tego tokena, w ogóle
nie rusza. Śpią.</p>
<p>Stąd rozjazd liczb. Parametrów <strong>całkowitych</strong> (<em>total</em>) jest 46,7 miliarda — potocznie
~47B. Ale na przejście jednego tokena <strong>aktywnych</strong> (<em>active</em>) jest tylko 12,9 miliarda,
czyli ~13B.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> Płacisz obliczeniami za trzynastkę, a nosisz na barana czterdziestkę
siódemkę.</p>
<h2 id="skąd-47-a-nie-56">Skąd 47, a nie 56?</h2>
<p>No dobra, ale skoro to „8x7B&quot;, to czemu nie wychodzi 56 miliardów? Bo mnożenie „osiem razy
siedem&quot; jest po prostu błędne. Powielona jest <strong>tylko</strong> ta część FFN. Cała reszta — warstwy
uwagi, embeddingi, normalizacje — siedzi w modelu <strong>raz</strong> i jest współdzielona przez
wszystkich ekspertów i wszystkie tokeny.<sup id="fnref1:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> Nie mnożysz jej przez osiem ani w sumie,
ani w liczbie aktywnej. Dlatego total ląduje na 46,7B (a nie 56B), a active na 12,9B
(a nie na jednej ósmej z 56).</p>
<p>Zajrzyj zresztą do <code>config.json</code> na karcie modelu — tam to stoi czarno na białym:
<code>num_local_experts: 8</code>, <code>num_experts_per_tok: 2</code>, przy <code>hidden_size: 4096</code>
i <code>num_hidden_layers: 32</code>.<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> Osiem ekspertów na warstwę, dwóch na token, trzydzieści
dwie warstwy. Mnożenie odpala się w każdej z tych 32 warstw z osobna.</p>
<p>I tu druga rzecz, która ludziom umyka: wybór ekspertów <strong>nie jest stały</strong>. Router decyduje
oddzielnie <strong>dla każdego tokena i dla każdej warstwy</strong>.<sup id="fnref3:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> Słowo „kot&quot; w warstwie 3
może trafić do ekspertów 1 i 5, a to samo „kot&quot; w warstwie 4 — do 2 i 7. A następne słowo
w tym samym zdaniu pojedzie zupełnie inną trasą. To nie jest tak, że model „wybiera dwóch
specjalistów na cały prompt&quot; i tyle. To osiem tysięcy mikro-decyzji na akapit, każda robiona
w locie.</p>
<h2 id="to-czemu-nie-zajmuje-tyle-co-trzynastka">To czemu nie zajmuje tyle co trzynastka?</h2>
<p>I tu dochodzimy do pułapki, na którą łapie się pół internetu. Skoro na token aktywne jest
tylko ~13B, to model „zajmuje tyle co trzynastka&quot;, nie? Odpalę na karcie pod 13B i będzie
grało?</p>
<p>Nie będzie. I to jest najważniejsze zdanie w całym tym wpisie.</p>
<p>Router wybiera ekspertów <strong>dopiero w trakcie</strong> przejścia przez daną warstwę, osobno dla
każdego tokena. Znaczy: <strong>z góry nie wiesz, którzy eksperci będą potrzebni</strong>. Następne słowo
może chcieć dowolnej pary z ósemki, w dowolnej z 32 warstw. Więc żeby model w ogóle mógł
ruszyć, <strong>wszystkie 47 miliardów wag musi siedzieć w pamięci naraz</strong> — cała ósemka
ekspertów w każdej warstwie, gotowa do wezwania.<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> Oszczędność z MoE dotyczy
<strong>obliczeń</strong> (compute, latencji), nie <strong>pamięci</strong>. Mistral mówi to wprost: model „przetwarza
wejście i generuje wyjście z taką samą szybkością i kosztem jak model 12,9B&quot;.<sup id="fnref2:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>
Szybkością i kosztem obliczeń — nie apetytem na VRAM.</p>
<div class="callout">
  <span class="callout__label">Dlaczego to ważne dla lokalnego LLM</span>
<p>Zapamiętaj to jedno rozróżnienie, a przestaniesz się przejeżdżać na MoE: <strong>aktywne parametry
liczą się do prędkości, całkowite — do pamięci</strong>. Mixtral 8x7B liczy jak trzynastka, ale
ważyć musisz go jak czterdziestkę siódemkę. Do samej inferencji w Q4 potrzebujesz około
<a href="https://huggingface.co/TheBloke/Mixtral-8x7B-v0.1-GGUF">25–30 GB na wagi</a>, zanim jeszcze dołożysz KV-cache i kontekst. Karta pod 13B tego nie
udźwignie — a naiwne „mało aktywnych = mało VRAM-u&quot; to najczęstsza wpadka przy planowaniu
sprzętu. Ile realnie wchodzi na wagi, liczyliśmy <a href="/poradniki/ile-vram-na-model/">tutaj</a>.</p>
</div>
<p>Zbierzmy to w tabelce, bo dwie kolumny mówią więcej niż akapit:</p>
<table>
	<thead>
			<tr>
					<th>Co porównujemy</th>
					<th>Model gęsty (dense)</th>
					<th>Mixtral 8x7B (MoE)</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Parametry całkowite</td>
					<td>tyle, ile aktywnych</td>
					<td>46,7B (~47B)</td>
			</tr>
			<tr>
					<td>Parametry aktywne na token</td>
					<td>wszystkie</td>
					<td>12,9B (~13B)</td>
			</tr>
			<tr>
					<td>Ekspertów w warstwie FFN</td>
					<td>1</td>
					<td>8</td>
			</tr>
			<tr>
					<td>Ekspertów na token</td>
					<td>1</td>
					<td>2 (top-2)</td>
			</tr>
			<tr>
					<td>Co decyduje o prędkości</td>
					<td>parametry (= wszystkie)</td>
					<td>parametry <strong>aktywne</strong></td>
			</tr>
			<tr>
					<td>Co decyduje o VRAM</td>
					<td>parametry (= wszystkie)</td>
					<td>parametry <strong>całkowite</strong></td>
			</tr>
	</tbody>
</table>
<p>Po co ten cały cyrk, skoro pamięć i tak trzeba mieć całą? Bo za cenę pamięci klasy 47B
dostajesz jakość, która w benchmarkach <strong>dorównuje albo bije Llamę 2 70B i GPT-3.5</strong> — przy
koszcie obliczeniowym trzynastki.<sup id="fnref4:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> To jest ten deal: płacisz VRAM-em jak za dużego,
liczysz jak za małego, a mądrość dostajesz bliżej tego dużego. Dla kogoś, kto ma dość RAM-u,
ale mało cierpliwości do tokenów na sekundę — świetny interes.</p>
<h2 id="zrób-to-sam-a-przynajmniej-zaplanuj">Zrób to sam (a przynajmniej zaplanuj)</h2>
<p>MoE to <strong>architektura</strong>, nie kompresja. Łatwo pomylić to z kwantyzacją czy pruningiem, ale
to inna bajka: MoE od początku trenuje się jako rzadko aktywowaną sieć, a kwantyzacja ściska
wagi już po treningu — i spokojnie łączysz jedno z drugim (kwantyzowany GGUF Mixtrala to
codzienność). Jak działają same nazwy kwantyzacji, rozgryzaliśmy
<a href="/poradniki/nazwy-kwantyzacji-gguf/">tu</a> — ta sama logika oszczędzania pamięci działa i na
kwantyzowanym Mixtralu, tyle że na innym piętrze niż samo MoE.</p>
<p>W praktyce <code>llama.cpp</code> daje ci pod to dedykowaną flagę. Skoro attention i embeddingi są
„zawsze aktywne&quot;, a bloki eksperckie odpalają się wybiórczo, to najwięcej ugrasz, trzymając
te zawsze-aktywne komponenty na GPU, a same wagi ekspertów spychając do RAM-u. Od tego jest
<code>--n-cpu-moe N</code> — „trzymaj wagi MoE z pierwszych N warstw na CPU&quot; — albo <code>--cpu-moe</code>, które
przenosi wszystkie.<sup id="fnref1:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> Kroisz VRAM kosztem szybkości — właśnie dlatego, że pamięć musi
pomieścić <strong>całość</strong>, a nie tylko aktywną ścieżkę.</p>
<p>Najprościej — cały model na kartę, jak wejdzie:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">llama-cli -m mixtral-8x7b-instruct-v0.1.Q4_K_M.gguf -ngl all -c <span class="m">8192</span> -fa on -cnv
</span></span></code></pre></div><p>Nie wchodzi (a przy 47B na przeciętnej karcie do gier nie wejdzie)? Zostaw wszystkie warstwy
na GPU, ale wypchnij eksperckie FFN-y z części z nich do RAM-u:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">llama-cli -m mixtral-8x7b-instruct-v0.1.Q4_K_M.gguf <span class="se">\
</span></span></span><span class="line"><span class="cl">  -ngl all --n-cpu-moe <span class="m">12</span> -c <span class="m">8192</span> -fa on -ctk q8_0 -ctv q8_0 -cnv
</span></span></code></pre></div><p>Które flagi co robią i w jakiej kolejności się nimi ratować przy „out of memory&quot;,
rozpisaliśmy w <a href="/poradniki/flagi-llama-cpp/">poradniku o flagach llama.cpp</a>. Sam Mixtral
8x7B (base i Instruct) jest na licencji Apache 2.0<sup id="fnref5:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> — ściągasz, odpalasz, kombinujesz
bez proszenia nikogo o zgodę.</p>
<p>Jedno ostrzeżenie na koniec, żebyś nie wpadł w drugą pułapkę. To „liczy jak 13B&quot;
obowiązuje, gdy generujesz jeden strumień token po tokenie. Gdy walisz <strong>batchem</strong> — wiele
sekwencji naraz — różne tokeny w paczce wybierają różnych ekspertów, więc w danej warstwie
i tak zwykle rozgrzewa się większość albo cała ósemka jednocześnie. Teoretyczne
przyspieszenie topnieje. MoE lubi pojedynczego użytkownika przy biurku bardziej niż
zatłoczony serwer.</p>
<p>Wyobraź sobie warsztat z ośmioma fachowcami przy każdym stanowisku. Do każdej śrubki brygadzista
woła tylko dwóch — akurat tych, co się na niej znają. Reszta stoi i pije kawę. Robota idzie
szybko, jakbyś płacił za dwóch. Ale wypłatę i tak wystawiasz całej ósemce, bo nigdy nie wiesz,
która śrubka przyjdzie następna i kogo do niej trzeba będzie zawołać. I to jest cały Mixtral:
tempo dwójki, lista płac ósemki. Twoja karta nie płaci za tych, co pracują — płaci za tych,
co czekają w gotowości.</p>
<h2 id="przypisy--źródła">Przypisy · źródła</h2>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Jiang i in., <em>Mixtral of Experts</em>, arXiv:2401.04088 — abstrakt i sekcja architektury: 8 ekspertów na warstwę FFN, routing top-2, wynik dorównujący/bijący Llamę 2 70B i GPT-3.5, licencja Apache 2.0. <a href="https://arxiv.org/abs/2401.04088">arxiv.org/abs/2401.04088</a>.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref2:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref3:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref4:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref5:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Mistral AI, oficjalne ogłoszenie <em>Mixtral of Experts</em> — dokładne liczby 46,7B total / 12,9B active, mechanizm routera (8 grup parametrów, wybór 2, addytywne łączenie wyjść), stwierdzenie, że koszt i szybkość odpowiadają modelowi 12,9B. <a href="https://mistral.ai/news/mixtral-of-experts">mistral.ai/news/mixtral-of-experts</a>.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref2:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p><code>config.json</code> modelu <code>mistralai/Mixtral-8x7B-v0.1</code> — <code>num_local_experts: 8</code>, <code>num_experts_per_tok: 2</code>, <code>hidden_size: 4096</code>, <code>num_hidden_layers: 32</code>. <a href="https://huggingface.co/mistralai/Mixtral-8x7B-v0.1/raw/main/config.json">huggingface.co/mistralai/Mixtral-8x7B-v0.1/raw/main/config.json</a>.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>llama.cpp, definicje flag <code>-cmoe</code>/<code>--cpu-moe</code> („keep all Mixture of Experts (MoE) weights in the CPU&quot;) oraz <code>-ncmoe</code>/<code>--n-cpu-moe N</code> („keep the Mixture of Experts (MoE) weights of the first N layers in the CPU&quot;) — sam mechanizm istnieje właśnie dlatego, że wag ekspertów nie da się usunąć z pamięci; można je jedynie przenieść z VRAM-u do RAM-u. <a href="https://github.com/ggml-org/llama.cpp/blob/master/common/arg.cpp">common/arg.cpp</a>.&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>Speculative decoding: jak mały model przyspiesza duży</title><link>https://inferownia.pl/poradniki/speculative-decoding/</link><pubDate>Wed, 08 Jul 2026 00:00:00 +0200</pubDate><category>inferencja</category><guid>https://inferownia.pl/poradniki/speculative-decoding/</guid><description>Duży model musi robić pełny przebieg dla każdego pojedynczego tokena — stąd ten mielący rytm generacji. Speculative decoding łamie ten rytm: mały model zgaduje kilka tokenów naprzód, duży sprawdza je hurtem. I najlepsze: bez utraty jakości.</description><content:encoded><![CDATA[<p>Token. Chwila. Token. Chwila. Lokalny model wypluwa tekst jak stara drukarka igłowa, literka
po literce, a ty gapisz się w kursor, jakby to on dyktował tempo twojego życia. I prawie każdy jęk o
prędkości sprowadza się do jednego: „mam ładne 4 tokeny na sekundę i cierpliwość
mnicha&quot;. I człowiek zaczyna kombinować — może da się to jakoś oszukać?</p>
<p>Bo pod spodem dzieje się rzecz z pozoru absurdalnie rozrzutna. Żeby dołożyć jeden token,
model o siedmiu czy siedemdziesięciu miliardach parametrów musi przepchnąć całe swoje
wnętrze — pełny przebieg naprzód — po czym dokłada jedną literkę i zaczyna od nowa. Cały
ten kolos rozgrzewa się do jednego słowa, potem znowu, i znowu. Trochę jak odpalać silnik
ciężarówki za każdym razem, gdy chcesz przejechać metr.</p>
<h2 id="skąd-w-ogóle-ten-pomysł">Skąd w ogóle ten pomysł</h2>
<p>W 2022 roku trójka badaczy z Google&rsquo;a — Leviathan, Kalman i Matias — zadała pytanie, które
brzmi jak sabotaż: a co, jeśli większość kolejnych tokenów jest tak oczywista, że nie
trzeba do nich całego dużego modelu? Że „w dniu dzisiejszym&quot; po słowie „w&quot; i „dniu&quot; da się
zgadnąć czymś dużo tańszym? Opisali to w pracy <em>Fast Inference from Transformers via
Speculative Decoding</em> i pokazali coś, co brzmi zbyt pięknie: 2–3 razy szybsza generacja
na modelach klasy T5-XXL, bez douczania czegokolwiek i <strong>bez zmiany rozkładu wyjściowego</strong>
— czyli model gada dokładnie to samo, co gadałby normalnie, tylko szybciej.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> Papier
poszedł na ICML 2023 jako prezentacja ustna, więc nie była to notka na marginesie.</p>
<p>Dwa miesiące później niezależnie tę samą ideę opisała ekipa z DeepMind — Chen, Borgeaud,
Irving, Lespiau, Sifre i Jumper — pod nazwą <em>speculative sampling</em>, z lekko innym schematem
matematycznym i przyspieszeniem 2–2,5x na Chinchilli (70B) w środowisku rozproszonym.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>
Dwa zespoły, ten sam pomysł, ta sama gwarancja: przyspieszamy, jakości nie ruszamy. Kiedy
dwie niezależne grupy trafiają w to samo, to zwykle znak, że pod spodem siedzi coś prawdziwego.</p>
<h2 id="jak-to-działa-krok-po-kroku">Jak to działa krok po kroku</h2>
<p>Dobra, ale jak to działa naprawdę? Bierzesz dwa modele. Jeden duży — ten, którego odpowiedzi
chcesz — nazwijmy go <strong>docelowym</strong> (<em>target</em>). I jeden mały, szybki — <strong>szkicownik</strong>
(<em>draft</em>) — najlepiej tej samej rodziny, żeby „myślał&quot; podobnie. I teraz taniec w trzech krokach:</p>
<ol>
<li><strong>Szkicownik zgaduje.</strong> Mały model generuje autoregresywnie kilka kolejnych tokenów —
powiedzmy K sztuk. Robi to szybko, bo jest mały. To są propozycje, brudnopis.</li>
<li><strong>Duży weryfikuje — jednym przebiegiem.</strong> I tu jest cały myk: duży model dostaje te K
proponowanych tokenów naraz i sprawdza je w <strong>jednym</strong> przebiegu naprzód, wsadowo. Nie
K osobnych przejazdów ciężarówką — jeden.</li>
<li><strong>Akceptuj albo popraw.</strong> Tam, gdzie duży model zgadza się ze szkicownikiem, tokeny
wpadają za darmo. Przy pierwszym niezgodnym — odrzucamy resztę, duży dokłada swój token
i cała zabawa rusza od nowa.</li>
</ol>
<p>Jeśli szkicownik trafił w pięć tokenów z rzędu, to policzyłeś je jednym przebiegiem dużego
modelu zamiast pięciu. Jeśli spudłował od razu — koszt jest praktycznie taki, jak przy
zwykłym dekodowaniu. Downside prawie żaden, upside spory. Ta sama logika weryfikacji „licz
wsad, nie sekwencję&quot; siedzi zresztą u podstaw tego, czemu KV-cache w ogóle się opłaca —
rozgryzaliśmy to <a href="/poradniki/kv-cache-vram/">tutaj</a>.</p>
<h2 id="czemu-to-nie-psuje-jakości">Czemu to nie psuje jakości</h2>
<p>W tym miejscu każdy rozsądny człowiek zapala czerwoną lampkę. „Mały model zgaduje za dużego?
To brzmi jak destylacja na skróty, na pewno wychodzi gorsza jakość.&quot; Otóż nie — i to jest
najładniejsza część całej sztuczki.</p>
<p>To <strong>nie</strong> jest kwantyzacja ani destylacja. Wag dużego modelu nikt nie dotyka, jego
dokładność zostaje nietknięta. Sekret siedzi w tym, jak akceptowane są tokeny — używa się
zmodyfikowanego <strong>rejection sampling</strong> (próbkowania odrzucającego). Token zaproponowany
przez szkicownik przyjmujemy z prawdopodobieństwem <code>min(1, p_target(x) / p_draft(x))</code> —
czyli: jeśli duży model uważa dany token za co najmniej tak samo prawdopodobny jak
szkicownik, bierzemy go w ciemno. Jeśli mniej — akceptujemy proporcjonalnie, a przy
odrzuceniu dobieramy nowy token z <strong>rozkładu resztkowego</strong> (residual), tak dobranego, żeby
wszystko się matematycznie spięło.<sup id="fnref1:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p>Efekt? Łączny rozkład tokenów na wyjściu jest <strong>identyczny</strong> z tym, co dałoby zwykłe,
niespekulacyjne próbkowanie samego dużego modelu. Nie „prawie taki sam&quot;, nie „w granicach
błędu&quot; — dokładnie taki sam, z dowodem.</p>
<div class="callout">
  <span class="callout__label">Dlaczego to ważne dla lokalnego LLM</span>
<p>To jest ten rzadki obiad, za który nikt nie płaci. Speculative decoding nie jest
kompromisem „szybciej za cenę jakości&quot;, jak kwantyzacja czy przycinanie kontekstu. Rozkład
wyjściowy zostaje matematycznie nienaruszony — dostajesz te same odpowiedzi co z gołego
dużego modelu, tylko prędzej. Jedyne, czym płacisz, to trochę VRAM-u na drugi, mały model
w pamięci i odrobina narzutu na weryfikację.</p>
</div>
<h2 id="skąd-się-bierze-przyspieszenie">Skąd się bierze przyspieszenie</h2>
<p>Skoro nic nie tniemy, to gdzie ten darmowy obiad się chowa? W wąskim gardle, o którym
lokalny LLM-owiec i tak wie w kościach: inferencja jednego tokena naraz jest
<strong>ograniczona pamięcią</strong> (bandwidth-bound), nie liczeniem. Karta większość czasu nie liczy,
tylko czeka, aż wagi modelu przemaszerują z VRAM-u do rdzeni. Przepchnięcie przez ten sam
kanał jednego tokena a pięciu naraz kosztuje prawie tyle samo — bo i tak głównie czekasz na
pamięć, a nie na arytmetykę.</p>
<p>Stąd cały zysk: policzenie K+1 tokenów w jednym wsadowym przebiegu jest znacznie tańsze niż
K+1 osobnych, sekwencyjnych kroków. Im więcej propozycji szkicownika duży model akceptuje,
tym mocniej wygrywasz.<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> I tu ważne zastrzeżenie, bo to nie jest magiczne pokrętło
„więcej = szybciej&quot;: przyspieszenie <strong>nie</strong> rośnie liniowo z liczbą draftowanych tokenów.
Wszystko wisi na <strong>współczynniku akceptacji</strong> — jak często szkicownik trafia. Każąc mu
zgadywać dwadzieścia tokenów naprzód przy kiepskiej trafności, generujesz górę brudnopisu,
który duży model i tak wyrzuci do kosza. Bywa, że wtedy jest wolniej niż bez całej zabawy.</p>
<h2 id="zrób-to-sam-w-llamacpp">Zrób to sam w llama.cpp</h2>
<p>Teoria teorią, ale <code>llama.cpp</code> (repo <code>ggml-org/llama.cpp</code>) obsługuje to od dawna — i w
<code>llama-server</code>, i w osobnym narzędziu <code>llama-speculative-simple</code>. Cały wjazd na klasyczny
wariant to jedna flaga: <code>--spec-draft-model</code> (krócej <code>-md</code>), która wskazuje plik GGUF
modelu szkicowego.<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> Bierzesz duży model, dokładasz mały z tej samej rodziny i lecisz:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">llama-server -m qwen3-14b-q4_k_m.gguf <span class="se">\
</span></span></span><span class="line"><span class="cl">  -md qwen3-0.6b-q4_k_m.gguf <span class="se">\
</span></span></span><span class="line"><span class="cl">  --spec-draft-n-max <span class="m">4</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  -ngl all -c <span class="m">8192</span> -fa on
</span></span></code></pre></div><p><code>--spec-draft-n-max N</code> (domyślnie 3) ustawia maksymalną liczbę tokenów szkicowanych na
krok, a <code>--spec-draft-n-min N</code> (domyślnie 0) dolną granicę.<sup id="fnref1:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> Progi akceptacji stroisz
przez <code>--spec-draft-p-min</code> (domyślnie 0.00) i <code>--spec-draft-p-split</code> (domyślnie 0.10). Do
reszty flag <code>llama-server</code> — <code>-ngl</code>, <code>-c</code>, <code>-fa</code> i spółka — wracaliśmy osobno
<a href="/poradniki/flagi-llama-cpp/">tutaj</a>.</p>
<p>Strategię wybiera <code>--spec-type</code>, i tu robi się ciekawie, bo klasyczny draft model to
dopiero początek:</p>
<table>
	<thead>
			<tr>
					<th><code>--spec-type</code></th>
					<th>Co robi</th>
					<th>Potrzebny osobny model draft?</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>draft-simple</code></td>
					<td>zwykły mały model szkicowy</td>
					<td>tak (GGUF przez <code>-md</code>)</td>
			</tr>
			<tr>
					<td><code>draft-eagle3</code></td>
					<td>jedna warstwa czytająca stany ukryte dużego modelu</td>
					<td>tak (lekka głowica EAGLE-3)</td>
			</tr>
			<tr>
					<td><code>draft-dflash</code></td>
					<td>generuje cały blok szkicu jednym przebiegiem (block diffusion)</td>
					<td>tak</td>
			</tr>
			<tr>
					<td><code>ngram-simple</code></td>
					<td>zgaduje z historii wygenerowanego tekstu</td>
					<td><strong>nie</strong></td>
			</tr>
			<tr>
					<td><code>ngram-cache</code></td>
					<td>wariant n-gramowy z podręczną pamięcią</td>
					<td><strong>nie</strong></td>
			</tr>
	</tbody>
</table>
<p>Warianty <strong>n-gramowe</strong> to osobny gatunek: nie potrzebują żadnego drugiego modelu, tylko
zgadują kolejne tokeny na podstawie tego, co już się w tekście pojawiło (świetne, gdy model
dużo cytuje sam siebie albo klepie strukturę). Mechanizm weryfikacji jest ten sam — po
prostu brudnopis powstaje inaczej.<sup id="fnref1:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> A na drugim biegunie <strong>EAGLE-3</strong> (<code>draft-eagle3</code>)
to lekka, jednotransformerowa głowica, która podgląda stany ukryte dużego modelu i przez to
trafia częściej niż samodzielny mały model tej samej wagi. Uruchomienie z dokumentacji
wygląda tak:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">llama-server -m Qwen3-4B.gguf <span class="se">\
</span></span></span><span class="line"><span class="cl">  -md Qwen3-4B-eagle3.gguf <span class="se">\
</span></span></span><span class="line"><span class="cl">  --spec-type draft-eagle3
</span></span></code></pre></div><p>Jedna pułapka na koniec, żebyś nie stracił wieczora na kopiowanie starych poradników:
dawne flagi <code>--draft</code>, <code>--draft-max</code>, <code>--draft-min</code> <strong>zostały usunięte</strong>. Aktualna składnia
to <code>--spec-draft-n-max</code> i pochodne <code>--spec-*</code>.<sup id="fnref2:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> Jak przepiszesz przykład sprzed roku
i <code>llama-server</code> prycha, że nie zna flagi — to nie ty zwariowałeś, to repo poszło naprzód.</p>
<p>Najładniejsze w tym wszystkim jest to, że speculative decoding nie jest żadnym oszustwem na
jakości — jest raczej jak dobry sekretarz. Ktoś szybki i tani pisze pod dyktando pierwszy
szkic, przewidując, dokąd zmierza zdanie, a szef tylko przelatuje wzrokiem i skreśla to, co
się nie zgadza. Reszta zostaje, jak stała. Szef odpowiada za każde słowo tak samo jak
wtedy, gdy pisał sam — tyle że nie musi już maczać pióra w kałamarzu przy każdej literce.</p>
<p>I to jest ten moment, w którym twoja drukarka igłowa spod biurka nagle łapie zryw — te same
słowa, ten sam rozkład, ta sama karta. Tylko kursor przestaje w końcu dyktować ci tempo.</p>
<h2 id="przypisy--źródła">Przypisy · źródła</h2>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Leviathan, Kalman, Matias, <em>Fast Inference from Transformers via Speculative Decoding</em>, ICML 2023 (oral) — definicja metody, gwarancja identycznego rozkładu wyjściowego przez zmodyfikowany rejection sampling, przyspieszenie 2–3x na T5-XXL. <a href="https://arxiv.org/abs/2211.17192">arxiv.org/abs/2211.17192</a>.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Chen, Borgeaud, Irving, Lespiau, Sifre, Jumper (DeepMind), <em>Accelerating Large Language Model Decoding with Speculative Sampling</em> — niezależne sformułowanie, przyspieszenie 2–2,5x na Chinchilli 70B w środowisku rozproszonym. <a href="https://arxiv.org/abs/2302.01318">arxiv.org/abs/2302.01318</a>.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>llama.cpp, mechanizm weryfikacji wsadowej i lista strategii (<code>draft-eagle3</code>, <code>draft-dflash</code>, warianty <code>ngram-*</code>), przykład uruchomienia z EAGLE-3, <a href="https://github.com/ggml-org/llama.cpp/blob/master/docs/speculative.md">docs/speculative.md</a>.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>llama.cpp, dokładne nazwy i domyślne wartości flag <code>--spec-draft-model</code>/<code>-md</code>, <code>--spec-type</code>, <code>--spec-draft-n-max</code>/<code>n-min</code>, <code>--spec-draft-p-min</code>/<code>p-split</code> oraz informacja o usunięciu dawnych <code>--draft</code>/<code>--draft-max</code>/<code>--draft-min</code>, <a href="https://github.com/ggml-org/llama.cpp/blob/master/tools/server/README.md">tools/server/README.md</a>.&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref2:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>YaRN: jak rozciągnąć kontekst, którego model nie widział</title><link>https://inferownia.pl/naukowy/yarn-kontekst/</link><pubDate>Sun, 05 Jul 2026 00:00:00 +0200</pubDate><author>Bowen Peng</author><author>Jeffrey Quesnelle</author><author>Honglu Fan</author><author>Enrico Shippole</author><category>inferencja</category><guid>https://inferownia.pl/naukowy/yarn-kontekst/</guid><description>Model uczony na 4 tysiącach tokenów nagle ogarnia 128 tysięcy. Nie magia, nie retrening od zera — tylko sprytne przekręcenie zegara pozycji w RoPE i kilkaset kroków douczenia.</description><content:encoded><![CDATA[<p>Przewijasz kartę modelu na Hugging Face i w tabelce mruga do ciebie „128k context”.
Sto dwadzieścia osiem tysięcy tokenów — cała książka wleci w jeden prompt. Serce rośnie.
A potem, gdzieś na dole, drobnym druczkiem: model bazowy trenowany na sekwencjach 4096.
No i człowiek głupieje. Jak to? Uczyłeś go patrzeć na cztery tysiące tokenów naraz, a teraz
ma ogarnąć trzydzieści dwa razy więcej? Skąd on niby wie, jak wygląda token numer 100 000,
skoro w życiu takiego nie widział?</p>
<p>To nie jest pytanie z gatunku czepialstwa. Sam pewnie nieraz widziałeś, jak model
z „długim kontekstem” pięknie streszcza pierwsze pięć stron, a od dziesiątej zaczyna
zmyślać, mieszać imiona i gubić wątek — dokładnie w miejscu, w którym teoretycznie
miał błyszczeć. Bo rozciąganie okna kontekstu to nie jest przesunięcie
suwaka. To operacja na sercu tego, jak model w ogóle rozumie „gdzie” jest dany token.
I tu wchodzi YaRN — metoda z pracy grupy z Nous Research, która robi to tanio i, co
ważniejsze, robi to dobrze.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<h2 id="skąd-model-w-ogóle-wie-który-token-jest-który">Skąd model w ogóle wie, który token jest który</h2>
<p>Zacznijmy od tego, że transformer sam z siebie nie ma pojęcia o kolejności. Wrzuć mu
„pies goni kota” i „kota goni pies” — bez informacji o pozycji to dla niego ta sama torba
słów. Trzeba mu jakoś powiedzieć, że ten token jest pierwszy, tamten setny.</p>
<p>Współczesne modele — LLaMA, Mistral, Qwen — robią to przez <strong>RoPE</strong> (<em>rotary position
embeddings</em>, rotacyjne osadzenia pozycyjne) z pracy Su i innych.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> Pomysł jest
elegancki: zamiast doklejać pozycję jako osobny wektor, RoPE <strong>obraca</strong> wektory zapytań
i kluczy o kąt proporcjonalny do pozycji tokena. Każdy wymiar embeddingu kręci się z inną
częstotliwością — jedne szybko, jak wskazówka sekundowa, inne wolno, jak godzinowa. Token
na pozycji 5 jest obrócony trochę, token na pozycji 5000 — dużo. Model uczy się czytać
te kąty i z różnicy obrotów między dwoma tokenami wnioskuje, jak daleko od siebie leżą.</p>
<p>I teraz clou problemu: model widział te wskazówki tykające tylko do pozycji 4096. Dalej
jest terra incognita. Kąty, które w treningu nigdy się nie pojawiły. Puść go na token
50 000, a wskazówka sekundowa zakręci się tyle razy, że model nie ma bladego pojęcia, co
z tym zrobić — to jak pokazać komuś zegar, który obrócił się poza tarczę, w miejsce,
którego nigdy nie oznaczono.</p>
<h2 id="dlaczego-po-prostu-ściśnij-pozycje-psuje-robotę">Dlaczego „po prostu ściśnij pozycje” psuje robotę</h2>
<p>Pierwszy odruch jest oczywisty. Skoro model zna zakres do 4096, a chcemy 64k, to
przeskalujmy pozycje liniowo — token 64 000 udawaj, że jesteś tokenem 4000. Ściśnij całą
oś czasu tak, żeby zmieściła się w znanym zakresie. To jest <strong>Position Interpolation</strong> (PI,
interpolacja pozycji) z pracy Chena i innych — i na papierze brzmi rozsądnie.</p>
<p>Problem w tym, że PI traktuje <strong>wszystkie</strong> częstotliwości RoPE jednakowo — zwalnia i
sekundnik, i wskazówkę godzinową o ten sam czynnik.<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> A to katastrofa dla tych
najszybszych wymiarów. To właśnie one odpowiadają za rozróżnianie tokenów leżących tuż
obok siebie — „czy słowo A jest bezpośrednio przed B, czy jest między nimi jeszcze jedno”.
Ściśnij je razem z resztą, a model traci rozdzielczość na najdrobniejszym poziomie.
Efekt? Jakość spada nawet na <strong>krótkim</strong> kontekście, który przecież działał bez zarzutu.
Rozciągnąłeś okno i po drodze rozmyłeś to, co model umiał od początku. Klasyczny handel,
w którym oddajesz więcej, niż dostajesz.</p>
<h2 id="jak-yarn-dzieli-częstotliwości-na-pasma">Jak YaRN dzieli częstotliwości na pasma</h2>
<p>No to jak zrobić to mądrzej? Skoro problem jest w tym, że traktujemy wszystkie
częstotliwości jednakowo — to <strong>przestańmy</strong>. To jest cała intuicja YaRN.</p>
<p>Zanim doszli do wersji finalnej, po drodze była <strong>NTK-aware interpolation</strong> (interpolacja
świadoma NTK) — zamiast liniowo ściskać pozycje, zmienia się podstawę RoPE z <code>b</code> na
<code>b * s^(|D|/(|D|-2))</code>, przez co „nacisk” interpolacji rozkłada się nierówno po
wymiarach.<sup id="fnref1:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> Lepiej, ale wciąż z grubsza jednym pociągnięciem po wszystkim.</p>
<p>YaRN idzie krok dalej z podejściem <strong>NTK-by-parts</strong> (interpolacja pasmowa) — i to jest
serce metody. Dzielisz wymiary częstotliwości na pasma za pomocą funkcji rampy <code>γ(r)</code>:<sup id="fnref2:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup></p>
<ul>
<li><strong>wysokie częstotliwości</strong> (te lokalne, szybkie wskazówki — sąsiad przy sąsiedzie)
zostawiasz w spokoju, bez interpolacji. Ekstrapolujesz. Niech tykają jak tykały.</li>
<li><strong>niskie częstotliwości</strong> (te globalne, wolne — gruby zarys „to jest gdzieś na końcu
dokumentu”) interpolujesz normalnie, bo to one muszą pomieścić nowy, większy zakres.</li>
<li>pasmo <strong>pośrodku</strong> rampa miesza płynnie, żeby nie było skoku na styku.</li>
</ul>
<p>Sedno: nie ruszasz tego, co model umie najlepiej (drobna rozdzielczość lokalna), a
rozciągasz tylko to, co i tak opisuje wielką skalę. Zegar dostaje większą tarczę, ale
sekundnik dalej odmierza sekundy tak samo dokładnie.</p>
<table>
	<thead>
			<tr>
					<th>Metoda</th>
					<th>Co robi z częstotliwościami</th>
					<th>Efekt uboczny</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Position Interpolation (PI)</td>
					<td>ściska wszystkie jednakowo, liniowo</td>
					<td>psuje rozdzielczość lokalną, spada jakość i na krótkim kontekście</td>
			</tr>
			<tr>
					<td>NTK-aware</td>
					<td>zmienia podstawę RoPE, nacisk rozkłada nierówno</td>
					<td>lepiej, ale wciąż jeden globalny gest</td>
			</tr>
			<tr>
					<td>NTK-by-parts (YaRN)</td>
					<td>dzieli na pasma rampą <code>γ(r)</code>: wysokie ekstrapoluje, niskie interpoluje</td>
					<td>zachowuje lokalną precyzję, rozciąga tylko skalę globalną</td>
			</tr>
	</tbody>
</table>
<h2 id="drugi-bezpiecznik-temperatura-uwagi">Drugi bezpiecznik: temperatura uwagi</h2>
<p>I tu jest smaczek, który łatwo przegapić, bo nie ma nic wspólnego ze skalowaniem
częstotliwości. YaRN dorzuca <strong>attention temperature scaling</strong> — skalowanie temperatury
w softmaxie uwagi.<sup id="fnref3:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> Kiedy rozciągasz kontekst, rozkład uwagi robi się „płaski”,
model rozprasza się na tysiące tokenów zamiast celować. Więc mnożysz logity uwagi przez
stały współczynnik, wyliczony z prostego, empirycznego wzoru:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">sqrt(1/t) = 0.1 * ln(s) + 1
</span></span></code></pre></div><p>gdzie <code>s</code> to współczynnik skalowania kontekstu (dla 4096 → 64k mamy <code>s = 16</code>). Piękne w
tym jest to, że działa jak zwykły mnożnik na logitach — <strong>nie dotyka wag modelu</strong>, nie
wymaga dodatkowego treningu, wchodzi za darmo. Osobne pokrętło, doklejone z boku,
a robi sporo dobrego dla ostrości. Nie pomyl go ze skalowaniem samego RoPE — to dwa różne
mechanizmy, które YaRN łączy w jedno.</p>
<h2 id="tanio-czyli-ile-to-naprawdę-kosztuje">Tanio, czyli ile to naprawdę kosztuje</h2>
<p>Teraz najlepsze — bo tu YaRN naprawdę błyszczy. To <strong>nie</strong> jest trening od zera. To nawet
nie jest porządny fine-tuning na górze danych. To <strong>krótkie douczenie</strong> już gotowego modelu,
żeby oswoił się z nowymi kątami.</p>
<p>Ile krótkie? W pracy LLaMA-2 7B i 13B douczono jakieś <strong>400 kroków</strong> przy <code>s = 16</code>
(4096 → 64k), plus dodatkowe <strong>~200 kroków</strong> przy <code>s = 32</code>, żeby dobić do 128k — razem
około 600 kroków, na danych PG19 w segmentach po 64k, przy batchu 64.<sup id="fnref4:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>
Objętościowo to szacunkowo <strong>~0,1% oryginalnego korpusu</strong> pretreningowego. Jedna dziesiąta
procenta. W liczbach z abstraktu: YaRN potrzebuje około <strong>10x mniej tokenów</strong> douczenia i
<strong>2,5x mniej kroków</strong> treningowych niż wcześniejsze metody.<sup id="fnref1:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p>I jeszcze jedna rzecz, która robi wrażenie: modele <strong>ekstrapolują poza</strong> długość, na której
je douczono. Serię Mistral 7B rozciągnięto z 8k do 64k i 128k tą samą metodą — a trening
na segmentach 64k dawał poprawne działanie aż do 128k.<sup id="fnref5:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> Model, który w douczeniu
nie widział nic dłuższego niż 64k, radzi sobie dwa razy dalej. To już nie jest tylko
„nauczyliśmy go nowego zakresu” — to „nauczyliśmy go <em>reguły</em>, którą sam rozciąga dalej”.</p>
<div class="callout">
  <span class="callout__label">Dlaczego to ważne dla lokalnego LLM</span>
<p>Dla ciebie, co odpalasz modele lokalnie, to zmienia rachunek na dysku i w głowie. Nie
musisz mieć farmy GPU, żeby dostać model z długim kontekstem — ktoś dorzucił kilkaset
kroków douczenia i wrzucił gotowca na Hugging Face. Ale okno kontekstu to nie darmowy
lunch: każdy dodatkowy tysiąc tokenów to więcej pamięci na KV-cache, a to on, nie wagi,
zjada VRAM przy długich promptach — rozgryzaliśmy to bliżej <a href="/poradniki/kv-cache-vram/">tutaj</a>.
128k w tabelce i 128k, które faktycznie wejdzie na twoją kartę, to dwie różne historie.</p>
</div>
<h2 id="zrób-to-sam-yarn-w-llamacpp">Zrób to sam: YaRN w llama.cpp</h2>
<p>I teraz uwaga na rozróżnienie, na którym łatwo się wyłożyć. Wszystko powyżej — kroki
douczenia, dane PG19 — to warstwa <strong>treningu</strong> opisana w papierze. Ale jest druga warstwa:
<strong>inferencja</strong>. <code>llama.cpp</code> implementuje skalowanie YaRN jako parametry runtime, które
przekręcasz przy uruchomieniu, bez żadnego douczania:<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">llama-server -m model-yarn-128k.gguf <span class="se">\
</span></span></span><span class="line"><span class="cl">  -c <span class="m">65536</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  --yarn-orig-ctx <span class="m">8192</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  --yarn-ext-factor 1.0 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -fa on
</span></span></code></pre></div><ul>
<li><strong><code>--yarn-orig-ctx</code></strong> — oryginalny kontekst treningowy modelu (domyślnie <code>0</code>, czyli
„wczytaj z metadanych GGUF”). To punkt odniesienia, względem którego liczy się <code>s</code>.</li>
<li><strong><code>--yarn-ext-factor</code></strong> — współczynnik mieszania ekstrapolacji (domyślnie <code>-1.00</code>;
<code>0.0</code> to pełna interpolacja).</li>
</ul>
<p>Jeśli model został wypuszczony jako wariant YaRN, metadane zwykle same podpowiedzą sensowne
wartości — wtedy w ogóle nie musisz w to grzebać. A jak flagi w <code>llama.cpp</code> w ogóle działają
i które z tej setki naprawdę musisz znać, rozbieraliśmy <a href="/poradniki/flagi-llama-cpp/">w osobnym wpisie</a>.
Jest jeszcze wariant <strong>Dynamic NTK</strong> — aktualizuje współczynnik skalowania <code>s = max(1, l'/L)</code>
w locie, per krok inferencji, zamiast trzymać stałą wartość ustaloną raz.<sup id="fnref6:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> Sprytne,
bo krótkie prompty nie płacą podatku za rozciągnięcie, którego akurat nie potrzebują.</p>
<p>Cała ta rodzina metod, swoją drogą, ładnie zazębia się z tym, jak w ogóle liczy się uwaga —
a to temat, który drążyliśmy przy <a href="/naukowy/flashattention/">FlashAttention</a>.</p>
<h2 id="większa-tarcza-ten-sam-sekundnik">Większa tarcza, ten sam sekundnik</h2>
<p>Wróćmy na koniec do tego zegara. Naiwna interpolacja bierze tarczę i ściska ją tak, że
sekundnik zlewa się z minutową — niby zmieściłeś więcej godzin, ale nie odczytasz już,
czy jest 12:01, czy 12:02. YaRN robi odwrotnie: zostawia sekundnik w spokoju, a rozciąga
tylko wolne wskazówki, które i tak odmierzają grube kawałki. Dokładasz kilkaset kroków
douczenia — tyle, ile trzeba, żeby model oswoił nowe kąty — i nagle patrzy na sto
dwadzieścia osiem tysięcy tokenów tak, jakby zawsze umiał. Nie dlatego, że zobaczył każdą
nową pozycję. Dlatego, że ktoś mądrze przekręcił mu zegar, nie tłukąc szkła.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Bowen Peng, Jeffrey Quesnelle, Honglu Fan, Enrico Shippole, „YaRN: Efficient Context Window Extension of Large Language Models”, <a href="https://arxiv.org/abs/2309.00071">arXiv:2309.00071</a> (v1 z 31 sierpnia 2023, v2 z 1 listopada 2023). Abstrakt: ok. 10x mniej tokenów i 2,5x mniej kroków treningowych niż wcześniejsze metody.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Jianlin Su i in., „RoFormer: Enhanced Transformer with Rotary Position Embedding”, <a href="https://arxiv.org/abs/2104.09864">arXiv:2104.09864</a> — oryginalna praca o RoPE, na której stoi cała ta rodzina metod.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>Szczegóły techniczne (krytyka Position Interpolation, definicje NTK-aware / NTK-by-parts / Dynamic NTK, wzór na skalowanie temperatury uwagi, liczby kroków douczenia i długości kontekstu, eksperymenty na Mistral 7B) — pełny tekst pracy w wersji HTML: <a href="https://ar5iv.labs.arxiv.org/html/2309.00071">ar5iv.labs.arxiv.org/html/2309.00071</a>.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref2:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref3:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref4:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref5:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref6:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>Parametry runtime <code>--yarn-orig-ctx</code> i <code>--yarn-ext-factor</code> w <code>llama.cpp</code> — dokumentacja serwera: <a href="https://github.com/ggml-org/llama.cpp/blob/master/tools/server/README.md">ggml-org/llama.cpp, tools/server/README.md</a>.&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>GPTQ kontra AWQ: dwie szkoły kwantyzacji po treningu</title><link>https://inferownia.pl/naukowy/gptq-awq/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0200</pubDate><author>Elias Frantar</author><author>Saleh Ashkboos</author><author>Torsten Hoefler</author><author>Dan Alistarh</author><category>kwantyzacja</category><guid>https://inferownia.pl/naukowy/gptq-awq/</guid><description>GPTQ liczy odwrotność Hessianu i po kolei koryguje błąd warstwa po warstwie. AWQ patrzy na aktywacje i skaluje kanały wag. Rozkładamy obie szkoły na części i mówimy, po co komu która.</description><content:encoded><![CDATA[<p>Pod nazwą świeżego modelu ciągnie się zwykle ten sam korowód skrótów, co
zawsze: obok pliku <code>.gguf</code> leży katalog z dopiskiem <code>GPTQ</code>, kawałek niżej ktoś wrzucił
wariant <code>AWQ</code>, a w komentarzach ktoś pyta „którą wersję brać na 3060?&quot; i dostaje trzy
sprzeczne odpowiedzi. To jeden z tych tematów, o które nowy człowiek rozbija się na starcie — pół tuzina akronimów,
każdy obiecuje „ten sam model, tylko mniejszy&quot;, a człowiek stoi jak przed półką z mlekiem,
gdzie każde jest „bez laktozy, light i wzbogacone&quot;, i nie wiadomo, czym się właściwie różnią.</p>
<p>A różnią się. I nie chodzi o to, że jeden jest „lepszy&quot;, tylko że to dwie zupełnie różne
szkoły myślenia o tym samym problemie: jak zapisać miliardy wag ciaśniej i nie zepsuć
modelu. GPTQ i AWQ podchodzą do tego z dwóch przeciwnych stron — jeden liczy matematyczną
korekcję błędu, drugi pyta, które wagi są w ogóle ważne. A na dokładkę cały świat GGUF-a
i k-quantów, który znasz z <code>llama.cpp</code>, to trzecia bajka, jeszcze inna. Rozłóżmy to na części.</p>
<h2 id="najpierw-jedno-rozróżnienie-to-wszystko-jest-po-treningu">Najpierw jedno rozróżnienie: to wszystko jest PO treningu</h2>
<p>Zanim wejdziemy w szczegóły — jedna rzecz, na której wykłada się połowa dyskusji.
GPTQ, AWQ, GGUF: to wszystko <strong>kwantyzacja po treningu</strong> (<em>post-training quantization</em>, PTQ).
Bierzesz gotowy, wytrenowany model w FP16 i przerabiasz jego wagi na coś ciaśniejszego.
Żadna z tych metod <strong>nie trenuje modelu od nowa</strong>. To nie jest <em>quantization-aware training</em>
(QAT), gdzie model uczy się od początku z myślą o niskiej precyzji. Tu nikt niczego nie uczy
— bierzemy skończone dzieło i pakujemy je do mniejszego pudełka.</p>
<p>Dlaczego to ważne? Bo cała sztuka PTQ polega na tym, żeby <strong>nie mieć dostępu do treningu</strong>,
a i tak stracić jak najmniej. Nie przepuszczasz miliardów przykładów przez wsteczną
propagację. Masz gotowe wagi i najwyżej garść tekstu do „kalibracji&quot; — żeby zajrzeć, jak
model się zachowuje, i dobrać kwantyzację mądrzej niż zwykłym zaokrągleniem. I właśnie w tym
„mądrzej&quot; GPTQ i AWQ idą w dwie różne strony.</p>
<h2 id="gptq-kwantyzuj-wagę-popraw-resztę-powtórz">GPTQ: kwantyzuj wagę, popraw resztę, powtórz</h2>
<p>Pierwsza szkoła. GPTQ — z pracy Frantara, Ashkboosa, Hoeflera i Alistarha z 2022 roku<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>
— wywodzi się z klasycznej idei o nazwie <em>Optimal Brain Quantization</em> (OBQ). Pomysł jest
zaskakująco ludzki: <strong>nie kwantyzuj wszystkiego naraz na oślep</strong>. Rób to po jednej wadze,
a po każdym takim ruchu popraw pozostałe, jeszcze nietknięte wagi tak, żeby zrekompensować
błąd, który właśnie wprowadziłeś.</p>
<p>Wyobraź sobie, że rozstawiasz meble w za małym pokoju. Wciskasz szafę w róg — i widzisz, że
przez to stół musi się ciut przesunąć, a krzesło podjechać bliżej ściany. GPTQ robi
dokładnie to: kwantyzuje kolumnę wag, patrzy, ile błędu przez to narobił, i „dosuwa&quot;
pozostałe kolumny, żeby wyjście warstwy zostało jak najbliżej oryginału. Formalnie każdą
warstwę rozwiązuje osobno jako problem <code>argmin ||WX − ŴX||₂²</code> — czyli: znajdź skwantyzowane
wagi <code>Ŵ</code>, które na tych samych wejściach <code>X</code> dają wyjście najbliższe oryginalnym wagom <code>W</code>.<sup id="fnref1:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p>Skąd metoda wie, jak dosuwać? Z <strong>informacji drugiego rzędu</strong> — odwrotności Hessianu
warstwy, czyli macierzy mówiącej, jak wrażliwe jest wyjście na zmianę każdej wagi.<sup id="fnref2:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>
To nie jest zwykłe „zaokrąglij do najbliższej wartości&quot; (<em>round-to-nearest</em>). Zaokrąglenie
byłoby jak wciskanie mebli bez patrzenia. GPTQ za każdym krokiem konsultuje się z Hessianem
i wie, którą resztę i o ile przesunąć.</p>
<p>Problem: liczenie odwrotności Hessianu dla każdej wagi z osobna jest morderczo drogie.
I tu wchodzi kluczowy myk GPTQ względem starego OBQ. Autorzy zauważyli, że jeśli kwantyzować
kolumny w <strong>ustalonej, stałej kolejności</strong> — a nie „zawsze tę najmniej wrażliwą&quot; — to
odwrotność Hessianu można policzyć i współdzielić <strong>raz na całą warstwę</strong>, zamiast przeliczać
w kółko.<sup id="fnref3:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> To dało ponad trzy rzędy wielkości przyspieszenia i nagle metoda przestała
się dławić na dużych modelach. Do tego doszła tzw. <em>Cholesky Reformulation</em> — prekomputacja
przez rozkład Choleskiego, żeby przy skali setek miliardów parametrów nie rozjechała się
numeryka.<sup id="fnref4:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p>Efekt? OPT-175B skwantyzowany do 3–4 bitów na wagę w jakieś <strong>4,2 godziny na pojedynczym
GPU</strong> — i gotowy do generatywnej inferencji na jednej karcie, co wcześniej było poza
zasięgiem.<sup id="fnref5:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> Przy 4 bitach strata perplexity względem FP16 to według pracy raptem
<strong>około 0,03</strong>, a raportowane przyspieszenia inferencji siedzą w okolicach 3,25x–4,5x,
zależnie od sprzętu.<sup id="fnref6:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p>Kalibracja? Skromna — 128 losowych kawałków po 2048 tokenów ze zbioru C4, czyli ogólnego
tekstu z sieci. <strong>Bez danych zadaniowych.</strong> Autorzy podkreślają, że wynik pozostaje
„zero-shot&quot;, bo metoda nigdy nie widzi danych spod konkretnego zadania — patrzy tylko na to,
jak wagi reagują na w miarę losowy tekst.<sup id="fnref7:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<h2 id="awq-nie-wszystkie-wagi-są-równe">AWQ: nie wszystkie wagi są równe</h2>
<p>Druga szkoła podchodzi z zupełnie innej strony. AWQ — <em>Activation-aware Weight
Quantization</em>, praca Lina, Tanga, Tanga, Yanga i spółki, wyróżniona nagrodą Best Paper na
MLSys 2024<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> — startuje od obserwacji, która brzmi wręcz banalnie: <strong>nie wszystkie wagi
są tak samo ważne</strong>. Okazuje się, że wystarczy ochronić jakiś <strong>1% najistotniejszych</strong> wag,
żeby błąd kwantyzacji spadł drastycznie.<sup id="fnref1:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<p>Ale — i tu jest cały smaczek — jak poznać, które wagi są „istotne&quot;? Intuicja podpowiada:
te największe co do wartości. AWQ mówi: nie. Istotność wagi wyznacza się z <strong>rozkładu
aktywacji</strong>, a nie z samej wartości wagi.<sup id="fnref2:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> Stąd „activation-aware&quot; w nazwie: patrzysz,
przez które kanały płynie najwięcej „sygnału&quot; na wejściu, i to one decydują, które wagi
trzeba traktować z czułością. Waga może być mała, ale jeśli mnoży aktywację, która regularnie
strzela w górę — jest ważna.</p>
<p>I teraz najczęstsze nieporozumienie w internetach. Ludzie mówią: „AWQ trzyma ten 1% w FP16&quot;.
Nieprawda — a przynajmniej nie w wersji podstawowej. Trzymanie garstki wag w wyższej
precyzji to <em>mixed precision</em>, osobny nurt, który psuje jednolitość i utrudnia szybkie
jądra obliczeniowe. AWQ robi coś sprytniejszego: <strong>matematycznie skaluje w górę</strong> kanały wag
odpowiadające istotnym aktywacjom, jeszcze przed kwantyzacją, i kompensuje to odwrotnym
skalowaniem po drugiej stronie.<sup id="fnref3:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> Efekt jest taki, jakbyś dał tym ważnym wagom więcej
„rozdzielczości&quot; w siatce kwantyzacji — ale precyzja zostaje jednolita, bez wysepek FP16.</p>
<p>Co z tego wynika dla ciebie w praktyce: AWQ <strong>nie robi wstecznej propagacji</strong> przez model
i nie robi rekonstrukcji opartej na regresji.<sup id="fnref4:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> To ją odróżnia od GPTQ, która musi
przejść przez warstwy z rachunkiem Hessianu. Skutkiem ubocznym braku dopasowywania „na siłę&quot;
jest większa odporność na przeuczenie się na konkretnych danych kalibracyjnych — metoda nie
uczy się zbytnio pod ten akurat tekst, który jej pokazałeś.<sup id="fnref5:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<p>AWQ wspiera warianty W4A16 i W3A16 (wagi 4- lub 3-bitowe, aktywacje 16-bitowe), z opcjonalnym
grupowaniem kanałów, np. rozmiar grupy 128.<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> A towarzyszący jej framework
inferencyjny TinyChat osiąga ponad 3x przyspieszenie względem FP16 z Hugging Face na kartach
desktopowych i mobilnych; w nowszych benchmarkach na RTX 4090 z LLaMA-3-8B raportowano 2,7x,
a na NVIDIA Jetson Orin 2,9x i okolice 38 tokenów na sekundę.<sup id="fnref1:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup></p>
<h2 id="dwie-szkoły-jedna-tabela">Dwie szkoły, jedna tabela</h2>
<p>Zestawmy to na chłodno, bo gdy leżą obok siebie, różnica robi się oczywista:</p>
<table>
	<thead>
			<tr>
					<th></th>
					<th>GPTQ</th>
					<th>AWQ</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Rodowód</td>
					<td>OBQ — korekcja błędu drugim rzędem</td>
					<td>obserwacja o 1% istotnych wag</td>
			</tr>
			<tr>
					<td>Co napędza decyzję</td>
					<td>odwrotność Hessianu warstwy</td>
					<td>rozkład aktywacji</td>
			</tr>
			<tr>
					<td>Mechanizm</td>
					<td>kwantyzacja kolumna po kolumnie + korekcja reszty</td>
					<td>skalowanie kanałów wag przed kwantyzacją</td>
			</tr>
			<tr>
					<td>Wsteczna propagacja / regresja</td>
					<td>rachunek Hessianu przez warstwy</td>
					<td>brak, czysta matematyka skalowania</td>
			</tr>
			<tr>
					<td>Kalibracja</td>
					<td>128 × 2048 tokenów z C4</td>
					<td>statystyki aktywacji z garści tekstu</td>
			</tr>
			<tr>
					<td>Precyzja</td>
					<td>jednolita, sekwencyjna korekcja</td>
					<td>jednolita, bez wysepek FP16</td>
			</tr>
			<tr>
					<td>Typowe warianty</td>
					<td>3–4 bity (badane też 2-bit i ternarne)</td>
					<td>W4A16 / W3A16, group size np. 128</td>
			</tr>
	</tbody>
</table>
<div class="callout">
  <span class="callout__label">Dlaczego to ważne dla lokalnego LLM</span>
<p>Dla ciebie, odpalającego model pod biurkiem, praktyczny wniosek jest prosty: obie metody
celują w to samo — 4-bitowe wagi, aktywacje w 16 bitach, żeby model klasy 7B–13B wszedł na
kartę, na której FP16 nie miało prawa się zmieścić. GPTQ ma dłuższy staż i pełno gotowych
checkpointów; AWQ bywa odporniejsze na to, że dane kalibracyjne odbiegają od tego, do czego
faktycznie użyjesz modelu. Jak masz obie wersje tego samego modelu — bierzesz tę, którą
lepiej wspiera twój silnik inferencyjny, i sprawdzasz jakość na SWOICH promptach, nie na
cudzym benchmarku.</p>
</div>
<h2 id="a-gdzie-w-tym-wszystkim-gguf-i-k-quanty">A gdzie w tym wszystkim GGUF i k-quanty?</h2>
<p>Tu trzeba postawić granicę na czerwono, bo to najczęstsze pomieszanie z poplątaniem.
GGUF i k-quanty z <code>llama.cpp</code> (te wszystkie <code>Q4_K_M</code>, <code>Q5_K_S</code> i spółka, o których nazwach
rozpisaliśmy się osobno <a href="/poradniki/nazwy-kwantyzacji-gguf/">tutaj</a>) to <strong>trzeci, odrębny
nurt</strong>. Owszem, technicznie też PTQ — ale filozofia zupełnie inna.</p>
<p>GGUF to przede wszystkim <strong>format pliku</strong> ekosystemu <code>ggml-org/llama.cpp</code>, a k-quanty to
rodzina schematów kwantyzacji w środku. Zamiast kalibrowanej korekcji błędu opartej na Hessianie
czy aktywacjach, k-quanty grupują wagi w <strong>superbloki po 256 wartości</strong>, z osobnymi (często
też skwantyzowanymi) skalami i minimami dla podbloków oraz skalowaniem superbloku w fp16.<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> (Sama „szesnastka&quot; <code>fp16</code> — i czemu w treningu ML częściej widuje się <code>bf16</code> — to <a href="/poradniki/bf16-f16-f32/">osobny temat</a>.)
Stąd bierze się niecałkowita „efektywna&quot; liczba bitów na wagę — <code>Q4_K_M</code> to w praktyce
jakieś <strong>4,5 bita</strong> na wagę, nie równe 4.<sup id="fnref1:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup></p>
<p>Warianty z dopiskami <code>_S</code>, <code>_M</code>, <code>_L</code> różnią się kompromisem rozmiar/jakość — <code>_M</code>
selektywnie trzyma wrażliwsze warstwy w wyższej precyzji, a <code>Q4_K_M</code> społeczność od dawna
uznaje za praktyczny „sweet spot&quot; (jakieś 75% redukcji rozmiaru przy minimalnej odczuwalnej
stracie).<sup id="fnref2:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> To inżynierski nurt zorientowany na inferencję na CPU i mieszaną CPU–GPU,
nie na kartę wypchaną VRAM-em.</p>
<p>Kluczowe: <strong>nie utożsamiaj tych trzech</strong>. Wszystkie to PTQ, ale:</p>
<ul>
<li>GPTQ = sekwencyjna korekcja błędu przez odwrotność Hessianu,</li>
<li>AWQ = skalowanie kanałów wag wg aktywacji,</li>
<li>GGUF/k-quanty = superbloki z mieszaną precyzją per warstwa i osobnymi skalami.</li>
</ul>
<p>GGUF <em>może</em> korzystać z danych kalibracyjnych (tzw. <em>importance matrix</em>), ale mechanizm jest
inny niż w tamtych dwóch. To trzy różne odpowiedzi na to samo pytanie, nie trzy nazwy tej
samej rzeczy.</p>
<h2 id="zrób-to-sam-jak-to-rozpoznać-w-praniu">Zrób to sam: jak to rozpoznać w praniu</h2>
<p>Nie musisz liczyć żadnego Hessianu ręcznie — ale warto umieć rozpoznać, na co patrzysz.
Katalog z GPTQ albo AWQ na Hugging Face zwykle trzyma wagi w formacie <code>safetensors</code> plus
plik <code>quantize_config.json</code>, gdzie stoi jak byk liczba bitów i rozmiar grupy:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># GPTQ/AWQ zwykle: katalog safetensors + config kwantyzacji</span>
</span></span><span class="line"><span class="cl">ls TheModel-AWQ/
</span></span><span class="line"><span class="cl"><span class="c1"># config.json  model.safetensors  quantize_config.json  ...</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># GGUF to jeden plik z całą kwantyzacją zaszytą w nazwie</span>
</span></span><span class="line"><span class="cl">ls TheModel-GGUF/
</span></span><span class="line"><span class="cl"><span class="c1"># the-model-q4_k_m.gguf</span>
</span></span></code></pre></div><p>Reguła kciuka na dziś: jeśli odpalasz na <strong>karcie NVIDII</strong> przez silnik pokroju vLLM albo
TinyChat i chcesz maksimum przepustowości z 4-bitowych wag — patrzysz na GPTQ albo AWQ.
Jeśli grasz na <strong>CPU, Apple Silicon albo mieszance CPU–GPU</strong> i chcesz jeden plik, który
„po prostu działa&quot; w <code>llama.cpp</code> czy Ollamie — bierzesz GGUF, najpewniej <code>Q4_K_M</code>. Ta sama
walka o VRAM, którą liczyliśmy <a href="/poradniki/ile-vram-na-model/">we wzorze na pamięć</a>, tylko
rozegrana trzema różnymi szkołami.</p>
<p>I jeszcze jedno, żeby domknąć układankę: kwantyzacja po treningu to nie to samo, co
4-bitowy <strong>fine-tuning</strong> spod znaku QLoRA — tam ciśniemy model po to, żeby go <strong>doszkolić</strong> na
skromnym VRAM-ie, a rozgryzaliśmy to osobno <a href="/naukowy/qlora/">tutaj</a>. Tu nikt niczego nie
douczy. Tu tylko pakujemy skończony model do mniejszego pudełka.</p>
<p>Bo o to w tym całym zoo skrótów chodzi. Masz jeden wytrenowany mózg zapisany w FP16 i trzy
ekipy, które przyszły go spakować na wyjazd. GPTQ pakuje pedantycznie, walizka po walizce,
za każdym razem dosuwając resztę, żeby nic się nie pogniotło. AWQ najpierw pyta, co jest
naprawdę cenne, owija ten 1% w folię bąbelkową i resztę wciska bez ceregieli. A ekipa GGUF-a
nawet nie próbuje być elegancka — tnie na superbloki, pakuje w jeden plik i wrzuca do
bagażnika starego kombi, byle ruszyło z miejsca na tym, co masz pod ręką.</p>
<p>Trzy szkoły, jeden model, ten sam cel: żeby coś, co „się nie mieściło&quot;, w końcu odpaliło
u ciebie na biurku. Reszta to spór o to, jak elegancko go do tej walizki włożyć.</p>
<h2 id="przypisy--źródła">Przypisy · źródła</h2>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Frantar, Ashkboos, Hoefler, Alistarh, <em>GPTQ: Accurate Post-Training Quantization for Generative Pre-trained Transformers</em> — <a href="https://arxiv.org/abs/2210.17323">abstrakt, arXiv:2210.17323</a>; szczegóły metody (layer-wise problem <code>argmin ||WX − ŴX||₂²</code>, OBQ, stała kolejność kolumn i współdzielony Hessian, Cholesky Reformulation, kalibracja 128 × 2048 tokenów z C4, ~4,2 h dla OPT-175B, ~0,03 perplexity dla 4-bit, przyspieszenia 3,25x–4,5x) w <a href="https://ar5iv.labs.arxiv.org/html/2210.17323">pełnym tekście HTML na ar5iv</a>.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref2:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref3:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref4:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref5:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref6:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref7:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Lin, J. Tang, H. Tang, Yang, W.-M. Chen, W.-C. Wang, Xiao, Dang, Gan, Han, <em>AWQ: Activation-aware Weight Quantization for LLM Compression and Acceleration</em> (MLSys 2024, Best Paper) — <a href="https://arxiv.org/abs/2306.00978">abstrakt, arXiv:2306.00978</a>: ochrona ~1% istotnych wag, wybór wg rozkładu aktywacji, skalowanie kanałów zamiast mieszanej precyzji, brak backpropagation i rekonstrukcji regresyjnej.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref2:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref3:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref4:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref5:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>Oficjalne repozytorium AWQ, <a href="https://github.com/mit-han-lab/llm-awq">mit-han-lab/llm-awq</a> — implementacja W4A16 / W3A16, group size, framework TinyChat oraz benchmarki przyspieszenia (RTX 4090 z LLaMA-3-8B: 2,7x; Jetson Orin: 2,9x / ~38 tok/s).&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p><code>ggml-org/llama.cpp</code>, <a href="https://github.com/ggml-org/llama.cpp/wiki/Tensor-Encoding-Schemes">Tensor Encoding Schemes (Wiki)</a> — opis GGUF i rodziny k-quantów: superbloki po 256 wag, skwantyzowane skale/minima per podblok, ~4,5 bpw dla <code>Q4_K_M</code>, warianty <code>_S</code>/<code>_M</code>/<code>_L</code>; dokumentacja narzędzia w <a href="https://github.com/ggml-org/llama.cpp/blob/master/tools/quantize/README.md">tools/quantize/README.md</a>.&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref2:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item></channel></rss>