<?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>Kwantyzacja — Inferownia</title><link>https://inferownia.pl/tags/kwantyzacja/</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, 16 Jul 2026 00:00:00 +0200</lastBuildDate><atom:link href="https://inferownia.pl/tags/kwantyzacja/index.xml" rel="self" type="application/rss+xml"/><item><title>Nazwy kwantyzacji GGUF: co znaczy Q4_K_M i cała reszta</title><link>https://inferownia.pl/poradniki/nazwy-kwantyzacji-gguf/</link><pubDate>Thu, 16 Jul 2026 00:00:00 +0200</pubDate><category>kwantyzacja</category><guid>https://inferownia.pl/poradniki/nazwy-kwantyzacji-gguf/</guid><description>Wchodzisz na kartę modelu na Hugging Face, a tam kolumna pełna szyfrów: Q4_K_M, Q5_K_S, IQ2_XXS. Rozbieramy te nazwy litera po literze — żebyś wiedział, który plik pobrać, a nie zgadywał.</description><content:encoded><![CDATA[<p>Który plik w ogóle pobrać — <code>Q4_K_M</code>, <code>Q5_K_S</code>, a może to dziwne <code>IQ2_XXS</code>? Przychodzisz po jeden model, a zastajesz tabelę na dwadzieścia wierszy: <code>Q4_K_M</code>, <code>Q5_K_S</code>, <code>Q6_K</code>, <code>Q8_0</code>, niżej jeszcze <code>IQ2_XXS</code> i <code>IQ3_XS</code>, każdy z inną wagą w gigabajtach. Człowiek stoi nad tym jak nad półką z olejami silnikowymi w markecie — wszystkie coś tam obiecują, wszystkie mają cyferki, a ty nie wiesz, czy <code>5W-40</code> to lepiej, czy gorzej niż <code>10W-30</code>. Nowi ludzie zadają to pytanie mniej więcej co drugi dzień: „którą wersję pobrać?&quot;.</p>
<p>I najczęściej ktoś rzuca: „bierz Q4_K_M i się nie zastanawiaj&quot;. Dobra rada — ale zostawia cię z tym samym uczuciem, co „wlej 5W-40 i jedź&quot;. Działa, tylko dalej nie wiesz, co te znaczki znaczą. A one nie są przypadkowe. Każda litera i każda cyfra w <code>Q4_K_M</code> coś konkretnie mówi: ile bitów, jaka rodzina, ile precyzji odzyskujesz z powrotem. Jak raz to rozłożysz, ta tabela przestaje być loterią, a robi się listą z parametrami. Rozbierzmy ją.</p>
<h2 id="skąd-w-ogóle-biorą-się-te-nazwy">Skąd w ogóle biorą się te nazwy</h2>
<p>Zanim rozbierzemy nazwy, jedno pytanie: po co w ogóle tniemy? Model w oryginale trzyma każdą wagę na 16 bitach (format <code>F16</code>/<code>BF16</code>). Kwantyzacja to zwykłe upakowanie tych samych, już wytrenowanych liczb ciaśniej — nie douczanie, nie trening od nowa, tylko kompresja. To ważne rozróżnienie, bo ludzie mylą to z fine-tuningiem: tu nic się nie uczy, tylko chudnie.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> O tym, jak liczby idą w dół i czemu jakość spada tak zaskakująco mało, rozgryzaliśmy szerzej przy okazji <a href="/aktualnosci/ekosystem-gguf/">wyboru wersji modelu</a>.</p>
<p>GGUF (GPT-Generated Unified Format — binarny format plików modeli spod <code>llama.cpp</code>) każdemu z tych sposobów cięcia daje nazwę. I ta nazwa nie jest marketingowa — to zapis, jak dokładnie wagi są zakodowane w pliku. Format jest zaskakująco regularny: <code>QN_K_X</code>. <code>N</code> to przybliżona liczba bitów na wagę, <code>_K</code> to nazwa rodziny (tak zwane k-quanty), a przyrostek <code>X</code> (<code>S</code>/<code>M</code>/<code>L</code>) mówi, ile tensorów w modelu dostaje podbitą precyzję.<sup id="fnref1:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> Jak to raz przeczytasz jak zdanie, a nie jak szyfr, reszta wchodzi sama.</p>
<h2 id="q4_k_m--rozłóżmy-to-na-trzy-kawałki">Q4_K_M — rozłóżmy to na trzy kawałki</h2>
<p>Weźmy najsłynniejszy przykład i przejedźmy po nim znak po znaku.</p>
<p><strong>Q</strong> — to po prostu „quantized&quot;. Standardowa kwantyzacja na liczbach całkowitych. Nic ciekawego, ozdobnik.</p>
<p><strong>4</strong> — przybliżona liczba bitów na wagę. I tu pierwsza pułapka, w którą wpada każdy: to <em>przybliżenie</em>, nie dokładna wartość. Wydaje się, że <code>Q4</code> znaczy „równo 4 bity na liczbę&quot;. Nieprawda. Realnie, na modelu Llama-3.1-8B zmierzono, że <code>Q4_K_M</code> waży <strong>4,89 bita na wagę</strong>, a nie 4.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> Skąd ta nadwyżka? Właśnie z ostatniej litery — za chwilę.</p>
<p><strong>_K</strong> — rodzina „k-quant&quot;. To był przełom z połowy 2023 roku (PR #1684 od ikawrakowa).<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> Zamiast kodować każdy blok wag osobno i głupio, k-quanty pracują na <strong>super-blokach</strong>: większa grupa wag współdzieli metadane, a skala i minimum każdego mniejszego bloku są kodowane osobno, z własną precyzją. W praktyce: <code>Q4_K</code> i <code>Q5_K</code> mają super-bloki z 8 bloków po 32 wagi, a <code>Q2_K</code>, <code>Q3_K</code> i <code>Q6_K</code> — z 16 bloków po 16 wag.<sup id="fnref1:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> Dzięki temu ta sztuczka trzyma jakość dużo lepiej niż stare, proste cięcie.</p>
<p><strong>M</strong> — i tu jest cała finezja, którą prawie wszyscy przegapiają. <code>S</code>/<code>M</code>/<code>L</code> (Small/Medium/Large) <strong>nie</strong> znaczą „mały/średni/duży model&quot;. Znaczą: ile tensorów w środku dostaje podniesioną precyzję.<sup id="fnref2:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> Bo model to nie jednorodna zupa liczb — jedne tensory (np. te od atencji <code>attention.wv</code> albo <code>feed_forward.w2</code>) są dla jakości ważniejsze niż inne. Wariant <code>M</code> mówi: „te newralgiczne kawałki potnij łagodniej, resztę ostrzej&quot;.<sup id="fnref2:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> To dlatego <code>Q4_K_M</code> waży te 4,89 bita zamiast 4 — bo część wag siedzi wyżej niż na czterech bitach. To nie jest jedna globalna liczba bitów, to <em>mieszanka</em>.</p>
<p>Czyli <code>Q4_K_M</code> czytasz tak: „około czterobitowa, rodzina k-quant, ze średnim miksem podbitych tensorów&quot;. Trzy słowa, nie szyfr.</p>
<h2 id="ile-realnie-ważą--bo-teoria-kłamie-o-ułamek">Ile realnie ważą — bo teoria kłamie o ułamek</h2>
<p>Skoro cyfra po <code>Q</code> to przybliżenie, to najlepiej patrzeć na liczby zmierzone na prawdziwym modelu. Oto oficjalna tabela <code>llama.cpp</code> dla Llama-3.1-8B (dla porównania: oryginał w <code>F16</code> to 14,96 GiB, a plik źródłowy waży 32,1 GB).<sup id="fnref1:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<table>
	<thead>
			<tr>
					<th>Wariant</th>
					<th>Bity/wagę</th>
					<th>Rozmiar (Llama-3.1-8B)</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>Q4_K_S</code></td>
					<td>4,67</td>
					<td>4,36 GiB</td>
			</tr>
			<tr>
					<td><code>Q4_K_M</code></td>
					<td>4,89</td>
					<td>4,58 GiB</td>
			</tr>
			<tr>
					<td><code>Q5_K_S</code></td>
					<td>5,57</td>
					<td>5,21 GiB</td>
			</tr>
			<tr>
					<td><code>Q5_K_M</code></td>
					<td>5,70</td>
					<td>5,33 GiB</td>
			</tr>
			<tr>
					<td><code>Q6_K</code></td>
					<td>6,56</td>
					<td>6,14 GiB</td>
			</tr>
			<tr>
					<td><code>Q8_0</code></td>
					<td>8,50</td>
					<td>7,95 GiB</td>
			</tr>
			<tr>
					<td><code>F16</code></td>
					<td>16,00</td>
					<td>14,96 GiB</td>
			</tr>
	</tbody>
</table>
<p>Widać z tego dwie rzeczy. Raz — każdy kolejny bit kosztuje mniej więcej pół–jeden gigabajt na modelu tej klasy, więc skok z <code>Q4_K_M</code> na <code>Q6_K</code> to jakieś półtora giga VRAM-u więcej za coraz mniej odczuwalny zysk jakości (a ile pamięci realnie zeżre dany plik z kontekstem, wyliczasz <a href="/poradniki/ile-vram-na-model/">osobnym wzorem</a>). Dwa — <code>Q8_0</code> to niemal bezstratna referencja; różnicy między nim a <code>F16</code> w ślepym teście raczej nie wychwycisz, a plik jest o połowę lżejszy. Dlatego <code>Q8_0</code>, choć technicznie to stary „legacy&quot; format, wciąż żyje i ma się świetnie.<sup id="fnref3:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p>A ile to daje na dużym modelu? Oficjalna tabela pokazuje <code>Q4_K_M</code> jako jedyny przykład: Llama 3.1 8B chudnie z 32,1 GB do 4,9 GB, 70B z 280,9 GB do 43,1 GB, a monstrualne 405B z 1625 GB do 249 GB.<sup id="fnref2:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> Cztery bity zamiast szesnastu i nagle model, który potrzebował serwerowni, mieści się na jednej porządnej karcie.</p>
<h2 id="dlaczego-akurat-q4_k_m-jest-tym-domyślnym">Dlaczego akurat Q4_K_M jest tym „domyślnym&quot;</h2>
<p>No dobra, ale skąd się wziął ten konsensus, że <code>Q4_K_M</code> to bezpieczny strzał? Tu muszę cię uczciwie zderzyć z rzeczywistością: <code>llama.cpp</code> <strong>nigdzie formalnie nie ogłasza</strong> żadnego „oficjalnego domyślnego&quot; kwantu.<sup id="fnref3:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> To mit, który sam sobie chodzi po forach. Ale nie wziął się znikąd.</p>
<p>Po pierwsze, <code>Q4_K_M</code> ląduje w dokumentacji jako <strong>przykładowa komenda</strong> — kiedy README pokazuje, jak w ogóle uruchomić <code>llama-quantize</code>, robi to właśnie na nim:<sup id="fnref4:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">./build/bin/llama-quantize model-bf16.gguf model-Q4_K_M.gguf Q4_K_M
</span></span></code></pre></div><p>Po drugie — i ważniejsze — cztery bity to punkt, w którym krzywa „rozmiar kontra jakość&quot; ma najładniejsze kolano. Poniżej zaczynasz tracić jakość szybciej, niż zyskujesz na rozmiarze. Powyżej płacisz gigabajtami za coraz mniejszy zysk. <code>Q4_K_M</code> siedzi dokładnie w tym słodkim punkcie, dlatego społeczność go pokochała. Ale to konsensus praktyczny, nie dekret ze specyfikacji — i warto to trzymać w głowie, zanim ktoś ci powie, że „tak trzeba&quot;.</p>
<h2 id="a-te-dziwne-iq-z-wieloma-iksami">A te dziwne IQ z wieloma iksami?</h2>
<p>Zjeżdżasz w tabeli niżej i trafiasz na drugą rodzinę: <code>IQ2_XXS</code>, <code>IQ3_XS</code>, <code>IQ4_NL</code>. Wyglądają jak k-quanty, które napiły się kawy. To i-quanty (i-quants) — nowsza rodzina, zaprojektowana pod jeden cel: wycisnąć sensowną jakość z <strong>naprawdę</strong> niskich bitów, tam gdzie zwykłe k-quanty się już sypią.<sup id="fnref4:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p>Sekret jest w dwóch rzeczach. I-quanty operują na większych super-blokach po 256 wag i — to kluczowe — wyliczają wagi z pomocą tak zwanej importance matrix, w skrócie <strong>imatrix</strong>.<sup id="fnref5:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> Co to jest? Wyobraź sobie, że przed kompresją przepuszczasz model przez zestaw tekstów kalibracyjnych i patrzysz, które wagi <em>naprawdę</em> się aktywują, a które ledwo drgają. Imatrix to per-tensor tablica, która to zapamiętuje — dla każdej pozycji notuje, jak mocno ta waga „pracowała&quot;.<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> Potem, przy cięciu, algorytm chroni ważne wagi kosztem tych mało istotnych. Zamiast tłuc wszystko po równo, oszczędza tam, gdzie boli najmniej.</p>
<p>Litery za <code>IQ</code> układają się od najostrzejszego cięcia w górę: <code>XXS</code> (extra-extra-small), <code>XS</code>, <code>S</code>, <code>M</code>. Oto co to daje w liczbach, znów na Llama-3.1-8B:<sup id="fnref5:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<table>
	<thead>
			<tr>
					<th>Wariant</th>
					<th>Bity/wagę</th>
					<th>Rozmiar</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>IQ1_S</code></td>
					<td>2,00</td>
					<td>1,87 GiB</td>
			</tr>
			<tr>
					<td><code>IQ2_XXS</code></td>
					<td>2,38</td>
					<td>2,23 GiB</td>
			</tr>
			<tr>
					<td><code>IQ2_M</code></td>
					<td>2,93</td>
					<td>2,74 GiB</td>
			</tr>
			<tr>
					<td><code>IQ3_XXS</code></td>
					<td>3,25</td>
					<td>3,04 GiB</td>
			</tr>
			<tr>
					<td><code>IQ4_XS</code></td>
					<td>4,46</td>
					<td>4,17 GiB</td>
			</tr>
			<tr>
					<td><code>IQ4_NL</code></td>
					<td>4,68</td>
					<td>4,38 GiB</td>
			</tr>
	</tbody>
</table>
<p>Zauważ jedno: <code>IQ4_XS</code> waży 4,17 GiB, czyli <em>mniej</em> niż <code>Q4_K_S</code> (4,36 GiB) przy porównywalnej klasie. To właśnie ta magia imatrixa — więcej jakości na bit. Cena? I-quanty bywają wolniejsze w inferencji na słabszym sprzęcie, bo używają tablic przeglądowych, i mają sens głównie wtedy, gdy walczysz o każdy gigabajt — na przykład wciskasz model 70B na kartę, która teoretycznie go nie uniesie.</p>
<div class="callout">
  <span class="callout__label">Dlaczego to ważne dla lokalnego LLM</span>
<p>Nie daj się nabrać na jeden zbieg okoliczności: „i&quot; w <strong>i-quant</strong> i „i&quot; w <strong>imatrix</strong> to nie ten sam mechanizm. Imatrix (flaga <code>--imatrix</code>) działa uniwersalnie — możesz nim wesprzeć również k-quanty i stare legacy quanty. A i-quanty teoretycznie da się zrobić i bez niego, tyle że są <em>zaprojektowane</em>, żeby chodzić najlepiej właśnie z imatrixem.<sup id="fnref6:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> Dwie różne rzeczy, które przypadkiem zaczynają się na tę samą literę.</p>
</div>
<h2 id="legacy-czyli-dziadkowie-formatu">Legacy, czyli dziadkowie formatu</h2>
<p>Zanim były k-quanty, były proste cięcia: <code>Q4_0</code>, <code>Q4_1</code>, <code>Q5_0</code>, <code>Q5_1</code>, <code>Q8_0</code>. Dzielą wagi na płaskie bloki po 32 i kodują je najprościej jak się da — warianty <code>_0</code> wzorem <code>waga = q × skala</code>, a <code>_1</code> dokładają jeszcze minimum bloku (<code>+ block_minimum</code>).<sup id="fnref7:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> Bez super-bloków, bez finezji. Dokumentacja GGUF opisuje je wprost jako „metodę przestarzałą, dziś rzadko używaną&quot; — z jednym wyjątkiem. <code>Q8_0</code> przeżył wszystkich, bo na ośmiu bitach kompresja i tak niczego istotnego nie gubi, więc służy jako wygodna, niemal bezstratna referencja.<sup id="fnref8:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> Reszta to eksponaty muzealne — jeśli widzisz świeży model wypuszczony tylko w <code>Q4_0</code>, to znak, że coś jest nie tak z tym uploadem.</p>
<p>Dla porządku: świat GGUF ma jeszcze kilka egzotów, na które możesz się natknąć. <code>Q8_K</code> to nie format do dystrybucji, tylko typ roboczy do liczenia iloczynów skalarnych wewnątrz kwantów 2–6 bit.<sup id="fnref9:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> A z najnowszych — <code>TQ1_0</code>/<code>TQ2_0</code> (kwantyzacja ternarna) i <code>MXFP4</code> (4-bitowy microscaling float) to zupełnie młodsze rodziny niż klasyczne k- i i-quanty.<sup id="fnref10:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> Ale to już inna bajka.</p>
<h2 id="zrób-to-sam--i-nie-strzel-sobie-w-stopę">Zrób to sam — i nie strzel sobie w stopę</h2>
<p>Jak sam chcesz coś skwantyzować, narzędzie to <code>llama-quantize</code> z repo <code>ggml-org/llama.cpp</code>. Podstawa jest banalna — podajesz źródło, cel i typ. Ale są trzy grabie, na które warto uważać.</p>
<p>Po pierwsze — <strong>tnij zawsze z oryginału</strong> (<code>F16</code>/<code>BF16</code>/<code>F32</code>), nie z już skwantyzowanego pliku. Requantyzacja (flaga <code>--allow-requantize</code>) potrafi „poważnie obniżyć jakość&quot;, ostrzega o tym sama dokumentacja.<sup id="fnref6:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> To jak przegrywanie MP3 z MP3 — za każdym razem gorzej.</p>
<p>Po drugie — flaga <code>--pure</code> wyłącza całą tę inteligentną mieszankę k-quantów i tnie wszystkie tensory do jednego typu.<sup id="fnref7:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> Brzmi kusząco („czysto!&quot;), ale odbierasz sobie właśnie to, za co <code>_M</code> jest dobre. Zostaw domyślny miks.</p>
<p>Po trzecie — jak kwantyzujesz model multimodalny, <strong>nie tnij komponentów mmproj</strong> (enkoder obrazu/dźwięku i projektory) tak agresywnie jak reszty. Dokumentacja zaleca trzymać je w <code>bf16</code> albo <code>q8</code> (czym <code>bf16</code> różni się od zwykłej „szesnastki&quot; <code>f16</code>, jest <a href="/poradniki/bf16-f16-f32/">tutaj</a>), bo mają bezpośredni wpływ na jakość tego, co trafia do modelu, a oszczędność pamięci przy niższym kwancie i tak jest znikoma.<sup id="fnref8:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></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"># z importance matrix, dla i-quantów i lepszych niskobitowych k-quantów:</span>
</span></span><span class="line"><span class="cl">./build/bin/llama-quantize --imatrix model.imatrix <span class="se">\
</span></span></span><span class="line"><span class="cl">  model-f16.gguf model-IQ3_M.gguf IQ3_M
</span></span></code></pre></div><p>Cała lista pokręteł — <code>--tensor-type</code> (regex per tensor), <code>--output-tensor-type</code>, <code>--token-embedding-type</code>, <code>--leave-output-tensor</code>, <code>--prune-layers</code> — czeka w dokumentacji narzędzia.<sup id="fnref9:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> Ale szczerze? Na co dzień pobierasz gotowca z Hugging Face i cała ta zabawa sprowadza się do jednego pytania: który wiersz w tabeli kliknąć.</p>
<p>I tu wracamy do tej półki z olejami. Bo kiedy już wiesz, że <code>Q4_K_M</code> to „czterobitowa, k-quant, średni miks&quot;, <code>Q6_K</code> to „szóstka bez wariantów, bo i po co&quot;, a <code>IQ2_XXS</code> to „dwa bity ratowane imatrixem na modelu, który normalnie by się nie zmieścił&quot; — ta tabela na Hugging Face przestaje być ścianą szyfrów. Robi się listą z jasnymi parametrami, gdzie każdy znaczek mówi, ile zapłacisz pamięcią i ile odzyskasz jakości.</p>
<p>Bierz <code>Q4_K_M</code>, jak masz miejsce i nie chcesz myśleć. Schodź do <code>IQ</code>, jak walczysz o ostatni gigabajt. Idź w <code>Q6_K</code> albo <code>Q8_0</code>, jak karta ci pozwala i chcesz mieć pewność. Ale już nie zgadujesz — czytasz. A to jest dokładnie ta różnica między człowiekiem, który wlewa „ten niebieski olej, bo ładny&quot;, a takim, co spojrzał na spód i wie, po co sięga.</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>Hugging Face Hub — dokumentacja formatu GGUF: wzory wag i teoretyczne bits-per-weight każdego typu (Q2_K…Q6_K, IQ…), definicja super-bloków, rozróżnienie legacy quantów od k- i i-quantów, statusy <code>Q8_0</code> i <code>Q8_K</code>, wzmianki o TQ1_0/TQ2_0 i MXFP4. <a href="https://huggingface.co/docs/hub/en/gguf">huggingface.co/docs/hub/gguf</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>&#160;<a href="#fnref8:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref9:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref10:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>llama.cpp — dokumentacja narzędzia <code>llama-quantize</code>: tabele zmierzonych bits/weight i rozmiarów dla Llama-3.1-8B, przykładowa komenda <code>Q4_K_M</code>, flagi (<code>--pure</code>, <code>--allow-requantize</code>, <code>--imatrix</code>), zalecenie ws. mmproj oraz tabela rozmiarów 8B/70B/405B. <a href="https://github.com/ggml-org/llama.cpp/blob/master/tools/quantize/README.md">tools/quantize/README.md</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>&#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>&#160;<a href="#fnref6:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref7:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref8:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref9:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>k-quants (Q2_K…Q6_K), ikawrakow — wprowadzenie rodziny k-quantów, definicja super-bloków oraz miksu tensorów kryjącego się pod przyrostkami S/M/L. <a href="https://github.com/ggml-org/llama.cpp/pull/1684">Pull Request #1684, ggml-org/llama.cpp</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></p>
</li>
<li id="fn:4">
<p>Importance Matrix calculation, ikawrakow — wprowadzenie mechanizmu imatrix, per-tensor tablicy istotności wag używanej do priorytetyzacji precyzji przy kwantyzacji. <a href="https://github.com/ggml-org/llama.cpp/pull/4861">Pull Request #4861, ggml-org/llama.cpp</a>.&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>QLoRA: fine-tuning dużego modelu w 4 bitach</title><link>https://inferownia.pl/naukowy/qlora/</link><pubDate>Wed, 15 Jul 2026 00:00:00 +0200</pubDate><author>Tim Dettmers</author><author>Artidoro Pagnoni</author><author>Ari Holtzman</author><author>Luke Zettlemoyer</author><category>fine-tuning</category><guid>https://inferownia.pl/naukowy/qlora/</guid><description>Dostroić model 65B na jednej karcie 48 GB, z jakością pełnego 16-bitowego treningu? QLoRA mrozi wagi w 4 bitach i trenuje same adaptery. Rozkładamy na części, jak to w ogóle ma prawo działać.</description><content:encoded><![CDATA[<p>Wyobraź sobie na chwilę, że bierzesz się wreszcie za to, co wiecznie odkładasz „na kiedyś&quot;:
dostroić model pod siebie — pod swój żargon, swoje dane, swój ton. Wchodzisz na jakiś poradnik o fine-tuningu, czytasz do
połowy z zapałem, a potem pada zdanie, które gasi światło: „potrzebujesz kilku kart A100
po 80 GB&quot;. No i tyle. Zamykasz kartę w przeglądarce, wracasz do promptowania gotowca i
mówisz sobie, że przecież i tak nie miałeś na to czasu. Na forach o fine-tuningu to gatunek posta
sam w sobie — ktoś pyta, jak dotrenować siódemkę na własnym korpusie, i pierwsza odpowiedź
brzmi „wynajmij GPU w chmurze&quot;. Fine-tuning latami był tym pokojem za szybą: widzisz, co
w środku, ale klamka jest po drugiej stronie.</p>
<p>W maju 2023 czterech badaczy z University of Washington tę szybę stłukło. Praca nazywa się
<em>QLoRA: Efficient Finetuning of Quantized LLMs</em>, a jej główna teza brzmi jak przechwałka
z forum: da się dostroić model <strong>65B na jednej karcie 48 GB</strong> i zachować jakość pełnego,
16-bitowego fine-tuningu.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> Nie „prawie&quot;, nie „z gwiazdką na jakości&quot;. Tak blisko, że
w benchmarku różnicy praktycznie nie widać. Jak? Rozłóżmy to na części, bo cały trik jest
piękny i składa się z kilku osobnych sprytów, które dopiero razem zaskakują.</p>
<h2 id="skąd-się-bierze-ten-cały-vram">Skąd się bierze ten cały VRAM</h2>
<p>Zanim rozbijemy QLoRA na części, trzeba wiedzieć, na co przy treningu schodzi pamięć — bo to nie
tylko same wagi. Klasyczny fine-tuning musi trzymać na karcie cztery rzeczy naraz: wagi
modelu, gradienty (po jednej liczbie na każdą wagę), stany optymalizatora (Adam trzyma
ich dwa na wagę) i aktywacje. Przy pełnym treningu w 16 bitach to znaczy, że sama arytmetyka
optymalizatora potrafi zająć więcej niż sam model. Dlatego dotrenowanie „całego&quot; modelu 65B
to zabawa nie na jedną kartę, a na małą serwerownię.</p>
<p>Pierwszy pomysł, żeby to obejść, jest starszy od QLoRA i nazywa się <strong>LoRA</strong> (Low-Rank
Adapters). Zamiast ruszać wszystkie miliardy wag, zamrażasz je w całości, a obok doklejasz
malutkie, niskorangowe macierze — adaptery — i trenujesz tylko je. Nagle gradienty i
optymalizator dotyczą ułamka parametrów. To już był przełom. Ale został jeden gruby
kamień w bucie: <strong>zamrożone wagi bazowe i tak muszą siedzieć na karcie</strong>, żeby przez nie
puścić forward i backward. A dla modelu 65B te zamrożone wagi w 16 bitach to wciąż jakieś
130 GB. Adaptery są tanie, ale bilet wstępu — sam model — dalej jest za drogi.</p>
<h2 id="trzy-sprytne-rzeczy-które-robi-qlora">Trzy sprytne rzeczy, które robi QLoRA</h2>
<p>I tu wchodzi sedno pracy. Skoro te wagi bazowe są zamrożone — nikt ich w treningu nie
zmienia — to po co trzymać je w pełnej precyzji? QLoRA mówi: skwantyzujmy je do <strong>4 bitów</strong>,
zamroźmy je w tej postaci, a gradient przepuśćmy wstecz przez ten zamrożony 4-bitowy model
prosto do adapterów LoRA.<sup id="fnref1:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> Trenujemy dalej tylko adaptery — ale bilet wstępu właśnie
schudł czterokrotnie. Żeby to zagrało bez utraty jakości, autorzy dorzucili trzy osobne
patenty.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<p><strong>Raz — NF4, czyli 4-bit NormalFloat.</strong> Nie wszystkie 4-bitowe typy danych są sobie równe. Wagi
wytrenowanej sieci nie są rozrzucone byle jak — układają się mniej więcej w rozkład
normalny, taką dzwonową górkę wokół zera. NF4 to typ danych skrojony dokładnie pod ten
kształt, teoretycznie optymalny informacyjnie dla wag o rozkładzie normalnym. To ważne:
NF4 <strong>nie jest</strong> zwykłym zmiennoprzecinkowym FP4 z IEEE. To specjalny format, który
„marnuje&quot; mniej z tych czterech bitów, bo wie z góry, gdzie wagi się tłoczą.<sup id="fnref1:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<p><strong>Dwa — double quantization (podwójna kwantyzacja).</strong> Kwantyzacja blokowa musi gdzieś
zapisać stałe skalujące — po jednej na blok wag. To drobne liczby, ale jak masz ich
miliony, robi się z tego zauważalny narzut. QLoRA kwantyzuje więc… same stałe kwantyzacji.
Kwantyzacja kwantyzacji brzmi jak żart, a oszczędza realnie około 0,4 bita na parametr.<sup id="fnref2:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>
Przy modelu 65B te ułamki bitów zamieniają się w gigabajty.</p>
<p><strong>Trzy — paged optimizers (optymalizatory stronicowane).</strong> Przy gradient checkpointingu
zużycie pamięci potrafi nagle skoczyć — pojawia się pik i karta krztusi się „out of memory&quot;
w najmniej spodziewanym momencie. Paged optimizers używają NVIDIA unified memory, żeby te
skoki obsłużyć: gdy na GPU robi się ciasno, stany optymalizatora są po cichu przerzucane do
RAM-u i z powrotem, jak stronicowanie pamięci w systemie operacyjnym.<sup id="fnref2:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> Dzięki temu
pik nie wywala całego treningu.</p>
<div class="callout">
  <span class="callout__label">Dlaczego to ważne dla lokalnego LLM</span>
<p>Najważniejsza rzecz do zapamiętania: <strong>4 bity nie znaczą, że cały trening liczy się w 4
bitach.</strong> W 4 bitach siedzą tylko zamrożone wagi bazowe — i to po to, żeby zająć mało miejsca.
Kiedy przychodzi liczyć forward i backward, te wagi są w locie dekwantyzowane, a same
obliczenia na aktywacjach idą w wyższej precyzji (typowo bf16 albo fp16). QLoRA to sprytny
sposób na <strong>przechowanie</strong> modelu, nie na liczenie w niższej precyzji. Dlatego jakość nie
leci na łeb — arytmetyka zostaje porządna.</p>
</div>
<h2 id="to-nie-jest-to-samo-co-gptq-czy-awq">To nie jest to samo co GPTQ czy AWQ</h2>
<p>Tu łatwo o zamieszanie, więc warto postawić słup graniczny. Kiedy słyszysz „kwantyzacja&quot;,
najczęściej chodzi o coś zupełnie innego niż QLoRA: o GPTQ albo AWQ, czyli metody, które
biorą <strong>gotowy, wytrenowany model i ściskają go pod samą inferencję</strong> (post-training
quantization). Rozgryzaliśmy ten drugi świat osobno <a href="/naukowy/gptq-awq/">tutaj</a> — i to jest
inna bajka.</p>
<p>QLoRA nie służy do odchudzania modelu pod odpalanie. QLoRA to <strong>metoda fine-tuningu</strong> —
kwantyzacja 4-bitowa jest w niej tylko trikiem, który pozwala zmieścić trening na jednej
karcie. Efekt końcowy to wytrenowane adaptery LoRA, które doklejasz do modelu. Zapamiętaj
prostą regułę: GPTQ/AWQ to „jak taniej <strong>uruchomić</strong> cudzy model&quot;, QLoRA to „jak tanio
<strong>dostroić</strong> model pod siebie&quot;. Mylenie tych dwóch to klasyk początkującego na forach.</p>
<h2 id="guanaco-czyli-dowód-że-to-działa">Guanaco, czyli dowód, że to działa</h2>
<p>Teoria teorią, ale autorzy poszli na całość: tą metodą dostroili <strong>ponad 1000 modeli</strong> —
różne architektury (LLaMA, T5), różne skale (7B, 13B, 33B, 65B), osiem zestawów danych
instrukcyjnych. Skale, których zwykłym fine-tuningiem nikt by na tym sprzęcie nie ruszył.<sup id="fnref3:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p>Najlepsza rodzina dostała nazwę <strong>Guanaco</strong> (tak, po tej andyjskiej kuzynce lamy — w tym
świecie wszystko jest lamą). Trenowana na danych OpenAssistant, w benchmarku Vicuna
przebiła wszystkie wcześniej publicznie wydane modele i osiągnęła <strong>99,3% poziomu ChatGPT</strong>
— przy zaledwie <strong>24 godzinach</strong> fine-tuningu na jednej karcie.<sup id="fnref4:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p>Tu tylko jedna uczciwa gwiazdka, którą stawiają sami autorzy: te 99,3% to wynik z
konkretnego benchmarku Vicuna, ocenianego przez GPT-4 — nie jakaś uniwersalna miara „model
jest w 99% tak dobry jak ChatGPT do wszystkiego&quot;. Praca wprost pokazuje, że ocena GPT-4 to
tania i sensowna alternatywa dla oceny ludzkiej, ale zastrzega, że ówczesne benchmarki
chatbotów nie były w pełni wiarygodne do precyzyjnego rankingowania modeli.<sup id="fnref5:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> Liczba
robi wrażenie — traktuj ją jednak jako „zaskakująco blisko&quot;, a nie jako wyrok.</p>
<h2 id="zrób-to-sam-a-przynajmniej-wiedz-gdzie-klikać">Zrób to sam (a przynajmniej wiedz, gdzie klikać)</h2>
<p>Najlepsze jest to, że QLoRA nie została na papierze jako ciekawostka. Kod referencyjny i
modele Guanaco wylądowały na licencji MIT w repo <a href="https://github.com/artidoro/qlora">artidoro/qlora</a>,
razem z jądrami CUDA do treningu 4-bitowego.<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> A w praktyce prawie nikt nie pisze tego
od zera — całość jest wpięta w ekosystem Hugging Face. Kwantyzację robi biblioteka
<strong>bitsandbytes</strong>, adaptery dokłada <strong>PEFT</strong>, a spina to wszystko <code>transformers</code>.</p>
<p>Sercem konfiguracji jest <code>BitsAndBytesConfig</code> — i to w nim odnajdziesz jak żywe wszystkie
trzy patenty z papieru:<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-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">transformers</span> <span class="kn">import</span> <span class="n">BitsAndBytesConfig</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">torch</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">bnb_config</span> <span class="o">=</span> <span class="n">BitsAndBytesConfig</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">load_in_4bit</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>                    <span class="c1"># wagi bazowe w 4 bitach</span>
</span></span><span class="line"><span class="cl">    <span class="n">bnb_4bit_quant_type</span><span class="o">=</span><span class="s2">&#34;nf4&#34;</span><span class="p">,</span>            <span class="c1"># patent nr 1: NF4, nie zwykły FP4</span>
</span></span><span class="line"><span class="cl">    <span class="n">bnb_4bit_use_double_quant</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>       <span class="c1"># patent nr 2: podwójna kwantyzacja</span>
</span></span><span class="line"><span class="cl">    <span class="n">bnb_4bit_compute_dtype</span><span class="o">=</span><span class="n">torch</span><span class="o">.</span><span class="n">bfloat16</span> <span class="c1"># obliczenia w bf16, nie w 4 bitach!</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span></code></pre></div><p>Popatrz na ostatnią linijkę — <code>compute_dtype</code> to bf16 (a czym <code>bf16</code> różni się od <code>f16</code>, rozbieramy <a href="/poradniki/bf16-f16-f32/">osobno</a>), dokładnie ta wyższa precyzja
obliczeń, o której gadaliśmy wyżej. Wagi leżą w 4 bitach, ale liczy się porządnie.</p>
<p>A ile realnie karty to zjada? Zależy od modelu, długości sekwencji, batcha i checkpointingu
— liczby 65B/48 GB z papieru to konkretny eksperyment, nie uniwersalna tabelka. Ale skala
robi wrażenie z drugiej strony też: dzięki nested quantization dokumentacja Hugging Face
pokazuje dostrojenie <strong>Llama-13B na jednej karcie T4 16 GB</strong> — sekwencja 1024 tokeny,
batch 1, gradient accumulation na 4 kroki.<sup id="fnref1:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> T4 to karta, którą dostajesz za darmo
w Google Colab.</p>
<table>
	<thead>
			<tr>
					<th>Metoda</th>
					<th>Co robi z wagami bazowymi</th>
					<th>Ile parametrów trenujesz</th>
					<th>Po co</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Pełny fine-tuning</td>
					<td>16 bitów, wszystkie odmrożone</td>
					<td>wszystkie</td>
					<td>najdroższy, „złoty standard&quot;</td>
			</tr>
			<tr>
					<td>LoRA</td>
					<td>16 bitów, zamrożone</td>
					<td>tylko adaptery</td>
					<td>tanio, ale wagi 16-bit dalej na karcie</td>
			</tr>
			<tr>
					<td><strong>QLoRA</strong></td>
					<td><strong>4 bity (NF4), zamrożone</strong></td>
					<td><strong>tylko adaptery</strong></td>
					<td><strong>tanio i mieści się na jednej karcie</strong></td>
			</tr>
			<tr>
					<td>GPTQ / AWQ</td>
					<td>4 bity, pod inferencję</td>
					<td>nic (to nie trening)</td>
					<td>tanie odpalanie gotowca</td>
			</tr>
	</tbody>
