Temperature, Top-p и детерминизмът, който нямате
Temperature дели logits преди softmax — факт, който разбива идеята за „регулатор на креативността“.
На тази страница
Ето една и съща заявка, изпратена към един и същ модел пет пъти. Същите тегла, същият prompt, същата машина, същият случаен seed. Единственото, което се променя, е едно число.
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 ('$ספטמבר..."Нищо не се е счупило. Всеки token в последния ред е изтеглен напълно легитимно от собственото вероятностно разпределение на модела върху речника му от 151 936 записа. Числото, което се промени, се нарича temperature, в повечето документации се описва като регулатор на креативността и това описание е погрешно по начин, който тази глава може да покаже, а не просто да твърди.
Това е и главата, в която три по-ранни обещания стават дължими. Глава 4 дефинира logit и така и не го използва истински. Кутията за floating-point от Глава 2 завърши с инструкция — помнете това, когато Глава 17 попита защо един и същ prompt, модел и seed могат да произведат различни tokens. А кутията за mixture-of-experts от Глава 9 обеща каталог от четири причини за недетерминизъм. И трите пристигат по-долу.
Единственият ред, от който зависи цялата глава
Връзка към раздела: Единственият ред, от който зависи цялата главаГлава 4 въведе logit като ненормализирана реална оценка, по една за всеки клас. Глава 8 накара езиков модел да произведе по една такава за всеки запис в речника. softmax превръща този вектор във вероятности:
Temperature влиза тук — името е заето от статистическата физика, където същият параметър контролира колко рязко едно разпределение на Болцман се концентрира върху нискоенергийните си състояния1 — и той дели logits преди експонентата:
Това място е целият механизъм и си струва два реда алгебра, за да се види защо не би могло да бъде другаде. Да предположим, че се опитате да приложите temperature върху вероятностите вместо това — да ги мащабирате с и да нормализирате отново. Ще получите
Константата се съкращава. Мащабирането на вероятности не прави абсолютно нищо; разпределението се връща непроменено. Temperature има ефект само защото действа върху експонентата, където делението на преди експонентиране е същото като повдигане на всяка вероятност на степен — нелинейно преоформяне, което променя съотношенията между записите, а не общия им мащаб.
От това място и двете граници следват без допълнителна работа. Когато най-големият logit се откъсва от останалите и колабира върху единствения token с най-висока оценка: greedy decoding. Когато расте, всеки върви към нула, всяка експонента върви към 1 и разпределението се изравнява към uniform върху целия речник. При точно формулата дели на нула, така че всяка имплементация го обработва като специален случай до аритметичния максимум — включително widget-ът по-долу, който превключва към argmax при .
Едно предупреждение, защото сблъсъкът на имена причинява реално объркване. В machine learning има второ, несвързано нещо, наречено temperature: temperature scaling, метод за калибрация, който напасва една стойност върху validation set, така че увереността на класификатора да съответства на точността му.2 Същата формула, нищо общо с генерирането. Когато статии казват „temperature“, често имат предвид това; тази глава никога няма предвид него.
Ето това разпределение, с аритметиката пред вас. logits са фиксирани и правдоподобни, така че числата в текста по-долу могат да се проверят спрямо това, което виждате:
Temperature не е регулатор на креативността
Връзка към раздела: Temperature не е регулатор на креативносттаЧислото ␣banana е целият аргумент в миниатюра: повишаването на temperature не може да даде на модела идея, която той не е имал. logits вече са изчислени, подредбата вече е фиксирана и temperature я запазва точно — никаква топлина никога не премества по-ниско оценен token над по-високо оценен. Всичко, което прави, е да преразпределя маса надолу по подредбата, която самият модел е произвел. Висок temperature не прави модела по-изобретателен; прави го по-склонен да изведе tokens, които сам е оценил като лоши.
В реален речник това престава да бъде любопитен детайл и се превръща в причината output-ът при висок temperature да е неизползваем. Измерено върху Qwen/Qwen2.5-0.5B-Instruct, един forward pass, prompt-ът по-горе, с броене колко tokens са нужни, за да се натрупа даден дял от вероятностната маса:
| temperature | top-1 probability | entropy | tokens holding 80 % | 90 % | 95 % | 99 % |
|---|---|---|---|---|---|---|
| 0.5 | 99.98 % | 0.00 nats | 1 | 1 | 1 | 1 |
| 0.7 | 99.65 % | 0.03 nats | 1 | 1 | 1 | 1 |
| 1.0 | 96.01 % | 0.30 nats | 1 | 1 | 1 | 14 |
| 1.2 | 88.20 % | 0.88 nats | 1 | 2 | 13 | 252 |
| 1.5 | 62.83 % | 3.07 nats | 29 | 353 | 2,672 | 26,787 |
| 2.0 | 16.62 % | 8.19 nats | 13,516 | 32,966 | 55,231 | 101,205 |
Прочетете бавно последния ред. При , на въпрос с точно един верен отговор, 32 966 различни tokens споделят първите 90 % от вероятностната маса. Това не е по-широко творческо пространство. Това е модел, на който аритметиката е казала да третира корейска частица и C++ идентификатор като живи опции за думата след A:. Боклукът в началния блок е пряката последица и не е bug в модела или библиотеката — това е, което заявката е поискала.
Полезният диапазон е тесен и зависи от задачата, а не от вкуса. При фактологичен въпрос отговорът е един token и всяка топлина над около 1,2 инжектира грешка без полза. При отворена задача наистина има повече от едно добро продължение и малко топлина купува разнообразие, което остава плавно:
"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..."При 1,3 моделът е измислил собствено име и изречение, което не се парсва. Лентата между „идентично всеки път“ и „несвързано“ е приблизително от 0,6 до 1,1 за този модел върху тази задача, а честният съвет е да я намерите чрез измерване върху вашата задача, не чрез копиране на число от blog пост.
Защо най-вероятният текст е лош текст
Връзка към раздела: Защо най-вероятният текст е лош текстПод всичко това се крие очевиден въпрос: ако моделът има вероятностно разпределение и един token е най-вероятен, защо да не го взимаме винаги? Greedy decoding е безплатен, възпроизводим и не се нуждае от параметри.
Защото резултатът е това:
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 %Осем изречения, едно изречение. Почти девет от десет четири-token прозореца вече са се появили по-рано в същия output. Това е neural text degeneration, назовано и обяснено от Holtzman et al. в статията, която въведе top-p.3 Моделът не е счупен; максимизирането на вероятността на последователността просто е грешна цел за отворен текст. Човешкото писане не е най-вероятната последователност от думи — то носи изненада, вероятността му на token се лута, спада и се възстановява — докато пътят с максимална вероятност е фиксирана точка, която, веднъж влязла, няма причина да излезе.
Затова sampling изобщо съществува. Това също така — и това е частта, която се пропуска — не е универсален закон. Глава 12 измери 24 от 24 верни отговора на двустъпкови текстови задачи с обикновен greedy decoding, а sampling при temperature 0,8 свали това до 81 %; после self-consistency похарчи шест пъти повече tokens, за да се изкачи обратно до мястото, където greedy вече беше. И двата факта са верни едновременно:
Отворено генериране. Няма едно-единствено правилно продължение, така че най-вероятното е капан — то зацикля, а 87,6 % от него е копирано от самото себе си. Sample.
Задачи с един правилен отговор. Има едно-единствено правилно продължение, така че да изтеглите каквото и да е друго означава да изтеглите грешка. 100 % от Глава 12 станаха 81 % точно по тази причина. Не sample-вайте.
Повечето production prompts са от втория вид и се конфигурират като първия, защото temperature е оставен на каквото е използвал примерният код.
Два начина за отрязване и само един от тях се адаптира
Връзка към раздела: Два начина за отрязване и само един от тях се адаптираSampling от пълното разпределение не е това, което някой реално прави, защото опашката е огромна и пълна с безсмислици. Нещо трябва да се отреже. Има два класически отговора и те се различават по един аспект, който решава всичко.
Top-k запазва фиксиран брой кандидати. Сортирате по вероятност, запазвате първите , изхвърляте останалите, нормализирате отново.4 Top-p, наричан още nucleus sampling, запазва фиксирано количество маса: взимате tokens в низходящ ред, докато кумулативната им вероятност достигне , и спирате.3 Формално nucleus е най-малкото множество с
Разликата звучи козметична, но не е, защото двата prompts, които изпращате в една и съща минута, имат напълно различни форми на разпределение. И двете са един и същ модел при temperature 1:
Q: What is the capital of France?\nA: | Once upon a time, | |
|---|---|---|
| top-1 probability | 96.01 % | 25.39 % |
| tokens holding 90 % of the mass | 1 | 467 |
| top-k = 40 keeps | 99.61 % of the mass | 78.87 % of the mass |
| mass in ranks 2 to 40 | 3.61 % | 53.48 % |
| token at rank 40 | ␣Av, 0.0093 % | ␣Dr, 0.128 % |
Едно фиксирано , два провала в противоположни посоки. При фактологичния prompt допуска 39 tokens, които заедно струват 3,6 % — пропуска боклук, включително кандидат с девет хилядни от процента, защото правилото брои слотове, а не доказателство. При story prompt същото изхвърля 21 % от масата, която моделът наистина е присвоил, защото реалният nucleus там е широк 467 tokens.
Top-p кара точно едно число да върши и двете задачи. Задайте и той запазва 1 token при първия prompt и 467 при втория, защото задава въпрос за разпределението, вместо да му налага брой. Вижте тази адаптация директно — същият cut, четири temperatures:
Този widget също изяснява едно погрешно схващане, което струва реални пари. При уверено разпределение top_p = 0.9 не е „малко разнообразие“. То е greedy. При temperature 1 водещият token тук държи 96,90 %, което вече е над 0,9, така че nucleus е широк един token и нищо друго никога не може да бъде изтеглено. Екипи задават top_p на 0,9, вярвайки, че са отпуснали нещо, и после се чудят защо всеки отговор е идентичен.
Задайте top-k вместо това и противоположният провал е също толкова видим:
Санкциите, с формулите, защото объркването им е повсеместно
Връзка към раздела: Санкциите, с формулите, защото объркването им е повсеместноТри различни механизма се движат под сходни имена, правят различни неща и разликата е измерима. Нека е броят пъти, в които token вече се е появил.
Presence penalty
Връзка към раздела: Presence penaltyИзважда константа от всеки token, който изобщо се е появил. Да се появи веднъж и да се появи четиридесет пъти се наказва еднакво. Това е превключвател, не регулатор.
Frequency penalty
Връзка към раздела: Frequency penaltyИзважда пропорционално на броя. Token, използван четири пъти, се наказва четири пъти по-силно от token, използван веднъж, и натискът се натрупва с растежа на текста.
Repetition penalty (CTRL)
Връзка към раздела: Repetition penalty (CTRL)Оригиналът, от статията CTRL.7 Той дели, вместо да изважда, със случая за знак, нужен защото деленето на отрицателен logit би го направило по-голям. Следователно силата му зависи от големината на logit, което означава, че едно и също удря различно в различни точки на едно и също изречение.
Същото дегенерирало продължение от по-рано, с приложен всеки един от тях. „Променени стъпки“ брои колко от 120-те стъпки на генериране са избрали различен token от този, който моделът без penalty би избрал. Run-ът тук е 120 стъпки срещу 140 в блока по-горе, затова базовата линия без penalty показва 85,5 %, а не 87,6 %:
| setting | repeated 4-grams | steps altered |
|---|---|---|
| nothing | 85.5 % | 0 / 120 |
| presence 0.5 | 65.0 % | 3 / 120 |
| presence 1.0 | 3.4 % | 11 / 120 |
| frequency 0.5 | 6.0 % | 12 / 120 |
| frequency 1.0 | 0.0 % | 20 / 120 |
| repetition 1.2 (CTRL) | 0.0 % | 35 / 120 |
Оттук следват три неща. Presence при 0,5 промени три решения от 120 и намали повторението с една четвърт — цикълът се държеше от шепа tokens. Frequency при 0,5 промени четири пъти повече решения за далеч по-голям ефект, защото множителят по брой продължава да расте, а presence константата — не. А CTRL penalty при широко копираната стойност 1,2 пренаписа 35 от 120 решения, което не е леко побутване; това е различен модел.
Това последно число подготвя провала, за който никой не предупреждава.
Какво правят penalties с текст, който трябва да се повтаря
Връзка към раздела: Какво правят penalties с текст, който трябва да се повтаряКодът се повтаря. Таблиците се повтарят. Списъците се повтарят. Структурираният output се повтаря по дефиниция — това е структурата. Penalty не може да различи модел, заседнал в цикъл, от модел, който правилно извежда четвъртия ред на таблица, защото и двете изглеждат като token, който се появява отново.
Същите три задачи, генерирани по три начина:
| task | nothing | frequency 0.5 | repetition 1.2 |
|---|---|---|---|
| markdown table, 6 rows | 0 / 56 steps altered | 0 / 56 | 2 / 62 |
| Python function | 0 / 93 | 0 / 93 | 10 / 110 |
| bulleted list, 1 to 12 | 0 / 50 | 0 / 50 | 0 / 50 |
Frequency penalty при 0,5 се оказа безвреден и при трите, което е полезен и леко изненадващ резултат, и казва нещо точно: щом нито едно решение не се е променило, структурните tokens трябва да са печелили позициите си с повече от изваденото от penalty, дори след като са се появили пет и шест пъти. CTRL penalty, който дели, вместо това ги измества, а ето какво произведе:
repetition 1.2, markdown table:
| n | 2^n |
| --- | --- |
| 0 | 1 |
| 1 | 2 |
| 2 | 4 |Подравняването се разпада: количеството padding във всяка клетка се променя от ред на ред, защото поредицата от интервали преди затварящата вертикална черта е точно видът повторение, което penalty е създаден да разбие. Козметично е, и струва шест допълнителни tokens. Случаят с Python не е козметичен:
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,Penalty избута модела от total — вече използвано в docstring — към total_sum, напълни output-а с измислени коментари, за да похарчи бюджета си за неизползвани tokens, и после влезе в триаргументен range със stride. Коментарът казва incrementing by 2 each time, което е грешно за сума от квадрати от 1 до . Repetition penalty произведе неправилен код от prompt, на който без него беше отговорено правилно.
Правилото, което следва, е кратко: penalties са за отворена проза и трябва да са изключени за код, структуриран output, таблични данни и всичко със schema. Глава 18 е точно за тази втора категория.
Редът на прилагане и защо променя отговора
Връзка към раздела: Редът на прилагане и защо променя отговораВсяка реална имплементация прилага тези неща в една конкретна последователност:
penalties → temperature → top-k → top-p → sample
Това не е произволно счетоводство и размяната на два етапа произвежда наистина различни разпределения. Две измервания, и двете върху фактологичния prompt.
Cut преди или след temperature. Nucleus се изчислява върху разпределението, което му е подадено, а temperature променя това разпределение радикално:
| top-p 0.9 after temperature | top-p 0.9 before temperature | |
|---|---|---|
| 1 token | 1 token | |
| 353 tokens | 1 token | |
| 32,966 tokens | 1 token |
При същата номинална настройка дава кандидатно множество от 32 966 или от 1, само според това кой етап върви първи. Ако някога сте се чудили защо повишаването на temperature „не прави нищо“ при един provider и унищожава output-а при друг със същите две числа, тази таблица е правдоподобен отговор.
Penalizing преди или след temperature. Да извадите penalty и после да разделите на дава ефективен penalty ; да разделите първо и после да извадите дава . С presence penalty 1,0, приложен към водещия token:
| temperature | penalise, then temper | temper, then penalise |
|---|---|---|
| 0.5 | 99.858 % | 99.948 % |
| 1.0 | 89.839 % | 89.839 % |
| 2.0 | 10.783 % | 6.830 % |
Идентични при , както трябва да бъдат. Различни с фактор 1,58 при . „Presence penalty 1,0“ не е добре дефинирано количество penalty, освен ако не знаете и къде се прилага temperature, а никое API не документира това.
Покажи подробности
По избор: целият pipeline, в горния ред.
Шестнадесет реда и всичко в тази глава е в тях. Това е същото изчисление, което widget-ът изпълнява, върху реален logit вектор вместо върху десет фиксирани числа.
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 в реда за top-p е кумулативната маса без текущия token, което кара nucleus да включи token, който пресича прага, вместо да спре точно преди него. Сбъркайте това с едно и top_p = 0.9 тихо се превръща в малко по-строг cut от всяка друга имплементация.
Това е едно от малкото места във втората половина на курса, където Python е правилният език, и причината е структурна, не стилова: всеки ред по-горе се нуждае от пълния вектор logits във вашите ръце, а през HTTP API този вектор не съществува. Можете да изпратите temperature и top_p към provider; не можете да ги имплементирате и не можете да видите какво са направили.
Няма универсално sampling API
Връзка към раздела: Няма универсално sampling APIВсеки provider приема различно подмножество от тези контроли, с различни диапазони, и игнорира останалите мълчаливо. Това не е абстрактно оплакване. Всяко приложение, което предлага избор на модел, трябва да запише разликите някъде, а файлът, в който го прави, е карта на несъвместимостта. Ето какво декларира един такъв каталог за един параметър при деветте text sources, които поддържа:
| declared temperature range | sources |
|---|---|
| 0 to 1 | Anthropic, Google, Meta, Cerebras, PaLM |
| 0 to 1.5 | Mistral |
| 0 to 2 | OpenAI, DeepSeek, xAI |
Думата е същата; скалата не е. „Temperature 1“ е непромененото разпределение при един и максимално позволената топлина при друг, а половината каталог не може да изрази стойността, която другата половина третира като неутрално-плюс-малко. Останалите копчета са също толкова неравни: записите за OpenAI, DeepSeek и xAI приемат presence и frequency penalties и никакъв topK; записите за Google, Meta, Cerebras и PaLM приемат topK и никакви penalties; Anthropic приема topK, topP и stop sequences и никакви penalties; и точно един от деветте — Mistral — приема seed. Изпращането на параметър, който provider не имплементира, обикновено не произвежда никаква грешка: заявката успява, копчето не прави нищо и вие заключавате, че настройката няма ефект.
И забележете какво е такъв файл: твърдение за чуждо API, написано в един конкретен ден, което после не се проверява от нищо. Каталог, който казва 0 до 1 за provider, който вече приема 0 до 2, тихо ще ограничава всяка заявка.
Още два контрола принадлежат към същото семейство. logprobs, когато се предлага, връща log-probabilities на избрания token и често първите няколко алтернативи — единственият прозорец, който получавате към разпределението, за което е тази глава, и основата на всяка confidence heuristic, изградена върху затворен модел. А maximum tokens плюс stop sequences прекратяват генерирането без никаква връзка с вероятността: твърд лимит и съвпадение на низ. И двете се появяват като finish_reason от Глава 14, където length означава, че отговорът ви е отрязан по средата на изречение от бюджет, не завършен от модела.
Seed и детерминизмът, който нямате
Връзка към раздела: Seed и детерминизмът, който няматеЗадайте seed и sampling става възпроизводим. Тази част е реална и е лесна за проверка:
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\"."Идентични байт по байт в рамките на seed, различни между seed-ове, точно както е рекламирано. Значи seed фиксира случайното теглене в последния ред на функцията sample — кой token се избира при дадено разпределение.
Това, което не фиксира, е разпределението. И там е проблемът, защото векторът logits, който моделът ви произвежда, не е математически обект; той е output от милиарди floating-point събирания, а те имат ред.
Глава 2 остави този експеримент готов. Същият милион float32 числа, сумирани в различни групирания:
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Вижте последния ред. Броят chunks променя отговора. Това не е любопитен детайл за numpy; това е механизмът, защото когато inference server разделя reduction върху повече или по-малко паралелни единици, той прави точно това. А server-ът разделя според това колко заявки обслужва.
Ето този ефект върху самия модел. Същият prompt, същият forward pass, единствената разлика е колко други заявки случайно са били в batch:
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Пуснат сам, моделът е напълно детерминистичен — двадесет pass-а, идентични до bit. Поставете идентичния prompt в batch с несвързани заявки и 97 % от неговите logits се променят. Нищо във вашата заявка не се промени. Пристигна чужда заявка.
Сега честната част, защото това обикновено се разказва сякаш е краят на историята. Промяна от променя output-а само ако два кандидат-tokens са били на такова разстояние един от друг. В 717 стъпки на генериране върху дванадесет prompts най-малката разлика между първите два logits беше — сто пъти по-голяма от perturbation — и нито една стъпка не беше достатъчно близо, за да се обърне. Така че при този модел, във float32, на лаптоп, batching премести всеки logit и не промени нито един token.
Това е описание на благоприятни условия, не успокоение, и една промяна в тези условия е достатъчна:
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."Шест от осем отговора се разминават, а един от тях деградира сериозно. Таблицата от Глава 2 казва защо: bfloat16 пази 7 mantissa bits, така че близо до logit magnitude 16 представимите стойности са на 0,125 една от друга — 16,0, после 16,125, после 16,25 — и rounding може да премести logit с до 0,0625. Междувременно 4,7 % от измерените по-горе стъпки на генериране имаха top-two gap под 0,1. Това е цялата разлика между двата експеримента: във float32 perturbation беше сто пъти по-малка от най-близкото решение, а в bfloat16 е със същия размер. Production inference върви в 16-bit, на хардуер с fused kernels и reduction orders, които никой не обещава да запази. Дали „численият шум е пренебрежим“ е въпрос за precision и hardware, не за модела.
И така, четирите причини, каталогизирани както обеща Глава 9:
Floating-point addition is not associative
Връзка към раздела: Floating-point addition is not associativeКутията от Глава 2. Редът на една сума променя стойността ѝ, така че всяка промяна в начина, по който reduction се разделя, променя logits. Това е субстратът; останалите три са начини да се промени редът.
Dynamic batching groups your request with strangers'
Връзка към раздела: Dynamic batching groups your request with strangers'Continuous batching, от Глава 13, е причината inference да е достъпен — и означава, че формата на матриците, през които минават вашите tokens, зависи от трафика. Измерено по-горе: 147 321 logits се преместиха, защото batch size се промени.
Mixture-of-experts routing depends on the batch
Връзка към раздела: Mixture-of-experts routing depends on the batchКутията от Глава 9 вече го каза. Router-ът прави дискретен избор за token на layer, подчинен на лимити за капацитет по expert, изчислени върху batch-а. Token, който сам би отишъл при expert 7, отива при expert 12 в компания. Това не е rounding разлика; това е различен набор от тегла.
The model behind the name changes
Връзка към раздела: The model behind the name changesVersion string като -latest е pointer, а pointers се пренасочват. Providers също обновяват serving stack под фиксиран version identifier. Нито едното не се обявява с granularност, която би ви позволила да го свържете с промяна във вашия output.
Параметърът seed на OpenAI е честен за това по единствения възможен начин: идва заедно с поле system_fingerprint, което идентифицира backend configuration, а документацията казва, че детерминизмът е best-effort и че променен fingerprint означава, че резултатите може да се различават. Прочетете това като каквото е — provider ви казва, че контролира и четирите причини по-горе, че вие не контролирате нито една от тях и че единственото, което може да предложи, е да ви каже след факта, че нещо се е преместило.
Накъде продължава това
Връзка към раздела: Накъде продължава товаВсичко тук беше за едно копче и последствията му. Отстъпете едно ниво назад и се появява по-трудният проблем: обектът, който настройвахме, е вероятностно разпределение, а вероятностните разпределения нямат интерфейс.
Function call има такъв. Ред в база данни има такъв. Handler POST, който очаква JSON body с три задължителни полета, има такъв и ще отхвърли всичко друго. Между модела и всеки друг компонент във вашата система стои договор, за който едната страна не може да дава обещания: моделът ще произведе нещо, изтеглено от разпределение, което сте оформили, но не сте фиксирали, а кодът от другата страна се нуждае от стойност с познат тип или хвърля грешка.
Мостът между тези два свята се строи от материала на тази глава, не от parsing и retries. Ако даден token би счупил изискваната структура, не го sample-вате с надежда — задавате logit му на , преди softmax изобщо да го види. Constrained decoding е маска върху същия вектор, който цяла глава преоформяхме, и превръща „моля, отговори в JSON“ от заявка в гаранция.
Глава 18 е този договор: tool calling, JSON Schema, structured outputs и какво е нужно, за да направите детерминистична система безопасна за изграждане върху вероятностна.
Източници и метод
Връзка към раздела: Източници и методВсички измервания в тази глава идват от Qwen/Qwen2.5-0.5B-Instruct на CPU, float32 освен ако не е посочено друго, със sampling, имплементиран както е написан в секцията по избор, вместо делегиран на библиотека. Това е малък модел и конкретните стойности са негови; механизмите не са. How to generate text with different decoding methods на Von Platen (Hugging Face, 2020) е статията, спрямо която тази е измерена, и все още е най-доброто кратко въведение в същия материал. За секцията за детерминизъм: бележките на PyTorch за reproducibility описват какво seed фиксира и не фиксира на една машина, документацията на OpenAI за seed и system_fingerprint описва какво provider може и не може да обещае, а дискусията на Thinking Machines от 2025 г. за batch-invariant kernels е най-ясният публичен разказ защо поправянето на това на ниво inference server е възможно, но не е безплатно.
Препратки
Връзка към раздела: Препратки-
Ackley, D. H., Hinton, G. E. and Sejnowski, T. J. A Learning Algorithm for Boltzmann Machines. Cognitive Science 9(1), pp. 147–169 (1985), където temperature в softmax идва от статистическата физика. Hinton, G., Vinyals, O. and Dean, J., Distilling the Knowledge in a Neural Network, arXiv:1503.02531 (2015), раздел 2, е мястото, където същият параметър се появява отново в съвременния deep learning — като начин да се покаже пълното разпределение на teacher-а, което е soft labels от Глава 13, а не sampling от тази глава. ↩
-
Guo, C., Pleiss, G., Sun, Y. and Weinberger, K. Q. On Calibration of Modern Neural Networks. arXiv:1706.04599 (2017). Не бъркайте това с temperature в тази глава. Temperature scaling напасва една стойност върху validation set, така че увереността на модела да съответства на точността му; това е post-hoc calibration method, приложен към output-а на класификатор. Temperature sampling е runtime control върху начина, по който generator тегли tokens. Същата формула, различна цел и никаква споделена стойност. ↩
-
Holtzman, A., Buys, J., Du, L., Forbes, M. and Choi, Y. The Curious Case of Neural Text Degeneration. arXiv:1904.09751 (2019). Въвежда nucleus sampling и измерването, че decoding, базиран на максимизация, произвежда текст, чийто вероятностен профил изобщо не прилича на човешки текст. ↩ ↩2
-
Fan, A., Lewis, M. and Dauphin, Y. Hierarchical Neural Story Generation. arXiv:1805.04833 (2018). Статията, която популяризира top-k sampling. ↩
-
Nguyen, M. et al. Turning Up the Heat: Min-p Sampling for Creative and Coherent LLM Outputs. arXiv:2407.01082 (2024). ↩
-
Meister, C., Pimentel, T., Wiher, G. and Cotterell, R. Locally Typical Sampling. arXiv:2202.00666 (2022). ↩
-
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). Раздел 4.1 е оригиналният repetition penalty — този, който дели. ↩