Přeskočit na obsah
17/30Kapitola 17 z 30

Teplota, top-p a determinizmus, který nemáte

Teplota dělí logits před softmax — a tím padá představa o ovladači kreativity. Stejné greedy volání dvakrát, dva výsledky.

Na této stránce

Tady je stejný požadavek odeslaný stejnému modelu pětkrát. Stejné váhy, stejný prompt, stejný stroj, stejný náhodný seed. Mění se jen jedno číslo.

TEXT
prompt: "Q: What is the capital of France?\nA:"

T = 0.0   " Paris\nWhat is the question and does the answer answer it? The
           question is: What is the capital of France?..."

T = 0.7   " Paris\nWhat is the question: Which city is the capital of
           France?..."

T = 1.0   " Paris\nWhat is a good geographical qualifier for describing
           Paris concerning its location?\nA: Near the Mediterranean Sea..."

T = 1.5   " Paris\nWhat clue from premise allows we to conclude that Godwin
           was &, He chose Healing Crimson Colour No:white flour Pure..."

T = 2.0   "安全感金华.ITEMT]]];\naims assume parental.st-importe.valtermination
           Screens قطر_Zeroหมายเลข-zA ('$ספטמבר..."

Nic se nerozbilo. Každý token v posledním řádku byl legitimně vytažen z vlastní pravděpodobnostní distribuce modelu nad jeho slovníkem o 151 936 položkách. Číslo, které se změnilo, se jmenuje teplota, ve většině dokumentací se popisuje jako ovladač kreativity a ten popis je špatně způsobem, který tato kapitola může ukázat, ne jen tvrdit.

Tohle je také kapitola, kde se splatí tři dřívější sliby. Kapitola 4 definovala logit a nikdy ho pořádně nepoužila. Box o floating-point v kapitole 2 skončil pokynem — vzpomeňte si na to, až se kapitola 17 zeptá, proč stejný prompt, model a seed mohou vytvořit různé tokens. A box o mixture-of-experts z kapitoly 9 slíbil katalog čtyř příčin nedeterminizmu. Všechny tři přicházejí níže.

Jediný řádek, na kterém visí celá kapitola

Odkaz na sekci: Jediný řádek, na kterém visí celá kapitola

Kapitola 4 představila logit jako nenormalizované reálné skóre, jedno pro každou třídu. Kapitola 8 přiměla jazykový model vytvořit jedno pro každou položku slovníku. softmax převádí tento vektor z\mathbf{z} na pravděpodobnosti:

pi=ezijezjp_i = \frac{e^{z_i}}{\sum_j e^{z_j}}

Teplota vstupuje sem — název je vypůjčený ze statistické fyziky, kde stejný parametr řídí, jak ostře se Boltzmannova distribuce soustředí na své nízkoenergetické stavy1 — a dělí logits před exponenciálou:

pi(T)=ezi/Tjezj/Tp_i(T) = \frac{e^{z_i/T}}{\sum_j e^{z_j/T}}

Toto umístění je celý mechanismus a stojí za dva řádky algebry, abyste viděli, proč by nemohl být nikde jinde. Představte si, že byste se pokusili aplikovat teplotu až na pravděpodobnosti — vynásobit je 1/T1/T a znovu normalizovat. Dostali byste

pi/Tjpj/T=pijpj=pi\frac{p_i/T}{\sum_j p_j/T} = \frac{p_i}{\sum_j p_j} = p_i

Konstanta se vykrátí. Škálování pravděpodobností nedělá vůbec nic; distribuce se vrátí beze změny. Teplota má účinek jen proto, že působí na exponent, kde dělení TT před exponenciací odpovídá umocnění každé pravděpodobnosti na 1/T1/T — nelineárnímu přetvarování, které mění poměry mezi položkami, ne jejich společné měřítko.

Z tohoto umístění plynou oba limity bez další práce. Když T0T \to 0, největší logit uteče zbytku a pp se zhroutí na jediný nejvýše skórovaný token: greedy decoding. Když TT roste, každé zi/Tz_i/T míří k nule, každá exponenciála míří k 1 a distribuce se zplošťuje k uniformní distribuci nad celým slovníkem. Přesně při T=0T = 0 vzorec dělí nulou, takže každá implementace to řeší speciální větví na aritmetické maximum — včetně widgetu níže, který při T0.001T \le 0.001 přepne na argmax.

Jedno varování, protože střet názvů způsobuje skutečný zmatek. V machine learning existuje druhá, nesouvisející věc zvaná teplota: temperature scaling, kalibrační metoda, která na validační sadě najde jednu hodnotu tak, aby důvěra klasifikátoru odpovídala jeho přesnosti.2 Stejný vzorec, ale s generováním to nemá nic společného. Když papers říkají „temperature“, často myslí tuhle věc; tato kapitola nikdy.

Tady je ta distribuce, s aritmetikou přímo před vámi. logits jsou pevné a věrohodné, takže čísla v textu níže lze ověřit proti tomu, co vidíte:

  • ␣Paris96.9%
  • ␣the1.3%
  • ␣located0.8%
  • ␣a0.5%
  • ␣Lyon0.2%
  • ␣called0.1%
  • ␣home0.1%
  • ␣Marseille0.0%
  • ␣not0.0%
  • ␣banana0.0%

10 z 10 tokenů projde ořezem a rozdělí si pravděpodobnost.

Zobrazit data v tabulce
TokenlogitPo aplikaci teplotyPo ořezu
␣Paris⁨9.4⁩96.90%96.90%
␣the⁨5.1⁩1.31%1.31%
␣located⁨4.6⁩0.80%0.80%
␣a⁨4.1⁩0.48%0.48%
␣Lyon⁨3.2⁩0.20%0.20%
␣called⁨2.9⁩0.15%0.15%
␣home⁨2.4⁩0.09%0.09%
␣Marseille⁨1.8⁩0.05%0.05%
␣not⁨1.1⁩0.02%0.02%
␣banana⁨-2.6⁩0.00%0.00%
Vzorkování: teplota, top-p a top-k

Deset kandidátních pokračování věty The capital of France is, při teplotě 1 bez ořezávání. ␣Paris drží 96.90 % hmoty; ␣banana, dole s logit 2.6-2.6, dostane 0.00 %. Posuňte teplotu na 0 a přežije jeden token se 100 %. Posuňte ji na 2 a ␣Paris spadne na 69.81 %, zatímco ␣banana vystoupá na 0.17 % — odmítnutý token modelu, kterému čtenář otočením knoflíku dal reálnou pravděpodobnost.

Číslo ␣banana je celý argument v miniatuře: zvýšení teploty nemůže dát modelu nápad, který neměl. logits už jsou spočítané, pořadí už je pevné a teplota ho přesně zachovává — žádné množství žáru nikdy neposune níže skórovaný token nad výše skórovaný. Jen přerozdělí hmotu dolů po žebříčku, který vytvořil sám model. Vysoká teplota nedělá model vynalézavějším; jen zvyšuje šanci, že vypustí tokens, které sám označil za špatné.

Na skutečném slovníku to přestává být kuriozita a stává se důvodem, proč je výstup s vysokou teplotou nepoužitelný. Měřeno na Qwen/Qwen2.5-0.5B-Instruct, jeden forward pass, prompt výše, počítáno, kolik tokens je potřeba k nasbírání daného podílu pravděpodobnostní hmoty:

teplotapravděpodobnost top-1entropietokens držící 80 %90 %95 %99 %
0.599.98 %0.00 nats1111
0.799.65 %0.03 nats1111
1.096.01 %0.30 nats11114
1.288.20 %0.88 nats1213252
1.562.83 %3.07 nats293532,67226,787
2.016.62 %8.19 nats13,51632,96655,231101,205

Spodní řádek čtěte pomalu. Při T=2T = 2, u otázky s přesně jednou správnou odpovědí, 32,966 různých tokens sdílí horních 90 % pravděpodobnostní hmoty. To není širší kreativní prostor. To je model, kterému aritmetika řekla, aby považoval korejskou částici i C++ identifikátor za živé možnosti pro slovo po A:. Odpad v úvodním bloku je přímý důsledek a není to chyba modelu ani knihovny — je to to, o co požadavek požádal.

Užitečný rozsah je úzký a závisí spíš na úloze než na vkusu. U faktické otázky je odpověď jeden token a jakýkoli žár nad zhruba 1.2 jen zbytečně vkládá chybu. U otevřené otázky opravdu existuje víc než jedno dobré pokračování a trochu žáru kupuje pestrost, která zůstává plynulá:

TEXT
"Write a two-sentence story about a lighthouse."

T = 0.0  "The lighthouse stood tall and proud, its beacon illuminating the
          night sky above. A lone sailor, his eyes fixed on the distant
          horizon..."

T = 0.7  "In the quiet, stormy waters of the sea, a lighthouse stood
          sentinel over the horizon, its golden dome casting a warm glow
          on the fog-shrouded streets below..."

T = 1.0  "In the quiet night, a lone lighthouse stood sentinel over the
          sea, its shining beacon a beacon of hope and solace for sailors
          and fishermen across the vast and endless ocean..."

T = 1.3  "In the gentle sunlight, now reflecting upon the opening of Jack's
          lighthouse, Jim Trahan, a small-time individual difficult to
          define in paperwork, wondered about a career where simplicity
          reigns..."

Při 1.3 model vymyslel vlastní jméno a větu, která se syntakticky nerozebere. Pásmo mezi „pokaždé identické“ a „nesouvislé“ je pro tento model na této úloze zhruba 0.6 až 1.1 a poctivá rada zní: najděte ho měřením na své úloze, ne kopírováním čísla z blogpostu.

Proč nejpravděpodobnější text je špatný text

Odkaz na sekci: Proč nejpravděpodobnější text je špatný text

Pod tím vším se skrývá zjevná otázka: když má model pravděpodobnostní distribuci a jeden token je nejpravděpodobnější, proč ho nebrat vždy? Greedy decoding je zdarma, reprodukovatelný a nepotřebuje žádné parametry.

Protože výsledek je toto:

TEXT
prompt: "In a shocking finding, scientists discovered a herd of unicorns
         living in a remote valley."

greedy: " The unicorns were so rare that they were not even recognized by
         the local people. The unicorns were so rare that they were not
         even recognized by the local people. The unicorns were so rare
         that they were not even recognized by the local people. ..."

         repeated 4-grams: 87.6 %

Osm vět, jedna věta. Téměř devět z deseti čtyřtokenových oken se už dříve objevilo ve stejném výstupu. To je neural text degeneration, pojmenovaná a vysvětlená Holtzmanem et al. v paper, který představil top-p.3 Model není rozbitý; maximalizovat pravděpodobnost sekvence je prostě špatný cíl pro otevřený text. Lidské psaní není nejpravděpodobnější posloupnost slov — nese překvapení, jeho pravděpodobnost na token bloudí, klesá a zotavuje se — zatímco cesta s maximální pravděpodobností je pevný bod, který jakmile do něj vstoupí, nemá důvod ho opustit.

Proto sampling vůbec existuje. A také — to je část, která se vynechává — nejde o univerzální zákon. Kapitola 12 naměřila 24 správných odpovědí z 24 na dvoukrokových slovních úlohách s obyčejným greedy decoding a sampling při teplotě 0.8 to srazil na 81 %; self-consistency pak utratila šestkrát víc tokens, aby se vyšplhala zpět tam, kde greedy už byl. Obě věci platí současně:

Otevřené generování. Neexistuje jediné správné pokračování, takže to nejpravděpodobnější je past — smyčkuje a 87.6 % z něj je zkopírováno ze sebe samého. Samplujte.

Úlohy s jednou správnou odpovědí. Existuje jediné správné pokračování, takže vytáhnout cokoli jiného znamená vytáhnout chybu. 100 % z kapitoly 12 se změnilo na 81 % přesně z tohoto důvodu. Nesamplujte.

Většina produkčních prompts patří do druhého typu a konfiguruje se jako první, protože teplota zůstala na hodnotě, kterou použil ukázkový kód.

Dva způsoby řezu a jen jeden se přizpůsobuje

Odkaz na sekci: Dva způsoby řezu a jen jeden se přizpůsobuje

Sampling z plné distribuce ve skutečnosti nedělá nikdo, protože ocas je obrovský a plný nesmyslů. Něco se musí uříznout. Existují dvě klasické odpovědi a liší se v jednom ohledu, který rozhoduje o všem.

Top-k ponechá pevný počet kandidátů. Seřadit podle pravděpodobnosti, ponechat prvních kk, zahodit zbytek, znovu normalizovat.4 Top-p, také zvané nucleus sampling, ponechá pevné množství hmoty: vezměte tokens v sestupném pořadí, dokud jejich kumulativní pravděpodobnost nedosáhne pp, a zastavte.3 Formálně je nucleus nejmenší množina VpV_p s

iVppip\sum_{i \in V_p} p_i \ge p

Rozdíl zní kosmeticky, ale není, protože dva prompts, které odešlete ve stejné minutě, mají úplně jiné tvary distribuce. Obojí je stejný model při teplotě 1:

Q: What is the capital of France?\nA:Once upon a time,
pravděpodobnost top-196.01 %25.39 %
tokens držící 90 % hmoty1467
top-k = 40 ponechá99.61 % hmoty78.87 % hmoty
hmota v pořadích 2 až 403.61 %53.48 %
token na pořadí 40␣Av, 0.0093 %␣Dr, 0.128 %

Jedno pevné kk, dvě selhání opačnými směry. U faktického prompt k=40k = 40 vpustí 39 tokens, které dohromady stojí 3.6 % — propouští odpad, včetně kandidáta na devíti tisícinách procenta, protože pravidlo počítá sloty, ne důkazy. U příběhového prompt stejné k=40k = 40 zahodí 21 % hmoty, kterou model skutečně přidělil, protože skutečné nucleus je tam široké 467 tokens.

Top-p přiměje přesně jedno číslo dělat obě práce. Nastavte p=0.9p = 0.9 a ponechá 1 token na prvním prompt a 467 na druhém, protože se ptá na distribuci, místo aby na ni vnucovalo počet. Sledujte tu adaptaci přímo — stejný řez, čtyři teploty:

  • ␣Paris91.1%
  • ␣the5.2%
  • ␣located3.7%
  • ␣a0.0%
  • ␣Lyon0.0%
  • ␣called0.0%
  • ␣home0.0%
  • ␣Marseille0.0%
  • ␣not0.0%
  • ␣banana0.0%

3 z 10 tokenů projde ořezem a rozdělí si pravděpodobnost.

Zobrazit data v tabulce
TokenlogitPo aplikaci teplotyPo ořezu
␣Paris⁨9.4⁩85.03%91.10%
␣the⁨5.1⁩4.84%5.18%
␣located⁨4.6⁩3.47%3.71%
␣a⁨4.1⁩2.48%
␣Lyon⁨3.2⁩1.36%
␣called⁨2.9⁩1.12%
␣home⁨2.4⁩0.80%
␣Marseille⁨1.8⁩0.54%
␣not⁨1.1⁩0.34%
␣banana⁨-2.6⁩0.03%
Vzorkování: teplota, top-p a top-k

Top-p na 0.90 s teplotou 1.5: přežijí tři z deseti tokens a sdílí hmotu, ␣Paris renormalizované na 91.10 %. Teď pohněte jen teplotou. Při 0.7 stejné 0.90 ponechá jednoho přeživšího — tak úzké nucleus je greedy decoding pod jiným jménem. Při 2.0 jich ponechá pět. Řez se nepohnul; změnil se tvar pod ním.

Ten widget zároveň vyřeší omyl, který stojí lidi skutečné peníze. Na sebejisté distribuci top_p = 0.9 není „trocha pestrosti“. Je to greedy. Při teplotě 1 zde vedoucí token drží 96.90 %, což je už nad 0.9, takže nucleus je široké jeden token a nic jiného se nikdy nemůže vytáhnout. Týmy nastaví top_p na 0.9 v domnění, že něco uvolnily, a pak se diví, proč je každá odpověď identická.

Nastavte místo toho top-k a opačné selhání je stejně viditelné:

  • ␣Paris97.2%
  • ␣the1.3%
  • ␣located0.8%
  • ␣a0.5%
  • ␣Lyon0.2%
  • ␣called0.0%
  • ␣home0.0%
  • ␣Marseille0.0%
  • ␣not0.0%
  • ␣banana0.0%

5 z 10 tokenů projde ořezem a rozdělí si pravděpodobnost.

Zobrazit data v tabulce
TokenlogitPo aplikaci teplotyPo ořezu
␣Paris⁨9.4⁩96.90%97.20%
␣the⁨5.1⁩1.31%1.32%
␣located⁨4.6⁩0.80%0.80%
␣a⁨4.1⁩0.48%0.49%
␣Lyon⁨3.2⁩0.20%0.20%
␣called⁨2.9⁩0.15%
␣home⁨2.4⁩0.09%
␣Marseille⁨1.8⁩0.05%
␣not⁨1.1⁩0.02%
␣banana⁨-2.6⁩0.00%
Vzorkování: teplota, top-p a top-k

Top-k na 5, bez top-p. Pět tokens přežije při každé teplotě, protože pět bylo požadováno. Při teplotě 1, jak je vidět, mají čtyři kandidáti pod ␣Paris dohromady hodnotu 2.79 %. Snižte na 0.7 a stejní čtyři mají hodnotu 0.38 % — řez je divadlo a model je fakticky greedy. Zvyšte na 2.0 a mají hodnotu 22.54 %. Identické nastavení, identický počet přeživších, tři zcela odlišná chování a nic v požadavku vám neřekne, které dostáváte.

Penalizace, se vzorci, protože jejich zaměňování je endemické

Odkaz na sekci: Penalizace, se vzorci, protože jejich zaměňování je endemické

Tři různé mechanismy cestují pod podobnými názvy, dělají různé věci a rozdíl je měřitelný. Nechť cic_i je počet, kolikrát se token ii už objevil.

ziziα1[ci>0]z_i \leftarrow z_i - \alpha \cdot \mathbb{1}[c_i > 0]

Odečte konstantu od každého token, který se už vůbec objevil. Jeden výskyt a čtyřicet výskytů se penalizují identicky. Je to přepínač, ne ovladač.

ziziβciz_i \leftarrow z_i - \beta \, c_i

Odečte úměrně počtu. token použitý čtyřikrát je penalizován čtyřikrát tvrději než token použitý jednou a tlak se s rostoucím textem skládá.

zi{zi/ρif zi>0ziρif zi0z_i \leftarrow \begin{cases} z_i / \rho & \text{if } z_i > 0 \\ z_i \cdot \rho & \text{if } z_i \le 0 \end{cases}

Původní, z paper CTRL.7 Místo odečítání dělí, se zvláštní větví pro znaménko, protože dělení záporného logit by ho zvětšilo. Její síla tedy závisí na velikosti logit, což znamená, že stejné ρ\rho zasáhne v různých místech téže věty různě.

Stejné degenerované pokračování jako dříve, s každou z nich. „Změněné kroky“ počítají, kolik ze 120 generačních kroků vybralo jiný token, než by vybral nepenalizovaný model. Běh má zde 120 kroků oproti 140 v bloku výše, proto nepenalizovaná baseline ukazuje 85.5 %, ne 87.6 %:

nastaveníopakované 4-gramyzměněné kroky
nic85.5 %0 / 120
presence 0.565.0 %3 / 120
presence 1.03.4 %11 / 120
frequency 0.56.0 %12 / 120
frequency 1.00.0 %20 / 120
repetition 1.2 (CTRL)0.0 %35 / 120

Vyplývají z toho tři věci. Presence při 0.5 změnila tři rozhodnutí ze 120 a snížila opakování o čtvrtinu — smyčku držela pohromadě hrstka tokens. Frequency při 0.5 změnila čtyřikrát víc rozhodnutí s mnohem větším efektem, protože násobitel počtu dál roste, zatímco konstanta presence ne. A CTRL penalty na často kopírované hodnotě 1.2 přepsala 35 ze 120 rozhodnutí, což není pošťouchnutí; je to jiný model.

Toto poslední číslo připravuje selhání, před kterým nikdo nevaruje.

Co penalizace dělají s textem, který se má opakovat

Odkaz na sekci: Co penalizace dělají s textem, který se má opakovat

Kód se opakuje. Tabulky se opakují. Seznamy se opakují. Strukturovaný výstup se z definice opakuje — právě to je struktura. Penalizace nedokáže rozlišit mezi modelem zaseknutým ve smyčce a modelem, který správně vypisuje čtvrtý řádek tabulky, protože obojí vypadá jako token, který se objevil znovu.

Stejné tři úlohy, generované třemi způsoby:

úlohanicfrequency 0.5repetition 1.2
markdown tabulka, 6 řádků0 / 56 změněných kroků0 / 562 / 62
Python funkce0 / 930 / 9310 / 110
odrážkový seznam, 1 až 120 / 500 / 500 / 50

Frequency penalty na 0.5 se ukázala jako neškodná ve všech třech případech, což je užitečný a trochu překvapivý výsledek, a říká něco přesného: protože se nezměnilo žádné rozhodnutí, strukturální tokens musely své pozice vyhrávat o víc, než penalizace odečetla, i po pěti a šesti výskytech. CTRL penalty, která místo toho dělí, je už vychýlí, a tady je, co vytvořila:

TEXT
repetition 1.2, markdown table:
  | n | 2^n |
  | --- | --- |
  | 0 | 1      |
  | 1 | 2       |
  | 2 | 4       |

Zarovnání se rozpadá: množství výplně v každé buňce se mění řádek od řádku, protože řada mezer před uzavírací svislicí je přesně ten typ opakování, který má penalizace rozbíjet. Kosmetika, a stálo to šest tokens navíc. Případ Python není kosmetický:

TEXT
nothing / frequency 0.5:
      total = 0
      for i in range(1, n + 1):
          total += i ** 2
      return total

repetition 1.2:
      # Initialize total_sum with 0
      total_sum = 0
      # Loop through numbers from 1 to n, incrementing by 2 each time
      for i in range(1, n + 1,

Penalizace odtlačila model od total — už použitého v docstring — na total_sum, vycpala výstup vymyšlenými komentáři, aby utratila rozpočet za nepoužité tokens, a pak vešla do tříargumentového range se stride. Komentář říká incrementing by 2 each time, což je pro součet čtverců od 1 do nn špatně. Repetition penalty vytvořila nesprávný kód z prompt, který bez ní dostal správnou odpověď.

Pravidlo, které z toho plyne, je krátké: penalizace jsou pro otevřenou prózu a mají být vypnuté pro kód, strukturovaný výstup, tabulková data a cokoli se schématem. Kapitola 18 je přesně o této druhé kategorii.

Pořadí aplikace a proč mění odpověď

Odkaz na sekci: Pořadí aplikace a proč mění odpověď

Každá skutečná implementace aplikuje tyto kroky v jedné konkrétní sekvenci:

penalizace → teplota → top-k → top-p → sample

Není to libovolné účetnictví a prohození dvou fází vytváří skutečně jiné distribuce. Dvě měření, obě na faktickém prompt.

Řez před nebo po teplotě. Nucleus se počítá z distribuce, kterou dostane, a teplota ji radikálně mění:

top-p 0.9 po teplotětop-p 0.9 před teplotou
T=1.0T = 1.01 token1 token
T=1.5T = 1.5353 tokens1 token
T=2.0T = 2.032,966 tokens1 token

Při T=2T = 2 stejné nominální nastavení dá kandidátní množinu 32,966 nebo 1, čistě podle toho, která fáze běží první. Pokud jste někdy přemýšleli, proč zvýšení teploty u jednoho providera „nedělá nic“ a u jiného se stejnými dvěma čísly zničí výstup, tato tabulka je věrohodná odpověď.

Penalizace před nebo po teplotě. Odečtení penalizace α\alpha a následné dělení TT dává efektivní penalizaci α/T\alpha/T; dělení nejdřív a odečtení potom dává α\alpha. S presence penalty 1.0 aplikovanou na vedoucí token:

teplotapenalizovat, pak temperovattemperovat, pak penalizovat
0.599.858 %99.948 %
1.089.839 %89.839 %
2.010.783 %6.830 %

Při T=1T = 1 identické, jak musejí být. Při T=2T = 2 od sebe o faktor 1.58. „Presence penalty 1.0“ není dobře definované množství penalizace, pokud také nevíte, kde se aplikuje teplota, a žádné API to nedokumentuje.

Zobrazit podrobnosti

Volitelné: celá pipeline ve výše uvedeném pořadí.

Šestnáct řádků a je v nich všechno z této kapitoly. Je to stejný výpočet, který provádí widget, jen na skutečném vektoru logits místo deseti pevných čísel.

sample.pyPYTHON
def sample(logits, counts, presence=0.0, frequency=0.0,
           temperature=1.0, top_k=0, top_p=1.0, generator=None):
    z = logits.clone()

    idx = torch.tensor(list(counts))                       # 1. penalties
    if len(idx):
        z[idx] -= presence
        z[idx] -= frequency * torch.tensor([float(c) for c in counts.values()])

    if temperature <= 0:                                   # 2. temperature
        return int(z.argmax())                             #    T=0 is argmax
    p = torch.softmax(z / temperature, -1)

    p, order = p.sort(descending=True)
    if top_k:                                              # 3. top-k
        p[top_k:] = 0
    p = p * ((p.cumsum(0) - p) < top_p)                     # 4. top-p

    p = p / p.sum()                                        # 5. renormalise
    return int(order[torch.multinomial(p, 1, generator=generator)])

cumsum(0) - p v řádku top-p je kumulativní hmota bez aktuálního token, díky čemuž nucleus zahrne token, který překročí práh, místo aby skončilo těsně před ním. Ustřelte to o jedna a top_p = 0.9 se potichu stane o něco těsnějším řezem než v každé jiné implementaci.

Tohle je jedno z mála míst ve druhé polovině kurzu, kde je Python správný jazyk, a důvod je strukturální, ne stylistický: každý řádek výše potřebuje mít v rukou celý vektor logits, a přes HTTP API takový vektor neexistuje. Providerovi můžete poslat temperature a top_p; nemůžete je implementovat a nemůžete vidět, co udělaly.

Univerzální sampling API neexistuje

Odkaz na sekci: Univerzální sampling API neexistuje

Každý provider přijímá jinou podmnožinu těchto ovladačů, s jinými rozsahy, a zbytek mlčky ignoruje. Není to abstraktní stížnost. Každá aplikace, která nabízí výběr modelu, si musí rozdíly někam zapsat, a soubor, kde to dělá, je mapou nekompatibility. Tady je, co jeden takový katalog deklaruje pro jediný parametr napříč devíti textovými zdroji, které podporuje:

deklarovaný rozsah teplotyzdroje
0 až 1Anthropic, Google, Meta, Cerebras, PaLM
0 až 1.5Mistral
0 až 2OpenAI, DeepSeek, xAI

Slovo je stejné; měřítko není. „Teplota 1“ je u jednoho neupravená distribuce a u jiného maximální povolený žár, a polovina katalogu neumí vyjádřit hodnotu, kterou druhá polovina považuje za neutrální-plus-trochu. Ostatní ovladače jsou stejně nerovnoměrné: položky OpenAI, DeepSeek a xAI berou presence a frequency penalties a žádné topK; položky Google, Meta, Cerebras a PaLM berou topK a žádné penalizace; Anthropic bere topK, topP a stop sequences a žádné penalizace; a přesně jeden z devíti — Mistral — bere seed. Poslat parametr, který provider neimplementuje, obvykle nevyvolá žádnou chybu: požadavek uspěje, ovladač neudělá nic a vy uzavřete, že nastavení nemá efekt.

A všimněte si, co takový soubor je: tvrzení o cizím API, napsané v jeden konkrétní den a potom už ničím neověřované. Katalog, který říká 0 až 1 pro providera, jenž teď přijímá 0 až 2, bude potichu ořezávat každý požadavek.

Do stejné rodiny patří ještě dva ovladače. logprobs, kde je nabízené, vrací log-pravděpodobnosti zvoleného token a často několik nejlepších alternativ — jediné okno, které dostanete do distribuce, o níž tato kapitola je, a základ každé heuristiky důvěry postavené na uzavřeném modelu. A maximum tokens plus stop sequences ukončují generování úplně bez ohledu na pravděpodobnost: tvrdý strop a shoda řetězce. Obojí se projeví jako finish_reason z kapitoly 14, kde length znamená, že vaše odpověď byla rozpočtem uříznuta uprostřed věty, ne dokončena modelem.

Seed a determinizmus, který nemáte

Odkaz na sekci: Seed a determinizmus, který nemáte

Nastavte seed a sampling se stane reprodukovatelným. Tahle část je skutečná a snadno ověřitelná:

TEXT
seed = 1234  " Paris\nWhat is a good geographical qualifier for describing
               Paris concerning its location?\nA: Near the Mediterranean Sea"
seed = 1234  " Paris\nWhat is a good geographical qualifier for describing
               Paris concerning its location?\nA: Near the Mediterranean Sea"
seed = 7     " Paris is the capital of France. The appellation of Paris is
               \"Île de Paris\"."
seed = 7     " Paris is the capital of France. The appellation of Paris is
               \"Île de Paris\"."

Bajtově identické v rámci jednoho seed, odlišné mezi seeds, přesně jak slibováno. Takže seed fixuje náhodný tah v posledním řádku té funkce sample — který token se vybere při dané distribuci.

Co nefixuje, je distribuce. A tam začíná problém, protože vektor logits, který váš model vytvoří, není matematický objekt; je to výstup miliard floating-point sčítání, a ta mají pořadí.

Kapitola 2 nechala připravený tento experiment. Stejný milion čísel float32, sečtený v různých seskupeních:

TEXT
sequential          998.564270020    error vs float64: 6.393e-03
pairwise (numpy)    998.570556641    error vs float64: 1.061e-04
in 4 chunks         998.570495605    error vs float64: 1.672e-04
in 8 chunks         998.570556641    error vs float64: 1.061e-04
in 16 chunks        998.570678711    error vs float64: 1.594e-05

sequential == pairwise?  False
4 chunks == 8 chunks?    False

Podívejte se na poslední řádek. Počet chunků mění odpověď. To není kuriozita o numpy; je to mechanismus, protože když inference server rozdělí redukci přes více nebo méně paralelních jednotek, dělá přesně toto. A server rozděluje podle toho, kolik požadavků obsluhuje.

Tady je tentýž efekt na samotném modelu. Stejný prompt, stejný forward pass, jediný rozdíl je, kolik dalších požadavků se náhodou ocitlo v batch:

TEXT
20 identical forward passes, batch of 1:  20 / 20 bit-for-bit identical

the same prompt inside a batch of  2:  147,321 of 151,936 logits differ
the same prompt inside a batch of  4:  146,515 of 151,936 logits differ
the same prompt inside a batch of  8:  146,515 of 151,936 logits differ
the same prompt inside a batch of 16:  147,321 of 151,936 logits differ

largest change to any logit: 2.5e-05

Samostatně běžící model je dokonale deterministický — dvacet průchodů, bitově identických. Vložte identický prompt do batch s nesouvisejícími požadavky a 97 % jeho logits se změní. Na vašem požadavku se nezměnilo nic. Přišel požadavek někoho jiného.

Teď poctivá část, protože obvykle se to vypráví, jako by to byl konec příběhu. Změna 2.5×1052.5 \times 10^{-5} změní výstup jen tehdy, když dva kandidátní tokens byly od sebe nejvýše o tolik. Přes 717 generačních kroků napříč dvanácti prompts byla nejmenší mezera mezi dvěma nejvyššími logits 2.5×1032.5 \times 10^{-3} — stokrát větší než perturbace — a žádný krok nebyl dost blízko na překlopení. Takže na tomto modelu, ve float32, na laptopu, batching pohnul každým logit a nezměnil žádný token.

To je popis příznivých podmínek, ne uklidnění, a stačí jedna změna těchto podmínek:

TEXT
same weights, same prompts, greedy decoding, no seed involved
float32 vs bfloat16:   6 of 8 answers diverge
                       first divergence at step 23, on average

  float32: "...it is scattered and dispersed into different colors,
            including blue. The blue light is scattered more than other
            colors, so it appears to come from the sky."

  bfloat16: "...it is scattered and scattered, causing the colors of the
             sun to be scattered and scattered, creating the appearance
             of a blue color."

Šest z osmi odpovědí se rozejde a jedna se výrazně zhorší. Tabulka z kapitoly 2 říká proč: bfloat16 drží 7 mantisových bitů, takže poblíž velikosti logit 16 jsou reprezentovatelné hodnoty od sebe 0.125 — 16.0, potom 16.125, potom 16.25 — a zaokrouhlení může posunout logit až o 0.0625. Mezitím 4.7 % generačních kroků měřených výše mělo mezeru mezi top dvěma pod 0.1. To je celý rozdíl mezi těmi dvěma experimenty: ve float32 byla perturbace stokrát menší než nejbližší rozhodnutí a v bfloat16 je stejně velká. Produkční inference běží v 16 bitech, na hardwaru s fused kernels a pořadími redukcí, která nikdo neslibuje držet. Zda je „numerický šum zanedbatelný“, je otázka přesnosti a hardwaru, ne modelu.

Takže čtyři příčiny, katalogizované tak, jak slíbila kapitola 9:

Floating-point sčítání není asociativní

Odkaz na sekci: Floating-point sčítání není asociativní

Box z kapitoly 2. Pořadí součtu mění jeho hodnotu, takže jakákoli změna v tom, jak se redukce rozdělí, mění logits. To je substrát; další tři jsou způsoby, jak změnit pořadí.

Dynamic batching seskupuje váš požadavek s cizími

Odkaz na sekci: Dynamic batching seskupuje váš požadavek s cizími

Continuous batching z kapitoly 13 je důvod, proč je inference cenově dostupná — a znamená, že tvar matic, kterými vaše tokens procházejí, závisí na provozu. Měřeno výše: 147,321 logits se pohnulo, protože se změnila velikost batch.

Mixture-of-experts routing závisí na batch

Odkaz na sekci: Mixture-of-experts routing závisí na batch

Box z kapitoly 9 to už řekl. Router dělá diskrétní volbu pro každý token v každé vrstvě, podléhající kapacitním limitům jednotlivých expertů počítaným nad batch. token, který by sám šel k expertovi 7, jde ve společnosti k expertovi 12. To není rozdíl v zaokrouhlení; je to jiná sada vah.

Řetězec verze jako -latest je ukazatel a ukazatele se přesměrovávají. Providers také aktualizují serving stack pod pevným identifikátorem verze. Ani jedno se neoznamuje v granularitě, která by vám umožnila korelovat to se změnou vlastního výstupu.

Parametr seed od OpenAI je v tomhle poctivý jediným způsobem, jak může být: přichází spolu s polem system_fingerprint identifikujícím konfiguraci backendu a dokumentace uvádí, že determinizmus je best-effort a že změněný fingerprint znamená, že výsledky se mohou lišit. Čtěte to tak, jak to je — provider vám říká, že ovládá všechny čtyři příčiny výše, vy neovládáte žádnou a jediné, co může nabídnout, je říct vám ex post, že se něco pohnulo.

Všechno tady bylo o jednom ovladači a jeho důsledcích. Ustupte o jednu úroveň a objeví se těžší problém: objekt, který jsme ladili, je pravděpodobnostní distribuce, a pravděpodobnostní distribuce nemají rozhraní.

Function call ho má. Řádek databáze ho má. Handler POST očekávající JSON tělo se třemi povinnými poli ho má a odmítne cokoli jiného. Mezi modelem a každou další komponentou vašeho systému sedí kontrakt, o kterém jedna strana nemůže nic slíbit: model vytvoří něco, vytažené z distribuce, kterou jste vytvarovali, ale nefixovali, a kód na druhé straně potřebuje hodnotu známého typu, jinak spadne.

Most mezi těmito dvěma světy se staví z materiálu této kapitoly, ne z parsování a retry. Pokud by token rozbil požadovanou strukturu, nesamplujete ho a nedoufáte — nastavíte jeho logit na -\infty dřív, než ho softmax vůbec uvidí. Constrained decoding je maska nad stejným vektorem, který jsme celou kapitolu přetvarovávali, a mění „prosím odpovězte v JSON“ z požadavku na záruku.

Kapitola 18 je právě tento kontrakt: tool calling, JSON Schema, strukturované výstupy a co je potřeba k tomu, aby bylo bezpečné stavět deterministický systém na pravděpodobnostním.


Všechna měření v této kapitole pocházejí z Qwen/Qwen2.5-0.5B-Instruct na CPU, ve float32, pokud není uvedeno jinak, se sampling implementovaným tak, jak je zapsán ve volitelné sekci, ne delegovaným na knihovnu. Je to malý model a konkrétní hodnoty jsou jeho; mechanismy ne. Von Platenův článek How to generate text with different decoding methods (Hugging Face, 2020) je text, vůči němuž je tento měřen, a stále nejlepší krátký úvod do stejného materiálu. Pro sekci o determinizmu: poznámky PyTorch k reprodukovatelnosti popisují, co seed fixuje a nefixuje na jednom stroji, dokumentace OpenAI k seed a system_fingerprint popisuje, co provider může a nemůže slíbit, a diskuse Thinking Machines z roku 2025 o batch-invariant kernels je nejjasnější veřejný výklad toho, proč je oprava na úrovni inference serveru možná, ale ne zdarma.

  1. Ackley, D. H., Hinton, G. E. and Sejnowski, T. J. A Learning Algorithm for Boltzmann Machines. Cognitive Science 9(1), pp. 147–169 (1985), kde teplota v softmax pochází ze statistické fyziky. Hinton, G., Vinyals, O. and Dean, J., Distilling the Knowledge in a Neural Network, arXiv:1503.02531 (2015), sekce 2, je místo, kde se stejný parametr znovu objevuje v moderním deep learning — jako způsob, jak odhalit plnou distribuci učitele, což jsou soft labels z kapitoly 13, ne sampling této kapitoly.

  2. Guo, C., Pleiss, G., Sun, Y. and Weinberger, K. Q. On Calibration of Modern Neural Networks. arXiv:1706.04599 (2017). Nepleťte si to s teplotou v této kapitole. Temperature scaling fituje jednu hodnotu na validační sadě tak, aby důvěra modelu odpovídala jeho přesnosti; je to post-hoc kalibrační metoda aplikovaná na výstupy klasifikátoru. Temperature sampling je runtime ovladač toho, jak generátor tahá tokens. Stejný vzorec, jiný účel a žádná sdílená hodnota.

  3. Holtzman, A., Buys, J., Du, L., Forbes, M. and Choi, Y. The Curious Case of Neural Text Degeneration. arXiv:1904.09751 (2019). Představuje nucleus sampling a měření, že decoding založený na maximalizaci vytváří text, jehož pravděpodobnostní profil se vůbec nepodobá lidskému textu. 2

  4. Fan, A., Lewis, M. and Dauphin, Y. Hierarchical Neural Story Generation. arXiv:1805.04833 (2018). Paper, který popularizoval top-k sampling.

  5. Nguyen, M. et al. Turning Up the Heat: Min-p Sampling for Creative and Coherent LLM Outputs. arXiv:2407.01082 (2024).

  6. Meister, C., Pimentel, T., Wiher, G. and Cotterell, R. Locally Typical Sampling. arXiv:2202.00666 (2022).

  7. Keskar, N. S., McCann, B., Varshney, L. R., Xiong, C. and Socher, R. CTRL: A Conditional Transformer Language Model for Controllable Generation. arXiv:1909.05858 (2019). Sekce 4.1 je původní repetition penalty — ta, která dělí.

Necháte výběr modelu na LIA?

Tvořte se všemi modely AI na jednom místě – začněte ještě dnes zdarma.