</table>
<p>Jeśli martwi cię, czy w ogóle zmieścisz coś na swojej karcie, to osobna rachunkowość —
zbieraliśmy wzory na VRAM <a href="/poradniki/ile-vram-na-model/">tutaj</a>. Ale morał QLoRA jest
prosty: bilet wstępu spadł tak, że fine-tuning przestał być salą VIP.</p>
<p>Przez lata fine-tuning działał jak wędkarstwo z burty jachtu — niby każdy może, tylko
najpierw kup jacht. QLoRA wsadziła ci w rękę wędkę i pokazała, że ryba bierze tak samo z
pomostu za pięć złotych. Zamrożone wagi ściśnięte do czterech bitów, garść adapterów do
dotrenowania, jedna karta pod biurkiem — i model, który jeszcze wczoraj wymagał serwerowni,
uczy się twojego żargonu przez noc. Nie dlatego, że ktoś dołożył ci pamięci. Dlatego, że
ktoś w końcu zapytał, czy naprawdę trzeba trzymać w pełnej precyzji coś, czego i tak nikt
w treningu nie rusza.</p>
<hr>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Tim Dettmers, Artidoro Pagnoni, Ari Holtzman, Luke Zettlemoyer, <em>QLoRA: Efficient Finetuning of Quantized LLMs</em>, arXiv:2305.14314 (23 maja 2023) — abstrakt: 65B na jednej karcie 48 GB przy jakości 16-bit, NF4, double quantization, paged optimizers, rodzina Guanaco 99,3% ChatGPT w benchmarku Vicuna, ponad 1000 wytrenowanych modeli i zastrzeżenia co do wiarygodności benchmarków. <a href="https://arxiv.org/abs/2305.14314">arxiv.org/abs/2305.14314</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>Hugging Face blog, <em>Making LLMs even more accessible with bitsandbytes, 4-bit quantization and QLoRA</em> — opis trzech innowacji (NF4, double quantization, paged optimizers) i oszczędności ~0,4 bita/parametr z podwójnej kwantyzacji. <a href="https://huggingface.co/blog/4bit-transformers-bitsandbytes">huggingface.co/blog/4bit-transformers-bitsandbytes</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>Oficjalne repozytorium kodu QLoRA — implementacja referencyjna, jądra CUDA do treningu 4-bitowego, modele Guanaco (trenowane na OpenAssistant), licencja MIT. <a href="https://github.com/artidoro/qlora">github.com/artidoro/qlora</a>.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>Hugging Face Transformers, dokumentacja kwantyzacji bitsandbytes — parametry <code>BitsAndBytesConfig</code> (<code>load_in_4bit</code>, <code>bnb_4bit_quant_type=&quot;nf4&quot;</code>, <code>bnb_4bit_use_double_quant</code>, <code>bnb_4bit_compute_dtype</code>) oraz przykład dostrojenia Llama-13B na T4 16 GB z nested quantization. <a href="https://huggingface.co/docs/transformers/main/en/quantization/bitsandbytes">huggingface.co/docs/transformers/main/en/quantization/bitsandbytes</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>KV-cache: dlaczego długi kontekst zjada VRAM</title><link>https://inferownia.pl/poradniki/kv-cache-vram/</link><pubDate>Tue, 14 Jul 2026 00:00:00 +0200</pubDate><category>inferencja</category><guid>https://inferownia.pl/poradniki/kv-cache-vram/</guid><description>Wagi się załadowały, model wszedł na kartę z zapasem — a po dłuższej rozmowie znów wyskakuje out of memory. Winny nie jest model. Winny jest bufor, który puchnie z każdym tokenem.</description><content:encoded><![CDATA[<p>Znasz ten moment, kiedy model ładnie wszedł na kartę, zostało ci jeszcze ze dwa giga
zapasu, odpalasz dłuższą rozmowę albo wklejasz wielki plik do streszczenia — i po
kilkuset tokenach terminal wita cię tym samym, co zawsze: <code>CUDA out of memory</code>. Cała
na biało. I człowiek stoi zdziwiony, bo przecież model się mieścił. Wagi się nie
rozrosły. Karta nie zmalała. To co, u licha, zeżarło resztę pamięci między jednym
tokenem a drugim?</p>
<p>To wręcz podręcznikowy scenariusz — ktoś odpala 7B na karcie 8 GB, chwali się, że
działa, a dwa dni później wraca z płaczem, że „przy dłuższym kontekście się sypie&quot;.
I zawsze pada to samo pytanie w komentarzach: a ustawiłeś sobie cache? No właśnie.
Bo obok wag, które raz się ładują i grzecznie leżą, siedzi drugi żłob na VRAM —
taki, który rośnie z każdym słowem, jakie model przeczyta albo napisze. Nazywa się
KV-cache i jest głównym podejrzanym w prawie każdej sprawie „mieściło się, a potem
przestało&quot;.</p>
<h2 id="skąd-w-ogóle-bierze-się-ten-cache">Skąd w ogóle bierze się ten cache</h2>
<p>Cofnijmy się o krok. Model generuje tekst token po tokenie — jeden na raz, a każdy
kolejny patrzy wstecz na całą dotychczasową historię. Żeby policzyć uwagę (attention)
dla nowego tokenu, potrzebuje wektorów <strong>K</strong> (key) i <strong>V</strong> (value) dla wszystkiego,
co było wcześniej. I teraz klucz do zrozumienia: te wektory się nie zmieniają. K i V
dla trzeciego tokenu są takie same, kiedy generujesz token dziesiąty, jak wtedy,
gdy generowałeś czwarty.</p>
<p>Skoro się nie zmieniają — po co je liczyć od nowa przy każdym kroku? No i nie liczymy.
KV-cache to dokładnie ten bufor, w którym model odkłada raz wyliczone wektory K i V
dla każdego przetworzonego tokenu, w każdej warstwie, żeby przy generowaniu następnego
nie przemielać całej historii od zera.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> To sprytny handel: dokładasz pamięci,
żeby oszczędzić obliczenia. Bez niego każdy nowy token oznaczałby przeliczanie całego
kontekstu w kółko — decode zamieniłby się w mękę.</p>
<p>Cache jest więc dobry. Problem w tym, że za oszczędność obliczeń płacisz pamięcią.
A ta pamięć nie jest stała.</p>
<h2 id="dlaczego-on-rośnie-a-wagi-nie">Dlaczego on rośnie, a wagi nie</h2>
<p>Tu jest sedno całej sprawy, więc rozłóżmy to powoli. Wagi modelu to wartość stała.
Ładujesz plik GGUF raz, siada na karcie i tyle — czy generujesz jeden token, czy
dziesięć tysięcy, wagi zajmują dokładnie tyle samo. KV-cache zachowuje się odwrotnie:
puchnie z każdym tokenem, jaki wpadnie w okno kontekstu.<sup id="fnref1:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p>Ile dokładnie puchnie? Mechanizm da się złapać jednym wzorem, który krąży po całej
scenie:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">rozmiar_KV = 2 × n_layer × n_kv_heads × head_dim × długość_kontekstu × bajty_na_element
</span></span></code></pre></div><p>Rozbierzmy to na palcach. Dwójka na przedzie to osobno tensor K i osobno tensor V —
trzymasz dwa, stąd współczynnik.<sup id="fnref2:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> <code>n_layer</code> to liczba warstw transformera; cache
odkłada się w każdej z osobna. <code>n_kv_heads</code> to liczba głów KV, <code>head_dim</code> to wymiar
jednej głowy. A <code>długość_kontekstu</code> — no i tu jest pies pogrzebany: to jedyny człon,
który rośnie w trakcie. Reszta to stałe architektury danego modelu.</p>
<p>Zauważ, co z tego wynika: rozmiar cache rośnie <strong>liniowo</strong> z długością kontekstu.
Podwoisz kontekst — podwoisz cache. I liniowo z liczbą warstw oraz głów KV. Ani razu
w tym wzorze nie pada rozmiar wag.<sup id="fnref3:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> To dwa niezależne budżety pamięci.</p>
<div class="callout">
  <span class="callout__label">Dlaczego to ważne dla lokalnego LLM</span>
