<?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>RoPE — Inferownia</title><link>https://inferownia.pl/tags/rope/</link><description>Inferownia — polski hub wiedzy o lokalnym AI: uruchamianie modeli językowych na własnym sprzęcie, kwantyzacja, inferencja, małe modele, fine-tuning i RAG.</description><language>pl-PL</language><copyright>&#169; 2026 Inferownia</copyright><lastBuildDate>Sun, 05 Jul 2026 00:00:00 +0200</lastBuildDate><atom:link href="https://inferownia.pl/tags/rope/index.xml" rel="self" type="application/rss+xml"/><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></channel></rss>