<?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>Llama.cpp — Inferownia</title><link>https://inferownia.pl/tags/llama.cpp/</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>Sat, 18 Jul 2026 00:00:00 +0200</lastBuildDate><atom:link href="https://inferownia.pl/tags/llama.cpp/index.xml" rel="self" type="application/rss+xml"/><item><title>llama.cpp: flagi, które naprawdę musisz znać</title><link>https://inferownia.pl/poradniki/flagi-llama-cpp/</link><pubDate>Sat, 18 Jul 2026 00:00:00 +0200</pubDate><category>inferencja</category><guid>https://inferownia.pl/poradniki/flagi-llama-cpp/</guid><description>Wpisujesz llama-cli --help i dostajesz ścianą flag prosto w twarz. Rozbieramy te, które realnie decydują, czy model wejdzie na twój sprzęt i jak szybko będzie gadał.</description><content:encoded><![CDATA[<p>Wpisałeś kiedyś <code>llama-cli --help</code> i przez chwilę pomyślałeś, że terminal się zawiesił?
Zjechało z ekranu jakieś sto flag, jedna pod drugą, każda z krótkim opisem po angielsku,
a człowiek siedzi jak przed instrukcją do mebli z drugiej ręki — niby wszystko po kolei,
a i tak nie wiadomo, od czego zacząć. Łatwo wtedy zamknąć to okno i wrócić do klikania
w Ollamie, bo Ollama „po prostu działa&quot;.</p>
<p>I działa — do momentu, w którym chcesz coś, czego domyślnie nie dostajesz. Więcej kontekstu.
Model wciśnięty w kartę, która teoretycznie jest za mała. Serwer na całą sieć domową, bez
wysyłania czegokolwiek na zewnątrz. Wtedy schodzisz piętro niżej, do <code>llama.cpp</code> — silnika
w czystym C/C++, na którym pół tego lokalnego światka stoi (Ollama, LM Studio, KoboldCpp —
wszystkie mają go gdzieś pod maską). A tam rządzą flagi. I dobra wiadomość jest taka:
z tej setki na co dzień dotykasz może dziesięciu.</p>
<h2 id="zanim-ruszysz-dwie-binarki-nie-jedna">Zanim ruszysz: dwie binarki, nie jedna</h2>
<p>Kiedyś było <code>main</code>. Dziś, po sprzątaniu w repo, masz dwa najważniejsze narzędzia:
<code>llama-cli</code> (rozmowa w terminalu, testy, skrypty) i <code>llama-server</code> (serwer z web UI i API
kompatybilnym z OpenAI). Flagi w dużej mierze się pokrywają, więc to, co niżej, działa
w obu — chyba że zaznaczę inaczej.</p>
<p>Model podajesz jednym parametrem, <code>-m</code> (od <em>model</em>). Reszta to strojenie:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">llama-cli -m qwen2.5-7b-instruct-q4_k_m.gguf -cnv
</span></span></code></pre></div><p><code>-cnv</code> (od <em>conversation</em>) włącza tryb czatu, zamiast jednorazowego dokończenia promptu.
Do tego <code>-sys &quot;jesteś zwięzłym asystentem&quot;</code> ustawia prompt systemowy, a <code>-p &quot;...&quot;</code> podaje
zwykły prompt. Tyle wystarczy, żeby cokolwiek gadało. Tyle że domyślne ustawienia rzadko
są skrojone pod twoją konkretną kartę — więc przechodzimy do sedna.</p>
<h2 id="żeby-model-wszedł-do-karty">Żeby model wszedł do karty</h2>
<p>Tu toczy się cała gra, bo VRAM (pamięć karty) to waluta, której nigdy nie masz dość.
Cztery flagi, które robią 90% roboty:</p>
<ul>
<li><strong><code>-ngl N</code></strong> (<em>gpu-layers</em>) — ile warstw modelu wrzucić na GPU. To najważniejsza flaga
w całym zestawie. Przyjmuje liczbę albo słowo: <code>auto</code> (domyślnie — <code>llama.cpp</code> sam dobiera,
ile się zmieści) oraz <code>all</code> (wrzuć wszystko, co się da). Chcesz maksimum na karcie?
<code>-ngl all</code>. Za mało VRAM-u na cały model? Podaj mniejszą liczbę, np. <code>-ngl 20</code> — reszta
poleci na CPU. Mieszany offload jest wolniejszy, ale często to jedyny sposób, żeby
większy model w ogóle ruszył.</li>
<li><strong><code>-c N</code></strong> (<em>ctx-size</em>) — okno kontekstu w tokenach. Domyślnie <code>llama.cpp</code> bierze rozsądną
wartość, ale to właśnie kontekst potrafi po cichu zjeść ci pamięć. Podwoisz <code>-c</code> z 4096 na
8192 i nagle brakuje pół giga VRAM-u, którego przed chwilą było w sam raz. <code>-c 0</code> mówi
„weź maksimum z modelu&quot; — kuszące, ale sprawdź wcześniej, czy masz na to pamięć.</li>
<li><strong><code>-fa on</code></strong> (<em>flash attention</em>) — zoptymalizowana uwaga: mniej pamięci na KV-cache
(bufor, w którym model trzyma przetworzony kontekst) i zwykle ciut szybciej. Flaga
przyjmuje <code>on</code>/<code>off</code>/<code>auto</code>, a domyślnie stoi na <code>auto</code> (to <code>llama.cpp</code> decyduje). Chcesz
mieć pewność, że działa na nowszej karcie — wal <code>-fa on</code>.</li>
<li><strong><code>-ctk</code> / <code>-ctv</code></strong> (<em>cache-type-k / -v</em>) — kwantyzacja samego KV-cache, np. <code>q8_0</code>
albo <code>q4_0</code>. Przy długim kontekście to on, nie wagi, robi się największym żłobem na pamięć.
Ściśnięcie go do <code>q8_0</code> odzyskuje sporo VRAM-u niemal bez odczuwalnej straty. Najlepiej
razem z <code>-fa on</code>.</li>
</ul>
<div class="callout">
  <span class="callout__label">Dlaczego to ważne dla lokalnego LLM</span>
<p>Kolejność ratowania, gdy „nie wchodzi&quot;: najpierw <code>-fa on</code>, potem <code>-ctk q8_0 -ctv q8_0</code>,
dopiero na końcu tnij <code>-c</code> albo schodź z <code>-ngl</code>. Pierwsze dwa kosztują cię grosze na
jakości, a potrafią odzyskać na tyle VRAM-u, że model klasy 7B–8B wejdzie na kartę
12 GB z długim kontekstem, zamiast dławić się na starcie.</p>
</div>
<h2 id="żeby-było-szybciej">Żeby było szybciej</h2>
<p>Zmieściło się? To teraz o prędkość:</p>
<ul>
<li><strong><code>-t N</code></strong> (<em>threads</em>) — ile wątków CPU. Ustaw na liczbę <strong>fizycznych</strong> rdzeni, nie
logicznych. Więcej nie znaczy szybciej — powyżej liczby rdzeni zaczynasz sobie tylko
przeszkadzać. Liczy się głównie wtedy, gdy część modelu (albo całość) siedzi na CPU.</li>
<li><strong><code>-b</code> / <code>-ub</code></strong> (<em>batch-size / ubatch-size</em>) — jak duże porcje tokenów przetwarza model
naraz przy „czytaniu&quot; promptu. Domyślne wartości są sensowne; ruszaj je, dopiero jak
świadomie walczysz o przepustowość przy długich promptach.</li>
<li><strong><code>--mlock</code></strong> — przypnij model w RAM, żeby system nie wyrzucił go do swapu w najmniej
odpowiednim momencie. <strong><code>--no-mmap</code></strong> ładuje cały plik do pamięci z góry (przydatne na
dziwnych dyskach sieciowych). Na co dzień rzadko potrzebne, ale warto wiedzieć, że są.</li>
<li><strong><code>-ts</code> / <code>-sm</code></strong> (<em>tensor-split / split-mode</em>) — masz dwie karty? <code>-ts 3,1</code> rozłoży
model w proporcji 3:1, a <code>-sm</code> decyduje, jak dokładnie. Temat na osobny wpis, ale niech
ci mignie, że wielo-GPU to nie czarna magia.</li>
</ul>
<h2 id="żeby-generacja-miała-ręce-i-nogi">Żeby generacja miała ręce i nogi</h2>
<p>Te flagi nie dotykają pamięci — sterują tym, <em>jak</em> model dobiera słowa:</p>
<table>
	<thead>
			<tr>
					<th>Flaga</th>
					<th>Co robi</th>
					<th>Bezpieczny start</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>--temp</code></td>
					<td>„temperatura&quot; — im wyżej, tym bardziej kreatywnie (i chaotycznie)</td>
					<td><code>0.7</code></td>
			</tr>
			<tr>
					<td><code>--top-p</code></td>
					<td>odcina mało prawdopodobny ogon słów (nucleus)</td>
					<td><code>0.9</code></td>
			</tr>
			<tr>
					<td><code>--min-p</code></td>
					<td>nowsza, często lepsza alternatywa dla top-p</td>
					<td><code>0.05</code></td>
			</tr>
			<tr>
					<td><code>--repeat-penalty</code></td>
					<td>kara za powtórki, gdy model się zacina w kółko</td>
					<td><code>1.1</code></td>
			</tr>
			<tr>
					<td><code>-n N</code></td>
					<td>ile tokenów wygenerować (<code>-1</code> = bez limitu)</td>
					<td><code>-1</code></td>
			</tr>
	</tbody>
</table>
<p>Jak model gada od rzeczy — zbij <code>--temp</code>. Jak mieli w kółko to samo — podnieś
<code>--repeat-penalty</code> o włos. Bez fanatyzmu, to pokrętła, nie wyrocznia.</p>
<h2 id="serwer-czyli-twój-prywatny-endpoint">Serwer, czyli twój prywatny endpoint</h2>
<p>Najlepszy patent na co dzień: odpal <code>llama-server</code> raz i gadaj z modelem z przeglądarki
albo z kodu, przez API w formacie OpenAI — wszystko lokalnie, offline.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">llama-server -m qwen2.5-7b-instruct-q4_k_m.gguf <span class="se">\
</span></span></span><span class="line"><span class="cl">  -ngl all -c <span class="m">8192</span> -fa on <span class="se">\
</span></span></span><span class="line"><span class="cl">  --host 0.0.0.0 --port <span class="m">8080</span>
</span></span></code></pre></div><p>Wchodzisz na <code>http://localhost:8080</code> i masz web UI od strzału. <code>--host 0.0.0.0</code> wystawia
serwer na całą sieć domową (laptop w kuchni gada z kartą w gabinecie — klasyk domowego setupu).
<code>-np N</code> (<em>parallel</em>) obsłuży kilka rozmów naraz, a <code>-cb</code> (<em>continuous batching</em>) sensownie
je poupycha. <code>--jinja</code> każe użyć szablonu czatu wbudowanego w model — przydaje się, gdy
odpowiedzi wyglądają, jakby model nie wiedział, że rozmawia.</p>
<h2 id="zrób-to-sam">Zrób to sam</h2>
<p>Masz kartę 8 GB i model 7B w Q4? Zacznij od tego i patrz na zużycie VRAM:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">llama-cli -m qwen2.5-7b-instruct-q4_k_m.gguf -ngl all -c <span class="m">4096</span> -fa on -cnv
</span></span></code></pre></div><p>Nie wchodzi? Dorzuć ściśnięty cache i przytnij okno:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">llama-cli -m qwen2.5-7b-instruct-q4_k_m.gguf <span class="se">\
</span></span></span><span class="line"><span class="cl">  -ngl all -c <span class="m">4096</span> -fa on -ctk q8_0 -ctv q8_0 -cnv
</span></span></code></pre></div><p>Modele w formacie GGUF ściągniesz z Hugging Face (szukaj wariantów <code>Q4_K_M</code> — dobry
kompromis rozmiar/jakość). Kod, aktualne flagi i instrukcje budowania są w oficjalnym
repo: <a href="https://github.com/ggml-org/llama.cpp">github.com/ggml-org/llama.cpp</a>. A pełną,
zawsze aktualną listę wywołasz tym, od czego zaczęliśmy — <code>llama-cli --help</code>. Tylko teraz
ta ściana nie będzie już ścianą, a tablicą z pokrętłami, z których wiesz, które przekręcić.</p>
<blockquote>
<p>Wartości domyślne w <code>llama.cpp</code> bywają dobre, ale zmieniają się z wersją. Jak coś
zaczyna działać dziwnie po aktualizacji — najpierw zajrzyj, czy flaga, na której
polegasz, nie dostała nowego domyślnego zachowania.</p>
</blockquote>
<p>Bo cała zabawa w lokalnym AI sprowadza się właśnie do tego jednego uczucia: siedzisz przed
własnym sprzętem, przekręcasz pokrętło o jeden klik i patrzysz, jak model, który „się nie
mieścił&quot;, nagle mruczy pod biurkiem jak trzeba. Nikt ci nie dyktuje, ile masz mu dać
kontekstu ani na czym go odpalić. Ty, karta i garść flag.</p>
]]></content:encoded></item><item><title>Backendy llama.cpp: świat poza CUDA</title><link>https://inferownia.pl/aktualnosci/backendy-llama-cpp/</link><pubDate>Fri, 17 Jul 2026 00:00:00 +0200</pubDate><category>inferencja</category><guid>https://inferownia.pl/aktualnosci/backendy-llama-cpp/</guid><description>Nie masz zielonej karty i już myślisz, że lokalne LLM-y nie są dla ciebie? llama.cpp liczy na CPU, Metalu, Vulkanie i całej reszcie sprzętu — CUDA to tylko jedna z opcji, nie bilet wstępu.</description><content:encoded><![CDATA[<p>Wchodzisz na r/LocalLLaMA, ktoś pyta „jaką kartę wziąć pod modele&quot;, i pod postem od razu
ta sama litania: 3090, 3090, może 4090 jak budżet pozwala, koniecznie NVIDIA, koniecznie
CUDA. Człowiek z Radeonem w obudowie albo z MacBookiem na biurku czyta to i po cichu
zakłada, że wsiadł nie do tego pociągu — że lokalne LLM-y to zabawa wyłącznie dla posiadaczy
zielonych kart, a on ma co najwyżej oglądać z peronu.</p>
<p>I to jest jedno z tych przekonań, które żyje własnym życiem, choć z prawdą ma niewiele
wspólnego. Bo <code>llama.cpp</code> — silnik, na którym stoi pół tego lokalnego światka — od samego
początku był pisany pod jedną obietnicę: odpalać modele „na szerokim zakresie sprzętu&quot;,
lokalnie i w chmurze, w czystym C/C++ bez ciężkich zależności.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> CUDA jest w tym
zestawie, jasne. Ale jest jedną z kilkunastu opcji, nie warunkiem wejścia.</p>
<h2 id="co-to-w-ogóle-jest-backend">Co to w ogóle jest „backend&quot;</h2>
<p>Zajrzyj pod maskę <code>llama.cpp</code>, a znajdziesz <code>ggml</code> — bibliotekę, która robi całą matematykę
modelu. I tu wchodzi słowo klucz: <strong>backend</strong>. To po prostu moduł <code>ggml</code>, który wie, jak
wykonać te wszystkie mnożenia macierzy na konkretnym rodzaju sprzętu. Jeden backend gada
z układem NVIDIA. Inny z GPU Apple. Jeszcze inny z byle czym, co ma sterownik Vulkana.</p>
<p>Domyślnym, zawsze dostępnym backendem jest <strong>CPU</strong>. Zwykły procesor, ten sam, na którym
odpalasz przeglądarkę. Zbudujesz <code>llama.cpp</code> bez ani jednej flagi akceleracji i model
i tak ruszy — wolniej niż na GPU, ale ruszy.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> To jest ta część, o której cała ta litania
zapomina wspomnieć: żeby cokolwiek pogadało lokalnie, karta graficzna nie jest
ci technicznie potrzebna. Jest ci potrzebna, żeby było <em>szybko</em>. A to już zupełnie inna rozmowa.</p>
<h2 id="ilu-tych-backendów-w-ogóle-jest">Ilu tych backendów w ogóle jest?</h2>
<p>Więcej, niż podejrzewasz. Oficjalna tabela w README ciągnie się przez kilkanaście pozycji:
Metal, BLAS, SYCL, CUDA, HIP, Vulkan, MUSA, CANN, OpenCL, ZenDNN, WebGPU, IBM zDNN,
Hexagon i jeszcze parę egzotów.<sup id="fnref1:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> Większości z nich w życiu nie dotkniesz — Ascend
NPU czy IBM Z &amp; LinuxONE to sprzęt, którego nie trzymasz pod biurkiem. Ale te, które
realnie decydują o tym, czy twój sprzęt załapie się na zabawę, dają się policzyć na palcach:</p>
<table>
	<thead>
			<tr>
					<th>Backend</th>
					<th>Sprzęt</th>
					<th>Flaga przy budowaniu</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><strong>CPU</strong></td>
					<td>dowolny procesor</td>
					<td>żadna (domyślny)</td>
			</tr>
			<tr>
					<td><strong>CUDA</strong></td>
					<td>GPU NVIDIA</td>
					<td><code>-DGGML_CUDA=ON</code></td>
			</tr>
			<tr>
					<td><strong>Metal</strong></td>
					<td>Apple Silicon (M1–M4)</td>
					<td>domyślnie ON na macOS</td>
			</tr>
			<tr>
					<td><strong>Vulkan</strong></td>
					<td>GPU wielu producentów (AMD, Intel, NVIDIA)</td>
					<td><code>-DGGML_VULKAN=ON</code></td>
			</tr>
			<tr>
					<td><strong>HIP / ROCm</strong></td>
					<td>GPU AMD</td>
					<td><code>-DGGML_HIP=ON -DGPU_TARGETS=…</code></td>
			</tr>
			<tr>
					<td><strong>SYCL</strong></td>
					<td>GPU Intel</td>
					<td>osobna instrukcja</td>
			</tr>
			<tr>
					<td><strong>BLAS</strong></td>
					<td>CPU (przyspieszenie)</td>
					<td><code>-DGGML_BLAS=ON</code></td>
			</tr>
	</tbody>