<p>Częsty mit z forów: „KV-cache rośnie kwadratowo z kontekstem&quot;. Nie. <strong>Sam bufor rośnie
liniowo</strong> — podwajasz tokeny, podwajasz cache. Kwadratowy jest koszt <em>obliczeniowy</em>
samej operacji attention w naiwnej implementacji (i pamięć pośrednia, którą ona
zżera po drodze) — i to właśnie ten kwadratowy narzut łagodzi flash attention. Pamięć
samego cache to prosta linia w górę.<sup id="fnref4:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
</div>
<p>Skoro cache rośnie z kontekstem, a wagi stoją w miejscu, to przy dostatecznie długim
oknie następuje przecięcie: KV-cache zaczyna dominować zużycie VRAM, a przy naprawdę
długim kontekście potrafi przerosnąć rozmiar samych wag.<sup id="fnref5:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> I stąd twoje <code>out of memory</code> w połowie rozmowy — nie model spuchł, tylko jego pamięć krótkotrwała napęczniała
do rozmiarów, których karta nie udźwignęła. Jeśli chcesz policzyć ten budżet z wyprzedzeniem,
zamiast zgadywać, sam wzór na VRAM rozkładaliśmy krok po kroku <a href="/poradniki/ile-vram-na-model/">tutaj</a>.</p>
<h2 id="jak-go-ścisnąć--trzy-dźwignie">Jak go ścisnąć — trzy dźwignie</h2>
<p>Dobra wiadomość: skoro wiesz, z czego składa się wzór, wiesz też, gdzie przyłożyć.
Masz trzy dźwignie, każda ciągnie za inny człon.</p>
<h3 id="kwantyzacja-cache--najprostszy-zysk">Kwantyzacja cache — najprostszy zysk</h3>
<p>Spójrz na ostatni człon wzoru: <code>bajty_na_element</code>. Domyślnie cache K i V w llama.cpp
siedzą w <code>f16</code>, czyli po 2 bajty na element<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> — a czym <code>f16</code> różni się od <code>bf16</code>, tłumaczymy <a href="/poradniki/bf16-f16-f32/">tutaj</a>. A nikt nie powiedział, że musi
tak zostać. <code>llama.cpp</code> daje dwie flagi — <code>-ctk</code> (<code>--cache-type-k</code>) i <code>-ctv</code>
(<code>--cache-type-v</code>) — którymi ustawiasz typ danych osobno dla cache K i cache V.
Dozwolone wartości to <code>f32</code>, <code>f16</code>, <code>bf16</code>, <code>q8_0</code>, <code>q4_0</code>, <code>q4_1</code>, <code>iq4_nl</code>, <code>q5_0</code>,
<code>q5_1</code>; domyślnie oba stoją na <code>f16</code>.<sup id="fnref1:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<p>Matematyka jest tu boleśnie prosta. <code>q8_0</code> to 1 bajt na element zamiast dwóch — czyli
z grubsza <strong>dwa razy mniej</strong> pamięci na cache. <code>q4_0</code> to pół bajta — jakieś <strong>cztery
razy mniej</strong> niż <code>f16</code>.<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> Nie podam ci, ile to konkretnie megabajtów, bo to zależy
od <code>n_layer</code>, <code>n_kv_heads</code>, <code>head_dim</code> i długości kontekstu twojego modelu — kto rzuca
sztywnym „zaoszczędzisz 3 GB&quot;, ten zgaduje. Ale proporcja trzyma się zawsze: ścinasz
typ, ścinasz cache w tym samym stosunku.</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-q4_k_m.gguf -c <span class="m">8192</span> -fa on -ctk q8_0 -ctv q8_0
</span></span></code></pre></div><p>I tu ważne rozróżnienie, na którym potyka się pół forum: <strong>to nie jest to samo, co
kwantyzacja wag</strong>. Wagi kwantyzujesz przy pakowaniu GGUF-a (te wszystkie <code>Q4_K_M</code>
i spółka — nazewnictwo rozbieraliśmy <a href="/poradniki/nazwy-kwantyzacji-gguf/">tutaj</a>).
Cache kwantyzujesz flagami runtime, w locie. To dwa niezależne mechanizmy. Ściśnięcie
wag do 4 bitów ani o bajt nie zmniejszy KV-cache, i odwrotnie. Ktoś odpala model
w <code>Q4_K_M</code> i dziwi się, że kontekst dalej zjada pamięć — no bo cache dalej siedzi
w <code>f16</code>, dopóki mu tego ręcznie nie zmienisz.</p>
<h3 id="flash-attention--i-warunek-wstępny">Flash attention — i warunek wstępny</h3>
<p>Druga dźwignia to <code>-fa</code> (<code>--flash-attn</code>). Flaga przyjmuje <code>on</code>, <code>off</code> albo <code>auto</code>,
domyślnie stoi na <code>auto</code>.<sup id="fnref2:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> Co robi flash attention? To nie żadne przybliżenie
uwagi — i tu kolejna pułapka do ominięcia. FlashAttention (Dao i in., NeurIPS 2022)
to algorytm <strong>dokładny</strong> (exact): liczy dokładnie to samo, co naiwna uwaga, wynik
liczbowy jest identyczny.<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> Sztuczka jest gdzie indziej — jest „IO-aware&quot;, czyli
ogranicza liczbę odczytów i zapisów między wolną pamięcią HBM karty a szybką pamięcią
on-chip SRAM. Efekt: szybciej i z mniejszym narzutem pamięci pomocniczej niż naiwna
implementacja uwagi.</p>
<p>Ale flash attention ma tu jeszcze jedną, praktyczną rolę — jest <strong>warunkiem wstępnym</strong>
kwantyzacji cache V. W llama.cpp nie da się skwantyzować cache V bez włączonego flash
attention; implementacja zwyczajnie odmawia startu, rzucając runtime error w rodzaju
<code>quantized V cache was requested, but this requires Flash Attention</code>.<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup> Czyli jeśli chcesz <code>-ctv q8_0</code>, musisz najpierw dać <code>-fa on</code>.
Bez tego dostaniesz błąd, nie oszczędność.</p>
<h3 id="gqa--dźwignia-której-nie-przełączysz">GQA — dźwignia, której nie przełączysz</h3>
<p>Trzeci człon wzoru to <code>n_kv_heads</code>. Im mniej głów KV, tym mniejszy cache — wprost,
liniowo. I dokładnie na to celuje <strong>Grouped-Query Attention</strong> (GQA, Ainslie i in.,
EMNLP 2023).<sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup> Idea: zamiast tylu głów KV, ile jest głów zapytań (pełna uwaga
wielogłowowa, MHA), albo tylko jednej wspólnej (multi-query attention, MQA), bierzesz
liczbę pośrednią. Kilka głów zapytań dzieli jedną głowę KV. Skoro <code>n_kv_heads</code> w naszym
wzorze spada — spada i cache, przy jakości bliskiej pełnemu MHA i szybkości bliskiej
MQA.<sup id="fnref1:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup></p>
<p>Co ciekawe, model GQA nie powstaje od zera. Bierze się istniejący checkpoint MHA
i „doucza&quot; (uptraining) przy jakichś 5% oryginalnego budżetu treningu — tanio, bez
budowania nowej architektury od podstaw.<sup id="fnref2:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup></p>
<p>Jest tylko jeden haczyk, o którym musisz pamiętać: <strong>GQA to nie przełącznik</strong>.
To cecha architektury zapieczona w modelu podczas treningu. Nie ma flagi CLI, która
„włączy GQA&quot; na dowolnym modelu. Możesz co najwyżej <em>wybrać</em> model, który już ma
GQA — a większość nowszych 7B/8B ma — i taki z natury nosi mniejszy cache przy tej
samej długości kontekstu co stary model z pełnym MHA. To kryterium przy pobieraniu,
nie pokrętło w terminalu.</p>
<h2 id="zrób-to-sam-kolejność-ratowania-pamięci">Zrób to sam: kolejność ratowania pamięci</h2>
<p>No dobra, dość teorii — masz <code>out of memory</code> i chcesz uratować kontekst. Kolejność
ma znaczenie, bo pierwsze ruchy kosztują cię grosze na jakości, a ostatnie bolą.</p>
<ol>
<li><strong>Najpierw <code>-fa on</code>.</strong> Ścina narzut pamięci samej operacji uwagi i — co równie
ważne — odblokowuje kwantyzację cache V. To krok zero, nie negocjujemy.<sup id="fnref1:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup></li>
<li><strong>Potem skwantyzuj cache: <code>-ctk q8_0 -ctv q8_0</code>.</strong> <code>q8_0</code> to bezpieczny wybór —
połowa pamięci cache, strata jakości ledwo wyczuwalna. Dopiero gdy VRAM dalej
piszczy, schodź do <code>-ctk q4_0 -ctv q4_0</code> — ćwiartka pamięci, ale za to już płacisz
odczuwalniej jakością.<sup id="fnref1:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup></li>
<li><strong>Wybierając model, celuj w te z GQA.</strong> Tego nie zrobisz flagą po fakcie — to
decyzja przy pobieraniu. Model z GQA startuje z mniejszym cache, zanim jeszcze
dotkniesz jakiegokolwiek przełącznika.<sup id="fnref3:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup></li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># krok 1 + 2 razem — tak wygląda ratunek w praktyce</span>
</span></span><span class="line"><span class="cl">llama-server -m model-q4_k_m.gguf -c <span class="m">16384</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  -fa on -ctk q8_0 -ctv q8_0
</span></span></code></pre></div><p>Dopiero kiedy to wszystko wyczerpiesz, a i tak nie wchodzi — wtedy tnij samą długość
kontekstu (<code>-c</code>) albo schodź z warstw na GPU (<code>-ngl</code>). Ale to już ostatnia deska
ratunku, bo pierwsze odbiera modelowi pamięć, a drugie prędkość. Sam zestaw flag i to, co która
robi, przeszliśmy po kolei w <a href="/poradniki/flagi-llama-cpp/">osobnym wpisie o flagach llama.cpp</a>.</p>
<p>Pomyśl o tym tak. Wagi modelu to biblioteka — stoi w regale, zajmuje swoje półki
i tyle, nieważne, ile razy do niej zajrzysz. KV-cache to biurko, na którym rozkładasz
wszystko, co akurat czytasz. Im dłużej pracujesz nad jednym tematem, tym więcej kartek
ląduje na blacie — i w pewnym momencie nie ma gdzie postawić kubka, choć regał ani
drgnął. Kwantyzacja cache to składanie tych kartek na pół, flash attention to
sprzątanie w locie, GQA to od razu mniejszy stos notatek na tę samą robotę.</p>
<p>Karta się nie skurczyła. To po prostu twoja rozmowa urosła — a teraz już wiesz,
który człon wzoru za to odpowiada i za które pokrętło pociągnąć, zanim znów zobaczysz
to <code>out of memory</code>.</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>Mechanizm KV-cache: bufor par klucz-wartość liczonych raz na token i warstwę, o rozmiarze <code>2 × n_layer × n_kv_heads × head_dim × długość_kontekstu × bajty_na_element</code> — rośnie liniowo z kontekstem, niezależnie od rozmiaru wag; kwadratowy jest jedynie koszt obliczeniowy naiwnej uwagi. Wyprowadzenie mechanizmu spójne z definicją flash attention w FlashAttention (Dao i in.), <a href="https://arxiv.org/abs/2205.14135">arXiv:2205.14135</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>llama.cpp, definicje flag <code>-ctk</code>/<code>--cache-type-k</code>, <code>-ctv</code>/<code>--cache-type-v</code> (dozwolone typy: <code>f32</code>, <code>f16</code>, <code>bf16</code>, <code>q8_0</code>, <code>q4_0</code>, <code>q4_1</code>, <code>iq4_nl</code>, <code>q5_0</code>, <code>q5_1</code>; domyślnie <code>f16</code>) oraz <code>-fa</code>/<code>--flash-attn</code> (<code>on</code>/<code>off</code>/<code>auto</code>, domyślnie <code>auto</code>), <a href="https://github.com/ggml-org/llama.cpp/blob/master/common/arg.cpp">common/arg.cpp</a>. Te same parametry w <a href="https://github.com/ggml-org/llama.cpp/blob/master/tools/server/README.md">dokumentacji serwera</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>q8_0</code> = 1 bajt na element (ok. 2× mniej niż <code>f16</code>), <code>q4_0</code> = pół bajta (ok. 4× mniej niż <code>f16</code>); dokładne MB zależą od <code>n_layer</code>, <code>n_kv_heads</code>, <code>head_dim</code> i długości kontekstu konkretnego modelu. Typy zdefiniowane w llama.cpp, <a href="https://github.com/ggml-org/llama.cpp/blob/master/common/arg.cpp">common/arg.cpp</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>FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness (Dao, Fu, Ermon, Rudra, Ré, NeurIPS 2022) — dokładny, IO-aware algorytm uwagi redukujący odczyty/zapisy między HBM a SRAM GPU; nie jest aproksymacją uwagi, <a href="https://arxiv.org/abs/2205.14135">arXiv:2205.14135</a>.&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p>Zależność wymuszona w kodzie llama.cpp: przy wyłączonym flash attention skwantyzowany cache V rzuca runtime error <code>quantized V cache was requested, but this requires Flash Attention</code> — sprawdzenie w konstruktorze kontekstu, <a href="https://github.com/ggml-org/llama.cpp/blob/master/src/llama-context.cpp">src/llama-context.cpp</a>.&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6">
<p>GQA: Training Generalized Multi-Query Transformer Models from Multi-Head Checkpoints (Ainslie i in., EMNLP 2023) — grouped-query attention jako interpolacja między MHA a MQA, redukcja liczby głów KV, uptraining z ok. 5% oryginalnego budżetu treningu, <a href="https://arxiv.org/abs/2305.13245">arXiv:2305.13245</a>.&#160;<a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref2:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref3:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>BitNet b1.58: era 1-bitowych LLM-ów</title><link>https://inferownia.pl/naukowy/bitnet-1bit/</link><pubDate>Mon, 13 Jul 2026 00:00:00 +0200</pubDate><author>Shuming Ma</author><author>Hongyu Wang</author><author>Lingxiao Ma</author><author>Lei Wang</author><author>Wenhui Wang</author><author>Shaohan Huang</author><author>Li Dong</author><author>Ruiping Wang</author><author>Jilong Xue</author><author>Furu Wei</author><category>kwantyzacja</category><guid>https://inferownia.pl/naukowy/bitnet-1bit/</guid><description>Miliardy wag modelu ściśnięte do trzech wartości: minus jeden, zero, plus jeden. Bez mnożeń, prawie bez pamięci — i, od pewnego rozmiaru, prawie bez straty jakości. Rozbieramy pracę, która ogłosiła erę 1-bitowych LLM-ów.</description><content:encoded><![CDATA[<p>Odchudzanie modelu to sport bez mety — zawsze znajdzie się ktoś, kto urwie jeszcze jeden bit.
Najpierw było szesnaście bitów na wagę i pliki, przy których dysk kwiczał. Potem przyszła kwantyzacja
po treningu i zeszliśmy na osiem, na cztery, gdzieś na dnie forum ktoś odpalał dwa. Za każdym
razem wracało to samo pytanie — „a da się jeszcze niżej?&quot; — i za każdym razem ktoś
mądrzejszy odpowiadał, że przy dwóch bitach model zaczyna gadać jak po trzech piwach. No i
w tym całym schodzeniu w dół pada w końcu pytanie, które brzmi jak żart: a gdyby tak jeden
bit? Gdyby waga mogła być tylko włączona albo wyłączona?</p>
<p>Okazuje się, że to nie żart. W lutym 2024 grupa z Microsoft Research położyła na arXiv pracę
o wdzięcznym tytule „The Era of 1-bit LLMs&quot; i pokazała coś, co na pierwszy rzut oka wygląda
na herezję: model, w którym każda waga przyjmuje jedną z trzech wartości — minus jeden, zero
albo plus jeden — a mimo to od pewnego rozmiaru dorównuje pełnoprecyzyjnej LLaMie.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>
Nazwali go BitNet b1.58. I zanim złapiesz się na tym „1.58 bita&quot; jak na literówce — spokojnie,
to nie literówka. To rozłóżmy na części.</p>
<h2 id="skąd-te-158-bita--czyli-dlaczego-nie-okrągła-jedynka">Skąd te 1,58 bita — czyli dlaczego nie okrągła jedynka</h2>
<p>Najpierw sama nazwa, bo ona zdradza cały pomysł. Skoro waga może być tylko jedną z trzech
rzeczy — <code>-1</code>, <code>0</code> albo <code>+1</code> — to ile informacji trzeba, żeby taki stan zapisać? Logarytm
przy podstawie dwa z trzech. A <code>log2(3)</code> to jakieś 1,58.<sup id="fnref1:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> Stąd <code>b1.58</code>: nie dlatego,
że ktoś w pamięci trzyma ułamki bitów, tylko dlatego, że tyle wynosi teoretyczny koszt
informacyjny wyboru jednego z trzech wariantów.</p>
<p>I tu pierwsza pułapka, w którą łatwo wdepnąć: <strong>1,58 bita to nie jest dosłowna liczba bitów,
jaką waga zajmuje na dysku.</strong> To granica informacyjna, nie format zapisu. W praktycznych
implementacjach — kiedy ktoś pakuje taki model do GGUF-a — wartość ternarna i tak ląduje
zwykle na dwóch bitach plus trochę struktury dookoła. Więc jak zobaczysz „1.58-bit&quot; na karcie
modelu, czytaj to jako „trzy stany na wagę&quot;, a nie jako obietnicę, że plik jest fizycznie
pięciokrotnie mniejszy niż czterobitowy. To dwa różne piętra: ile <em>musisz</em> zapamiętać, a jak
<em>akurat</em> to zapakowałeś.</p>
<p>Dlaczego akurat ternarne, a nie czyste zero-jeden? Bo to zero pośrodku robi robotę. Waga
równa zeru to po ludzku „to połączenie mnie nie obchodzi&quot; — model dostaje wbudowaną możliwość
wycięcia neuronu z równania. Oryginalny BitNet, wcześniejsza praca tej samej ekipy z 2023
roku, jechał na czystych wagach binarnych, tylko <code>-1</code> i <code>+1</code>.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> Dorzucenie zera to
właśnie ten skok od BitNetu do BitNetu b1.58 — z pozoru drobny, a robi całą różnicę w jakości.</p>
<h2 id="trenowany-od-zera--i-czemu-to-nie-jest-zwykła-kwantyzacja">„Trenowany od zera&quot; — i czemu to nie jest zwykła kwantyzacja</h2>
<p>Teraz sedno, bo tu większość ludzi przy pierwszym czytaniu odruchowo myśli
o złym torze. Kiedy słyszysz „model na trzech wartościach&quot;, w głowie zapala się „aha, czyli
kolejna kwantyzacja&quot; — bierzemy gotową, wytrenowaną w FP16 sieć i po fakcie zaokrąglamy jej
wagi. Tak działa GGUF-owe <code>Q4_K_M</code>, tak działają GPTQ czy AWQ — rozgryzaliśmy te potreningowe
sztuczki <a href="/naukowy/gptq-awq/">tutaj</a>. Model istnieje najpierw w pełnej precyzji, a dopiero
potem go ściskasz i modlisz się, że nie zgłupieje.</p>
<p><strong>BitNet b1.58 działa dokładnie odwrotnie.</strong> Nikt tu niczego nie kwantyzuje po fakcie. Model
jest trenowany od zera z wagami zamkniętymi w tych trzech wartościach — sieć od pierwszego
kroku uczy się żyć w ternarnym świecie i sama układa sobie wagi tak, żeby jej to nie
przeszkadzało.<sup id="fnref2:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> To fundamentalna różnica: taki model <strong>nigdy nie istniał w FP16.</strong> Nie
ma oryginału, z którego zszedłeś w dół i coś po drodze zgubiłeś. Ternarność nie jest stratą
nałożoną na koniec — jest warunkiem brzegowym od startu.</p>
<table>
	<thead>
			<tr>
					<th></th>
					<th>Kwantyzacja po treningu (PTQ)</th>
					<th>BitNet b1.58</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Kiedy niska precyzja</td>
					<td>po treningu, na gotowej sieci</td>
					<td>od pierwszego kroku treningu</td>
			</tr>
			<tr>
					<td>Czy istnieje wersja FP16</td>
					<td>tak, to oryginał</td>
					<td>nie, nigdy nie istniała</td>
			</tr>
			<tr>
					<td>Wagi</td>
					<td>ściśnięte np. do 4 bitów</td>
					<td>ternarne <code>{-1, 0, +1}</code></td>
			</tr>
			<tr>
					<td>Aktywacje</td>
					<td>różnie</td>
					<td>8-bitowe (schemat W1.58A8)</td>
			</tr>
			<tr>
					<td>Typowe ryzyko</td>
					<td>jakość spada przy mocnym cięciu</td>
					<td>luka jakości znika dopiero od pewnego rozmiaru</td>
			</tr>
	</tbody>
