<?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/category/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>Sun, 19 Jul 2026 00:00:00 +0200</lastBuildDate><atom:link href="https://inferownia.pl/category/kwantyzacja/index.xml" rel="self" type="application/rss+xml"/><item><title>bf16, f16, f32 — o co chodzi z tymi bitami</title><link>https://inferownia.pl/poradniki/bf16-f16-f32/</link><pubDate>Sun, 19 Jul 2026 00:00:00 +0200</pubDate><category>kwantyzacja</category><guid>https://inferownia.pl/poradniki/bf16-f16-f32/</guid><description>Konwertujesz model do GGUF-a, widzisz --outtype f16 i --outtype bf16, i przez sekundę udajesz przed samym sobą, że wiesz, którego wybrać. Rozbieramy te formaty na bity — znak, wykładnik, mantysa — i tłumaczymy, czemu bf16 wygrał trening ML, choć jest mniej dokładny niż f16.</description><content:encoded><![CDATA[<p>Odpalasz <code>convert_hf_to_gguf.py</code> na świeżym modelu z Hugging Face, lecisz z palca <code>--outtype</code>
i nagle podpowiedź wypluwa ci listę: <code>f32</code>, <code>f16</code>, <code>bf16</code>, <code>q8_0</code>… Zatrzymujesz się na tych
trzech pierwszych. <code>f16</code> znasz, <code>f32</code> to „ten większy&quot;, ale <code>bf16</code> mruga jak nieznajomy na
imprezie, o którym wszyscy mówią, że jest spoko, tylko nikt nie potrafi powiedzieć czemu.
Klikasz <code>f16</code>, bo tak robiłeś zawsze, i ruszasz dalej — a z tyłu głowy zostaje to samo pytanie,
które wraca za każdym razem, gdy w konfiguracji treningu widzisz <code>compute_dtype=torch.bfloat16</code>:
czym one się właściwie różnią, skoro obie „szesnastki&quot; ważą tyle samo?</p>
<p>To jeden z klasycznych sporów o dwie „szesnastki&quot; — ktoś wrzuca skwantyzowany model, ktoś inny zaraz pyta,
czemu baza jest w <code>bf16</code>, a nie <code>f16</code>, i rozkręca się wątek na sto odpowiedzi, z których połowa
myli precyzję z zakresem. Bo to naprawdę nieoczywiste. Dwa formaty, oba po 16 bitów, oba po
2 bajty na liczbę — a jednak jeden wygrał trening wielkich modeli, a drugi został przy inferencji
i grafice. Cała różnica siedzi w tym, jak te 16 bitów pokroić. No to pokrójmy.</p>
<h2 id="liczba-zmiennoprzecinkowa-to-trzy-szufladki">Liczba zmiennoprzecinkowa to trzy szufladki</h2>
<p>Każda liczba w tych formatach — czy to <code>f32</code>, czy <code>f16</code>, czy <code>bf16</code> — składa się z trzech kawałków.
Wyobraź sobie zapis naukowy z podstawówki: −1,5 × 10⁴. Masz tam znak (minus), masz cyfry
znaczące (<code>1,5</code>) i masz potęgę, która mówi, gdzie postawić przecinek (<code>10⁴</code>). Komputer robi
dokładnie to samo, tylko na potęgach dwójki, i pakuje to w trzy pola bitów:</p>
<ul>
<li><strong>bit znaku</strong> (sign bit) — jeden bit, plus albo minus. Tu bez niespodzianek.</li>
<li><strong>wykładnik</strong> (exponent) — potęga dwójki, czyli <em>skala</em> liczby. To on decyduje, jak wielką
albo jak małą liczbę w ogóle da się zapisać. Nazywamy to <strong>zakresem dynamicznym</strong>.</li>
<li><strong>mantysa</strong> (significand, po staremu <em>mantissa</em>) — cyfry znaczące, czyli <em>dokładność</em>. To ona
decyduje, ile pozycji po przecinku odróżnisz od siebie. Nazywamy to <strong>precyzją</strong>.</li>
</ul>
<p>I teraz sedno, które trzeba wbić do głowy jak mantrę: <strong>zakres bierze się z wykładnika, precyzja
z mantysy. To dwie niezależne osie.</strong> Możesz mieć liczbę o gigantycznym zakresie i marnej
dokładności — albo odwrotnie. Nie idą w parze. Cała gra w tych formatach polega na tym, ile
z szesnastu (albo trzydziestu dwóch) dostępnych bitów oddasz jednemu, a ile drugiemu.</p>
<p>Jak to rozdzielono w praktyce? Tak — i to jest tabelka, którą warto zapamiętać:</p>
<table>
	<thead>
			<tr>
					<th>Format</th>
					<th style="text-align: center">Znak</th>
					<th style="text-align: center">Wykładnik</th>
					<th style="text-align: center">Mantysa</th>
					<th style="text-align: center">Zakres (maks.)</th>
					<th style="text-align: center">Precyzja dziesiętna</th>
					<th style="text-align: center">Bajty</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><strong>f32</strong> (single)</td>
					<td style="text-align: center">1</td>
					<td style="text-align: center">8</td>
					<td style="text-align: center">23</td>
					<td style="text-align: center">3,4 × 10³⁸</td>
					<td style="text-align: center">≈ 7 cyfr</td>
					<td style="text-align: center">4</td>
			</tr>
			<tr>
					<td><strong>f16</strong> (half)</td>
					<td style="text-align: center">1</td>
					<td style="text-align: center">5</td>
					<td style="text-align: center">10</td>
					<td style="text-align: center">65 504</td>
					<td style="text-align: center">≈ 3–4 cyfry</td>
					<td style="text-align: center">2</td>
			</tr>
			<tr>
					<td><strong>bf16</strong> (bfloat16)</td>
					<td style="text-align: center">1</td>
					<td style="text-align: center">8</td>
					<td style="text-align: center">7</td>
					<td style="text-align: center">3,4 × 10³⁸</td>
					<td style="text-align: center">≈ 2–3 cyfry</td>
					<td style="text-align: center">2</td>
			</tr>
	</tbody>