</table>
<p>Zanim pójdziemy dalej — jedna rzecz, która lubi umykać, a jest w tym wszystkim kluczowa.
Te flagi z <code>-D</code> na przedzie to opcje <strong>budowania</strong>, czyli kompilacji. <code>llama.cpp</code> bierzesz
w postaci kodu źródłowego i raz zamieniasz go w gotowy program — tym właśnie jest
budowanie, a robi to <code>cmake</code>. I to na tym etapie, <em>przed</em> pierwszym uruchomieniem, mówisz,
jakie backendy wkompilować. Nie podajesz <code>-DGGML_CUDA=ON</code> przy odpalaniu modelu — podajesz
to raz, zanim plik wykonywalny w ogóle powstanie. Potem uruchamiasz już gotową binarkę,
a ona z góry wie, na czym ma liczyć.</p>
<p>Przejdźmy tę listę tak, jak się ją czyta w praktyce — od strony pytania „a co ja mam
w obudowie&quot;.</p>
<p><strong>Masz NVIDIA?</strong> No to CUDA, ta zamknięta platforma obliczeniowa NVIDIA, która działa
tylko na ich kartach. Włączasz ją flagą <code>-DGGML_CUDA=ON</code> przy budowaniu.<sup id="fnref1:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> To ta
najbardziej wydeptana ścieżka, o której wszyscy piszą — i faktycznie działa jak trzeba.
Tylko że wydeptana nie znaczy jedyna.</p>
<p><strong>Masz MacBooka na Apple Silicon?</strong> Tu jest najlepszy żart całej układanki: nie musisz
robić absolutnie nic. Backend <strong>Metal</strong> — natywne API GPU Apple — jest na macOS włączony
domyślnie. Budujesz <code>llama.cpp</code> normalnie i twój M2 czy M3 od razu liczy na GPU. Chcesz
go z jakiegoś powodu wyłączyć? Musisz się wręcz postarać: <code>-DGGML_METAL=OFF</code>.<sup id="fnref2:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>
Zunifikowana pamięć w Makach robi tu robotę, o której właściciele osobnych kart mogą
tylko pomarzyć.</p>
<p><strong>Masz Radeona albo GPU Intela?</strong> I tu robi się najciekawiej, bo to dokładnie ci ludzie,
których ta litania odprawia z kwitkiem. A mają aż dwie drogi.</p>
<h2 id="vulkan-czyli-furtka-dla-całej-reszty">Vulkan, czyli furtka dla całej reszty</h2>
<p>Gdybym miał wskazać jeden backend, o którym za mało się mówi, to <strong>Vulkan</strong>. To otwarte,
wieloplatformowe API graficzno-obliczeniowe (ta sama Khronos, co od OpenGL-a), które
obsługuje mnóstwo sterowników GPU — NVIDIA, AMD, Intela. W <code>llama.cpp</code> włączasz je jedną
flagą, <code>-DGGML_VULKAN=ON</code>, i dostajesz akcelerację GPU działającą cross-platform, na
Windowsie, Linuksie i macOS naraz — bez instalowania dedykowanych, ciężkich stosów
producenta.<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup></p>
<p>I to jest sedno. Bo alternatywą dla AMD jest <strong>HIP/ROCm</strong> — cały software&rsquo;owy stos AMD do
liczenia na GPU (HIP to interfejs w jego ramach, pozwalający pisać kod zbliżony do CUDA;
to nie osobny produkt, tylko część tego samego ekosystemu). Bywa wydajniejszy od Vulkana,
ale kosztuje cię więcej zachodu przy konfiguracji i samym budowaniu. Zobacz, jak to wygląda:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nv">HIPCXX</span><span class="o">=</span><span class="s2">&#34;</span><span class="k">$(</span>hipconfig -l<span class="k">)</span><span class="s2">/clang&#34;</span> <span class="nv">HIP_PATH</span><span class="o">=</span><span class="s2">&#34;</span><span class="k">$(</span>hipconfig -R<span class="k">)</span><span class="s2">&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  cmake -S . -B build -DGGML_HIP<span class="o">=</span>ON -DGPU_TARGETS<span class="o">=</span>gfx1030 -DCMAKE_BUILD_TYPE<span class="o">=</span>Release
</span></span><span class="line"><span class="cl">cmake --build build --config Release
</span></span></code></pre></div><p><code>gfx1030</code> to nie ozdobnik — to kod architektury twojego konkretnego układu AMD, który
musisz najpierw poznać i podać ręcznie.<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> U Intela jest podobnie: backend <strong>SYCL</strong>
(otwarty standard programowania heterogenicznego, którym Intel wozi swoje GPU przez oneAPI)
ma na tyle osobną drogę budowania, że w repo doczekał się własnego pliku dokumentacji, obok
głównego <code>build.md</code>.<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup> Nikt nie mówi, że to zabawa bez łzawienia. Ale że się da — da się.</p>
<div class="callout">
  <span class="callout__label">Dlaczego to ważne dla lokalnego LLM</span>
<p>Dla kogoś bez „zielonej karty&quot; Vulkan bywa najkrótszą drogą do sensownej akceleracji GPU:
jedna flaga <code>-DGGML_VULKAN=ON</code>, sterownik, który i tak masz do grania, i tyle — zamiast
przekopywania się przez cały ROCm czy oneAPI. Nie zawsze wyciśnie z Radeona ostatni token,
ale różnica między „liczę na GPU&quot; a „mielę na samym CPU&quot; jest odczuwalnie większa niż
różnica między dwoma backendami GPU. Najpierw w ogóle wejdź na GPU; to, który z nich
wyciska więcej, dostroisz później.</p>
</div>
<h2 id="a-jeśli-gpu-w-ogóle-nie-ma">A jeśli GPU w ogóle nie ma?</h2>
<p>Zostaje CPU — i wcale nie jesteś skazany na gołą, referencyjną implementację. Masz <strong>BLAS</strong>
(np. OpenBLAS), który przyspiesza obliczenia na procesorze i włącza się parą flag
<code>-DGGML_BLAS=ON -DGGML_BLAS_VENDOR=OpenBLAS</code>.<sup id="fnref3:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> A jak siedzisz na czymś bardziej
konkretnym, <code>llama.cpp</code> ma nawet backendy skrojone pod rodzinę procesora: <strong>ZenDNN</strong> pod
serwerowe CPU AMD EPYC i <strong>Arm KleidiAI</strong> pod układy Arm — obok generycznego CPU.<sup id="fnref4:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>
Mrówcza robota optymalizacyjna, o której mało kto wie, a która potrafi wycisnąć z krzemu,
który już masz, zaskakująco dużo.</p>
<h2 id="zrób-to-sam">Zrób to sam</h2>
<p>Powiedzmy, że budujesz <code>llama.cpp</code> sam (a warto — <a href="/poradniki/flagi-llama-cpp/">rozbieraliśmy jego flagi po
kolei</a>, bo to na nich jedzie pół ekosystemu, od Ollamy po
LM Studio). Najprostszy build, sam CPU, bez żadnych ozdobników:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">cmake -B build
</span></span><span class="line"><span class="cl">cmake --build build --config Release
</span></span></code></pre></div><p>Chcesz akcelerację? Dokładasz jedną flagę pod swój sprzęt — <code>-DGGML_CUDA=ON</code>,
<code>-DGGML_VULKAN=ON</code>, <code>-DGGML_HIP=ON</code> — i tyle. Co więcej, backendy da się łączyć w jednym
buildzie (CUDA i Vulkan naraz to normalka), a którym urządzeniem policzysz, wybierasz już
w locie, przy odpalaniu:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">llama-cli --list-devices
</span></span><span class="line"><span class="cl">llama-cli -m model.gguf --device Vulkan0
</span></span><span class="line"><span class="cl">llama-cli -m model.gguf --device none
</span></span></code></pre></div><p><code>--list-devices</code> pokaże ci wszystkie akceleratory, które build widzi, <code>--device</code> wskaże
ten jeden, a <code>--device none</code> każe liczyć wyłącznie na CPU — wygodne, gdy chcesz zobaczyć,
ile tracisz bez GPU.<sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup> I jedno, co warto rozdzielić w głowie raz na zawsze: wybór
backendu to nie to samo co kwantyzacja modelu. Format GGUF i to, czy bierzesz Q4 czy Q8
(<a href="/poradniki/nazwy-kwantyzacji-gguf/">nazwy rozbieraliśmy osobno</a>), to jedna decyzja —
<em>na czym</em> to policzysz to zupełnie druga. Ten sam plik <code>.gguf</code> pojedzie na CUDA, na
Vulkanie i na gołym procesorze. Nie mieszaj tych dwóch pokręteł, bo to niezależne osie.</p>
<p>Cała aktualna prawda o budowaniu siedzi w jednym pliku:
<a href="https://github.com/ggml-org/llama.cpp/blob/master/docs/build.md">docs/build.md</a> w repo
<code>ggml-org/llama.cpp</code>. Sekcja po sekcji, backend po backendzie.</p>
<p>Bo ta redditowa litania „NVIDIA albo nic&quot; to trochę jak stać przed budynkiem z jednymi
oszklonymi drzwiami frontowymi, na które wszyscy się pchają, i nie wiedzieć, że dookoła
jest jeszcze pięć wejść — węższych, gorzej oznaczonych, część z lekko zacinającą się klamką,
ale prowadzących do tego samego środka. Zielona karta to najwygodniejsze drzwi. Nie jedyne.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><code>ggml-org/llama.cpp</code> — README (deklaracja celu projektu: „a wide range of hardware — locally and in the cloud&quot; i tabela wspieranych backendów, m.in. Metal, BLAS, SYCL, CUDA, HIP, Vulkan, CANN, OpenCL, ZenDNN, IBM zDNN, Hexagon): <a href="https://github.com/ggml-org/llama.cpp#readme">github.com/ggml-org/llama.cpp</a>.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p><code>ggml-org/llama.cpp</code> — dokumentacja budowania (CPU jako backend domyślny, <code>-DGGML_CUDA=ON</code>, Metal domyślnie ON na macOS z <code>-DGGML_METAL=OFF</code>, <code>-DGGML_BLAS=ON -DGGML_BLAS_VENDOR=OpenBLAS</code>, sekcje ZenDNN i Arm KleidiAI): <a href="https://github.com/ggml-org/llama.cpp/blob/master/docs/build.md">docs/build.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></p>
</li>
<li id="fn:3">
<p><code>ggml-org/llama.cpp</code> — sekcja Vulkan (flaga <code>-DGGML_VULKAN=ON</code>, wieloplatformowy backend na GPU różnych producentów): <a href="https://github.com/ggml-org/llama.cpp/blob/master/docs/build.md#vulkan">docs/build.md#vulkan</a>.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p><code>ggml-org/llama.cpp</code> — sekcja HIP (zmienne <code>HIPCXX</code>/<code>HIP_PATH</code> z <code>hipconfig</code>, flagi <code>-DGGML_HIP=ON -DGPU_TARGETS=gfx1030</code>): <a href="https://github.com/ggml-org/llama.cpp/blob/master/docs/build.md#hip">docs/build.md#hip</a>.&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p><code>ggml-org/llama.cpp</code> — osobna dokumentacja backendu SYCL dla GPU Intel: <a href="https://github.com/ggml-org/llama.cpp/blob/master/docs/backend/SYCL.md">docs/backend/SYCL.md</a>.&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6">
<p><code>ggml-org/llama.cpp</code> — definicje flag CLI <code>--list-devices</code>, <code>--device</code> (z obsługą wartości <code>none</code> w <code>parse_device_list</code>): <a href="https://github.com/ggml-org/llama.cpp/blob/master/common/arg.cpp">common/arg.cpp</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>Ile VRAM-u potrzebuje model? Liczymy na palcach</title><link>https://inferownia.pl/poradniki/ile-vram-na-model/</link><pubDate>Sun, 12 Jul 2026 00:00:00 +0200</pubDate><category>inferencja</category><guid>https://inferownia.pl/poradniki/ile-vram-na-model/</guid><description>Nowy model, ładna karta modelu, a na dole jedno zdanie: 16 GB VRAM. Ty masz te swoje 8. Zamiast wróżyć z fusów, policzmy na palcach, ile naprawdę zważy — i jak zejść niżej.</description><content:encoded><![CDATA[<p><code>Wymagania: 16 GB VRAM</code>. Zjeżdżasz na dół karty modelu, widzisz to jedno zdanie, zerkasz na
swoją kartę do gier z jej ośmioma gigabajtami — z czego połowę i tak zżera przeglądarka — i
serce ci lekko siada. A bywa gorzej: czasem nawet tej jednej liczby nie ma, jest za to tabelka
z dwudziestoma wariantami pliku, każdy waży inaczej, i dalej nie wiesz, czy w ogóle podchodzić,
czy zaraz przywita cię „CUDA out of memory&quot;, cała na biało. To codzienność — połowa pytań „will it fit?&quot; to ludzie, którzy kupili
GPU do gier, a teraz próbują wcisnąć na nie model wielkości małego miasta.</p>
<p>Dobra wiadomość: nie musisz zgadywać. VRAM da się policzyć na palcach, z grubsza, w głowie,
zanim cokolwiek ściągniesz. Nie do grama — do grama policzy ci <code>llama.cpp</code> przy starcie —
ale na tyle dokładnie, żeby wiedzieć, czy w ogóle podchodzić. O to cała sztuka:
zamiast wróżyć z rozmiaru pliku, umieć powiedzieć „7B w Q4 to jakieś pięć giga plus
zapas, wejdzie&quot; — i mieć rację.</p>
<h2 id="wzór-na-palcach">Wzór na palcach</h2>
<p>Cały fundament mieści się w jednej linijce:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">rozmiar wag (GB) ≈ liczba parametrów (mld) × bity na wagę / 8
</span></span></code></pre></div><p>I tyle. Bierzesz, ile model ma miliardów parametrów, mnożysz przez to, ile bitów zajmuje
jedna waga w danym formacie, dzielisz przez osiem (bo bajt ma osiem bitów) i masz gramaturę
samych wag w gigabajtach. Model 8B w kwantyzacji Q4, gdzie na wagę schodzi jakieś 4,5 bita?
<code>8 × 4,5 / 8 ≈ 4,5 GB</code>. I to nie jest teoretyczna wróżba — realny plik <code>Q4_K_M</code> dla
Llamy 3.1 8B waży 4,92 GB.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> Zgadza się co do joty, a właściwie co do paru setek megabajtów.</p>
<div class="callout">
  <span class="callout__label">Dlaczego to ważne dla lokalnego LLM</span>
<p>To „bity na wagę&quot; (po angielsku <em>bits-per-weight</em>, bpw) to sedno całej zabawy z kwantyzacją.
Surowy model trenuje się zwykle w 16 bitach na wagę. Kwantyzacja to nic innego jak zapisanie
tych samych wag ciaśniej — w 8, 5, 4, a nawet 2 bitach — już po treningu, bez ruszania
architektury. Nie myl tego z trenowaniem od zera w niskiej precyzji; to dwie różne bajki.
Jak działają nazwy w stylu <code>Q4_K_M</code> i skąd te dziwne litery, rozgryzaliśmy osobno
<a href="/poradniki/nazwy-kwantyzacji-gguf/">tutaj</a>.</p>
</div>
<h2 id="skąd-biorą-się-ułamkowe-bity">Skąd biorą się ułamkowe bity</h2>
<p>Tu pierwsza pułapka, na którą łapie się każdy początkujący: „Q4 to przecież cztery bity,
nie?&quot;. No właśnie nie do końca. Gdyby świat był prosty, <code>Q4_0</code> (stary, prościutki typ)
brałby równe 4,5 bita na wagę — bo pakuje 32 wagi w blok i dokłada do nich trochę metadanych
o skali.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> Ale nowsze formaty, tak zwane K-quanty (<code>Q2_K</code> do <code>Q6_K</code>, z dopiskami <code>_S</code>,
<code>_M</code>, <code>_L</code>), są sprytniejsze: mieszają precyzję między różnymi warstwami modelu. Te tensory,
od których jakość naprawdę zależy, dostają więcej bitów, reszcie się skąpi. Efekt? Liczba
bitów na wagę wychodzi ułamkowa.</p>
<p>Konkretnie, dla Llamy 3.1 8B według oficjalnej tabeli <code>llama.cpp</code>:<sup id="fnref1:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<table>
	<thead>
			<tr>
					<th>Format</th>
					<th>Bity na wagę</th>
					<th>Rozmiar (tabela llama.cpp)</th>
					<th>Realny plik GGUF</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>Q4_K_M</code></td>
					<td>4,8944</td>
					<td>4,58 GiB</td>
					<td>4,92 GB</td>
			</tr>
			<tr>
					<td><code>Q5_K_M</code></td>
					<td>5,7036</td>
					<td>5,33 GiB</td>
					<td>5,73 GB</td>
			</tr>
			<tr>
					<td><code>Q6_K</code></td>
					<td>~6,5</td>
					<td>—</td>
					<td>6,60 GB</td>
			</tr>
			<tr>
					<td><code>Q8_0</code></td>
					<td>~8,50</td>
					<td>—</td>
					<td>8,54 GB</td>
			</tr>
			<tr>
					<td><code>F16</code></td>
					<td>16,0005</td>
					<td>14,96 GiB</td>
					<td>—</td>
			</tr>
	</tbody>
</table>
<p>Widzisz tę drobną rozbieżność między kolumnami? Jedna to GiB (liczone po 1024), druga GB
(po 1000), plus K-quant miesza precyzję tensorów — stąd <code>Q4_K_M</code> ma prawie 4,9 bita, a nie
równe 4,5.<sup id="fnref1:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> Do liczenia na palcach nic to nie zmienia: bierzesz „mniej więcej 4,5–5
bita dla Q4&quot; i tak trafiasz w wynik z dokładnością do zapasu, który i tak musisz zostawić.
Jest jeszcze cała rodzina IQ-quantów (<code>IQ2_XXS</code>, <code>IQ3_S</code> i spółka), które schodzą poniżej
trzech bitów, ważąc jakość specjalną macierzą istotności — ale to temat na osobny wieczór.<sup id="fnref2:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<h2 id="ile-zważy-ten-twój-model">Ile zważy „ten twój&quot; model</h2>
<p>Przełóżmy wzór na cztery rozmiary, które realnie krążą po lokalnym światku. To są same wagi,
bez narzutu — o narzucie za chwilę:</p>
<table>
	<thead>
			<tr>
					<th>Model</th>
					<th>Q4_K_M</th>
					<th>Q5_K_M</th>
					<th>Q8_0</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Llama 3.1 <strong>8B</strong><sup id="fnref2:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></td>
					<td>4,92 GB</td>
					<td>5,73 GB</td>
					<td>8,54 GB</td>
			</tr>
			<tr>
					<td>Mistral Nemo <strong>12B</strong><sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup></td>
					<td>7,48 GB</td>
					<td>8,73 GB</td>
					<td>13,02 GB</td>
			</tr>
			<tr>
					<td>Qwen2.5 <strong>14B</strong><sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup></td>
					<td>8,99 GB</td>
					<td>10,51 GB</td>
					<td>15,70 GB</td>
			</tr>
	</tbody>