</table>
<p>Skąd model wie, na którą z trzech wartości zaokrąglić daną wagę podczas treningu? Robi to
funkcja o ładnej nazwie <code>absmean</code>. Cała macierz wag jest najpierw skalowana przez swoją
średnią wartość bezwzględną, a potem każda liczba idzie do najbliższej całkowitej ze zbioru
<code>{-1, 0, +1}</code>.<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> Proste jak konstrukcja cepa i o to chodzi — im mniej fikuśne, tym
lepiej się liczy.</p>
<h2 id="mnożenie-jakie-mnożenie">Mnożenie? Jakie mnożenie</h2>
<p>Tu wchodzi część, dla której cały ten cyrk się opłaca. Serce każdego LLM-a to mnożenie
macierzy — miliardy mnożeń liczby przez liczbę, i to właśnie one zżerają prąd i czas. A teraz
policz na palcach, co się dzieje, kiedy jeden z czynników może być tylko <code>-1</code>, <code>0</code> albo <code>+1</code>.
Mnożenie przez <code>+1</code>? To po prostu dodaj tę drugą liczbę. Przez <code>-1</code>? Odejmij. Przez <code>0</code>?
Pomiń, nic nie rób.</p>
<p>I nagle znika mnożenie. Zostają same dodawania i odejmowania — a te są dla krzemu dużo
tańsze. Praca podaje twardą liczbę: dla samej operacji mnożenia macierzy to około <strong>71 razy
mniej energii</strong> na chipie 7nm w porównaniu z odpowiednikiem w FP16.<sup id="fnref1:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> Nie o kilka
procent — o rząd wielkości i to z hakiem. To nie jest „lepsza kompresja&quot;, to zmiana tego,
jaką operację w ogóle wykonuje procesor.</p>
<div class="callout">
  <span class="callout__label">Dlaczego to ważne dla lokalnego LLM</span>