</table>
<p><code>f32</code>, czyli <em>single precision</em> z normy IEEE 754, to 1 bit znaku, 8 bitów wykładnika i 23 bity
mantysy.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> To złoty standard „normalnego&quot; liczenia na komputerze — szeroki zakres i siedem
cyfr znaczących. Kosztuje 4 bajty na liczbę i przez lata nikt się nad tym nie zastanawiał.</p>
<h2 id="dwie-szesnastki-dwa-różne-cięcia">Dwie szesnastki, dwa różne cięcia</h2>
<p>I teraz robi się ciekawie, bo <code>f16</code> i <code>bf16</code> to obie „połówki&quot; — po 16 bitów, po 2 bajty. Tyle że
tort pokrojony zupełnie inaczej.</p>
<p><code>f16</code>, czyli <em>half precision</em>, też z IEEE 754, tnie tak: 1 bit znaku, <strong>5 bitów wykładnika</strong>,
<strong>10 bitów mantysy</strong>.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> Zauważasz? Oddano dużo miejsca mantysie, mało wykładnikowi. Efekt:
przyzwoita precyzja jak na 16 bitów, ale <strong>wąziutki zakres</strong> — największa skończona liczba, jaką
<code>f16</code> w ogóle potrafi zapisać, to 65 504. Przekroczysz to i masz overflow, liczba ucieka
w nieskończoność. Zejdziesz za nisko — underflow, liczba znika do zera. Dla grafiki, skąd <code>f16</code>
się wywodzi, to bez znaczenia; kolory i tak siedzą w rozsądnym przedziale.</p>
<p><code>bf16</code> — <em>brain floating point</em>, dziecko Google Brain, i uwaga: <strong>nie jest częścią IEEE 754</strong> —
kroi zupełnie inaczej: 1 bit znaku, <strong>8 bitów wykładnika</strong>, <strong>7 bitów mantysy</strong>.<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> Ten
sam wykładnik co <code>f32</code>. Osiem bitów, ten sam bias 127, ta sama arytmetyka skali. A to znaczy, że
<strong><code>bf16</code> ma dokładnie taki sam zakres dynamiczny jak <code>f32</code></strong> — od jakichś 10⁻³⁸ do 3,4 × 10³⁸.
Google w dokumentacji TPU pisze to wprost, bez owijania: zakresy dynamiczne <code>bfloat16</code> i <code>float32</code>
są równoważne, <code>bfloat16</code> po prostu zjada połowę pamięci.<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup></p>
<p>Cena? Mantysa skurczona do siedmiu bitów. Czyli <code>bf16</code> jest <strong>mniej dokładny liczbowo niż <code>f16</code></strong> —
odróżnia mniej cyfr znaczących. I tu wielu ludzi się wykłada, bo intuicja podpowiada: skoro <code>bf16</code>
ma mniej precyzji, to jest gorszą szesnastką. A to nieprawda. To po prostu inny kompromis, zrobiony
pod inny cel.</p>
<h2 id="czemu-bf16-wygrał-trening-choć-jest-mniej-dokładny">Czemu bf16 wygrał trening, choć jest mniej dokładny</h2>
<p>To jest ta część, która zaskakuje każdego, kto pierwszy raz na to trafia. Bierzemy format o <em>gorszej</em>
precyzji i to on stał się domyślnym wyborem do trenowania największych modeli świata. Jak to możliwe?</p>
<p>Bo w treningu sieci neuronowej najgroźniejszym wrogiem nie jest to, że liczba jest odrobinę
niedokładna. Najgroźniejsze jest to, że liczba <strong>wypadnie poza zakres</strong>. Gradienty potrafią być
malutkie jak 10⁻⁷, a aktywacje potrafią chwilowo strzelić wysoko. <code>f16</code> ze swoim sufitem na
65 504 i płytką podłogą po prostu tego nie łapie — wartości albo overflowują do nieskończoności,
albo underflowują do zera, a wyzerowany gradient to gradient, który nic już nie uczy. Dlatego
trening w <code>f16</code> wymagał sztuczek w rodzaju <em>loss scalingu</em> — ręcznego podbijania skali strat, żeby
zmieścić gradienty w tym wąskim oknie.</p>
<p><code>bf16</code> rozwiązuje to od strzału, bo ma zakres <code>f32</code>. Gradient rzędu 10⁻³⁰? Mieści się. Aktywacja,
która na moment wystrzeliła? Też się mieści. Owszem, każda z tych liczb jest zapisana ciut mniej
dokładnie — ale sieci neuronowe są na taki szum zdumiewająco odporne, a na wypadnięcie z zakresu
już nie. Pokazało to twardo osobne badanie tego formatu — praca <em>„A Study of BFLOAT16 for Deep
Learning Training&quot;</em> z 2019 roku (zespół inżynierów Intela i Facebooka): trening w <code>bf16</code> osiąga te same
wyniki co <code>f32</code>, w tej samej liczbie iteracji, bez ruszania hiperparametrów, bez loss scalingu —
czego <code>f16</code> nigdy nie dawał za darmo.<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup></p>
<div class="callout">
  <span class="callout__label">Dlaczego to ważne dla lokalnego LLM</span>