</table>
<p>Popatrz na to przez chwilę, bo tu widać całą matematykę życia lokalnego LLM-owca. Masz kartę
8 GB? Ósemka w Q4 wchodzi na luzie, w Q5 już z zadyszką, a Q8 nawet nie podchodź. Dwunastka
w Q4 to 7,5 GB samych wag — teoretycznie „się mieści&quot;, tylko że na sam narzut nie zostaje już
nic. A czternastka w Q4 to prawie dziewięć giga — twoje 8 GB kończy się, zanim się zaczęło.
Dlatego „te swoje 8 GB&quot; znika tak szybko: liczysz gramaturę wag, cieszysz się, że wchodzi,
a potem uruchamiasz i okazuje się, że o czymś zapomniałeś.</p>
<h2 id="czego-zapominasz-kv-cache-i-bufory">Czego zapominasz: KV-cache i bufory</h2>
<p>O to zapominasz. Rozmiar pliku GGUF to tylko wagi. Do tego dochodzi KV-cache — pamięć, w której
model trzyma przetworzone klucze i wartości dla każdego tokena, który model już wygenerował lub
wczytał z promptu. I to jest ten cichy złodziej VRAM-u, bo rośnie <strong>liniowo z długością
kontekstu</strong>. Im dłuższa rozmowa, tym większy żłób na pamięć.</p>
<p>Ile dokładnie? Też się liczy na palcach. Na jeden token przypada:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">KV na token = 2 (klucz + wartość) × liczba_warstw × n_head_kv × head_dim × bajty_na_element
</span></span></code></pre></div><p>Dla Llamy 3.1 8B te liczby to: 32 warstwy, <code>n_head_kv</code> = 8, <code>head_dim</code> = 128, a KV-cache
domyślnie leci w <code>f16</code>, czyli 2 bajty na element<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup> (co to za <code>f16</code> i czemu nie <code>bf16</code> — <a href="/poradniki/bf16-f16-f32/">tu</a>). Wrzuć to do wzoru:
<code>2 × 32 × 8 × 128 × 2 bajty = 128 KiB</code> na token. Przy kontekście 8192 tokenów robi się z tego
równiutki 1 GiB — tyle dokłada sam cache do tego, co już zjadły wagi. Rozbieramy ten mechanizm
dokładniej <a href="/poradniki/kv-cache-vram/">tutaj</a>, ale zasada do zapamiętania jest prosta: dłuższy
kontekst = więcej VRAM-u, po prostu.</p>
<p>Zwróć uwagę na <code>n_head_kv</code> = 8 — pełna liczba głów uwagi w tym modelu jest większa, ale dzięki
GQA (<em>grouped-query attention</em>, mechanizm współdzielenia głów klucza/wartości) do cache liczy się
tylko ta zredukowana ósemka. Bez GQA, na starej architekturze, ten narzut potrafił być
wielokrotnie większy. Do tego dochodzą jeszcze bufory obliczeniowe samego procesu inferencji.
Dlatego świętą zasadą jest: <strong>zostaw 1–2 GB zapasu ponad rozmiar wag</strong>. Kto liczy VRAM tylko
z gramatury pliku GGUF, ten regularnie ląduje na „out of memory&quot; przy trzecim akapicie rozmowy.</p>
<h2 id="jak-zejść-niżej-gdy-nie-wchodzi">Jak zejść niżej, gdy nie wchodzi</h2>
<p>Powiedzmy, że policzyłeś i wychodzi za dużo. Masz trzy dźwignie, w tej właśnie kolejności.</p>
<p>Raz — <strong>kwantyzacja niżej</strong>. To najprostszy ruch: zamiast Q8 bierzesz Q5, zamiast Q5 bierzesz
Q4. Czternastka w Q8 to 15,7 GB, a w Q4 już tylko 8,99 GB<sup id="fnref1:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> — prawie połowa mniej, a
jakość spada zaskakująco mało, bo K-quanty oszczędzają mądrze. Q4_K_M to nie bez powodu
domyślny wybór połowy internetu.</p>
<p>Dwa — <strong>kwantyzacja KV-cache</strong>. Skoro cache potrafi dołożyć giga przy dłuższym kontekście, to
jego też można ścisnąć. <code>llama-server</code> ma na to flagi <code>-ctk</code> / <code>--cache-type-k</code> i <code>-ctv</code> /
<code>--cache-type-v</code>, osobno dla kluczy i wartości; domyślnie oba stoją na <code>f16</code>, ale przyjmują też
<code>q8_0</code>, <code>q4_0</code> i całą listę innych typów.<sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup> Ściśnięcie cache z <code>f16</code> do <code>q8_0</code> to znów
zejście z jego rozmiaru mniej więcej o połowę.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">llama-server -m qwen2.5-14b-instruct-q4_k_m.gguf <span class="se">\
</span></span></span><span class="line"><span class="cl">  -ngl all -c <span class="m">8192</span> -fa on <span class="se">\
</span></span></span><span class="line"><span class="cl">  -ctk q8_0 -ctv q8_0
</span></span></code></pre></div><p>Widzisz to <code>-fa on</code>? To nie ozdoba. Flaga <code>-fa</code> / <code>--flash-attn</code> (przyjmuje <code>on</code>/<code>off</code>/<code>auto</code>,
domyślnie <code>auto</code>) włącza Flash Attention — a kwantyzacja KV-cache do typów poniżej <code>f16</code>
praktycznie <strong>wymaga</strong> włączonej Flash Attention.<sup id="fnref1:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup> Ustawisz <code>-ctk q4_0</code> bez <code>-fa on</code> i
w najlepszym razie nic nie zyskasz, w najgorszym nie ruszy. Więc: najpierw Flash Attention,
potem duś cache.</p>
<p>Trzy — <strong>przytnij kontekst albo zrzuć część warstw na CPU</strong>. To ostateczność, bo krótszy
kontekst to mniejsza pamięć modelu, a offload na CPU (flaga <code>-ngl</code> z liczbą mniejszą niż
„wszystko&quot;) spowalnia generację. Ale gdy 12B w Q4 nie chce wejść na 8 GB nawet po ściśnięciu
cache — czasem to jedyny sposób, żeby w ogóle ruszyło. Cała ta chirurgia flagami to osobny
rozdział, który rozpisaliśmy <a href="/poradniki/flagi-llama-cpp/">tu</a>.</p>
<h2 id="zrób-to-sam-rachunek-w-trzy-sekundy">Zrób to sam: rachunek w trzy sekundy</h2>
<p>Następnym razem, gdy staniesz przed nowym modelem, przelicz go w głowie, zanim klikniesz
pobieranie:</p>
<ol>
<li><strong>Wagi</strong>: <code>parametry × bity / 8</code>. Ósemka w Q4 → <code>8 × 4,5 / 8 ≈ 4,5 GB</code>. Albo zerknij na
realny plik na Hugging Face i weź gotową liczbę.</li>
<li><strong>Cache</strong>: przy zwykłym kontekście (4–8k) dorzuć z grubsza 0,5–1 GB dla modelu klasy 7–14B
z GQA. Planujesz 32k albo więcej? Licz się z kilkoma gigabajtami i od razu myśl o <code>-fa on</code>
plus <code>-ctk q8_0</code>.</li>
<li><strong>Zapas</strong>: dorzuć 1–2 GB na bufory i oddech.</li>
</ol>
<p>Suma większa niż twój VRAM? Schodzisz z kwantyzacją albo duszisz cache — w tej kolejności.
Suma mniejsza? Ściągaj śmiało, wejdzie.</p>
<p>Bo cała sztuka z VRAM-em nie polega na wróżeniu, tylko na tym jednym prostym mnożeniu na
palcach — parametry razy bity, przez osiem, plus zapas. Kiedy raz je poczujesz, folder <code>models</code>
przestaje być loterią, a zaczyna być tym, czym powinien: półką, na której z góry wiesz, co się
zmieści, a co poleży, aż dokupisz kartę „na później&quot;. A dokupisz. Wszyscy dokupujemy.</p>
<h2 id="przypisy--źródła">Przypisy · źródła</h2>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Realne rozmiary plików GGUF dla Meta-Llama-3.1-8B-Instruct (<code>Q4_K_M</code> = 4,92 GB, <code>Q5_K_M</code> = 5,73 GB, <code>Q6_K</code> = 6,60 GB, <code>Q8_0</code> = 8,54 GB), <a href="https://huggingface.co/bartowski/Meta-Llama-3.1-8B-Instruct-GGUF">bartowski/Meta-Llama-3.1-8B-Instruct-GGUF</a>.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref2:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>llama.cpp, tabela typów kwantyzacji z wartościami bits-per-weight i rozmiarami dla Llama 3.1 8B (<code>Q4_K_M</code> = 4,8944 bpw / 4,58 GiB, <code>Q5_K_M</code> = 5,7036 bpw / 5,33 GiB, <code>F16</code> = 16,0005 bpw / 14,96 GiB; <code>Q4_0</code> = 4,5 bpw), <a href="https://github.com/ggml-org/llama.cpp/blob/master/tools/quantize/README.md">tools/quantize/README.md</a>.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref2:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>Realne rozmiary plików GGUF dla Mistral-Nemo-Instruct-2407 (12B): <code>Q4_K_M</code> = 7,48 GB, <code>Q5_K_M</code> = 8,73 GB, <code>Q8_0</code> = 13,02 GB, <a href="https://huggingface.co/bartowski/Mistral-Nemo-Instruct-2407-GGUF">bartowski/Mistral-Nemo-Instruct-2407-GGUF</a>.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>Realne rozmiary plików GGUF dla Qwen2.5-14B-Instruct: <code>Q4_K_M</code> = 8,99 GB, <code>Q5_K_M</code> = 10,51 GB, <code>Q8_0</code> = 15,70 GB, <a href="https://huggingface.co/bartowski/Qwen2.5-14B-Instruct-GGUF">bartowski/Qwen2.5-14B-Instruct-GGUF</a>.&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p>llama.cpp, parametry KV-cache dla Llama 3 8B (<code>n_layer</code> = 32, <code>n_head_kv</code> = 8, <code>head_dim</code> = 128, <code>n_embd_k_gqa</code>/<code>v_gqa</code> = 1024) i potwierdzenie domyślnego typu cache <code>f16</code>, <a href="https://github.com/ggml-org/llama.cpp/discussions/7949">GitHub Discussion #7949 — KV Cache Dimension</a>.&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6">
<p>llama.cpp, opis flag <code>llama-server</code>: <code>-ctk</code>/<code>--cache-type-k</code>, <code>-ctv</code>/<code>--cache-type-v</code> (domyślnie <code>f16</code>, dozwolone m.in. <code>q8_0</code>, <code>q4_0</code>) oraz <code>-fa</code>/<code>--flash-attn</code> (<code>on</code>/<code>off</code>/<code>auto</code>, domyślnie <code>auto</code>); kwantyzacja KV-cache poniżej <code>f16</code> wymaga Flash Attention, <a href="https://github.com/ggml-org/llama.cpp/blob/master/tools/server/README.md">tools/server/README.md</a>.&#160;<a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>FlashAttention: uwaga, która nie zjada pamięci</title><link>https://inferownia.pl/naukowy/flashattention/</link><pubDate>Sat, 11 Jul 2026 00:00:00 +0200</pubDate><author>Tri Dao</author><author>Daniel Y. Fu</author><author>Stefano Ermon</author><author>Atri Rudra</author><author>Christopher Ré</author><category>inferencja</category><guid>https://inferownia.pl/naukowy/flashattention/</guid><description>Wrzucasz jedną flagę, -fa, i model nagle mieści dłuższy kontekst na tej samej karcie. Za tym skrótem stoi praca, która nie przybliża uwagi ani jej nie tnie — tylko przestawia kolejność liczenia tak, żeby wielka macierz N×N nigdy nie musiała wylądować w wolnej pamięci.</description><content:encoded><![CDATA[<p>Znasz to <code>-fa on</code>, które wklepujesz przy odpalaniu <code>llama.cpp</code> prawie z automatu, jak
człowiek zapinający pas w aucie — nie zastanawiasz się, po prostu wiesz, że tak trzeba?
To jeden z tych patentów, który powtarza się jak refren:
„nie wchodzi na kartę? dorzuć flash attention&quot;. Wrzucasz, kontekst się rozciąga, VRAM-u
ubywa wolniej, i jedziesz dalej, nawet nie pytając, co ta flaga właściwie robi pod maską.</p>
<p>A pod maską siedzi jedna z ładniejszych prac ostatnich lat — <em>FlashAttention</em>, Tri Dao
i spółka ze Stanfordu, maj 2022.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> Ładna nie dlatego, że wymyśliła nowy rodzaj uwagi.
Wręcz przeciwnie — wynik matematyczny wychodzi <strong>co do liczby taki sam</strong> jak w zwykłej
uwadze. Cały myk polega na czymś, co brzmi jak herezja dla kogoś, kto liczył złożoność
na studiach: żeby przyspieszyć obliczenia, zamiast liczyć mniej, autorzy kazali karcie
<strong>mniej biegać po pamięci</strong>. I to załatwiło sprawę.</p>
<h2 id="skąd-w-ogóle-ten-głód-pamięci">Skąd w ogóle ten głód pamięci</h2>
<p>Najpierw ustalmy, czego uwaga potrzebuje, bo tu leży cały problem. Uwaga (ta z „Attention
Is All You Need&quot;, z 2017 — inna praca, inni ludzie, nie mylić) dla sekwencji o długości <code>N</code>
tokenów porównuje każdy token z każdym. Każdy z każdym to macierz <code>N × N</code>. Masz 4 tysiące
tokenów kontekstu? To 16 milionów liczb w jednej tablicy. Osiem tysięcy? Już 64 miliony.
Pamięć rośnie z kwadratem długości — podwajasz kontekst, płacisz czterokrotnie.<sup id="fnref1:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p>I teraz najważniejsze, co ludzie przeoczają: nie chodzi o to, że tych mnożeń jest dużo.
GPU uwielbia mnożyć, od tego jest. Problem w tym, <strong>gdzie</strong> ta wielka macierz ląduje.
Karta ma dwa rodzaje pamięci. Jest HBM — ta duża, te twoje 8, 12 czy 24 giga, wolna jak
poranny ruch na obwodnicy. I jest SRAM — malutka pamięć tuż przy rdzeniach obliczeniowych,
błyskawiczna, ale mieści tyle co nic. Zwykła uwaga liczy całą macierz <code>N × N</code>, zapisuje ją
w HBM, potem odczytuje z powrotem, żeby policzyć softmax, znowu zapisuje, znowu czyta…
i to bieganie tam i z powrotem — nie samo mnożenie — jest tym, co zżera czas i pamięć.</p>
<h2 id="io-aware-czyli-licz-tam-gdzie-blisko">„IO-aware&quot;, czyli licz tam, gdzie blisko</h2>
<p>Tu wchodzi pomysł, od którego cała praca dostała podtytuł: <em>IO-awareness</em>, świadomość
operacji wejścia-wyjścia.<sup id="fnref2:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> Brzmi korporacyjnie, a znaczy rzecz prostą jak drut:
przestań optymalizować liczbę mnożeń, zacznij optymalizować liczbę <strong>podróży do wolnej
pamięci</strong>. Bo to one są wąskim gardłem.</p>
<p>Jak to zrobić, skoro macierz <code>N × N</code> z definicji jest ogromna i nie zmieści się w tej
malutkiej szybkiej SRAM? No właśnie — nie zmieści się <strong>w całości</strong>. Ale w kawałkach już
tak. I to jest sedno.</p>
<ul>
<li><strong>Tiling (kafelkowanie).</strong> Zamiast liczyć naraz całą macierz, tniesz <code>Q</code>, <code>K</code> i <code>V</code>
na bloki i przerabiasz je kafelek po kafelku. Każdy kafelek jest na tyle mały, że wjeżdża
do SRAM, tam się go przemiela, wypluwa wynik i bierze następny. Wielka macierz <code>N × N</code>
jako całość <strong>nigdy nie powstaje</strong> w wolnej pamięci — istnieje tylko przelotnie, we
fragmentach, w tej szybkiej pamięci przy rdzeniach.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></li>
<li><strong>Online softmax.</strong> Tu jest haczyk, bo softmax klasycznie potrzebuje całego wiersza
naraz — musi znormalizować po wszystkich elementach. FlashAttention liczy go „w locie&quot;,
z bieżącą normalizacją: przerabia kolejne bloki i na bieżąco koryguje wynik, tak jakby
sumował rachunek pozycja po pozycji, poprawiając napiwek za każdym razem, gdy dojdzie
nowa pozycja. Efekt końcowy identyczny, a pełnego wiersza nigdy nie trzeba trzymać.<sup id="fnref1:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></li>
<li><strong>Rekomputacja.</strong> Przy uczeniu (backward pass) normalnie trzymasz w pamięci wszystkie
pośrednie wyniki, żeby policzyć gradienty. FlashAttention mówi: po co je trzymać, skoro
taniej je <strong>policzyć jeszcze raz</strong> z tego, co już mamy w szybkiej pamięci? Dokłada trochę
mnożenia, żeby oszczędzić na pamięci i na tym przeklętym bieganiu do HBM. I na tym
akurat wychodzi na plus.<sup id="fnref3:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></li>
</ul>
<p>Zbierz to razem i dostajesz uwagę, w której zużycie pamięci rośnie <strong>liniowo</strong> z długością
sekwencji, <code>O(N)</code>, zamiast kwadratowo, <code>O(N²)</code>.<sup id="fnref4:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> Ta sama matematyka, ta sama odpowiedź
co do ostatniej cyfry — tylko poukładana tak sprytnie, że wielka tablica pośrednia nigdy
nie musi się zmaterializować tam, gdzie jest wolno.</p>
<div class="callout">
  <span class="callout__label">Dlaczego to ważne dla lokalnego LLM</span>
<p>Dla ciebie, odpalającego model w domu, sedno jest takie: FlashAttention <strong>nie jest</strong>
przybliżeniem. To nie sparse attention, nie low-rank, nie żadna sztuczka, która coś obcina
i modli się, żeby jakość nie spadła. Dostajesz dokładnie ten sam wynik co przy zwykłej
uwadze — po prostu policzony taniej i mniejszym kosztem VRAM-u. To rzadki przypadek obiadu,
za który naprawdę nikt nie płaci. I dlatego w nowszych buildach <code>llama.cpp</code> z backendem CUDA
ta flaga po cichu bywa włączona domyślnie.</p>
</div>
<h2 id="czy-to-na-pewno-działało-liczby-z-pracy">Czy to na pewno działało? Liczby z pracy</h2>
<p>Bo można sobie opowiadać o eleganckim algorytmie do rana, ale papier bez liczb to wróżenie
z fusów. Autorzy zmierzyli i pokazali czarno na białym.<sup id="fnref5:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<table>
	<thead>
			<tr>
					<th>Model / zadanie</th>
					<th>Długość sekwencji</th>
					<th>Efekt</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>BERT-large</td>
					<td>512</td>
					<td>15% szybciej end-to-end</td>
			</tr>
			<tr>
					<td>GPT-2</td>
					<td>1K</td>
					<td>3× szybciej</td>
			</tr>
			<tr>
					<td>Long-Range Arena</td>
					<td>1K–4K</td>
					<td>2,4× szybciej</td>
			</tr>
	</tbody>