<p>Dla ciebie, człowieka z jedną kartą pod biurkiem, znaczenie jest dwustronne. Z jednej strony
mniej pamięci i mniej prądu na token to marzenie każdego, kto liczył, ile VRAM-u zeżre
<a href="/poradniki/kv-cache-vram/">KV-cache przy długim kontekście</a>. Z drugiej — mnożenie zamienione
na dodawanie najlepiej opłaca się na sprzęcie, który to rozumie. Autorzy wprost sugerują, że
era 1-bitowych modeli woła o nowy, dedykowany krzem projektowany pod ternarną arytmetykę.<sup id="fnref2:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>
Dzisiejsze GPU są zbudowane pod mnożenie zmiennoprzecinkowe — więc pełnię tej obietnicy
zobaczymy dopiero, gdy hardware nadgoni pomysł.</p>
</div>
<h2 id="haczyk-to-działa-dopiero-od-pewnego-rozmiaru">Haczyk: to działa dopiero od pewnego rozmiaru</h2>
<p>Zanim pobiegniesz ogłaszać koniec FP16, jedna trzeźwa uwaga — i to taka, którą sami autorzy
kładą na stół, bez chowania pod dywan. Ta cudowna „jakość jak w pełnej precyzji&quot; <strong>nie pojawia
się na małych modelach.</strong> Przy 700M czy 1,3B parametrów między BitNetem b1.58 a pełną LLaMą
wciąż widać zauważalną lukę — ternarny model gada wyraźnie gorzej.<sup id="fnref3:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup></p>
<p>Magia zaczyna się gdzieś od 3 miliardów parametrów w górę. Dopiero od tego progu BitNet b1.58
dogania pełnoprecyzyjną LLaMę zarówno pod względem perplexity (miary tego, jak model „dziwi się&quot;
tekstowi), jak i na zadaniach końcowych.<sup id="fnref4:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> Model 3,9B potrafi już przebić 3B LLaMę — i to
przy mniejszym zużyciu pamięci oraz niższej latencji.<sup id="fnref5:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> Reszta liczb z pracy jest w tym
samym duchu: redukcja pamięci 2,6–3,55×, przyspieszenie generacji 1,23–4,1× (rośnie z
rozmiarem), a dla modelu 70B przepustowość wyższa 8,9× przy jedenastokrotnie większym
batchu.<sup id="fnref6:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup></p>
<p>Stąd bierze się najciekawszy wniosek pracy — nowe „prawo skalowania&quot;. Autorzy pokazują, że
13-miliardowy BitNet b1.58 potrafi być bardziej efektywny — pamięciowo, energetycznie i
czasowo — niż zwykły model 3B w FP16.<sup id="fnref7:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> Innymi słowy: przestań patrzeć na samą liczbę
parametrów. Przy ternarnych wagach większy model może być lżejszy w użyciu od mniejszego
pełnoprecyzyjnego. To wywraca do góry nogami intuicję, którą wszyscy nosimy — że więcej
parametrów zawsze znaczy cięższy plik.</p>
<h2 id="zrób-to-sam--microsoft-wypuścił-konkretny-model">Zrób to sam — Microsoft wypuścił konkretny model</h2>
<p>To nie zostało teorią na papierze. Microsoft wydał gotowy, produkcyjny model:
<code>microsoft/bitnet-b1.58-2B-4T</code> — około 2 miliardów parametrów, wytrenowany na 4 bilionach
tokenów, na licencji MIT.<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> Pod maską to transformer z warstwami BitLinear (to w nich
siedzą te ternarne wagi), z RoPE, z aktywacją squared ReLU w FFN, normalizacją subln i bez
biasów.<sup id="fnref1:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> Schemat, który już znasz z tego wpisu: W1.58A8 — wagi 1,58-bitowe, aktywacje
ośmiobitowe.</p>
<p>I teraz najważniejsza pułapka praktyczna, bo tu połowa ludzi się nabiera. <strong>Jeśli odpalisz
ten model przez zwykłą bibliotekę <code>transformers</code> z Hugging Face — nie zobaczysz żadnych
oszczędności.</strong> Karta modelu mówi to wprost: efektywność pamięci i energii ujawnia się
wyłącznie z dedykowaną implementacją.<sup id="fnref2:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> <code>transformers</code> policzy ci ten model jak każdy
inny, gęsto i drogo — bo nie umie skorzystać z tego, że mnożenia są zbędne. To trochę jak
kupić samochód na prąd i tankować go benzyną z kanistra.</p>
<p>Tą dedykowaną implementacją jest <code>bitnet.cpp</code> — framework inferencyjny z repozytorium
<code>microsoft/BitNet</code> na GitHubie.<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup> I tu druga pułapka, tym razem dla porządkowych: to
<strong>nie</strong> jest część <code>ggml-org</code>, mimo że stoi na llama.cpp i pachnie tym samym ekosystemem.
To osobny projekt utrzymywany przez Microsoft, opisany we własnej pracy na arXiv.<sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup>
Dostarcza dedykowane kernele — <code>I2_S</code> na x86 i ARM, <code>TL1</code> na ARM, <code>TL2</code> na x86 — i to one
robią tę robotę z zamianą mnożeń na dodawania.<sup id="fnref1:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git clone --recursive https://github.com/microsoft/BitNet.git
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> BitNet
</span></span><span class="line"><span class="cl"><span class="c1"># dokładne kroki (setup_env.py, wybór kernela, pobranie GGUF-a)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># są w README repozytorium — trzymaj się ich, bo się zmieniają</span>
</span></span></code></pre></div><p>Liczby z <code>bitnet.cpp</code> są równie soczyste jak z papieru: na ARM-owym CPU przyspieszenia
1,37–5,07× i redukcja energii 55–70%, na x86 przyspieszenia 2,37–6,17× i redukcja energii
72–82%.<sup id="fnref2:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup> Wisienka na torcie: framework pozwala uruchomić model klasy 100B na pojedynczym
CPU z prędkością rzędu 5–7 tokenów na sekundę.<sup id="fnref3:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup> Sto miliardów parametrów. Na procesorze.
Bez karty. Przeczytaj to zdanie jeszcze raz i przypomnij sobie, ile kosztuje GPU zdolne
udźwignąć taki model po staremu.</p>
<p>Cała droga lokalnego LLM-owca to jedno długie schodzenie po drabinie precyzji. Szesnaście
bitów, osiem, cztery — i za każdym razem ktoś na forum wróżył, że niżej już się nie da, że
na dole tej drabiny model rozpłynie się w bełkocie. BitNet b1.58 nie tyle zszedł o kolejny
szczebel, co zauważył, że drabina od początku stała nie tam. Zamiast ściskać gotowe liczby
coraz mocniej — nauczył sieć od urodzenia myśleć trzema stanami i wyrzucił mnożenie za burtę.</p>
<p>Czy to już koniec ery zmiennoprzecinkowej? Nie dziś — dopóki krzem pod biurkiem woli mnożyć
niż dodawać, pełnia tej obietnicy leży zapakowana i czeka na sprzęt, który ją rozpakuje. Ale
kierunek jest wytyczony, model do ściągnięcia leży na Hugging Face, a <code>bitnet.cpp</code> już liczy.
Czasem nie zmieniasz odpowiedzi na stare pytanie — zmieniasz pytanie. Nie „ile bitów damy
radę utrzymać&quot;, tylko „po co nam w ogóle mnożenie&quot;.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Ma, Wang, Ma i in., <em>The Era of 1-bit LLMs: All Large Language Models are in 1.58 Bits</em>, arXiv:2402.17764, <a href="https://arxiv.org/abs/2402.17764">arxiv.org/abs/2402.17764</a> — tytuł, autorzy, data zgłoszenia (27 lutego 2024), definicja wag ternarnych <code>{-1, 0, +1}</code>, <code>log2(3) ≈ 1,58 bita</code> i trening od zera.&#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></p>
</li>
<li id="fn:2">
<p>Wang i in., <em>BitNet: Scaling 1-bit Transformers for Large Language Models</em>, arXiv:2310.11453, <a href="https://arxiv.org/abs/2310.11453">arxiv.org/abs/2310.11453</a> — poprzednik b1.58 z wagami binarnymi <code>{-1, +1}</code>.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p><em>The Era of 1-bit LLMs</em> — pełny tekst, <a href="https://arxiv.org/html/2402.17764v1">arxiv.org/html/2402.17764v1</a> — funkcja <code>absmean</code>, próg ~3B dla dopasowania do FP16, luka na 700M/1.3B, model 3.9B kontra 3B LLaMA, liczby dot. pamięci/latencji/przepustowości, ~71× mniej energii na matmul (7nm), nowe prawo skalowania i wniosek o dedykowanym sprzęcie.&#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>&#160;<a href="#fnref7:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>Karta modelu <code>microsoft/bitnet-b1.58-2B-4T</code>, <a href="https://huggingface.co/microsoft/bitnet-b1.58-2B-4T">huggingface.co/microsoft/bitnet-b1.58-2B-4T</a> — 2B parametrów, 4T tokenów, schemat W1.58A8, warstwy BitLinear, RoPE, squared ReLU, subln, brak biasów, licencja MIT oraz zastrzeżenie o braku zysków wydajności przez <code>transformers</code>.&#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>
<li id="fn:5">
<p><code>microsoft/BitNet</code> — oficjalne repozytorium <code>bitnet.cpp</code>, <a href="https://github.com/microsoft/BitNet">github.com/microsoft/BitNet</a> — kernele <code>I2_S</code>/<code>TL1</code>/<code>TL2</code>, zamiana mnożeń na dodawania/odejmowania, przyspieszenia i redukcja energii na ARM/x86, uruchomienie modelu 100B na pojedynczym CPU (~5–7 tok/s).&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref2:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref3:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6">
<p>Wang i in., <em>Bitnet.cpp: Efficient Edge Inference for Ternary LLMs</em>, arXiv:2502.11880, <a href="https://arxiv.org/pdf/2502.11880">arxiv.org/pdf/2502.11880</a> — osobna praca opisująca framework inferencyjny dla modeli ternarnych.&#160;<a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>Ile VRAM-u potrzebuje model? Liczymy na palcach</title><link>https://inferownia.pl/poradniki/ile-vram-na-model/</link><pubDate>Sun, 12 Jul 2026 00:00:00 +0200</pubDate><category>inferencja</category><guid>https://inferownia.pl/poradniki/ile-vram-na-model/</guid><description>Nowy model, ładna karta modelu, a na dole jedno zdanie: 16 GB VRAM. Ty masz te swoje 8. Zamiast wróżyć z fusów, policzmy na palcach, ile naprawdę zważy — i jak zejść niżej.</description><content:encoded><![CDATA[<p><code>Wymagania: 16 GB VRAM</code>. Zjeżdżasz na dół karty modelu, widzisz to jedno zdanie, zerkasz na
swoją kartę do gier z jej ośmioma gigabajtami — z czego połowę i tak zżera przeglądarka — i
serce ci lekko siada. A bywa gorzej: czasem nawet tej jednej liczby nie ma, jest za to tabelka
z dwudziestoma wariantami pliku, każdy waży inaczej, i dalej nie wiesz, czy w ogóle podchodzić,
czy zaraz przywita cię „CUDA out of memory&quot;, cała na biało. To codzienność — połowa pytań „will it fit?&quot; to ludzie, którzy kupili
GPU do gier, a teraz próbują wcisnąć na nie model wielkości małego miasta.</p>
<p>Dobra wiadomość: nie musisz zgadywać. VRAM da się policzyć na palcach, z grubsza, w głowie,
zanim cokolwiek ściągniesz. Nie do grama — do grama policzy ci <code>llama.cpp</code> przy starcie —
ale na tyle dokładnie, żeby wiedzieć, czy w ogóle podchodzić. O to cała sztuka:
zamiast wróżyć z rozmiaru pliku, umieć powiedzieć „7B w Q4 to jakieś pięć giga plus
zapas, wejdzie&quot; — i mieć rację.</p>
<h2 id="wzór-na-palcach">Wzór na palcach</h2>
<p>Cały fundament mieści się w jednej linijce:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">rozmiar wag (GB) ≈ liczba parametrów (mld) × bity na wagę / 8
</span></span></code></pre></div><p>I tyle. Bierzesz, ile model ma miliardów parametrów, mnożysz przez to, ile bitów zajmuje
jedna waga w danym formacie, dzielisz przez osiem (bo bajt ma osiem bitów) i masz gramaturę
samych wag w gigabajtach. Model 8B w kwantyzacji Q4, gdzie na wagę schodzi jakieś 4,5 bita?
<code>8 × 4,5 / 8 ≈ 4,5 GB</code>. I to nie jest teoretyczna wróżba — realny plik <code>Q4_K_M</code> dla
Llamy 3.1 8B waży 4,92 GB.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> Zgadza się co do joty, a właściwie co do paru setek megabajtów.</p>
<div class="callout">
  <span class="callout__label">Dlaczego to ważne dla lokalnego LLM</span>