<p>Zapamiętaj to jako jedno zdanie: <strong><code>bf16</code> = zakres <code>f32</code>, precyzja gorsza niż <code>f16</code>; <code>f16</code> = precyzja
lepsza niż <code>bf16</code>, ale zakres wąski jak balkon.</strong> Do treningu i tam, gdzie liczy się stabilność
numeryczna, wybierasz zakres — więc <code>bf16</code> (na sprzęcie, który go wspiera natywnie; szybka akceleracja
<code>bf16</code> w Tensor Core to dopiero Ampere i nowsze). Dodatkowa dokładność <code>f16</code> na nic ci się zda, jeśli
połowa liczb ucieknie do nieskończoności. To dlatego „więcej bitów mantysy&quot; nie znaczy „lepszy format&quot;.</p>
</div>
<p>Jest jeszcze jeden bohater drugiego planu, o którym warto wiedzieć, żeby się nie pogubić: <strong>TF32</strong>
od NVIDIA. To nie jest format przechowywania danych — to wewnętrzny tryb obliczeniowy Tensor Core,
z 8-bitowym wykładnikiem (zakres jak <code>f32</code>) i 10-bitową mantysą (precyzja jak <code>f16</code>).<sup id="fnref1:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> Dane
w pamięci nadal leżą jako <code>f32</code>; karta tylko liczy na nich szybciej, po cichu przycinając mantysę
w trakcie mnożenia. Nie pomyl go z <code>bf16</code> jako typem tensora — to inna bajka, tryb liczenia, a nie
etykieta na danych.</p>
<h2 id="po-co-ci-to-przy-lokalnym-llm">Po co ci to przy lokalnym LLM</h2>
<p>„Fajnie, ale ja nic nie trenuję, tylko odpalam gotowca&quot; — i słusznie, więc przełóżmy to na twoje
podwórko.</p>
<p>Przy inferencji <code>f16</code> albo <code>bf16</code> to typowy <strong>compute dtype</strong>, czyli format, w którym faktycznie
lecą obliczenia — 2 bajty na parametr, dwa razy taniej niż <code>f32</code>, przy jakości wyjścia praktycznie
nie do odróżnienia. Ten sam podział bitów wraca też w <strong>KV-cache</strong>: bufor, w którym model trzyma
przemielony kontekst, standardowo siedzi w <code>f16</code> lub <code>bf16</code>, po 2 bajty na wartość — i przy długim
oknie to właśnie on, a nie wagi, robi się największym żłobem na VRAM (rozgryzaliśmy to bliżej
<a href="/poradniki/kv-cache-vram/">tutaj</a>). Compute dtype pojawi ci się też, jak zajrzysz do
<a href="/naukowy/qlora/">QLoRA</a> — tam wagi leżą w 4 bitach, ale liczenie i tak idzie w <code>bf16</code>.</p>
<p>A <code>f32</code>? Przy samej inferencji lokalnego LLM to zwykle <strong>przepłacanie pamięcią</strong>. Ładujesz dwa razy
cięższy tensor, zżerasz dwa razy więcej VRAM-u, a jakości odpowiedzi i tak nie odróżnisz od <code>f16</code>.
Pełne <code>f32</code> ma sens tam, gdzie liczy się stabilność akumulacji — sumowanie tysięcy iloczynów
cząstkowych, niektóre warstwy normalizacyjne, stany optymalizatora przy treningu. Nie przypadkiem
TPU mnoży w <code>bf16</code>, ale <strong>sumy akumuluje w <code>f32</code></strong>: to klasyka <em>mixed precision</em> — licz tanio, sumuj
dokładnie.<sup id="fnref1:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> Ale to dzieje się pod maską frameworka, nie w twoim <code>--outtype</code>.</p>
<p>No i konkret, od którego zaczęliśmy — konwersja do GGUF. Gdy robisz bazę pod dalszą kwantyzację,
<code>--outtype f16</code> albo <code>bf16</code> to punkt wyjścia: pełna szesnastka jako fundament, z którego dopiero
schodzisz do <code>Q4_K_M</code>, <code>Q5_K_M</code> czy <code>Q8_0</code> (o tych nazwach jest osobno
<a href="/poradniki/nazwy-kwantyzacji-gguf/">tu</a>). Prosta zasada kciuka:</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"># model wytrenowano w bf16 (dziś większość) -&gt; zachowaj zakres, bierz bf16:</span>
</span></span><span class="line"><span class="cl">python convert_hf_to_gguf.py ./Meta-Llama-3-8B --outtype bf16 --outfile llama3-8b-bf16.gguf
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># starszy model natywnie w f16 albo docelowy sprzęt bez akceleracji bf16 -&gt; f16:</span>
</span></span><span class="line"><span class="cl">python convert_hf_to_gguf.py ./stary-model --outtype f16 --outfile model-f16.gguf
</span></span></code></pre></div><p>Skąd wiesz, w czym model żył oryginalnie? Zajrzyj do <code>config.json</code> na karcie modelu — pole
<code>torch_dtype</code>. Jeśli stoi tam <code>bfloat16</code> (a przy nowych modelach zwykle stoi), to konwersja do
<code>f16</code> po drodze przycięłaby zakres i mogłaby narobić drobnych przekłamań w skrajnych wartościach.
Trzymaj bazę w tym, w czym model się urodził. Nazwy <code>torch.float32</code>, <code>torch.float16</code> i <code>torch.bfloat16</code>
to zresztą dokładnie te trzy formaty z tabelki wyżej, tyle że w słowniku PyTorcha.<sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup></p>
<p>Bo cała ta szarpanina o bity sprowadza się do jednej sceny: masz 16 kratek do wypełnienia i decydujesz,
ile z nich pójdzie na „jak wielka może być liczba&quot;, a ile na „jak dokładnie ją znam&quot;. <code>f16</code> upchnął
dokładność i został z ciasnym balkonem zamiast horyzontu. <code>bf16</code> odpuścił parę cyfr po przecinku
i dostał w zamian cały zakres <code>f32</code> w połowie miejsca — i właśnie dlatego, kiedy następnym razem
zawahasz się nad <code>--outtype</code>, nie będziesz już zgadywał. Będziesz wiedział, że wybierasz nie „lepszą
szesnastkę&quot;, tylko odpowiedni kształt tego samego pudełka.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Struktura pola <code>binary32</code> (single precision) wg IEEE 754 — 1 bit znaku, 8 bitów wykładnika (bias 127), 23 bity mantysy, zakres do ok. 3,4 × 10³⁸, ok. 7 cyfr znaczących: <a href="https://en.wikipedia.org/wiki/Single-precision_floating-point_format">Single-precision floating-point format (Wikipedia / IEEE 754)</a>.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Struktura pola <code>binary16</code> (half precision) wg IEEE 754-2008 — 1 bit znaku, 5 bitów wykładnika (bias 15), 10 bitów mantysy, maksymalna skończona wartość 65 504: <a href="https://en.wikipedia.org/wiki/Half-precision_floating-point_format">Half-precision floating-point format (Wikipedia / IEEE 754)</a>.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>Dokładny układ bitów FP32 / FP16 / BF16 / TF32 (m.in. „FP32… 1 sign bit, an 8-bit exponent, and a 23-bit mantissa&quot;, FP16 max = 65504, TF32 jako tryb obliczeniowy Tensor Core): <a href="https://docs.nvidia.com/deeplearning/tensorrt/latest/inference-library/accuracy-considerations.html">NVIDIA TensorRT — Accuracy Considerations</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>„The dynamic range of bfloat16 and float32 are equivalent. However, bfloat16 uses half of the memory space&quot; oraz mnożenie w bfloat16 z akumulacją w float32: <a href="https://docs.cloud.google.com/tpu/docs/bfloat16">Google Cloud — The bfloat16 numerical format</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>Empiryczny dowód, że trening w BF16 osiąga wyniki SOTA jak FP32, w tej samej liczbie iteracji i bez zmiany hiperparametrów, dzięki zachowaniu zakresu dynamicznego FP32: Kalamkar i in., „A Study of BFLOAT16 for Deep Learning Training&quot;, <a href="https://arxiv.org/abs/1905.12322">arXiv:1905.12322</a>.&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6">
<p>Oficjalne nazwy i opisy typów <code>torch.float32</code>, <code>torch.float16</code> (half) oraz <code>torch.bfloat16</code> (Brain floating point): <a href="https://docs.pytorch.org/docs/stable/tensor_attributes.html">PyTorch — Tensor Attributes / torch.dtype</a>.&#160;<a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><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>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>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>