</table>
<p>I najciekawsze na koniec — nie samo „szybciej&quot;, ale „w ogóle się dało&quot;. Skoro pamięć
przestała rosnąć kwadratowo, można było pchać modele na kontekst, na którym wcześniejsze
Transformery po prostu się poddawały. Na Path-X (sekwencja 16 tysięcy) FlashAttention
wyciągnął 61,4% dokładności, a na Path-256 (64 tysiące!) — 63,1%.<sup id="fnref6:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> To zadania, na
których wcześniej model radził sobie <strong>nie lepiej niż rzut monetą</strong>. Dłuższy kontekst,
który dziś traktujesz jak coś oczywistego, częściowo wziął się właśnie stąd.</p>
<h2 id="zrób-to-sam--i-nie-pomyl-tego-z-kv-cache">Zrób to sam — i nie pomyl tego z KV-cache</h2>
<p>Nie musisz nic implementować, cały ten mechanizm masz w jednej fladze. W <code>llama.cpp</code>
to <code>-fa</code>, <code>--flash-attn</code>, przyjmuje <code>on</code>/<code>off</code>/<code>auto</code>, domyślnie stoi na <code>auto</code>.<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">llama-cli -m qwen2.5-7b-instruct-q4_k_m.gguf -ngl all -c <span class="m">8192</span> -fa on -cnv
</span></span></code></pre></div><p>Jedna rzecz, którą warto tu dorzucić, bo ludzie się na niej wykładają: w <code>llama.cpp</code>
flash attention jest <strong>twardym wymogiem</strong> dla kwantyzacji KV-cache. Chcesz ścisnąć cache
do <code>q8_0</code>, żeby odzyskać VRAM przy długim kontekście? Bez <code>-fa on</code> się nie da — kwantyzacja
V-cache wymaga flash attention wprost, kod ucina to jednym komunikatem błędu.<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup>
Więc te dwie sztuczki chodzą w parze:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">llama-cli -m qwen2.5-7b-instruct-q4_k_m.gguf <span class="se">\
</span></span></span><span class="line"><span class="cl">  -ngl all -c <span class="m">8192</span> -fa on -ctk q8_0 -ctv q8_0 -cnv
</span></span></code></pre></div><p>I tu ostrzeżenie, żebyś nie wpadł w częstą pułapkę pojęciową. FlashAttention <strong>to nie
jest</strong> KV-cache — to dwie różne rzeczy, które akurat obie oszczędzają pamięć. KV-cache
dotyczy tego, że model <strong>przechowuje</strong> przeliczony kontekst między kolejnymi tokenami
generacji (rozgryzaliśmy to bliżej <a href="/poradniki/kv-cache-vram/">tutaj</a>). FlashAttention
dotyczy tego, <strong>jak</strong> liczysz uwagę w jednym przejściu — kolejności obliczeń, nie
przechowywania stanu. Oszczędność bierze się wyłącznie z tilingu i rekomputacji, a nie
z jakiejkolwiek utraty precyzji — nikt tu niczego nie kwantyzuje ani nie zaokrągla.</p>
<p>Dwa zastrzeżenia od siebie, żeby cię to potem nie zaskoczyło. Raz — wsparcie nie jest
uniwersalne: na backendzie CUDA jest jak znalazł, ale na Vulkanie czy SYCL dla części kart
flaga bywała kapryśna, więc nie traktuj jej jak działającej wszędzie i zawsze (więcej o tym,
czym różnią się <a href="/aktualnosci/backendy-llama-cpp/">backendy</a>). Dwa — jak zwykle w
<code>llama.cpp</code>, resztę flag, które chodzą obok tej, <a href="/poradniki/flagi-llama-cpp/">ogarnialiśmy osobno</a>.</p>
<p>Historia nie skończyła się w 2022. Rok później przyszedł <em>FlashAttention-2</em> — już samego
Tri Dao — który poprzestawiał podział pracy między wątkami GPU i wycisnął jeszcze z grubsza
dwukrotne przyspieszenie względem jedynki, dobijając do 50–73% teoretycznego maksimum
kart A100.<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup> Kod jednego i drugiego (plus wersji trzeciej) siedzi w oficjalnym repo
<code>Dao-AILab/flash-attention</code>, na licencji BSD-3.<sup id="fnref2:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<p>Ale najfajniejsze w tej całej historii jest to, jak przewrotnie brzmi jej morał. Przez lata
uczono nas, że żeby program był szybszy, trzeba go zmusić do liczenia mniej. A tu przyszli
ludzie, którzy kazali karcie <strong>policzyć część rzeczy dwa razy</strong> — i wyszło szybciej. Bo
okazało się, że najdroższa rzecz na GPU to nie mnożenie liczb, tylko chodzenie po nie na
drugi koniec pamięci. Trochę jak z tym kolegą, który zamiast nosić zakupy po jednej siatce
z auta pod blok, woli obładować się wszystkim naraz i przejść ten dystans <strong>raz</strong>. Nie jest
leniwy. Po prostu wie, że najgorszy jest sam spacer tam i z powrotem.</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>Tri Dao, Daniel Y. Fu, Stefano Ermon, Atri Rudra, Christopher Ré, <em>FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness</em>, arXiv:2205.14135 (v1 z 27.05.2022) — złożoność <code>O(N)</code> zamiast <code>O(N²)</code>, tiling, rekomputacja, dokładna (nie przybliżona) uwaga, wyniki 15% / 3× / 2,4× oraz Path-X 61,4% i Path-256 63,1%: <a href="https://arxiv.org/abs/2205.14135">arxiv.org/abs/2205.14135</a>.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref2:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref3:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref4:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref5:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref6:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Oficjalna implementacja referencyjna, licencja BSD-3-Clause — tiling, online softmax, brak materializacji macierzy <code>N × N</code>, kod FlashAttention 1/2/3: <a href="https://github.com/Dao-AILab/flash-attention">github.com/Dao-AILab/flash-attention</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>llama.cpp (ggml-org), opis flagi <code>-fa, --flash-attn [on|off|auto]</code> (default: auto): <a href="https://github.com/ggml-org/llama.cpp/blob/master/tools/server/README.md">tools/server/README.md</a>.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>llama.cpp (ggml-org), twardy wymóg flash attention dla kwantyzacji V-cache — komunikat błędu „V cache quantization requires flash_attn&quot;: <a href="https://github.com/ggml-org/llama.cpp/blob/master/src/llama-context.cpp#L3564">src/llama-context.cpp#L3564</a>.&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p>Tri Dao, <em>FlashAttention-2: Faster Attention with Better Parallelism and Work Partitioning</em>, arXiv:2307.08691 (17.07.2023) — osobna praca, ok. 2× przyspieszenie względem FlashAttention-1 i 50–73% teoretycznego FLOPs/s na A100: <a href="https://arxiv.org/abs/2307.08691">arxiv.org/abs/2307.08691</a>.&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>Mixture of Experts: czemu aktywne parametry to nie wszystkie</title><link>https://inferownia.pl/naukowy/mixture-of-experts/</link><pubDate>Thu, 09 Jul 2026 00:00:00 +0200</pubDate><author>Albert Q. Jiang</author><author>Alexandre Sablayrolles</author><author>Antoine Roux</author><author>Arthur Mensch</author><author>Blanche Savary</author><author>Chris Bamford</author><author>Devendra Singh Chaplot</author><author>Diego de las Casas</author><author>Emma Bou Hanna</author><author>Florian Bressand</author><author>Gianna Lengyel</author><author>Guillaume Bour</author><author>Guillaume Lample</author><author>Lélio Renard Lavaud</author><author>Lucile Saulnier</author><author>Marie-Anne Lachaux</author><author>Pierre Stock</author><author>Sandeep Subramanian</author><author>Sophia Yang</author><author>Szymon Antoniak</author><author>Teven Le Scao</author><author>Théophile Gervet</author><author>Thibaut Lavril</author><author>Thomas Wang</author><author>Timothée Lacroix</author><author>William El Sayed</author><category>inferencja</category><guid>https://inferownia.pl/naukowy/mixture-of-experts/</guid><description>Mixtral 8x7B na token rusza tylko ~13 z 47 miliardów wag, ale do pamięci musisz wcisnąć wszystkie. Rozbieramy, czemu MoE oszczędza obliczenia, a nie ani grama VRAM-u.</description><content:encoded><![CDATA[<p>Osiem razy siedem to pięćdziesiąt sześć. Tyle że model nazywa się „8x7B&quot;, a waży czterdzieści
siedem miliardów parametrów — nie pięćdziesiąt sześć. I zanim zdążysz się z tym pogodzić, ktoś
dorzuca, że aktywnych jest tylko trzynaście. Trzy liczby, z których żadna nie klei się
z sąsiednią. Ściągasz GGUF-a, plik ma dwadzieścia kilka giga w Q4, a gość w komentarzach pisze,
że „chodzi jak siódemka&quot;. No to jak? Duży jak czterdziestka, szybki jak trzynastka,
a nazwany jak pięćdziesiątka szóstka?</p>
<p>To nie literówka i nie marketing. To Mixtral 8x7B<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> — i cała ta trójca liczb bierze
się z jednego pomysłu architektonicznego, który nazywa się <strong>Mixture of Experts</strong>. Jak go
raz zrozumiesz, przestaniesz się dziwić, czemu model „na 13B&quot; i tak każe ci kupić kartę pod
model „na 47B&quot;. Rozłóżmy to na części.</p>
<h2 id="trzy-liczby-jeden-trik">Trzy liczby, jeden trik</h2>
<p>Klasyczny model językowy jest <strong>gęsty</strong> (<em>dense</em>): na każdy token przepycha wszystkie swoje
wagi. Wrzucasz słowo, przechodzi przez każdą warstwę, w każdej warstwie dotyka każdego
neuronu. Prosto, uczciwie, drogo — bo im więcej parametrów, tym więcej roboty na literkę.</p>
<p>Mixtral robi inaczej. Bierze architekturę zwykłego Mistrala 7B, ale w każdej warstwie
podmienia jeden blok — ten feedforwardowy (FFN), czyli tę część, która „przetwarza&quot; token
po tym, jak uwaga (<em>attention</em>) już popatrzyła na kontekst. Zamiast jednego takiego bloku
wstawia <strong>osiem równoległych</strong>. To są właśnie „eksperci&quot;.<sup id="fnref1:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> Osiem kopii tej samej
maszynerii, każda wytrenowana ciut inaczej.</p>
<p>I teraz clou: dla danego tokena <strong>nie odpalają się wszyscy</strong>. Przed blokiem eksperckim stoi
mały <strong>router</strong> (sieć bramkująca, <em>gating network</em>), który patrzy na token i wybiera z ósemki
tylko <strong>dwóch</strong> ekspertów — top-2.<sup id="fnref2:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> Ich wyjścia miesza ważoną sumą (wagi bierze
z softmaxu) i jedzie dalej. Sześciu pozostałych w tej warstwie, dla tego tokena, w ogóle
nie rusza. Śpią.</p>
<p>Stąd rozjazd liczb. Parametrów <strong>całkowitych</strong> (<em>total</em>) jest 46,7 miliarda — potocznie
~47B. Ale na przejście jednego tokena <strong>aktywnych</strong> (<em>active</em>) jest tylko 12,9 miliarda,
czyli ~13B.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> Płacisz obliczeniami za trzynastkę, a nosisz na barana czterdziestkę
siódemkę.</p>
<h2 id="skąd-47-a-nie-56">Skąd 47, a nie 56?</h2>
<p>No dobra, ale skoro to „8x7B&quot;, to czemu nie wychodzi 56 miliardów? Bo mnożenie „osiem razy
siedem&quot; jest po prostu błędne. Powielona jest <strong>tylko</strong> ta część FFN. Cała reszta — warstwy
uwagi, embeddingi, normalizacje — siedzi w modelu <strong>raz</strong> i jest współdzielona przez
wszystkich ekspertów i wszystkie tokeny.<sup id="fnref1:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> Nie mnożysz jej przez osiem ani w sumie,
ani w liczbie aktywnej. Dlatego total ląduje na 46,7B (a nie 56B), a active na 12,9B
(a nie na jednej ósmej z 56).</p>
<p>Zajrzyj zresztą do <code>config.json</code> na karcie modelu — tam to stoi czarno na białym:
<code>num_local_experts: 8</code>, <code>num_experts_per_tok: 2</code>, przy <code>hidden_size: 4096</code>
i <code>num_hidden_layers: 32</code>.<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> Osiem ekspertów na warstwę, dwóch na token, trzydzieści
dwie warstwy. Mnożenie odpala się w każdej z tych 32 warstw z osobna.</p>
<p>I tu druga rzecz, która ludziom umyka: wybór ekspertów <strong>nie jest stały</strong>. Router decyduje
oddzielnie <strong>dla każdego tokena i dla każdej warstwy</strong>.<sup id="fnref3:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> Słowo „kot&quot; w warstwie 3
może trafić do ekspertów 1 i 5, a to samo „kot&quot; w warstwie 4 — do 2 i 7. A następne słowo
w tym samym zdaniu pojedzie zupełnie inną trasą. To nie jest tak, że model „wybiera dwóch
specjalistów na cały prompt&quot; i tyle. To osiem tysięcy mikro-decyzji na akapit, każda robiona
w locie.</p>
<h2 id="to-czemu-nie-zajmuje-tyle-co-trzynastka">To czemu nie zajmuje tyle co trzynastka?</h2>
<p>I tu dochodzimy do pułapki, na którą łapie się pół internetu. Skoro na token aktywne jest
tylko ~13B, to model „zajmuje tyle co trzynastka&quot;, nie? Odpalę na karcie pod 13B i będzie
grało?</p>
<p>Nie będzie. I to jest najważniejsze zdanie w całym tym wpisie.</p>
<p>Router wybiera ekspertów <strong>dopiero w trakcie</strong> przejścia przez daną warstwę, osobno dla
każdego tokena. Znaczy: <strong>z góry nie wiesz, którzy eksperci będą potrzebni</strong>. Następne słowo
może chcieć dowolnej pary z ósemki, w dowolnej z 32 warstw. Więc żeby model w ogóle mógł
ruszyć, <strong>wszystkie 47 miliardów wag musi siedzieć w pamięci naraz</strong> — cała ósemka
ekspertów w każdej warstwie, gotowa do wezwania.<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> Oszczędność z MoE dotyczy
<strong>obliczeń</strong> (compute, latencji), nie <strong>pamięci</strong>. Mistral mówi to wprost: model „przetwarza
wejście i generuje wyjście z taką samą szybkością i kosztem jak model 12,9B&quot;.<sup id="fnref2:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>
Szybkością i kosztem obliczeń — nie apetytem na VRAM.</p>
<div class="callout">
  <span class="callout__label">Dlaczego to ważne dla lokalnego LLM</span>
<p>Zapamiętaj to jedno rozróżnienie, a przestaniesz się przejeżdżać na MoE: <strong>aktywne parametry
liczą się do prędkości, całkowite — do pamięci</strong>. Mixtral 8x7B liczy jak trzynastka, ale
ważyć musisz go jak czterdziestkę siódemkę. Do samej inferencji w Q4 potrzebujesz około
<a href="https://huggingface.co/TheBloke/Mixtral-8x7B-v0.1-GGUF">25–30 GB na wagi</a>, zanim jeszcze dołożysz KV-cache i kontekst. Karta pod 13B tego nie
udźwignie — a naiwne „mało aktywnych = mało VRAM-u&quot; to najczęstsza wpadka przy planowaniu
sprzętu. Ile realnie wchodzi na wagi, liczyliśmy <a href="/poradniki/ile-vram-na-model/">tutaj</a>.</p>
</div>
<p>Zbierzmy to w tabelce, bo dwie kolumny mówią więcej niż akapit:</p>
<table>
	<thead>
			<tr>
					<th>Co porównujemy</th>
					<th>Model gęsty (dense)</th>
					<th>Mixtral 8x7B (MoE)</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Parametry całkowite</td>
					<td>tyle, ile aktywnych</td>
					<td>46,7B (~47B)</td>
			</tr>
			<tr>
					<td>Parametry aktywne na token</td>
					<td>wszystkie</td>
					<td>12,9B (~13B)</td>
			</tr>
			<tr>
					<td>Ekspertów w warstwie FFN</td>
					<td>1</td>
					<td>8</td>
			</tr>
			<tr>
					<td>Ekspertów na token</td>
					<td>1</td>
					<td>2 (top-2)</td>
			</tr>
			<tr>
					<td>Co decyduje o prędkości</td>
					<td>parametry (= wszystkie)</td>
					<td>parametry <strong>aktywne</strong></td>
			</tr>
			<tr>
					<td>Co decyduje o VRAM</td>
					<td>parametry (= wszystkie)</td>
					<td>parametry <strong>całkowite</strong></td>
			</tr>
	</tbody>