<p>To „bity na wagę&quot; (po angielsku <em>bits-per-weight</em>, bpw) to sedno całej zabawy z kwantyzacją.
Surowy model trenuje się zwykle w 16 bitach na wagę. Kwantyzacja to nic innego jak zapisanie
tych samych wag ciaśniej — w 8, 5, 4, a nawet 2 bitach — już po treningu, bez ruszania
architektury. Nie myl tego z trenowaniem od zera w niskiej precyzji; to dwie różne bajki.
Jak działają nazwy w stylu <code>Q4_K_M</code> i skąd te dziwne litery, rozgryzaliśmy osobno
<a href="/poradniki/nazwy-kwantyzacji-gguf/">tutaj</a>.</p>
</div>
<h2 id="skąd-biorą-się-ułamkowe-bity">Skąd biorą się ułamkowe bity</h2>
<p>Tu pierwsza pułapka, na którą łapie się każdy początkujący: „Q4 to przecież cztery bity,
nie?&quot;. No właśnie nie do końca. Gdyby świat był prosty, <code>Q4_0</code> (stary, prościutki typ)
brałby równe 4,5 bita na wagę — bo pakuje 32 wagi w blok i dokłada do nich trochę metadanych
o skali.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> Ale nowsze formaty, tak zwane K-quanty (<code>Q2_K</code> do <code>Q6_K</code>, z dopiskami <code>_S</code>,
<code>_M</code>, <code>_L</code>), są sprytniejsze: mieszają precyzję między różnymi warstwami modelu. Te tensory,
od których jakość naprawdę zależy, dostają więcej bitów, reszcie się skąpi. Efekt? Liczba
bitów na wagę wychodzi ułamkowa.</p>
<p>Konkretnie, dla Llamy 3.1 8B według oficjalnej tabeli <code>llama.cpp</code>:<sup id="fnref1:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<table>
	<thead>
			<tr>
					<th>Format</th>
					<th>Bity na wagę</th>
					<th>Rozmiar (tabela llama.cpp)</th>
					<th>Realny plik GGUF</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>Q4_K_M</code></td>
					<td>4,8944</td>
					<td>4,58 GiB</td>
					<td>4,92 GB</td>
			</tr>
			<tr>
					<td><code>Q5_K_M</code></td>
					<td>5,7036</td>
					<td>5,33 GiB</td>
					<td>5,73 GB</td>
			</tr>
			<tr>
					<td><code>Q6_K</code></td>
					<td>~6,5</td>
					<td>—</td>
					<td>6,60 GB</td>
			</tr>
			<tr>
					<td><code>Q8_0</code></td>
					<td>~8,50</td>
					<td>—</td>
					<td>8,54 GB</td>
			</tr>
			<tr>
					<td><code>F16</code></td>
					<td>16,0005</td>
					<td>14,96 GiB</td>
					<td>—</td>
			</tr>
	</tbody>