</table>
<p>Po co ten cały cyrk, skoro pamięć i tak trzeba mieć całą? Bo za cenę pamięci klasy 47B
dostajesz jakość, która w benchmarkach <strong>dorównuje albo bije Llamę 2 70B i GPT-3.5</strong> — przy
koszcie obliczeniowym trzynastki.<sup id="fnref4:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> To jest ten deal: płacisz VRAM-em jak za dużego,
liczysz jak za małego, a mądrość dostajesz bliżej tego dużego. Dla kogoś, kto ma dość RAM-u,
ale mało cierpliwości do tokenów na sekundę — świetny interes.</p>
<h2 id="zrób-to-sam-a-przynajmniej-zaplanuj">Zrób to sam (a przynajmniej zaplanuj)</h2>
<p>MoE to <strong>architektura</strong>, nie kompresja. Łatwo pomylić to z kwantyzacją czy pruningiem, ale
to inna bajka: MoE od początku trenuje się jako rzadko aktywowaną sieć, a kwantyzacja ściska
wagi już po treningu — i spokojnie łączysz jedno z drugim (kwantyzowany GGUF Mixtrala to
codzienność). Jak działają same nazwy kwantyzacji, rozgryzaliśmy
<a href="/poradniki/nazwy-kwantyzacji-gguf/">tu</a> — ta sama logika oszczędzania pamięci działa i na
kwantyzowanym Mixtralu, tyle że na innym piętrze niż samo MoE.</p>
<p>W praktyce <code>llama.cpp</code> daje ci pod to dedykowaną flagę. Skoro attention i embeddingi są
„zawsze aktywne&quot;, a bloki eksperckie odpalają się wybiórczo, to najwięcej ugrasz, trzymając
te zawsze-aktywne komponenty na GPU, a same wagi ekspertów spychając do RAM-u. Od tego jest
<code>--n-cpu-moe N</code> — „trzymaj wagi MoE z pierwszych N warstw na CPU&quot; — albo <code>--cpu-moe</code>, które
przenosi wszystkie.<sup id="fnref1:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> Kroisz VRAM kosztem szybkości — właśnie dlatego, że pamięć musi
pomieścić <strong>całość</strong>, a nie tylko aktywną ścieżkę.</p>
<p>Najprościej — cały model na kartę, jak wejdzie:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">llama-cli -m mixtral-8x7b-instruct-v0.1.Q4_K_M.gguf -ngl all -c <span class="m">8192</span> -fa on -cnv
</span></span></code></pre></div><p>Nie wchodzi (a przy 47B na przeciętnej karcie do gier nie wejdzie)? Zostaw wszystkie warstwy
na GPU, ale wypchnij eksperckie FFN-y z części z nich do RAM-u:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">llama-cli -m mixtral-8x7b-instruct-v0.1.Q4_K_M.gguf <span class="se">\
</span></span></span><span class="line"><span class="cl">  -ngl all --n-cpu-moe <span class="m">12</span> -c <span class="m">8192</span> -fa on -ctk q8_0 -ctv q8_0 -cnv
</span></span></code></pre></div><p>Które flagi co robią i w jakiej kolejności się nimi ratować przy „out of memory&quot;,
rozpisaliśmy w <a href="/poradniki/flagi-llama-cpp/">poradniku o flagach llama.cpp</a>. Sam Mixtral
8x7B (base i Instruct) jest na licencji Apache 2.0<sup id="fnref5:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> — ściągasz, odpalasz, kombinujesz
bez proszenia nikogo o zgodę.</p>
<p>Jedno ostrzeżenie na koniec, żebyś nie wpadł w drugą pułapkę. To „liczy jak 13B&quot;
obowiązuje, gdy generujesz jeden strumień token po tokenie. Gdy walisz <strong>batchem</strong> — wiele
sekwencji naraz — różne tokeny w paczce wybierają różnych ekspertów, więc w danej warstwie
i tak zwykle rozgrzewa się większość albo cała ósemka jednocześnie. Teoretyczne
przyspieszenie topnieje. MoE lubi pojedynczego użytkownika przy biurku bardziej niż
zatłoczony serwer.</p>
<p>Wyobraź sobie warsztat z ośmioma fachowcami przy każdym stanowisku. Do każdej śrubki brygadzista
woła tylko dwóch — akurat tych, co się na niej znają. Reszta stoi i pije kawę. Robota idzie
szybko, jakbyś płacił za dwóch. Ale wypłatę i tak wystawiasz całej ósemce, bo nigdy nie wiesz,
która śrubka przyjdzie następna i kogo do niej trzeba będzie zawołać. I to jest cały Mixtral:
tempo dwójki, lista płac ósemki. Twoja karta nie płaci za tych, co pracują — płaci za tych,
co czekają w gotowości.</p>
<h2 id="przypisy--źródła">Przypisy · źródła</h2>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Jiang i in., <em>Mixtral of Experts</em>, arXiv:2401.04088 — abstrakt i sekcja architektury: 8 ekspertów na warstwę FFN, routing top-2, wynik dorównujący/bijący Llamę 2 70B i GPT-3.5, licencja Apache 2.0. <a href="https://arxiv.org/abs/2401.04088">arxiv.org/abs/2401.04088</a>.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref2:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref3:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref4:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref5:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Mistral AI, oficjalne ogłoszenie <em>Mixtral of Experts</em> — dokładne liczby 46,7B total / 12,9B active, mechanizm routera (8 grup parametrów, wybór 2, addytywne łączenie wyjść), stwierdzenie, że koszt i szybkość odpowiadają modelowi 12,9B. <a href="https://mistral.ai/news/mixtral-of-experts">mistral.ai/news/mixtral-of-experts</a>.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref2:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p><code>config.json</code> modelu <code>mistralai/Mixtral-8x7B-v0.1</code> — <code>num_local_experts: 8</code>, <code>num_experts_per_tok: 2</code>, <code>hidden_size: 4096</code>, <code>num_hidden_layers: 32</code>. <a href="https://huggingface.co/mistralai/Mixtral-8x7B-v0.1/raw/main/config.json">huggingface.co/mistralai/Mixtral-8x7B-v0.1/raw/main/config.json</a>.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>llama.cpp, definicje flag <code>-cmoe</code>/<code>--cpu-moe</code> („keep all Mixture of Experts (MoE) weights in the CPU&quot;) oraz <code>-ncmoe</code>/<code>--n-cpu-moe N</code> („keep the Mixture of Experts (MoE) weights of the first N layers in the CPU&quot;) — sam mechanizm istnieje właśnie dlatego, że wag ekspertów nie da się usunąć z pamięci; można je jedynie przenieść z VRAM-u do RAM-u. <a href="https://github.com/ggml-org/llama.cpp/blob/master/common/arg.cpp">common/arg.cpp</a>.&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>Speculative decoding: jak mały model przyspiesza duży</title><link>https://inferownia.pl/poradniki/speculative-decoding/</link><pubDate>Wed, 08 Jul 2026 00:00:00 +0200</pubDate><category>inferencja</category><guid>https://inferownia.pl/poradniki/speculative-decoding/</guid><description>Duży model musi robić pełny przebieg dla każdego pojedynczego tokena — stąd ten mielący rytm generacji. Speculative decoding łamie ten rytm: mały model zgaduje kilka tokenów naprzód, duży sprawdza je hurtem. I najlepsze: bez utraty jakości.</description><content:encoded><![CDATA[<p>Token. Chwila. Token. Chwila. Lokalny model wypluwa tekst jak stara drukarka igłowa, literka
po literce, a ty gapisz się w kursor, jakby to on dyktował tempo twojego życia. I prawie każdy jęk o
prędkości sprowadza się do jednego: „mam ładne 4 tokeny na sekundę i cierpliwość
mnicha&quot;. I człowiek zaczyna kombinować — może da się to jakoś oszukać?</p>
<p>Bo pod spodem dzieje się rzecz z pozoru absurdalnie rozrzutna. Żeby dołożyć jeden token,
model o siedmiu czy siedemdziesięciu miliardach parametrów musi przepchnąć całe swoje
wnętrze — pełny przebieg naprzód — po czym dokłada jedną literkę i zaczyna od nowa. Cały
ten kolos rozgrzewa się do jednego słowa, potem znowu, i znowu. Trochę jak odpalać silnik
ciężarówki za każdym razem, gdy chcesz przejechać metr.</p>
<h2 id="skąd-w-ogóle-ten-pomysł">Skąd w ogóle ten pomysł</h2>
<p>W 2022 roku trójka badaczy z Google&rsquo;a — Leviathan, Kalman i Matias — zadała pytanie, które
brzmi jak sabotaż: a co, jeśli większość kolejnych tokenów jest tak oczywista, że nie
trzeba do nich całego dużego modelu? Że „w dniu dzisiejszym&quot; po słowie „w&quot; i „dniu&quot; da się
zgadnąć czymś dużo tańszym? Opisali to w pracy <em>Fast Inference from Transformers via
Speculative Decoding</em> i pokazali coś, co brzmi zbyt pięknie: 2–3 razy szybsza generacja
na modelach klasy T5-XXL, bez douczania czegokolwiek i <strong>bez zmiany rozkładu wyjściowego</strong>
— czyli model gada dokładnie to samo, co gadałby normalnie, tylko szybciej.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> Papier
poszedł na ICML 2023 jako prezentacja ustna, więc nie była to notka na marginesie.</p>
<p>Dwa miesiące później niezależnie tę samą ideę opisała ekipa z DeepMind — Chen, Borgeaud,
Irving, Lespiau, Sifre i Jumper — pod nazwą <em>speculative sampling</em>, z lekko innym schematem
matematycznym i przyspieszeniem 2–2,5x na Chinchilli (70B) w środowisku rozproszonym.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>
Dwa zespoły, ten sam pomysł, ta sama gwarancja: przyspieszamy, jakości nie ruszamy. Kiedy
dwie niezależne grupy trafiają w to samo, to zwykle znak, że pod spodem siedzi coś prawdziwego.</p>
<h2 id="jak-to-działa-krok-po-kroku">Jak to działa krok po kroku</h2>
<p>Dobra, ale jak to działa naprawdę? Bierzesz dwa modele. Jeden duży — ten, którego odpowiedzi
chcesz — nazwijmy go <strong>docelowym</strong> (<em>target</em>). I jeden mały, szybki — <strong>szkicownik</strong>
(<em>draft</em>) — najlepiej tej samej rodziny, żeby „myślał&quot; podobnie. I teraz taniec w trzech krokach:</p>
<ol>
<li><strong>Szkicownik zgaduje.</strong> Mały model generuje autoregresywnie kilka kolejnych tokenów —
powiedzmy K sztuk. Robi to szybko, bo jest mały. To są propozycje, brudnopis.</li>
<li><strong>Duży weryfikuje — jednym przebiegiem.</strong> I tu jest cały myk: duży model dostaje te K
proponowanych tokenów naraz i sprawdza je w <strong>jednym</strong> przebiegu naprzód, wsadowo. Nie
K osobnych przejazdów ciężarówką — jeden.</li>
<li><strong>Akceptuj albo popraw.</strong> Tam, gdzie duży model zgadza się ze szkicownikiem, tokeny
wpadają za darmo. Przy pierwszym niezgodnym — odrzucamy resztę, duży dokłada swój token
i cała zabawa rusza od nowa.</li>
</ol>
<p>Jeśli szkicownik trafił w pięć tokenów z rzędu, to policzyłeś je jednym przebiegiem dużego
modelu zamiast pięciu. Jeśli spudłował od razu — koszt jest praktycznie taki, jak przy
zwykłym dekodowaniu. Downside prawie żaden, upside spory. Ta sama logika weryfikacji „licz
wsad, nie sekwencję&quot; siedzi zresztą u podstaw tego, czemu KV-cache w ogóle się opłaca —
rozgryzaliśmy to <a href="/poradniki/kv-cache-vram/">tutaj</a>.</p>
<h2 id="czemu-to-nie-psuje-jakości">Czemu to nie psuje jakości</h2>
<p>W tym miejscu każdy rozsądny człowiek zapala czerwoną lampkę. „Mały model zgaduje za dużego?
To brzmi jak destylacja na skróty, na pewno wychodzi gorsza jakość.&quot; Otóż nie — i to jest
najładniejsza część całej sztuczki.</p>
<p>To <strong>nie</strong> jest kwantyzacja ani destylacja. Wag dużego modelu nikt nie dotyka, jego
dokładność zostaje nietknięta. Sekret siedzi w tym, jak akceptowane są tokeny — używa się
zmodyfikowanego <strong>rejection sampling</strong> (próbkowania odrzucającego). Token zaproponowany
przez szkicownik przyjmujemy z prawdopodobieństwem <code>min(1, p_target(x) / p_draft(x))</code> —
czyli: jeśli duży model uważa dany token za co najmniej tak samo prawdopodobny jak
szkicownik, bierzemy go w ciemno. Jeśli mniej — akceptujemy proporcjonalnie, a przy
odrzuceniu dobieramy nowy token z <strong>rozkładu resztkowego</strong> (residual), tak dobranego, żeby
wszystko się matematycznie spięło.<sup id="fnref1:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p>Efekt? Łączny rozkład tokenów na wyjściu jest <strong>identyczny</strong> z tym, co dałoby zwykłe,
niespekulacyjne próbkowanie samego dużego modelu. Nie „prawie taki sam&quot;, nie „w granicach
błędu&quot; — dokładnie taki sam, z dowodem.</p>
<div class="callout">
  <span class="callout__label">Dlaczego to ważne dla lokalnego LLM</span>
<p>To jest ten rzadki obiad, za który nikt nie płaci. Speculative decoding nie jest
kompromisem „szybciej za cenę jakości&quot;, jak kwantyzacja czy przycinanie kontekstu. Rozkład
wyjściowy zostaje matematycznie nienaruszony — dostajesz te same odpowiedzi co z gołego
dużego modelu, tylko prędzej. Jedyne, czym płacisz, to trochę VRAM-u na drugi, mały model
w pamięci i odrobina narzutu na weryfikację.</p>
</div>
<h2 id="skąd-się-bierze-przyspieszenie">Skąd się bierze przyspieszenie</h2>
<p>Skoro nic nie tniemy, to gdzie ten darmowy obiad się chowa? W wąskim gardle, o którym
lokalny LLM-owiec i tak wie w kościach: inferencja jednego tokena naraz jest
<strong>ograniczona pamięcią</strong> (bandwidth-bound), nie liczeniem. Karta większość czasu nie liczy,
tylko czeka, aż wagi modelu przemaszerują z VRAM-u do rdzeni. Przepchnięcie przez ten sam
kanał jednego tokena a pięciu naraz kosztuje prawie tyle samo — bo i tak głównie czekasz na
pamięć, a nie na arytmetykę.</p>
<p>Stąd cały zysk: policzenie K+1 tokenów w jednym wsadowym przebiegu jest znacznie tańsze niż
K+1 osobnych, sekwencyjnych kroków. Im więcej propozycji szkicownika duży model akceptuje,
tym mocniej wygrywasz.<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> I tu ważne zastrzeżenie, bo to nie jest magiczne pokrętło
„więcej = szybciej&quot;: przyspieszenie <strong>nie</strong> rośnie liniowo z liczbą draftowanych tokenów.
Wszystko wisi na <strong>współczynniku akceptacji</strong> — jak często szkicownik trafia. Każąc mu
zgadywać dwadzieścia tokenów naprzód przy kiepskiej trafności, generujesz górę brudnopisu,
który duży model i tak wyrzuci do kosza. Bywa, że wtedy jest wolniej niż bez całej zabawy.</p>
<h2 id="zrób-to-sam-w-llamacpp">Zrób to sam w llama.cpp</h2>
<p>Teoria teorią, ale <code>llama.cpp</code> (repo <code>ggml-org/llama.cpp</code>) obsługuje to od dawna — i w
<code>llama-server</code>, i w osobnym narzędziu <code>llama-speculative-simple</code>. Cały wjazd na klasyczny
wariant to jedna flaga: <code>--spec-draft-model</code> (krócej <code>-md</code>), która wskazuje plik GGUF
modelu szkicowego.<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> Bierzesz duży model, dokładasz mały z tej samej rodziny i lecisz:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">llama-server -m qwen3-14b-q4_k_m.gguf <span class="se">\
</span></span></span><span class="line"><span class="cl">  -md qwen3-0.6b-q4_k_m.gguf <span class="se">\
</span></span></span><span class="line"><span class="cl">  --spec-draft-n-max <span class="m">4</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  -ngl all -c <span class="m">8192</span> -fa on
</span></span></code></pre></div><p><code>--spec-draft-n-max N</code> (domyślnie 3) ustawia maksymalną liczbę tokenów szkicowanych na
krok, a <code>--spec-draft-n-min N</code> (domyślnie 0) dolną granicę.<sup id="fnref1:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> Progi akceptacji stroisz
przez <code>--spec-draft-p-min</code> (domyślnie 0.00) i <code>--spec-draft-p-split</code> (domyślnie 0.10). Do
reszty flag <code>llama-server</code> — <code>-ngl</code>, <code>-c</code>, <code>-fa</code> i spółka — wracaliśmy osobno
<a href="/poradniki/flagi-llama-cpp/">tutaj</a>.</p>
<p>Strategię wybiera <code>--spec-type</code>, i tu robi się ciekawie, bo klasyczny draft model to
dopiero początek:</p>
<table>
	<thead>
			<tr>
					<th><code>--spec-type</code></th>
					<th>Co robi</th>
					<th>Potrzebny osobny model draft?</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>draft-simple</code></td>
					<td>zwykły mały model szkicowy</td>
					<td>tak (GGUF przez <code>-md</code>)</td>
			</tr>
			<tr>
					<td><code>draft-eagle3</code></td>
					<td>jedna warstwa czytająca stany ukryte dużego modelu</td>
					<td>tak (lekka głowica EAGLE-3)</td>
			</tr>
			<tr>
					<td><code>draft-dflash</code></td>
					<td>generuje cały blok szkicu jednym przebiegiem (block diffusion)</td>
					<td>tak</td>
			</tr>
			<tr>
					<td><code>ngram-simple</code></td>
					<td>zgaduje z historii wygenerowanego tekstu</td>
					<td><strong>nie</strong></td>
			</tr>
			<tr>
					<td><code>ngram-cache</code></td>
					<td>wariant n-gramowy z podręczną pamięcią</td>
					<td><strong>nie</strong></td>
			</tr>
	</tbody>
</table>
<p>Warianty <strong>n-gramowe</strong> to osobny gatunek: nie potrzebują żadnego drugiego modelu, tylko
zgadują kolejne tokeny na podstawie tego, co już się w tekście pojawiło (świetne, gdy model
dużo cytuje sam siebie albo klepie strukturę). Mechanizm weryfikacji jest ten sam — po
prostu brudnopis powstaje inaczej.<sup id="fnref1:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> A na drugim biegunie <strong>EAGLE-3</strong> (<code>draft-eagle3</code>)
to lekka, jednotransformerowa głowica, która podgląda stany ukryte dużego modelu i przez to
trafia częściej niż samodzielny mały model tej samej wagi. Uruchomienie z dokumentacji
wygląda tak:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">llama-server -m Qwen3-4B.gguf <span class="se">\
</span></span></span><span class="line"><span class="cl">  -md Qwen3-4B-eagle3.gguf <span class="se">\
</span></span></span><span class="line"><span class="cl">  --spec-type draft-eagle3
</span></span></code></pre></div><p>Jedna pułapka na koniec, żebyś nie stracił wieczora na kopiowanie starych poradników:
dawne flagi <code>--draft</code>, <code>--draft-max</code>, <code>--draft-min</code> <strong>zostały usunięte</strong>. Aktualna składnia
to <code>--spec-draft-n-max</code> i pochodne <code>--spec-*</code>.<sup id="fnref2:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> Jak przepiszesz przykład sprzed roku
i <code>llama-server</code> prycha, że nie zna flagi — to nie ty zwariowałeś, to repo poszło naprzód.</p>
<p>Najładniejsze w tym wszystkim jest to, że speculative decoding nie jest żadnym oszustwem na
jakości — jest raczej jak dobry sekretarz. Ktoś szybki i tani pisze pod dyktando pierwszy
szkic, przewidując, dokąd zmierza zdanie, a szef tylko przelatuje wzrokiem i skreśla to, co
się nie zgadza. Reszta zostaje, jak stała. Szef odpowiada za każde słowo tak samo jak
wtedy, gdy pisał sam — tyle że nie musi już maczać pióra w kałamarzu przy każdej literce.</p>
<p>I to jest ten moment, w którym twoja drukarka igłowa spod biurka nagle łapie zryw — te same
słowa, ten sam rozkład, ta sama karta. Tylko kursor przestaje w końcu dyktować ci tempo.</p>
<h2 id="przypisy--źródła">Przypisy · źródła</h2>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Leviathan, Kalman, Matias, <em>Fast Inference from Transformers via Speculative Decoding</em>, ICML 2023 (oral) — definicja metody, gwarancja identycznego rozkładu wyjściowego przez zmodyfikowany rejection sampling, przyspieszenie 2–3x na T5-XXL. <a href="https://arxiv.org/abs/2211.17192">arxiv.org/abs/2211.17192</a>.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Chen, Borgeaud, Irving, Lespiau, Sifre, Jumper (DeepMind), <em>Accelerating Large Language Model Decoding with Speculative Sampling</em> — niezależne sformułowanie, przyspieszenie 2–2,5x na Chinchilli 70B w środowisku rozproszonym. <a href="https://arxiv.org/abs/2302.01318">arxiv.org/abs/2302.01318</a>.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>llama.cpp, mechanizm weryfikacji wsadowej i lista strategii (<code>draft-eagle3</code>, <code>draft-dflash</code>, warianty <code>ngram-*</code>), przykład uruchomienia z EAGLE-3, <a href="https://github.com/ggml-org/llama.cpp/blob/master/docs/speculative.md">docs/speculative.md</a>.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>llama.cpp, dokładne nazwy i domyślne wartości flag <code>--spec-draft-model</code>/<code>-md</code>, <code>--spec-type</code>, <code>--spec-draft-n-max</code>/<code>n-min</code>, <code>--spec-draft-p-min</code>/<code>p-split</code> oraz informacja o usunięciu dawnych <code>--draft</code>/<code>--draft-max</code>/<code>--draft-min</code>, <a href="https://github.com/ggml-org/llama.cpp/blob/master/tools/server/README.md">tools/server/README.md</a>.&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref2:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>Wymuszanie JSON-a w llama.cpp: gramatyki GBNF</title><link>https://inferownia.pl/poradniki/gbnf-json/</link><pubDate>Mon, 06 Jul 2026 00:00:00 +0200</pubDate><category>inferencja</category><guid>https://inferownia.pl/poradniki/gbnf-json/</guid><description>Prosisz model o czysty JSON, a dostajesz esej, blok w markdownie i przecinek w złym miejscu. Gramatyki GBNF w llama.cpp odbierają mu wybór: model może wypluć tylko te tokeny, które pasują do reguł. Rozbieramy, jak to działa na poziomie samplera.</description><content:encoded><![CDATA[<p>Znasz ten moment. Piszesz w prompcie wielkimi literami „ODPOWIEDZ WYŁĄCZNIE POPRAWNYM
JSON-em, BEZ ŻADNEGO KOMENTARZA&quot;, dorzucasz przykład, błagalnie dodajesz „no proszę&quot;,
a model i tak zaczyna od „Oczywiście! Oto Twój JSON:&quot;, opakowuje go w blok <code>```json</code>,
gdzieś w środku gubi cudzysłów, a na końcu dorzuca jeszcze zdanie, że gdyby coś, to chętnie
pomoże dalej. Twój <code>json.loads()</code> wybucha, a ty siedzisz i parsujesz to regexpem jak jaskiniowiec.
To częsty punkt zapalny — rozjechany nawias, ucięty cudzysłów i podpis w stylu
„przecież prosiłem grzecznie&quot;.</p>
<p>No i tu jest cały myk: prośba w prompcie to prośba. Model może ją spełnić albo nie, bo prompt
to tylko sugestia w tekście. A gramatyka GBNF to coś zupełnie innego — to kaganiec założony na
sampler. Nie mówisz modelowi „bądź tak miły i zwróć JSON&quot;. Ty mu fizycznie <strong>nie pozwalasz</strong>
wygenerować tokenu, który by JSON-a psuł. Różnica jak między „proszę, nie wychodź poza linie&quot;
a kartką do kolorowania, w której poza liniami po prostu nie ma gdzie pociągnąć kredką.</p>
<h2 id="o-co-chodzi-z-gbnf">O co chodzi z GBNF</h2>
<p>GBNF (GGML BNF) to format gramatyki formalnej, którym <code>llama.cpp</code> ogranicza — po angielsku
<em>constrain</em> — to, co model może wygenerować.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> Nazwa zdradza rodowód: to wariant
BNF-a, tej samej notacji, którą od dekad opisuje się składnię języków programowania. Piszesz
zestaw reguł „co po czym może stać&quot;, a silnik pilnuje, żeby wyjście trzymało się tych reguł
znak po znaku, token po tokenie.</p>
<p>W praktyce masz dwie drogi do tego samego celu. Albo piszesz gramatykę GBNF ręcznie i podajesz
ją flagą <code>--grammar</code> (wprost jako string) lub <code>--grammar-file</code> (z pliku).<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> Albo, jeśli
myślisz kategoriami JSON Schema, podajesz schemat flagą <code>-j</code> / <code>--json-schema</code> — a <code>llama.cpp</code>
sam przemieli go wewnętrznie na GBNF funkcją <code>json_schema_to_grammar</code>.<sup id="fnref1:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> Do tego jest
bliźniacza para <code>-jf</code> / <code>--json-schema-file</code>, gdy schemat wolisz trzymać w pliku. Efekt
końcowy ten sam: pod spodem i tak rządzi gramatyka.</p>
<div class="callout">
  <span class="callout__label">Dlaczego to ważne dla lokalnego LLM</span>
<p>Najważniejsza rzecz, którą trzeba zrozumieć, i zarazem najczęstszy mit: schemat ani gramatyka
<strong>nie trafiają do promptu</strong>. Model nie widzi w kontekście ani jednej reguły — nie dostaje
few-shota, nie dostaje instrukcji „pamiętaj o cudzysłowach&quot;. Ograniczenie działa wyłącznie na
poziomie samplera, przy każdym kolejnym generowanym tokenie.<sup id="fnref1:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> To dlatego działa nawet
na małym modelu, który normalnie olewa prośby o format: nie prosisz go o współpracę, tylko
zamykasz mu wszystkie złe drzwi.</p>
</div>
<h2 id="jak-to-w-ogóle-działa-pod-maską">Jak to w ogóle działa pod maską?</h2>
<p>Skoro model niczego nie widzi w prompcie, to skąd wie, że ma trzymać się formatu? Nie wie.
I to jest piękne. Cała robota dzieje się piętro niżej, w samplerze — komponencie, który po
każdym przejściu przez sieć wybiera następny token z rozkładu prawdopodobieństw.</p>
<p>Przypomnij sobie, jak model generuje. Sieć wypluwa <strong>logity</strong> — po jednej liczbie na każdy
token ze słownika, im wyższa, tym „chętniej&quot; model by go postawił. Normalnie sampler bierze
tę tablicę kandydatów (<code>llama_token_data_array</code>) i losuje z niej według reguł temperatury,
top-p i reszty pokręteł, które rozgryzaliśmy <a href="/poradniki/flagi-llama-cpp/">tutaj</a>. Gramatyka
wciska się dokładnie w to miejsce, tuż przed losowaniem. Funkcja <code>llama_grammar_apply_impl</code>
przechodzi po całej tablicy kandydatów i każdemu tokenowi, który <strong>nie pasuje</strong> do gramatyki,
ustawia logit na <code>-inf</code>.<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> Minus nieskończoność to wyrok: taki token ma po
softmaksie prawdopodobieństwo zero, więc sampler nie ma prawa go wybrać, choćby model bardzo
chciał. To właśnie <strong>maskowanie logitów</strong> — zerowanie szans tokenów niezgodnych z regułami.</p>
<p>A skąd gramatyka wie, co „pasuje&quot; akurat teraz? Trzyma stan. I to nie jeden stan, tylko cały
zbiór jednoczesnych stosów — klasyczny automat ze stosem (<em>pushdown automaton</em>). Każdy stos
to jedna możliwa ścieżka przez gramatykę, zgodna z tym, co model wygenerował do tej pory.
Token przeżywa tylko wtedy, gdy pasuje do terminala na szczycie <strong>któregokolwiek</strong> z aktywnych
stosów — jeśli nie pasuje do żadnego, dostaje <code>-inf</code> i wypada.<sup id="fnref1:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> Kiedy token już
zostanie wybrany, <code>llama_grammar_accept</code> przesuwa stan gramatyki po zaakceptowanych znakach,
stosy się aktualizują, część ścieżek umiera, część zostaje — i cała zabawa leci od nowa przy
następnym tokenie.<sup id="fnref2:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup></p>
<p>Wyobraź sobie to jak korytarz z bramkami. Model na każdym kroku pcha się we wszystkie drzwi
naraz, a gramatyka trzyma przy każdych strażnika, który przepuszcza tylko wtedy, gdy za drzwiami
jest legalna kontynuacja. Model nie musi znać planu budynku — i tak nie skręci w ślepy zaułek,
bo tamtędy fizycznie nie da się przejść.</p>
<h2 id="składnia-w-pigułce">Składnia w pigułce</h2>
<p>GBNF wygląda znajomo dla każdego, kto liznął kiedyś opis gramatyki. Reguła to
<code>nonterminal ::= sekwencja</code>. Klocki, z których się to składa:<sup id="fnref2:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<ul>
<li><strong>Terminale</strong> — literały w cudzysłowach (<code>&quot;1&quot;</code>, <code>&quot;true&quot;</code>) albo zakresy znaków w nawiasach
kwadratowych (<code>[0-9]</code>, <code>[a-zA-Z]</code>), z negacją przez <code>^</code> (<code>[^&quot;]</code> = cokolwiek poza cudzysłowem).</li>
<li><strong>Operatory powtórzeń</strong> — <code>*</code> (zero lub więcej), <code>+</code> (jeden lub więcej), <code>?</code> (opcjonalnie),
oraz precyzyjne <code>{m}</code>, <code>{m,}</code>, <code>{m,n}</code> (dokładnie / co najmniej / od–do).</li>
<li><strong>Alternatywy</strong> przez <code>|</code> i <strong>grupowanie</strong> przez <code>( )</code> — dokładnie jak w regexpach.</li>
<li><strong>Unicode</strong> w komplecie, z ucieczkami <code>\xXX</code> (8-bit), <code>\uXXXX</code> (16-bit) i <code>\UXXXXXXXX</code>
(32-bit), więc polskie znaki czy emoji nie są problemem.</li>
</ul>
<p>Jest też smaczek, którego regexp nie ma: GBNF potrafi dopasowywać po konkretnych <strong>tokenach
tokenizera</strong>, nie po znakach. <code>&lt;[1000]&gt;</code> to token o ID 1000, <code>&lt;think&gt;</code> to token o dokładnym
tekście <code>think</code>, a <code>!&lt;...&gt;</code> to negacja.<sup id="fnref3:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> Przydaje się, gdy chcesz np. sterować
sekcjami <code>&lt;think&gt;...&lt;/think&gt;</code> u modeli rozumujących — operujesz wtedy na realnych tokenach,
a nie zgadujesz, jak model potnie tekst.</p>
<p>Jedna pułapka na start: nazwy nieterminali piszemy <strong>małymi literami z myślnikami</strong>
(<code>item-name-kv</code>), nie <code>camelCase</code> i nie z podkreślnikami. Tak jest w oficjalnych przykładach
i tego się trzymaj.<sup id="fnref4:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<h2 id="mały-przykład-od-zera">Mały przykład od zera</h2>
<p>Powiedzmy, że chcesz z modelu wyciągnąć obiekt z polem <code>name</code> (tekst) i <code>age</code> (liczba).
Minimalna gramatyka, w duchu przykładu z oficjalnego README:<sup id="fnref5:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">root   ::= &#34;{&#34; ws &#34;\&#34;name\&#34;:&#34; ws string &#34;,&#34; ws &#34;\&#34;age\&#34;:&#34; ws number ws &#34;}&#34;
</span></span><span class="line"><span class="cl">string ::= &#34;\&#34;&#34; char{1,100} &#34;\&#34;&#34;
</span></span><span class="line"><span class="cl">char   ::= [^&#34;\\]
</span></span><span class="line"><span class="cl">number ::= [0-9]+
</span></span><span class="line"><span class="cl">ws     ::= [ \t\n]*
</span></span></code></pre></div><p>Czyta się to prawie jak zdanie: korzeń to klamra, w środku pole <code>name</code> ze stringiem od 1 do
100 znaków, przecinek, pole <code>age</code> z liczbą, klamra zamykająca — a <code>ws</code> to opcjonalne białe
znaki, żeby model mógł sobie ładnie wciąć. Podajesz to modelowi i już na poziomie samplera
nie ma opcji, żeby zaczął od „Oto JSON:&quot;. Pierwszy token, który wolno postawić, to <code>{</code>.
Kropka. Cała reszta korytarza jest zamknięta.</p>
<p>Odpalasz to najprościej tak — gramatyka wprost w linii poleceń:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">llama-cli -m qwen2.5-7b-instruct-q4_k_m.gguf <span class="se">\
</span></span></span><span class="line"><span class="cl">  --grammar <span class="s1">&#39;root ::= &#34;{&#34; &#34;\&#34;ok\&#34;:&#34; (&#34;true&#34; | &#34;false&#34;) &#34;}&#34;&#39;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  -p <span class="s2">&#34;Czy 7 jest liczbą pierwszą? Odpowiedz.&#34;</span> -no-cnv
</span></span></code></pre></div><p>Albo, gdy myślisz schematami, oddajesz robotę konwerterowi. <code>--json-schema '{}'</code> przyjmie
dowolny JSON, a węższy schemat zawęzi wyjście:<sup id="fnref2: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">llama-cli -m qwen2.5-7b-instruct-q4_k_m.gguf <span class="se">\
</span></span></span><span class="line"><span class="cl">  --json-schema <span class="s1">&#39;{&#34;type&#34;:&#34;object&#34;,&#34;properties&#34;:{&#34;pierwsza&#34;:{&#34;type&#34;:&#34;boolean&#34;}},&#34;required&#34;:[&#34;pierwsza&#34;]}&#39;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  -p <span class="s2">&#34;Czy 7 jest liczbą pierwszą?&#34;</span> -no-cnv
</span></span></code></pre></div><h2 id="--grammar-czy---json-schema-to-nie-to-samo"><code>--grammar</code> czy <code>--json-schema</code>? To nie to samo</h2>
<p>Łatwo je pomylić, bo cel jest jeden, ale wejście zupełnie inne. Ściągawka:</p>
<table>
	<thead>
			<tr>
					<th></th>
					<th><code>--grammar</code> / <code>--grammar-file</code></th>
					<th><code>-j</code> / <code>--json-schema</code> (+ <code>-jf</code> z pliku)</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Co podajesz</td>
					<td>gotowy GBNF, napisany ręcznie</td>
					<td>schemat JSON Schema (json-schema.org)</td>
			</tr>
			<tr>
					<td>Kto pisze gramatykę</td>
					<td>ty</td>
					<td><code>json_schema_to_grammar</code>, automatycznie</td>
			</tr>
			<tr>
					<td>Kiedy sięgać</td>
					<td>dowolna struktura, nie tylko JSON</td>
					<td>masz już schemat, myślisz „typami&quot;</td>
			</tr>
			<tr>
					<td>Złożone <code>$ref</code></td>
					<td>ogarnia, bo piszesz wprost</td>
					<td>patrz niżej — jest haczyk</td>
			</tr>
	</tbody>
</table>
<p>Mechanizm wykonawczy w obu wypadkach ten sam: pod spodem i tak powstaje GBNF, który maskuje
logity. To dwa różne wejścia do tej samej maszyny.</p>
<p>I ten haczyk z <code>$ref</code>: jeśli twój schemat JSON Schema wciąga zewnętrzne referencje (<code>$ref</code> do
innych plików), <code>--json-schema</code> może się na tym wyłożyć. Oficjalna dokumentacja radzi wtedy
wygenerować GBNF <strong>offline</strong> skryptem <code>examples/json_schema_to_grammar.py</code>, zapisać do pliku
i podać go przez <code>--grammar-file</code>.<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> Rozdzielasz konwersję od inferencji — najpierw
w spokoju robisz gramatykę, potem karmisz nią model.</p>
<h2 id="gramatyka-pilnuje-składni-nie-sensu">Gramatyka pilnuje składni, nie sensu</h2>
<p>Zanim uwierzysz, że rozwiązałeś wszystkie problemy świata — jedno trzeźwiące zastrzeżenie.
Gramatyka gwarantuje ci poprawną <strong>składnię</strong>, nie <strong>semantykę</strong>. Wymusisz, że <code>age</code> to
liczba — ale nic nie broni modelowi wpisać tam <code>999</code>, jeśli akurat tak mu wyjdzie z rozkładu.
Wymusisz strukturę odpowiedzi RAG-owej — ale nie to, że treść w polach będzie prawdziwa;
o samym łączeniu wyszukiwania z generacją pisaliśmy <a href="/poradniki/rag-lokalnie/">tutaj</a>.
GBNF ogranicza dopuszczalne <strong>sekwencje tokenów</strong>, a nie sens tego, co w nich siedzi. To
kaganiec na formę, nie na głupoty. Walidację wartości i logikę trzymaj po swojej stronie,
tak jak trzymałeś ją zawsze.</p>
<h2 id="zrób-to-sam-przez-serwer">Zrób to sam: przez serwer</h2>
<p>Na co dzień najwygodniej pchać to przez <code>llama-server</code>. Endpoint <code>/completion</code> przyjmuje
w body te same dwa parametry co CLI: <code>grammar</code> (tekst GBNF) oraz <code>json_schema</code> (schemat JSON),
oba domyślnie puste.<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup> Odpalasz serwer raz:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">llama-server -m qwen2.5-7b-instruct-q4_k_m.gguf -ngl all -c <span class="m">8192</span> --port <span class="m">8080</span>
</span></span></code></pre></div><p>i strzelasz do niego z gramatyką w środku:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl -s http://localhost:8080/completion -d <span class="s1">&#39;{
</span></span></span><span class="line"><span class="cl"><span class="s1">  &#34;prompt&#34;: &#34;Wygeneruj kartę postaci.&#34;,
</span></span></span><span class="line"><span class="cl"><span class="s1">  &#34;grammar&#34;: &#34;root ::= \&#34;{\&#34; \&#34;\\\&#34;imie\\\&#34;:\&#34; \&#34;\\\&#34;\&#34; [a-zA-Z]+ \&#34;\\\&#34;\&#34; \&#34;}\&#34;&#34;
</span></span></span><span class="line"><span class="cl"><span class="s1">}&#39;</span>
</span></span></code></pre></div><p>Jak wsadzisz gramatykę z błędem składni, serwer nie udaje, że jest dobrze — odbija żądanie
z HTTP 400 i komunikatem <code>Failed to parse grammar</code>.<sup id="fnref1:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup> To dobra wiadomość: literówkę
w regule wyłapiesz od razu, na etapie parsowania, a nie po godzinie zastanawiania się, czemu
wyjście wygląda dziwnie. Gotowe przykłady GBNF — w tym pełną gramatykę dla tablicy obiektów
JSON — znajdziesz w katalogu <code>grammars/</code> w repo <code>ggml-org/llama.cpp</code>.<sup id="fnref6:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p>Bo cała ta zabawa sprowadza się do jednej zmiany perspektywy. Przestajesz błagać model
o dobry format i zaczynasz mu ten format wytyczać — nie słowami w prompcie, których i tak
może nie usłuchać, tylko ścianami korytarza, którymi go prowadzisz. Model dalej myśli, co
chce. Po prostu wyjściem z tego myślenia jest już tylko ta jedna furtka, którą sam mu
zostawiłeś otwartą.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Pełna składnia GBNF (reguły <code>nonterminal ::=</code>, terminale, zakresy znaków, operatory powtórzeń, dopasowanie po tokenach <code>&lt;[...]&gt;</code>), przykładowa gramatyka JSON oraz uwaga, że schemat/gramatyka nie trafiają do promptu — <a href="https://github.com/ggml-org/llama.cpp/blob/master/grammars/README.md">grammars/README.md, ggml-org/llama.cpp</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>Definicje i teksty pomocy flag <code>--grammar</code>, <code>--grammar-file</code>, <code>-j</code>/<code>--json-schema</code>, <code>-jf</code>/<code>--json-schema-file</code> oraz konwersja przez <code>json_schema_to_grammar</code> — <a href="https://github.com/ggml-org/llama.cpp/blob/master/common/arg.cpp">common/arg.cpp, ggml-org/llama.cpp</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>Sygnatury <code>llama_grammar_apply_impl</code> (nakłada ograniczenia gramatyki na tablicę kandydatów tokenów / logity) i <code>llama_grammar_accept</code> (przesuwa stan po zaakceptowanym znaku), potwierdzające mechanizm stosów pushdown i maskowanie logitów — <a href="https://github.com/ggml-org/llama.cpp/blob/master/src/llama-grammar.h">src/llama-grammar.h, 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>Opis flag <code>--grammar</code>/<code>--grammar-file</code>/<code>--json-schema</code> w narzędziu CLI oraz rekomendacja <code>--grammar</code> + <code>examples/json_schema_to_grammar.py</code> dla schematów z zewnętrznymi <code>$ref</code> — <a href="https://github.com/ggml-org/llama.cpp/blob/master/tools/cli/README.md">tools/cli/README.md, ggml-org/llama.cpp</a>.&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p>Parametry <code>grammar</code> i <code>json_schema</code> w body żądania <code>/completion</code> (domyślnie puste) oraz zachowanie przy błędnej gramatyce (HTTP 400, <code>Failed to parse grammar</code>) — <a href="https://github.com/ggml-org/llama.cpp/blob/master/tools/server/README.md">tools/server/README.md, ggml-org/llama.cpp</a>.&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>YaRN: jak rozciągnąć kontekst, którego model nie widział</title><link>https://inferownia.pl/naukowy/yarn-kontekst/</link><pubDate>Sun, 05 Jul 2026 00:00:00 +0200</pubDate><author>Bowen Peng</author><author>Jeffrey Quesnelle</author><author>Honglu Fan</author><author>Enrico Shippole</author><category>inferencja</category><guid>https://inferownia.pl/naukowy/yarn-kontekst/</guid><description>Model uczony na 4 tysiącach tokenów nagle ogarnia 128 tysięcy. Nie magia, nie retrening od zera — tylko sprytne przekręcenie zegara pozycji w RoPE i kilkaset kroków douczenia.</description><content:encoded><![CDATA[<p>Przewijasz kartę modelu na Hugging Face i w tabelce mruga do ciebie „128k context”.
Sto dwadzieścia osiem tysięcy tokenów — cała książka wleci w jeden prompt. Serce rośnie.
A potem, gdzieś na dole, drobnym druczkiem: model bazowy trenowany na sekwencjach 4096.
No i człowiek głupieje. Jak to? Uczyłeś go patrzeć na cztery tysiące tokenów naraz, a teraz
ma ogarnąć trzydzieści dwa razy więcej? Skąd on niby wie, jak wygląda token numer 100 000,
skoro w życiu takiego nie widział?</p>
<p>To nie jest pytanie z gatunku czepialstwa. Sam pewnie nieraz widziałeś, jak model
z „długim kontekstem” pięknie streszcza pierwsze pięć stron, a od dziesiątej zaczyna
zmyślać, mieszać imiona i gubić wątek — dokładnie w miejscu, w którym teoretycznie
miał błyszczeć. Bo rozciąganie okna kontekstu to nie jest przesunięcie
suwaka. To operacja na sercu tego, jak model w ogóle rozumie „gdzie” jest dany token.
I tu wchodzi YaRN — metoda z pracy grupy z Nous Research, która robi to tanio i, co
ważniejsze, robi to dobrze.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<h2 id="skąd-model-w-ogóle-wie-który-token-jest-który">Skąd model w ogóle wie, który token jest który</h2>
<p>Zacznijmy od tego, że transformer sam z siebie nie ma pojęcia o kolejności. Wrzuć mu
„pies goni kota” i „kota goni pies” — bez informacji o pozycji to dla niego ta sama torba
słów. Trzeba mu jakoś powiedzieć, że ten token jest pierwszy, tamten setny.</p>
<p>Współczesne modele — LLaMA, Mistral, Qwen — robią to przez <strong>RoPE</strong> (<em>rotary position
embeddings</em>, rotacyjne osadzenia pozycyjne) z pracy Su i innych.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> Pomysł jest
elegancki: zamiast doklejać pozycję jako osobny wektor, RoPE <strong>obraca</strong> wektory zapytań
i kluczy o kąt proporcjonalny do pozycji tokena. Każdy wymiar embeddingu kręci się z inną
częstotliwością — jedne szybko, jak wskazówka sekundowa, inne wolno, jak godzinowa. Token
na pozycji 5 jest obrócony trochę, token na pozycji 5000 — dużo. Model uczy się czytać
te kąty i z różnicy obrotów między dwoma tokenami wnioskuje, jak daleko od siebie leżą.</p>
<p>I teraz clou problemu: model widział te wskazówki tykające tylko do pozycji 4096. Dalej
jest terra incognita. Kąty, które w treningu nigdy się nie pojawiły. Puść go na token
50 000, a wskazówka sekundowa zakręci się tyle razy, że model nie ma bladego pojęcia, co
z tym zrobić — to jak pokazać komuś zegar, który obrócił się poza tarczę, w miejsce,
którego nigdy nie oznaczono.</p>
<h2 id="dlaczego-po-prostu-ściśnij-pozycje-psuje-robotę">Dlaczego „po prostu ściśnij pozycje” psuje robotę</h2>
<p>Pierwszy odruch jest oczywisty. Skoro model zna zakres do 4096, a chcemy 64k, to
przeskalujmy pozycje liniowo — token 64 000 udawaj, że jesteś tokenem 4000. Ściśnij całą
oś czasu tak, żeby zmieściła się w znanym zakresie. To jest <strong>Position Interpolation</strong> (PI,
interpolacja pozycji) z pracy Chena i innych — i na papierze brzmi rozsądnie.</p>
<p>Problem w tym, że PI traktuje <strong>wszystkie</strong> częstotliwości RoPE jednakowo — zwalnia i
sekundnik, i wskazówkę godzinową o ten sam czynnik.<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> A to katastrofa dla tych
najszybszych wymiarów. To właśnie one odpowiadają za rozróżnianie tokenów leżących tuż
obok siebie — „czy słowo A jest bezpośrednio przed B, czy jest między nimi jeszcze jedno”.
Ściśnij je razem z resztą, a model traci rozdzielczość na najdrobniejszym poziomie.
Efekt? Jakość spada nawet na <strong>krótkim</strong> kontekście, który przecież działał bez zarzutu.
Rozciągnąłeś okno i po drodze rozmyłeś to, co model umiał od początku. Klasyczny handel,
w którym oddajesz więcej, niż dostajesz.</p>
<h2 id="jak-yarn-dzieli-częstotliwości-na-pasma">Jak YaRN dzieli częstotliwości na pasma</h2>
<p>No to jak zrobić to mądrzej? Skoro problem jest w tym, że traktujemy wszystkie
częstotliwości jednakowo — to <strong>przestańmy</strong>. To jest cała intuicja YaRN.</p>
<p>Zanim doszli do wersji finalnej, po drodze była <strong>NTK-aware interpolation</strong> (interpolacja
świadoma NTK) — zamiast liniowo ściskać pozycje, zmienia się podstawę RoPE z <code>b</code> na
<code>b * s^(|D|/(|D|-2))</code>, przez co „nacisk” interpolacji rozkłada się nierówno po
wymiarach.<sup id="fnref1:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> Lepiej, ale wciąż z grubsza jednym pociągnięciem po wszystkim.</p>
<p>YaRN idzie krok dalej z podejściem <strong>NTK-by-parts</strong> (interpolacja pasmowa) — i to jest
serce metody. Dzielisz wymiary częstotliwości na pasma za pomocą funkcji rampy <code>γ(r)</code>:<sup id="fnref2:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup></p>
<ul>
<li><strong>wysokie częstotliwości</strong> (te lokalne, szybkie wskazówki — sąsiad przy sąsiedzie)
zostawiasz w spokoju, bez interpolacji. Ekstrapolujesz. Niech tykają jak tykały.</li>
<li><strong>niskie częstotliwości</strong> (te globalne, wolne — gruby zarys „to jest gdzieś na końcu
dokumentu”) interpolujesz normalnie, bo to one muszą pomieścić nowy, większy zakres.</li>
<li>pasmo <strong>pośrodku</strong> rampa miesza płynnie, żeby nie było skoku na styku.</li>
</ul>
<p>Sedno: nie ruszasz tego, co model umie najlepiej (drobna rozdzielczość lokalna), a
rozciągasz tylko to, co i tak opisuje wielką skalę. Zegar dostaje większą tarczę, ale
sekundnik dalej odmierza sekundy tak samo dokładnie.</p>
<table>
	<thead>
			<tr>
					<th>Metoda</th>
					<th>Co robi z częstotliwościami</th>
					<th>Efekt uboczny</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Position Interpolation (PI)</td>
					<td>ściska wszystkie jednakowo, liniowo</td>
					<td>psuje rozdzielczość lokalną, spada jakość i na krótkim kontekście</td>
			</tr>
			<tr>
					<td>NTK-aware</td>
					<td>zmienia podstawę RoPE, nacisk rozkłada nierówno</td>
					<td>lepiej, ale wciąż jeden globalny gest</td>
			</tr>
			<tr>
					<td>NTK-by-parts (YaRN)</td>
					<td>dzieli na pasma rampą <code>γ(r)</code>: wysokie ekstrapoluje, niskie interpoluje</td>
					<td>zachowuje lokalną precyzję, rozciąga tylko skalę globalną</td>
			</tr>
	</tbody>
</table>
<h2 id="drugi-bezpiecznik-temperatura-uwagi">Drugi bezpiecznik: temperatura uwagi</h2>
<p>I tu jest smaczek, który łatwo przegapić, bo nie ma nic wspólnego ze skalowaniem
częstotliwości. YaRN dorzuca <strong>attention temperature scaling</strong> — skalowanie temperatury
w softmaxie uwagi.<sup id="fnref3:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> Kiedy rozciągasz kontekst, rozkład uwagi robi się „płaski”,
model rozprasza się na tysiące tokenów zamiast celować. Więc mnożysz logity uwagi przez
stały współczynnik, wyliczony z prostego, empirycznego wzoru:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">sqrt(1/t) = 0.1 * ln(s) + 1
</span></span></code></pre></div><p>gdzie <code>s</code> to współczynnik skalowania kontekstu (dla 4096 → 64k mamy <code>s = 16</code>). Piękne w
tym jest to, że działa jak zwykły mnożnik na logitach — <strong>nie dotyka wag modelu</strong>, nie
wymaga dodatkowego treningu, wchodzi za darmo. Osobne pokrętło, doklejone z boku,
a robi sporo dobrego dla ostrości. Nie pomyl go ze skalowaniem samego RoPE — to dwa różne
mechanizmy, które YaRN łączy w jedno.</p>
<h2 id="tanio-czyli-ile-to-naprawdę-kosztuje">Tanio, czyli ile to naprawdę kosztuje</h2>
<p>Teraz najlepsze — bo tu YaRN naprawdę błyszczy. To <strong>nie</strong> jest trening od zera. To nawet
nie jest porządny fine-tuning na górze danych. To <strong>krótkie douczenie</strong> już gotowego modelu,
żeby oswoił się z nowymi kątami.</p>
<p>Ile krótkie? W pracy LLaMA-2 7B i 13B douczono jakieś <strong>400 kroków</strong> przy <code>s = 16</code>
(4096 → 64k), plus dodatkowe <strong>~200 kroków</strong> przy <code>s = 32</code>, żeby dobić do 128k — razem
około 600 kroków, na danych PG19 w segmentach po 64k, przy batchu 64.<sup id="fnref4:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>
Objętościowo to szacunkowo <strong>~0,1% oryginalnego korpusu</strong> pretreningowego. Jedna dziesiąta
procenta. W liczbach z abstraktu: YaRN potrzebuje około <strong>10x mniej tokenów</strong> douczenia i
<strong>2,5x mniej kroków</strong> treningowych niż wcześniejsze metody.<sup id="fnref1:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p>I jeszcze jedna rzecz, która robi wrażenie: modele <strong>ekstrapolują poza</strong> długość, na której
je douczono. Serię Mistral 7B rozciągnięto z 8k do 64k i 128k tą samą metodą — a trening
na segmentach 64k dawał poprawne działanie aż do 128k.<sup id="fnref5:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> Model, który w douczeniu
nie widział nic dłuższego niż 64k, radzi sobie dwa razy dalej. To już nie jest tylko
„nauczyliśmy go nowego zakresu” — to „nauczyliśmy go <em>reguły</em>, którą sam rozciąga dalej”.</p>
<div class="callout">
  <span class="callout__label">Dlaczego to ważne dla lokalnego LLM</span>
<p>Dla ciebie, co odpalasz modele lokalnie, to zmienia rachunek na dysku i w głowie. Nie
musisz mieć farmy GPU, żeby dostać model z długim kontekstem — ktoś dorzucił kilkaset
kroków douczenia i wrzucił gotowca na Hugging Face. Ale okno kontekstu to nie darmowy
lunch: każdy dodatkowy tysiąc tokenów to więcej pamięci na KV-cache, a to on, nie wagi,
zjada VRAM przy długich promptach — rozgryzaliśmy to bliżej <a href="/poradniki/kv-cache-vram/">tutaj</a>.
128k w tabelce i 128k, które faktycznie wejdzie na twoją kartę, to dwie różne historie.</p>
</div>
<h2 id="zrób-to-sam-yarn-w-llamacpp">Zrób to sam: YaRN w llama.cpp</h2>
<p>I teraz uwaga na rozróżnienie, na którym łatwo się wyłożyć. Wszystko powyżej — kroki
douczenia, dane PG19 — to warstwa <strong>treningu</strong> opisana w papierze. Ale jest druga warstwa:
<strong>inferencja</strong>. <code>llama.cpp</code> implementuje skalowanie YaRN jako parametry runtime, które
przekręcasz przy uruchomieniu, bez żadnego douczania:<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">llama-server -m model-yarn-128k.gguf <span class="se">\
</span></span></span><span class="line"><span class="cl">  -c <span class="m">65536</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  --yarn-orig-ctx <span class="m">8192</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  --yarn-ext-factor 1.0 <span class="se">\
</span></span></span><span class="line"><span class="cl">  -fa on
</span></span></code></pre></div><ul>
<li><strong><code>--yarn-orig-ctx</code></strong> — oryginalny kontekst treningowy modelu (domyślnie <code>0</code>, czyli
„wczytaj z metadanych GGUF”). To punkt odniesienia, względem którego liczy się <code>s</code>.</li>
<li><strong><code>--yarn-ext-factor</code></strong> — współczynnik mieszania ekstrapolacji (domyślnie <code>-1.00</code>;
<code>0.0</code> to pełna interpolacja).</li>
</ul>
<p>Jeśli model został wypuszczony jako wariant YaRN, metadane zwykle same podpowiedzą sensowne
wartości — wtedy w ogóle nie musisz w to grzebać. A jak flagi w <code>llama.cpp</code> w ogóle działają
i które z tej setki naprawdę musisz znać, rozbieraliśmy <a href="/poradniki/flagi-llama-cpp/">w osobnym wpisie</a>.
Jest jeszcze wariant <strong>Dynamic NTK</strong> — aktualizuje współczynnik skalowania <code>s = max(1, l'/L)</code>
w locie, per krok inferencji, zamiast trzymać stałą wartość ustaloną raz.<sup id="fnref6:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> Sprytne,
bo krótkie prompty nie płacą podatku za rozciągnięcie, którego akurat nie potrzebują.</p>
<p>Cała ta rodzina metod, swoją drogą, ładnie zazębia się z tym, jak w ogóle liczy się uwaga —
a to temat, który drążyliśmy przy <a href="/naukowy/flashattention/">FlashAttention</a>.</p>
<h2 id="większa-tarcza-ten-sam-sekundnik">Większa tarcza, ten sam sekundnik</h2>
<p>Wróćmy na koniec do tego zegara. Naiwna interpolacja bierze tarczę i ściska ją tak, że
sekundnik zlewa się z minutową — niby zmieściłeś więcej godzin, ale nie odczytasz już,
czy jest 12:01, czy 12:02. YaRN robi odwrotnie: zostawia sekundnik w spokoju, a rozciąga
tylko wolne wskazówki, które i tak odmierzają grube kawałki. Dokładasz kilkaset kroków
douczenia — tyle, ile trzeba, żeby model oswoił nowe kąty — i nagle patrzy na sto
dwadzieścia osiem tysięcy tokenów tak, jakby zawsze umiał. Nie dlatego, że zobaczył każdą
nową pozycję. Dlatego, że ktoś mądrze przekręcił mu zegar, nie tłukąc szkła.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Bowen Peng, Jeffrey Quesnelle, Honglu Fan, Enrico Shippole, „YaRN: Efficient Context Window Extension of Large Language Models”, <a href="https://arxiv.org/abs/2309.00071">arXiv:2309.00071</a> (v1 z 31 sierpnia 2023, v2 z 1 listopada 2023). Abstrakt: ok. 10x mniej tokenów i 2,5x mniej kroków treningowych niż wcześniejsze metody.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Jianlin Su i in., „RoFormer: Enhanced Transformer with Rotary Position Embedding”, <a href="https://arxiv.org/abs/2104.09864">arXiv:2104.09864</a> — oryginalna praca o RoPE, na której stoi cała ta rodzina metod.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>Szczegóły techniczne (krytyka Position Interpolation, definicje NTK-aware / NTK-by-parts / Dynamic NTK, wzór na skalowanie temperatury uwagi, liczby kroków douczenia i długości kontekstu, eksperymenty na Mistral 7B) — pełny tekst pracy w wersji HTML: <a href="https://ar5iv.labs.arxiv.org/html/2309.00071">ar5iv.labs.arxiv.org/html/2309.00071</a>.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref2:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref3:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref4:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref5:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref6:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>Parametry runtime <code>--yarn-orig-ctx</code> i <code>--yarn-ext-factor</code> w <code>llama.cpp</code> — dokumentacja serwera: <a href="https://github.com/ggml-org/llama.cpp/blob/master/tools/server/README.md">ggml-org/llama.cpp, tools/server/README.md</a>.&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>Ollama, llama.cpp, LM Studio: co siedzi pod maską</title><link>https://inferownia.pl/poradniki/ollama-llama-cpp-lm-studio/</link><pubDate>Sat, 04 Jul 2026 00:00:00 +0200</pubDate><category>inferencja</category><guid>https://inferownia.pl/poradniki/ollama-llama-cpp-lm-studio/</guid><description>Instalujesz Ollamę, model gada od strzału i myślisz: magia. A pod tym ładnym `ollama run` siedzi ten sam silnik, co pod resztą. Rozbieramy, kto tu jest maszyną, a kto tylko obudową.</description><content:encoded><![CDATA[<p>Jedna linijka — <code>ollama run llama3</code> — enter, kilkanaście sekund mielenia i nagle coś
w terminalu zaczyna z tobą gadać. Bez kompilowania, bez flag, bez wgryzania się w sto
stron README: jedna komenda i masz lokalny model pod ręką. Człowiek wtedy myśli,
że Ollama to jakiś osobny, samowystarczalny cud techniki. A potem na jakimś forum
trafiasz na komentarz, gdzie ktoś na dzień dobry rzuca: „przecież to i tak leci na llama.cpp pod
spodem&quot;. I nagle cały ten ekosystem — Ollama, LM Studio, KoboldCpp — zaczyna wyglądać jak
scena, na której jest o wiele więcej podwójnych ról, niż się wydawało.</p>
<p>Bo lokalny LLM to nie jedno pudełko. To silnik i obudowa. I zanim wybierzesz, w czym
odpalać modele, warto wiedzieć, kto tu jest maszyną, a kto tylko ładnie polakierowanym
panelem z pokrętłami. Nie po to, żeby wybierać strony w kolejnej wojnie plemiennej — po to,
żeby wiedzieć, kiedy nakładka ci wystarczy, a kiedy trzeba zejść piętro niżej.</p>
<h2 id="silnik-którego-nie-widać-llamacpp-i-ggml">Silnik, którego nie widać: llama.cpp i ggml</h2>
<p>Zacznijmy od dna, bo tam mieszka bohater tej historii. <code>llama.cpp</code> (repo
<code>ggml-org/llama.cpp</code>) to biblioteka i silnik inferencji LLM napisane w czystym C/C++,
z minimalnymi zależnościami, na licencji MIT.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> Nie ma ładnego okienka. Nie ma
listy modeli do kliknięcia. To surowy kod, który bierze wagi modelu i liczy — a liczy na
bibliotece tensorowej niższego poziomu o nazwie <strong>ggml</strong>, i to ona jest właściwym mięśniem,
który mnoży macierze pod całą tą konstrukcją.</p>
<p>To właśnie tu żyją rzeczy, które w codziennym „odpal i gadaj&quot; są niewidzialne. Kwantyzacja
po treningu (<em>post-training quantization</em> — ściskanie gotowych wag, nie trenowanie modelu
od zera w niskiej precyzji, bo to dwie różne bajki) w formacie GGUF, z poziomami od
1.5-bitowego po 8-bitowy.<sup id="fnref1:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> Akceleracja na Metal (Apple Silicon), CUDA (NVIDIA),
HIP (AMD), Vulkan, SYCL (Intel), plus optymalizacje CPU (AVX/AVX2/AVX512).<sup id="fnref2:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>
Zestaw narzędzi z linii komend: <code>llama-cli</code> do rozmowy, <code>llama-server</code> z API,
<code>llama-bench</code> do mierzenia wydajności, <code>llama-quantize</code> do ściskania modeli.<sup id="fnref3:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>
Rozbieraliśmy te flagi na czynniki pierwsze <a href="/poradniki/flagi-llama-cpp/">tutaj</a>, a nazwy
kwantyzacji GGUF (<code>Q4_K_M</code> i cała ta rodzina) <a href="/poradniki/nazwy-kwantyzacji-gguf/">tutaj</a>.</p>
<p>Zapamiętaj jedno: kiedy Ollama, LM Studio albo KoboldCpp „uruchamia model&quot;, to w
przytłaczającej większości przypadków pod spodem kręci się właśnie ten silnik. Reszta to
obudowa. Świetna, wygodna, oszczędzająca ci godzin — ale obudowa.</p>
<h2 id="ollama-nakładka-która-po-cichu-wyhodowała-własny-silnik">Ollama: nakładka, która po cichu wyhodowała własny silnik</h2>
<p>Ollama to najpopularniejszy sposób, w jaki ludzie wchodzą w lokalne LLM-y, i przez długi
czas najkrótszy opis brzmiał: „llama.cpp z ładnym zarządzaniem modelami&quot;. Sama dokumentacja
projektu mówi, że Ollama jest oparta na <code>llama.cpp</code> założonym przez Georgiego Gerganova i
historycznie korzystała z niego jako silnika wykonawczego pod spodem; licencja MIT.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<p>Do tego dokłada rzeczy, których gołe <code>llama.cpp</code> ci nie da od strzału. <strong>Modelfile</strong> —
format konfiguracyjny w duchu Dockerfile, który opisuje parametry, prompt systemowy i wagi,
żeby zbudować z tego gotowy „obraz&quot; modelu.<sup id="fnref1:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> Rejestr <code>ollama.com/library</code>, z którego
ściągasz modele jednym <code>ollama pull</code>, jak obrazy z Docker Huba. I własne REST API na porcie
<code>11434</code> (<code>http://localhost:11434/api/</code>), odrębne od tego, co wystawia <code>llama-server</code>.<sup id="fnref2:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>
To jest właśnie ta wygoda, za którą się Ollamę kocha: nie myślisz o plikach GGUF, o ścieżkach,
o flagach. Wpisujesz nazwę i działa.</p>
<p>Ale tu jest zwrot akcji, o którym połowa internetu wciąż nie wie. Od maja 2025, wraz z
ogłoszeniem „Ollama&rsquo;s new engine for multimodal models&quot;, Ollama dla części modeli — między
innymi Meta Llama 4, Google Gemma 3, Qwen 2.5 VL czy Mistral Small 3.1 — przeszła na własny
silnik napisany w Go, który korzysta bezpośrednio z biblioteki ggml, z pominięciem
<code>llama.cpp</code>.<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> <code>llama.cpp</code> i ggml zostają dla wstecznej kompatybilności na CPU i
dla pozostałych modeli, ale samo hasło „Ollama to po prostu llama.cpp w ładnym opakowaniu&quot;
zrobiło się nieaktualne. Ciekawostka, ale ważna: nawet gdy nakładka odcina się od
<code>llama.cpp</code>, i tak stoi na tym samym fundamencie — ggml. Mięsień został ten sam, zmieniło się
tylko, kto go obsługuje.</p>
<h2 id="lm-studio-okno-dwa-silniki-i-zamknięte-drzwi">LM Studio: okno, dwa silniki i zamknięte drzwi</h2>
<p>A co, jeśli w ogóle nie chcesz terminala? Wtedy wchodzi LM Studio — aplikacja desktopowa na
Windows, Linux i macOS, z prawdziwym GUI: przeglądasz modele, klikasz, pobierasz, gadasz.
Pod spodem uruchamia modele przez <code>llama.cpp</code> w formacie GGUF, a na Apple Silicon dorzuca
dodatkowo silnik <strong>MLX</strong> — inferencyjny framework Apple — jako alternatywę dla
<code>llama.cpp</code>.<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> Czyli znów: ten sam silnik u podstaw, tylko schowany za oknem, w
które można klikać.</p>
<p>Dla programisty LM Studio wystawia dwie rzeczy naraz: <strong>OpenAI Compatibility API</strong> (żeby
podpiąć się jak do OpenAI, tylko lokalnie) oraz własne <strong>LM Studio REST API</strong> (w wersji
beta), a do tego CLI o nazwie <code>lms</code> do zarządzania modelami, serwerem i pobieraniem.<sup id="fnref1:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup>
Wygodne? Bardzo. Jest jednak jeden haczyk, który łatwo przeoczyć: to, że LM Studio stoi na
otwartoźródłowych silnikach (<code>llama.cpp</code>, MLX), <strong>nie</strong> czyni z niego projektu open source.
Sama aplikacja jest zamkniętym oprogramowaniem. Silnik w środku możesz obejrzeć do ostatniej
linijki; obudowę wokół niego — już nie.</p>
<h2 id="koboldcpp-nie-osobny-silnik-tylko-llamacpp-w-jednym-pliku">KoboldCpp: nie osobny silnik, tylko llama.cpp w jednym pliku</h2>
<p>Na koniec przypadek, który najłatwiej źle zaszufladkować. KoboldCpp (<code>LostRuins/koboldcpp</code>)
wygląda jak osobne narzędzie — jeden plik wykonywalny, który odpalasz i masz gotowy interfejs.
Ale to nie konkurencyjny silnik wobec <code>llama.cpp</code>. To fork i nakładka bazująca bezpośrednio
na jego kodzie, spakowana w pojedynczą binarkę i rozszerzona o dodatkowe funkcje. Sam
KoboldCpp jest na licencji AGPL v3.0, przy czym zależność <code>llama.cpp</code> w środku pozostaje na
MIT.<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup></p>
<p>Obsługuje modele GGUF (z kompatybilnością wsteczną dla starszego GGML — o czym za chwilę) i
wystawia równolegle dwa API: własne KoboldAI/KoboldCpp oraz endpoint kompatybilny z OpenAI
pod ścieżką <code>/v1</code>. Do tego dorzuca interfejs KoboldAI Lite z trybami chat, adventure, instruct
i storywriter.<sup id="fnref1:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup> Innymi słowy: bierze ten sam silnik, co reszta, i buduje wokół niego
inny zestaw wygód — mocno wychylony w stronę pisania i grania. Nazwanie tego „rywalem
llama.cpp&quot; to jak nazwanie karoserii rywalem silnika.</p>
<div class="callout">
  <span class="callout__label">Dlaczego to ważne dla lokalnego LLM</span>
<p>Przy okazji rozprawmy się z pułapką, na której potyka się pół forum: <strong>GGML to nie GGUF</strong>.
GGML to starszy, dziś przestarzały format plików; GGUF to jego następca — i to właśnie jego
używają dziś <code>llama.cpp</code>, Ollama, LM Studio i KoboldCpp. Jeśli natrafisz w sieci na model
„w GGML&quot;, patrzysz na zabytek. Bierz GGUF. Cały obecny ekosystem stoi na tym drugim —
rozgryzaliśmy go bliżej <a href="/aktualnosci/ekosystem-gguf/">tutaj</a>.</p>
</div>
<h2 id="to-którego-w-końcu-wziąć">To którego w końcu wziąć?</h2>
<p>Teraz konkret, bo o to naprawdę chodzi. Nie o to, który silnik „lepszy&quot; — bo silnik u
podstaw (ggml) jest w dużej mierze wspólny — tylko o to, ile wygody wymieniasz za ile
kontroli.</p>
<table>
	<thead>
			<tr>
					<th>Narzędzie</th>
					<th>Czym jest</th>
					<th>Silnik pod spodem</th>
					<th>API</th>
					<th>Otwarte źródło</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><strong>llama.cpp</strong></td>
					<td>silnik + CLI</td>
					<td>ggml (bezpośrednio)</td>
					<td><code>llama-server</code>, OpenAI-compat</td>
					<td>tak (MIT)</td>
			</tr>
			<tr>
					<td><strong>Ollama</strong></td>
					<td>nakładka + zarządzanie modelami</td>
					<td>llama.cpp/ggml, częściowo własny silnik w Go</td>
					<td>własne REST (port 11434)</td>
					<td>tak (MIT)</td>
			</tr>
			<tr>
					<td><strong>LM Studio</strong></td>
					<td>aplikacja desktopowa (GUI)</td>
					<td>llama.cpp, MLX na Apple Silicon</td>
					<td>OpenAI-compat + LM Studio REST</td>
					<td>nie (zamknięta)</td>
			</tr>
			<tr>
					<td><strong>KoboldCpp</strong></td>
					<td>fork/nakładka w jednym pliku</td>
					<td>llama.cpp</td>
					<td>KoboldAI + OpenAI-compat <code>/v1</code></td>
					<td>tak (AGPL v3.0)</td>
			</tr>
	</tbody>
</table>
<p>Nakładka wystarczy — i to z nawiązką — kiedy chcesz po prostu ściągać modele jednym
poleceniem, przełączać się między nimi bez myślenia o ścieżkach i mieć endpoint, do którego
podepniesz swój kod. To codzienność dziewięciu na dziesięciu lokalnych LLM-owców. Ollama do
szybkiego <code>pull</code> i <code>run</code>, LM Studio jak wolisz klikać w okno, KoboldCpp jak siedzisz w
pisaniu i przygodówkach.</p>
<p>Do gołego <code>llama.cpp</code> schodzisz, gdy zaczyna ci brakować pokręteł. Chcesz wycisnąć konkretny
offload warstw na kartę, ustawić dokładny typ kwantyzacji KV-cache, złapać najświeższą flagę,
która w repo pojawiła się wczoraj, a do nakładki dojdzie za dwa wydania. Albo chcesz
<code>llama-server</code> z równoległym dekodowaniem (<em>parallel decoding</em>), serwujący do tego modele
embeddingowe i rerankingowe pod twojego RAG-a.<sup id="fnref4:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>
Wtedy obudowa zaczyna przeszkadzać, a ty otwierasz maskę i grzebiesz przy silniku sam.</p>
<h2 id="zrób-to-sam-zejdź-o-piętro">Zrób to sam: zejdź o piętro</h2>
<p>Chcesz na własne oczy zobaczyć, że pod spodem siedzi jeden i ten sam mechanizm? Weź dowolny
model GGUF, którego już używasz w Ollamie, i odpal go bezpośrednio w <code>llama.cpp</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git clone https://github.com/ggml-org/llama.cpp
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> llama.cpp
</span></span><span class="line"><span class="cl"><span class="c1"># zbuduj wg instrukcji z README pod swój backend (Metal/CUDA/Vulkan/CPU)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">llama-server -m twoj-model-q4_k_m.gguf -c <span class="m">8192</span> -fa on
</span></span></code></pre></div><p>Wchodzisz na <code>http://localhost:8080</code>, dostajesz web UI i OpenAI-kompatybilne API — to samo,
co daje ci nakładka, tylko bez pośrednika i z pełnym dostępem do każdej flagi. Kod, aktualne
narzędzia i instrukcje budowania są w repo:
<a href="https://github.com/ggml-org/llama.cpp">github.com/ggml-org/llama.cpp</a>. Jak zechcesz wrócić
do wygody — Ollama, LM Studio i KoboldCpp czekają tam, gdzie je zostawiłeś. Nic nie tracisz,
bo pliki GGUF działają wszędzie tak samo.</p>
<p>Bo cała ta scena — Ollama, LM Studio, KoboldCpp — to w gruncie rzeczy różne obudowy
przykręcone do wspólnego silnika. Jedni lubią jeździć autem, nie otwierając nigdy maski, i
to jest w porządku — dojedziesz, gdzie chcesz. Inni muszą wiedzieć, co tam warczy, i co
jakiś czas zaglądają, czy wszystko na swoim miejscu. Lokalne AI ma miejsce dla obu. Ważne
tylko, żeby wiedzieć, że pod każdą z tych błyszczących karoserii bije mniej więcej to samo
serce — i że w każdej chwili możesz podnieść klapę i mu się przyjrzeć.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><code>ggml-org/llama.cpp</code> — README (silnik C/C++ na ggml, kwantyzacja GGUF 1.5–8 bit, narzędzia <code>llama-cli</code>/<code>llama-server</code>/<code>llama-bench</code>/<code>llama-quantize</code>, backendy Metal/CUDA/HIP/Vulkan/SYCL/CPU, parallel decoding, embeddingi i reranking, licencja MIT): <a href="https://github.com/ggml-org/llama.cpp">github.com/ggml-org/llama.cpp</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></p>
</li>
<li id="fn:2">
<p><code>ollama/ollama</code> — README (Ollama oparta na <code>llama.cpp</code> założonym przez Georgiego Gerganova, Modelfile, rejestr <code>ollama.com/library</code>, REST API na porcie 11434, licencja MIT): <a href="https://github.com/ollama/ollama">github.com/ollama/ollama</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>„Ollama&rsquo;s new engine for multimodal models&quot; — blog Ollamy (przejście części modeli, m.in. Llama 4, Gemma 3, Qwen 2.5 VL, Mistral Small 3.1, na własny silnik w Go korzystający bezpośrednio z ggml): <a href="https://ollama.com/blog/multimodal-models">ollama.com/blog/multimodal-models</a>.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>LM Studio — dokumentacja (aplikacja desktopowa uruchamiająca modele przez <code>llama.cpp</code>/GGUF, MLX na Apple Silicon, OpenAI Compatibility API i LM Studio REST API, CLI <code>lms</code>): <a href="https://lmstudio.ai/docs">lmstudio.ai/docs</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><code>LostRuins/koboldcpp</code> — README (fork/nakładka na <code>llama.cpp</code> w jednym pliku wykonywalnym, obsługa GGUF, API KoboldAI oraz OpenAI-compat <code>/v1</code>, UI KoboldAI Lite, licencja AGPL v3.0): <a href="https://github.com/LostRuins/koboldcpp">github.com/LostRuins/koboldcpp</a>.&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</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>