</table>
<p>Widzisz tę drobną rozbieżność między kolumnami? Jedna to GiB (liczone po 1024), druga GB
(po 1000), plus K-quant miesza precyzję tensorów — stąd <code>Q4_K_M</code> ma prawie 4,9 bita, a nie
równe 4,5.<sup id="fnref1:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> Do liczenia na palcach nic to nie zmienia: bierzesz „mniej więcej 4,5–5
bita dla Q4&quot; i tak trafiasz w wynik z dokładnością do zapasu, który i tak musisz zostawić.
Jest jeszcze cała rodzina IQ-quantów (<code>IQ2_XXS</code>, <code>IQ3_S</code> i spółka), które schodzą poniżej
trzech bitów, ważąc jakość specjalną macierzą istotności — ale to temat na osobny wieczór.<sup id="fnref2:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<h2 id="ile-zważy-ten-twój-model">Ile zważy „ten twój&quot; model</h2>
<p>Przełóżmy wzór na cztery rozmiary, które realnie krążą po lokalnym światku. To są same wagi,
bez narzutu — o narzucie za chwilę:</p>
<table>
	<thead>
			<tr>
					<th>Model</th>
					<th>Q4_K_M</th>
					<th>Q5_K_M</th>
					<th>Q8_0</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Llama 3.1 <strong>8B</strong><sup id="fnref2:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></td>
					<td>4,92 GB</td>
					<td>5,73 GB</td>
					<td>8,54 GB</td>
			</tr>
			<tr>
					<td>Mistral Nemo <strong>12B</strong><sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup></td>
					<td>7,48 GB</td>
					<td>8,73 GB</td>
					<td>13,02 GB</td>
			</tr>
			<tr>
					<td>Qwen2.5 <strong>14B</strong><sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup></td>
					<td>8,99 GB</td>
					<td>10,51 GB</td>
					<td>15,70 GB</td>
			</tr>
	</tbody>
</table>
<p>Popatrz na to przez chwilę, bo tu widać całą matematykę życia lokalnego LLM-owca. Masz kartę
8 GB? Ósemka w Q4 wchodzi na luzie, w Q5 już z zadyszką, a Q8 nawet nie podchodź. Dwunastka
w Q4 to 7,5 GB samych wag — teoretycznie „się mieści&quot;, tylko że na sam narzut nie zostaje już
nic. A czternastka w Q4 to prawie dziewięć giga — twoje 8 GB kończy się, zanim się zaczęło.
Dlatego „te swoje 8 GB&quot; znika tak szybko: liczysz gramaturę wag, cieszysz się, że wchodzi,
a potem uruchamiasz i okazuje się, że o czymś zapomniałeś.</p>
<h2 id="czego-zapominasz-kv-cache-i-bufory">Czego zapominasz: KV-cache i bufory</h2>
<p>O to zapominasz. Rozmiar pliku GGUF to tylko wagi. Do tego dochodzi KV-cache — pamięć, w której
model trzyma przetworzone klucze i wartości dla każdego tokena, który model już wygenerował lub
wczytał z promptu. I to jest ten cichy złodziej VRAM-u, bo rośnie <strong>liniowo z długością
kontekstu</strong>. Im dłuższa rozmowa, tym większy żłób na pamięć.</p>
<p>Ile dokładnie? Też się liczy na palcach. Na jeden token przypada:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">KV na token = 2 (klucz + wartość) × liczba_warstw × n_head_kv × head_dim × bajty_na_element
</span></span></code></pre></div><p>Dla Llamy 3.1 8B te liczby to: 32 warstwy, <code>n_head_kv</code> = 8, <code>head_dim</code> = 128, a KV-cache
domyślnie leci w <code>f16</code>, czyli 2 bajty na element<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup> (co to za <code>f16</code> i czemu nie <code>bf16</code> — <a href="/poradniki/bf16-f16-f32/">tu</a>). Wrzuć to do wzoru:
<code>2 × 32 × 8 × 128 × 2 bajty = 128 KiB</code> na token. Przy kontekście 8192 tokenów robi się z tego
równiutki 1 GiB — tyle dokłada sam cache do tego, co już zjadły wagi. Rozbieramy ten mechanizm
dokładniej <a href="/poradniki/kv-cache-vram/">tutaj</a>, ale zasada do zapamiętania jest prosta: dłuższy
kontekst = więcej VRAM-u, po prostu.</p>
<p>Zwróć uwagę na <code>n_head_kv</code> = 8 — pełna liczba głów uwagi w tym modelu jest większa, ale dzięki
GQA (<em>grouped-query attention</em>, mechanizm współdzielenia głów klucza/wartości) do cache liczy się
tylko ta zredukowana ósemka. Bez GQA, na starej architekturze, ten narzut potrafił być
wielokrotnie większy. Do tego dochodzą jeszcze bufory obliczeniowe samego procesu inferencji.
Dlatego świętą zasadą jest: <strong>zostaw 1–2 GB zapasu ponad rozmiar wag</strong>. Kto liczy VRAM tylko
z gramatury pliku GGUF, ten regularnie ląduje na „out of memory&quot; przy trzecim akapicie rozmowy.</p>
<h2 id="jak-zejść-niżej-gdy-nie-wchodzi">Jak zejść niżej, gdy nie wchodzi</h2>
<p>Powiedzmy, że policzyłeś i wychodzi za dużo. Masz trzy dźwignie, w tej właśnie kolejności.</p>
<p>Raz — <strong>kwantyzacja niżej</strong>. To najprostszy ruch: zamiast Q8 bierzesz Q5, zamiast Q5 bierzesz
Q4. Czternastka w Q8 to 15,7 GB, a w Q4 już tylko 8,99 GB<sup id="fnref1:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> — prawie połowa mniej, a
jakość spada zaskakująco mało, bo K-quanty oszczędzają mądrze. Q4_K_M to nie bez powodu
domyślny wybór połowy internetu.</p>
<p>Dwa — <strong>kwantyzacja KV-cache</strong>. Skoro cache potrafi dołożyć giga przy dłuższym kontekście, to
jego też można ścisnąć. <code>llama-server</code> ma na to flagi <code>-ctk</code> / <code>--cache-type-k</code> i <code>-ctv</code> /
<code>--cache-type-v</code>, osobno dla kluczy i wartości; domyślnie oba stoją na <code>f16</code>, ale przyjmują też
<code>q8_0</code>, <code>q4_0</code> i całą listę innych typów.<sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup> Ściśnięcie cache z <code>f16</code> do <code>q8_0</code> to znów
zejście z jego rozmiaru mniej więcej o połowę.</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 qwen2.5-14b-instruct-q4_k_m.gguf <span class="se">\
</span></span></span><span class="line"><span class="cl">  -ngl all -c <span class="m">8192</span> -fa on <span class="se">\
</span></span></span><span class="line"><span class="cl">  -ctk q8_0 -ctv q8_0
</span></span></code></pre></div><p>Widzisz to <code>-fa on</code>? To nie ozdoba. Flaga <code>-fa</code> / <code>--flash-attn</code> (przyjmuje <code>on</code>/<code>off</code>/<code>auto</code>,
domyślnie <code>auto</code>) włącza Flash Attention — a kwantyzacja KV-cache do typów poniżej <code>f16</code>
praktycznie <strong>wymaga</strong> włączonej Flash Attention.<sup id="fnref1:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup> Ustawisz <code>-ctk q4_0</code> bez <code>-fa on</code> i
w najlepszym razie nic nie zyskasz, w najgorszym nie ruszy. Więc: najpierw Flash Attention,
potem duś cache.</p>
<p>Trzy — <strong>przytnij kontekst albo zrzuć część warstw na CPU</strong>. To ostateczność, bo krótszy
kontekst to mniejsza pamięć modelu, a offload na CPU (flaga <code>-ngl</code> z liczbą mniejszą niż
„wszystko&quot;) spowalnia generację. Ale gdy 12B w Q4 nie chce wejść na 8 GB nawet po ściśnięciu
cache — czasem to jedyny sposób, żeby w ogóle ruszyło. Cała ta chirurgia flagami to osobny
rozdział, który rozpisaliśmy <a href="/poradniki/flagi-llama-cpp/">tu</a>.</p>
<h2 id="zrób-to-sam-rachunek-w-trzy-sekundy">Zrób to sam: rachunek w trzy sekundy</h2>
<p>Następnym razem, gdy staniesz przed nowym modelem, przelicz go w głowie, zanim klikniesz
pobieranie:</p>
<ol>
<li><strong>Wagi</strong>: <code>parametry × bity / 8</code>. Ósemka w Q4 → <code>8 × 4,5 / 8 ≈ 4,5 GB</code>. Albo zerknij na
realny plik na Hugging Face i weź gotową liczbę.</li>
<li><strong>Cache</strong>: przy zwykłym kontekście (4–8k) dorzuć z grubsza 0,5–1 GB dla modelu klasy 7–14B
z GQA. Planujesz 32k albo więcej? Licz się z kilkoma gigabajtami i od razu myśl o <code>-fa on</code>
plus <code>-ctk q8_0</code>.</li>
<li><strong>Zapas</strong>: dorzuć 1–2 GB na bufory i oddech.</li>
</ol>
<p>Suma większa niż twój VRAM? Schodzisz z kwantyzacją albo duszisz cache — w tej kolejności.
Suma mniejsza? Ściągaj śmiało, wejdzie.</p>
<p>Bo cała sztuka z VRAM-em nie polega na wróżeniu, tylko na tym jednym prostym mnożeniu na
palcach — parametry razy bity, przez osiem, plus zapas. Kiedy raz je poczujesz, folder <code>models</code>
przestaje być loterią, a zaczyna być tym, czym powinien: półką, na której z góry wiesz, co się
zmieści, a co poleży, aż dokupisz kartę „na później&quot;. A dokupisz. Wszyscy dokupujemy.</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>Realne rozmiary plików GGUF dla Meta-Llama-3.1-8B-Instruct (<code>Q4_K_M</code> = 4,92 GB, <code>Q5_K_M</code> = 5,73 GB, <code>Q6_K</code> = 6,60 GB, <code>Q8_0</code> = 8,54 GB), <a href="https://huggingface.co/bartowski/Meta-Llama-3.1-8B-Instruct-GGUF">bartowski/Meta-Llama-3.1-8B-Instruct-GGUF</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></p>
</li>
<li id="fn:2">
<p>llama.cpp, tabela typów kwantyzacji z wartościami bits-per-weight i rozmiarami dla Llama 3.1 8B (<code>Q4_K_M</code> = 4,8944 bpw / 4,58 GiB, <code>Q5_K_M</code> = 5,7036 bpw / 5,33 GiB, <code>F16</code> = 16,0005 bpw / 14,96 GiB; <code>Q4_0</code> = 4,5 bpw), <a href="https://github.com/ggml-org/llama.cpp/blob/master/tools/quantize/README.md">tools/quantize/README.md</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>Realne rozmiary plików GGUF dla Mistral-Nemo-Instruct-2407 (12B): <code>Q4_K_M</code> = 7,48 GB, <code>Q5_K_M</code> = 8,73 GB, <code>Q8_0</code> = 13,02 GB, <a href="https://huggingface.co/bartowski/Mistral-Nemo-Instruct-2407-GGUF">bartowski/Mistral-Nemo-Instruct-2407-GGUF</a>.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>Realne rozmiary plików GGUF dla Qwen2.5-14B-Instruct: <code>Q4_K_M</code> = 8,99 GB, <code>Q5_K_M</code> = 10,51 GB, <code>Q8_0</code> = 15,70 GB, <a href="https://huggingface.co/bartowski/Qwen2.5-14B-Instruct-GGUF">bartowski/Qwen2.5-14B-Instruct-GGUF</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>
<li id="fn:5">
<p>llama.cpp, parametry KV-cache dla Llama 3 8B (<code>n_layer</code> = 32, <code>n_head_kv</code> = 8, <code>head_dim</code> = 128, <code>n_embd_k_gqa</code>/<code>v_gqa</code> = 1024) i potwierdzenie domyślnego typu cache <code>f16</code>, <a href="https://github.com/ggml-org/llama.cpp/discussions/7949">GitHub Discussion #7949 — KV Cache Dimension</a>.&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6">
<p>llama.cpp, opis flag <code>llama-server</code>: <code>-ctk</code>/<code>--cache-type-k</code>, <code>-ctv</code>/<code>--cache-type-v</code> (domyślnie <code>f16</code>, dozwolone m.in. <code>q8_0</code>, <code>q4_0</code>) oraz <code>-fa</code>/<code>--flash-attn</code> (<code>on</code>/<code>off</code>/<code>auto</code>, domyślnie <code>auto</code>); kwantyzacja KV-cache poniżej <code>f16</code> wymaga Flash Attention, <a href="https://github.com/ggml-org/llama.cpp/blob/master/tools/server/README.md">tools/server/README.md</a>.&#160;<a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:6" 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><item><title>Ekosystem GGUF: skąd się biorą modele, które odpalasz</title><link>https://inferownia.pl/aktualnosci/ekosystem-gguf/</link><pubDate>Wed, 01 Jul 2026 00:00:00 +0200</pubDate><category>kwantyzacja</category><guid>https://inferownia.pl/aktualnosci/ekosystem-gguf/</guid><description>Ściągasz jeden plik z Hugging Face, dwuklik i model gada. Ale kto go w ogóle zrobił, czemu wariantów jest dwadzieścia i co siedzi w środku tego jednego .gguf? Rozbieramy fabrykę.</description><content:encoded><![CDATA[<p>Wchodzisz na Hugging Face po świeży model, otwierasz stronę i widzisz listę plików jak menu w barze mlecznym, w którym wszystko nazywa się prawie tak samo. <code>Q4_K_M</code>, <code>Q5_K_S</code>, <code>Q6_K</code>, <code>Q8_0</code>, <code>IQ4_XS</code>, gdzieś niżej jakiś <code>BF16</code> na 30 gigabajtów. Wszystkie kończą się na <code>.gguf</code>, wszystkie są tego samego modelu, a ty stoisz i myślisz: dobra, ale który? I skąd się to w ogóle wzięło, skoro autor modelu wrzucił coś zupełnie innego?</p>
<p>To jest ten moment, w którym większość ludzi bierze na chybił trafił ten <code>Q4_K_M</code>, bo tak radzą na Reddicie, odpala i idzie dalej. I słusznie — działa. Ale pod tym jednym plikiem stoi cała cicha fabryka: ludzie, narzędzia, konwersje i format, który ktoś kiedyś usiadł i zaprojektował, żebyś ty mógł zrobić dwuklik. Warto wiedzieć, co tam mruczy pod maską — bo dzięki temu przestajesz zgadywać, a zaczynasz wybierać świadomie.</p>
<h2 id="co-to-w-ogóle-jest-ten-gguf">Co to w ogóle jest, ten GGUF</h2>
<p>Zacznijmy od rozczarowania, bo lepiej od razu: <strong>GGUF to nie jest model</strong>. To nie nowa architektura, nie nowy sposób trenowania, nie magiczny AI od ggerganova. To pojemnik. Format pliku — dosłownie „GGML Universal File” — do trzymania modelu na dysku tak, żeby silnik inferencyjny mógł go szybko wczytać i zacząć gadać.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> Model (Llama, Qwen, Mistral — cokolwiek) rodzi się gdzie indziej, w treningu na klastrze, którego rachunku za prąd żaden z nas nie chciałby zobaczyć. GGUF to dopiero opakowanie, w które ten wytrenowany model się przepakowuje na koniec, pod kątem tego, że odpalisz go u siebie na jednej karcie.</p>
<p>Format wymyślił Georgi Gerganov, ten sam, który stoi za <code>llama.cpp</code> i biblioteką <code>ggml</code>.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> GGUF jest następcą kilku starszych formatów o równie uroczych nazwach — GGML, GGMF, GGJT — które po drodze się wykruszyły. I tu pierwsza pułapka, w którą łatwo wdepnąć: <strong>GGML i GGUF to nie synonimy</strong>. GGML to ten stary, w dużej mierze wyparty format pliku (i przy okazji wciąż nazwa biblioteki tensorowej pod spodem), a GGUF to jego młodszy, mądrzejszy brat z porządnymi metadanymi.<sup id="fnref1:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<h2 id="co-siedzi-w-tym-jednym-pliku">Co siedzi w tym jednym pliku</h2>
<p>To jest właśnie sedno, dla którego GGUF wygrał. Otwórz sobie w głowie taki plik jak pudełko z przegródkami:</p>
<ul>
<li><strong>Nagłówek</strong> — na samym początku cztery bajty <code>GGUF</code> (magic number, bajt po bajcie <code>0x47 0x47 0x55 0x46</code>), zaraz za nimi numer wersji formatu, liczba tensorów i sterta metadanych.<sup id="fnref2:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></li>
<li><strong>Metadane klucz-wartość</strong> — i tu robi się ciekawie. Zamiast beztypowej listy liczb jak w starych formatach, GGUF trzyma nazwane pola w hierarchii z kropkami: <code>general.architecture</code>, <code>llama.context_length</code>, <code>general.quantization_version</code>. Klucz <code>general.architecture</code> (czym ten model właściwie jest) musi być zawsze, a jak model jest skwantowany, to musi być też <code>general.quantization_version</code>.<sup id="fnref3:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></li>
<li><strong>Tokenizer</strong> — w środku, w tym samym pliku. To jest ten myk, którego naprawdę brakowało wcześniej. GGUF potrafi wnieść cały tokenizer: natywne tablice tokenów, scores i merges, albo pełny <code>tokenizer.json</code> prosto z Hugging Face, albo szablony czatu w Jinja.<sup id="fnref4:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> Dlatego jeden <code>.gguf</code> wystarcza — nie musisz dociągać pięciu plików obok.</li>
<li><strong>Same wagi</strong> — na końcu surowe dane tensorów, wyrównane do granicy (domyślnie co 32 bajty, klucz <code>general.alignment</code>), żeby silnik mógł je wczytać przez <code>mmap</code> bez mielenia całego pliku.<sup id="fnref5:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></li>
</ul>
<div class="callout">
  <span class="callout__label">Dlaczego to ważne dla lokalnego LLM</span>
<p>To „wszystko w jednym pliku” jest dokładnie tym, co robi z GGUF format do <strong>lokalnej</strong> inferencji. Ściągasz jeden plik i masz komplet: wagi, architekturę i tokenizer. Nie ma cyrku z dociąganiem <code>config.json</code>, <code>tokenizer.json</code> i trzech innych obok. Silnik typu <code>llama.cpp</code> bierze go przez <code>mmap</code> i startuje w sekundę, bez deserializacji. Mniej ruchomych części to mniej rzeczy, które ci się rozjadą o 23:00, gdy chcesz tylko o coś zapytać model.</p>
</div>
<h2 id="skąd-biorą-się-wszystkie-te-warianty">Skąd biorą się wszystkie te warianty</h2>
<p>No dobra, a czemu tego samego modelu jest dwadzieścia sztuk? Bo GGUF wspiera całą tęczę typów tensorów: pełną precyzję (F32, F16, BF16), serię K (<code>Q2_K</code> do <code>Q6_K</code>), stare legacy (<code>Q4_0</code>, <code>Q8_0</code>), nowszą serię IQ opartą na macierzy istotności (<code>IQ4_XS</code>, <code>IQ2_XXS</code>) i egzotykę w stylu ternarnego <code>TQ1_0</code>.<sup id="fnref6:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> Każdy z tych <code>Q</code>-ów to inny schemat ściśnięcia wag. <code>Q4_K</code> upycha wagę w jakichś 4,5 bita, <code>Q6_K</code> w 6,56, <code>Q2_K</code> w 2,63 — i to są dokładnie te przegródki, w których leży cały kompromis rozmiar/jakość/szybkość.<sup id="fnref1:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<p>I tu druga pułapka: <strong>te warianty to nie są różne modele</strong>. <code>Q4_K_M</code>, <code>Q5_K_S</code>, <code>Q8_0</code> tego samego modelu to te same wagi bazowe, tylko skompresowane innym sitem. Grubszy plik = bliżej oryginału i wolniej, chudszy = mniej VRAM-u i szybciej, ale z lekkim ubytkiem jakości. Co ważne, to jest kwantyzacja <strong>post-training</strong> — bierzesz gotowy, wytrenowany model i ściskasz go po fakcie. Nie mylić z trenowaniem od zera w niskiej precyzji. W nazewnictwo tych literek zagłębialiśmy się osobno <a href="/poradniki/nazwy-kwantyzacji-gguf/">tutaj</a>, bo to temat na własny wpis.</p>
<h2 id="kto-to-wszystko-właściwie-robi">Kto to wszystko właściwie robi</h2>
<p>Teraz najlepsze. Ten <code>Q4_K_M</code>, który ściągasz — bardzo często <strong>nie zrobił go autor modelu</strong>. Autor wrzuca zwykle wagi w safetensors i może jedną-dwie wersje GGUF, a resztę tego bufetu produkują ludzie z boku: community-quantizerzy. Konta jak bartowski czy mradermacher (dawniej głośny TheBloke) siedzą i masowo mielą cudze modele na wszystkie możliwe warianty, dzień po dniu, jak taka lokalna piekarnia GGUF-ów.</p>
<p>Jak to robią bez klastra? Dwiema drogami. Pierwsza to skrypt <code>convert_hf_to_gguf.py</code> prosto z repo <code>llama.cpp</code> — łyka wagi z Hugging Face i wypluwa GGUF, obsługuje kupę architektur (w tym multimodalne i MoE), ma opcje <code>--outtype</code> (bierzesz <code>f16</code>, <code>bf16</code>, <code>q8_0</code>… — czym te „szesnastki&quot; różnią się od siebie, jest <a href="/poradniki/bf16-f16-f32/">tu</a>), <code>--vocab-only</code>, <code>--split-max-size</code> do krojenia wielkich plików, a nawet <code>--remote</code>, żeby konwertować prosto z Huba.<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> Druga droga jest jeszcze leniwsza: <code>ggml-org/gguf-my-repo</code>, gotowy Space na Hugging Face, gdzie wklejasz repo, klikasz i dostajesz komplet kwantyzacji bez kompilowania czegokolwiek u siebie.<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"><span class="c1"># klasyczna droga: własny build llama.cpp</span>
</span></span><span class="line"><span class="cl">python convert_hf_to_gguf.py ./Qwen2.5-7B-Instruct <span class="se">\
</span></span></span><span class="line"><span class="cl">  --outtype f16 --outfile qwen2.5-7b-f16.gguf
</span></span><span class="line"><span class="cl"><span class="c1"># a potem osobno kwantyzacja narzędziem llama-quantize do Q4_K_M itd.</span>
</span></span></code></pre></div><p>Sam Hugging Face traktuje ten format jak pełnoprawnego obywatela: masz filtr <code>library=gguf</code>, który pokazuje wszystko, co gęgnięte (<a href="https://huggingface.co/models?library=gguf">hf.co/models?library=gguf</a>), a na stronie konkretnego pliku podejrzysz metadane i listę tensorów wprost z przeglądarki, bez ściągania.<sup id="fnref2:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> Praktyczny wniosek: skoro warianty sypie kto chce, <strong>sprawdzaj, czyje repo bierzesz</strong>. Zerknij na autora, na to, czy imatrix był użyty, czy karta modelu ma ręce i nogi. Na co patrzeć przy świeżym modelu rozkładaliśmy <a href="/aktualnosci/nowy-model-na-co-patrzec/">w osobnej notce</a>.</p>
<h2 id="gguf-kontra-safetensors-to-nie-jest-ranking">GGUF kontra safetensors: to nie jest ranking</h2>
<p>Skoro autor wrzuca safetensors, a ty odpalasz GGUF — który jest lepszy? Źle postawione pytanie. To dwa pojemniki do dwóch różnych rzeczy.</p>
<table>
	<thead>
			<tr>
					<th></th>
					<th>GGUF</th>
					<th>safetensors</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Co trzyma</td>
					<td>wagi + architektura + tokenizer, w jednym pliku</td>
					<td><strong>tylko</strong> tensory, goła macierz liczb</td>
			</tr>
			<tr>
					<td>Metadane / tokenizer</td>
					<td>w środku</td>
					<td>osobno (<code>config.json</code>, <code>tokenizer.json</code>)</td>
			</tr>
			<tr>
					<td>Kwantyzacja</td>
					<td>tak, to jego chleb (<code>Q4_K_M</code>, IQ…)</td>
					<td>zwykle pełna precyzja lub bf16/fp16</td>
			</tr>
			<tr>
					<td>Pod co skrojony</td>
					<td>inferencja przez <code>llama.cpp</code>/<code>ggml</code>, CPU/GPU</td>
					<td>trening/inferencja w PyTorch, transformers, diffusers</td>
			</tr>
			<tr>
					<td>Po co powstał</td>
					<td>jeden plik gotowy do odpalenia</td>
					<td>bezpieczna, szybka (zero-copy) alternatywa dla <code>.bin</code>/pickle</td>
			</tr>
	</tbody>
</table>
<p>Safetensors narodził się z zupełnie innej potrzeby: żeby nie ładować cudzych wag przez <code>pickle</code> (który potrafi odpalić dowolny kod przy wczytaniu) i żeby robić to szybko, bez kopiowania w pamięci.<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup> To rekomendowany format wag na Hugging Face — ale trzyma <strong>wyłącznie</strong> tensory. Chcesz z niego odpalić model, musisz dorzucić config i tokenizer obok. GGUF ten cały komplet nosi w sobie i dokłada kwantyzację. Nie „lepszy/gorszy” — inne zadanie. Jak chcesz to potem faktycznie uruchomić garścią flag, jest o tym <a href="/poradniki/flagi-llama-cpp/">osobny poradnik</a>.</p>
<h2 id="wróć-do-tego-menu">Wróć do tego menu</h2>
<p>Wróć teraz do tego menu na Hugging Face, do listy dwudziestu plików kończących się na <code>.gguf</code>. Już nie wygląda jak bar mleczny, w którym wszystko nazywa się tak samo. Widzisz teraz, że ktoś wziął model wytrenowany na drugim końcu świata, przepuścił go przez <code>convert_hf_to_gguf.py</code>, upchał wagi, architekturę i tokenizer do jednego pudełka z przegródkami, ścisnął sitem <code>Q4_K_M</code> i wrzucił na Huba, żebyś ty mógł go zassać jednym plikiem i usłyszeć, jak mruczy pod biurkiem. Cała ta cicha fabryka — od klastra treningowego, przez konto bartowskiego, po twój dysk — sprowadza się do jednego czterobajtowego napisu na początku pliku: <code>GGUF</code>. Reszta to już tylko ty i pytanie, który wariant dziś bierzesz.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Specyfikacja formatu: struktura pliku, magic number <code>GGUF</code> (<code>0x47 0x47 0x55 0x46</code>), wersje 1–3, wyrównanie <code>general.alignment</code>, wymagane klucze metadanych, typy tensorów i osadzanie tokenizera, historia jako następca GGML/GGMF/GGJT — <a href="https://github.com/ggml-org/ggml/blob/master/docs/gguf.md">ggml-org/ggml, <code>docs/gguf.md</code></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></p>
</li>
<li id="fn:2">
<p>Twórca formatu (@ggerganov), integracja z Hub (tag <code>library=gguf</code>, viewer metadanych i tensorów), narzędzie <code>gguf-my-repo</code>, porównanie z safetensors oraz tabela typów kwantyzacji z bitami-na-wagę — <a href="https://huggingface.co/docs/hub/en/gguf">Hugging Face Hub docs, GGUF</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>Mechanizm konwersji modeli HF do GGUF, obsługiwane architektury i opcje <code>--outtype</code>, <code>--vocab-only</code>, <code>--split-max-size</code>, <code>--remote</code> — <a href="https://github.com/ggml-org/llama.cpp/blob/master/convert_hf_to_gguf.py">llama.cpp, <code>convert_hf_to_gguf.py</code></a>.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>Narzędzie, którym community-quantizerzy konwertują i kwantyzują dowolne wagi HF do GGUF bez lokalnej kompilacji <code>llama.cpp</code> — <a href="https://huggingface.co/spaces/ggml-org/gguf-my-repo">ggml-org/gguf-my-repo (Hugging Face Space)</a>.&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p>Definicja safetensors jako bezpiecznego (bez deserializacji pickle), zero-copy formatu przechowywania samych tensorów oraz jego rola w ekosystemie transformers/diffusers/candle — <a href="https://huggingface.co/docs/safetensors/index">Hugging Face, dokumentacja safetensors</a>.&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item></channel></rss>