Температура, top-p і детермінізм, якого у вас немає
Температура ділить logits перед softmax — і цим руйнує ідею «регулятора креативності». Один greedy виклик двічі — дві відповіді.
На цій сторінці
Ось той самий запит, надісланий до тієї самої моделі п’ять разів. Ті самі ваги, той самий 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 елементів. Число, яке змінилося, називається температурою; у більшості документації його описують як регулятор креативності, і цей опис помилковий так, що цей розділ може це показати, а не просто стверджувати.
Це також розділ, у якому сходяться три попередні обіцянки. Розділ 4 визначив logit і так ним по-справжньому не скористався. Блок про числа з рухомою комою з розділу 2 закінчився інструкцією — згадайте це, коли розділ 17 спитає, чому той самий prompt, модель і seed можуть породити різні tokens. А блок про mixture-of-experts із розділу 9 пообіцяв каталог із чотирьох причин недетермінізму. Усі три з’являються нижче.
Один рядок, на якому тримається весь розділ
Посилання на розділ: Один рядок, на якому тримається весь розділРозділ 4 ввів logit як ненормалізовану дійснозначну оцінку, по одній на клас. Розділ 8 змусив мовну модель виробляти по одній такій оцінці на кожен елемент словника. softmax перетворює цей вектор на ймовірності:
Температура входить саме тут — назву запозичено зі статистичної фізики, де той самий параметр керує тим, наскільки різко розподіл Больцмана концентрується на станах із низькою енергією1 — і вона ділить logits перед експонентою:
Це розташування і є всім механізмом, і варто витратити два рядки алгебри, щоб побачити, чому вона не могла бути ніде більше. Припустімо, ви спробували застосувати температуру до ймовірностей — помножити їх на і перенормалізувати. Ви отримали б
Константа скорочується. Масштабування ймовірностей не робить взагалі нічого; розподіл повертається незмінним. Температура має ефект лише тому, що діє на експоненту: ділення на перед піднесенням до експоненти еквівалентне піднесенню кожної ймовірності до степеня — нелінійному переформуванню, яке змінює співвідношення між елементами, а не їхній спільний масштаб.
З цього розташування без жодної додаткової роботи випливають обидві межі. Коли , найбільший logit відривається від решти, і схлопується до одного token з найвищою оцінкою: greedy decoding. Коли зростає, кожне прямує до нуля, кожна експонента прямує до 1, а розподіл сплющується до рівномірного по всьому словнику. Рівно при формула ділить на нуль, тому кожна реалізація обробляє це окремо як арифметичний максимум — зокрема віджет нижче, який при перемикається на argmax.
Одне попередження, бо зіткнення назв справді плутає людей. У машинному навчанні є друга, не пов’язана з цим річ під назвою температура: temperature scaling, метод калібрування, який підбирає одне значення на валідаційному наборі, щоб упевненість класифікатора відповідала його точності.2 Формула та сама, але до генерації це не має стосунку. Коли в статтях пишуть «temperature», часто мають на увазі саме це; цей розділ — ніколи.
Ось цей розподіл, з арифметикою перед вами. logits зафіксовані й правдоподібні, тож числа в тексті нижче можна звірити з тим, що ви бачите:
Температура — не регулятор креативності
Посилання на розділ: Температура — не регулятор креативностіЧисло ␣banana — це весь аргумент у мініатюрі: підвищення температури не може дати моделі ідею, якої в неї не було. logits уже обчислені, ранжування вже зафіксоване, і температура зберігає його точно — жодна кількість тепла ніколи не підніме token з нижчою оцінкою вище за token з вищою. Вона лише перерозподіляє масу вниз уздовж ранжування, яке сама модель і створила. Висока температура не робить модель винахідливішою; вона робить імовірнішим виведення tokens, які модель сама оцінила як погані.
На реальному словнику це перестає бути курйозом і стає причиною, чому результат із високою температурою непридатний. Виміряно на Qwen/Qwen2.5-0.5B-Instruct, один forward pass, prompt вище; підраховано, скільки tokens потрібно, щоб накопичити задану частку маси ймовірності:
| температура | top-1 ймовірність | ентропія | tokens для 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:. Сміття у вступному блоці — прямий наслідок, і це не баг у моделі чи бібліотеці: це саме те, що попросив запит.
Корисний діапазон вузький і залежить від задачі, а не від смаку. У фактичному питанні відповідь — один 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, і чесна порада така: знайдіть її вимірюванням на вашій задачі, а не копіюванням числа з блогу.
Чому найімовірніший текст — поганий текст
Посилання на розділ: Чому найімовірніший текст — поганий текстПід усім цим ховається очевидне питання: якщо модель має розподіл ймовірностей і один 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 вікон уже з’являлися раніше в тому самому виводі. Це нейронна дегенерація тексту, названа й пояснена Holtzman та співавт. у статті, що представила top-p.3 Модель не зламана; максимізація ймовірності послідовності просто є неправильним об’єктивом для відкритого тексту. Людське письмо — не найімовірніша послідовність слів: у ньому є несподіванка, його ймовірність на token блукає, падає й відновлюється; натомість шлях максимальної ймовірності — це фіксована точка, яка, щойно в неї потрапили, не має причини її залишати.
Саме тому sampling взагалі існує. І також — це частина, яку часто опускають — це не універсальний закон. Розділ 12 виміряв 24 правильні відповіді з 24 у двокрокових словесних задачах зі звичайним greedy decoding, а sampling при температурі 0,8 знизив це до 81 %; self-consistency потім витратила вшестеро більше tokens, щоб повернутися туди, де greedy вже був. Обидва факти істинні одночасно:
Відкрита генерація. Немає одного правильного продовження, тому найімовірніше — пастка: воно зациклюється, і 87,6 % його скопійовано з нього самого. Семплюйте.
Задачі з однією правильною відповіддю. Є одне правильне продовження, тому витягнути щось інше — означає витягнути помилку. 100 % із розділу 12 стали 81 % саме з цієї причини. Не семплюйте.
Більшість production prompts належать до другого типу, але налаштовані як перший, бо температуру залишили такою, як у прикладі коду.
Два способи обрізати — і лише один адаптується
Посилання на розділ: Два способи обрізати — і лише один адаптуєтьсяSampling із повного розподілу — не те, що насправді хтось робить, бо хвіст величезний і повний нісенітниці. Щось треба обрізати. Є дві класичні відповіді, і вони відрізняються однією рисою, яка вирішує все.
Top-k залишає фіксовану кількість кандидатів. Відсортуйте за ймовірністю, залиште перші , відкиньте решту, перенормалізуйте.4 Top-p, також зване nucleus sampling, залишає фіксовану кількість маси: беріть tokens у спадному порядку, доки їхня кумулятивна ймовірність не досягне , і зупиніться.3 Формально nucleus — це найменша множина з
Різниця звучить косметично, але такою не є, бо два prompts, які ви надсилаєте в одну хвилину, мають цілком різні форми розподілу. Обидва нижче — та сама модель при температурі 1:
Q: What is the capital of France?\nA: | Once upon a time, | |
|---|---|---|
| top-1 ймовірність | 96,01 % | 25,39 % |
| tokens для 90 % маси | 1 | 467 |
| top-k = 40 залишає | 99,61 % маси | 78,87 % маси |
| маса в рангах 2–40 | 3,61 % | 53,48 % |
| token на ранзі 40 | ␣Av, 0,0093 % | ␣Dr, 0,128 % |
Один фіксований , дві протилежні помилки. На фактичному prompt пропускає 39 tokens, які разом варті 3,6 % — він впускає сміття, включно з кандидатом на дев’ять тисячних відсотка, бо правило рахує слоти, а не докази. На сюжетному prompt той самий відкидає 21 % маси, яку модель справді призначила, бо справжній nucleus там має ширину 467 tokens.
Top-p змушує одне число виконувати обидві роботи. Встановіть — і воно залишить 1 token на першому prompt та 467 на другому, бо ставить запитання про розподіл, а не нав’язує йому кількість. Подивіться на цю адаптацію напряму — той самий зріз, чотири температури:
Цей віджет також закриває хибне уявлення, яке варто назвати, бо воно коштує людям реальних грошей. На впевненому розподілі top_p = 0.9 — це не «трохи різноманітності». Це greedy. При температурі 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, ніж вибрала б модель без штрафу. Тут прогін має 120 кроків проти 140 у блоці вище, тому baseline без штрафу показує 85,5 %, а не 87,6 %:
| налаштування | повторені 4-grams | змінені кроки |
|---|---|---|
| нічого | 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 рішень, тобто це не легкий поштовх; це інша модель.
Останнє число підводить до збою, про який ніхто не попереджає.
Що штрафи роблять із текстом, який має повторюватися
Посилання на розділ: Що штрафи роблять із текстом, який має повторюватисяКод повторюється. Таблиці повторюються. Списки повторюються. Структурований вивід повторюється за визначенням — у цьому й полягає структура. Штраф не може відрізнити модель, що застрягла в циклі, від моделі, яка правильно виводить четвертий рядок таблиці, бо обидва випадки виглядають як повторна поява token.
Ті самі три задачі, згенеровані трьома способами:
| задача | нічого | frequency 0,5 | repetition 1,2 |
|---|---|---|---|
| markdown-таблиця, 6 рядків | 0 / 56 змінених кроків | 0 / 56 | 2 / 62 |
| Python-функція | 0 / 93 | 0 / 93 | 10 / 110 |
| маркований список, 1–12 | 0 / 50 | 0 / 50 | 0 / 50 |
Frequency penalty на 0,5 виявився нешкідливим у всіх трьох випадках, що є корисним і трохи несподіваним результатом, і він означає щось точне: якщо жодне рішення не змінилося, структурні tokens мусили вигравати свої позиції більше, ніж віднімав штраф, навіть після п’яти й шести появ. CTRL penalty, який натомість ділить, їх таки вибиває, і ось що він створив:
repetition 1.2, markdown table:
| n | 2^n |
| --- | --- |
| 0 | 1 |
| 1 | 2 |
| 2 | 4 |Вирівнювання розсипається: кількість padding усередині кожної комірки змінюється від рядка до рядка, бо послідовність пробілів перед завершальною вертикальною рискою — саме той тип повторення, який штраф покликаний ламати. Косметика, і вона коштувала шість додаткових 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,Штраф зіштовхнув модель із total — уже використаного в docstring — на total_sum, наповнив вивід вигаданими коментарями, щоб витратити бюджет на невикористані tokens, а потім зайшов у триаргументний range із кроком. Коментар каже incrementing by 2 each time, що неправильно для суми квадратів від 1 до . Repetition penalty створив неправильний код із prompt, на який без нього було дано правильну відповідь.
Правило звідси коротке: штрафи — для відкритої прози, і їх треба вимикати для коду, структурованого виводу, табличних даних і всього, що має schema. Розділ 18 саме про цю другу категорію.
Порядок застосування — і чому він змінює відповідь
Посилання на розділ: Порядок застосування — і чому він змінює відповідьКожна реальна реалізація застосовує це в певній послідовності:
penalties → temperature → top-k → top-p → sample
Це не довільна бухгалтерія, і перестановка двох етапів дає справді інші розподіли. Два вимірювання, обидва на фактичному prompt.
Обрізання до або після температури. Nucleus обчислюється на тому розподілі, який йому передали, а температура радикально змінює цей розподіл:
| top-p 0,9 після температури | top-p 0,9 до температури | |
|---|---|---|
| 1 token | 1 token | |
| 353 tokens | 1 token | |
| 32 966 tokens | 1 token |
При те саме номінальне налаштування дає множину кандидатів із 32 966 або з 1 — залежно лише від того, який етап виконується першим. Якщо ви колись дивувалися, чому підвищення температури «нічого не робить» в одного provider і руйнує вивід в іншого з тими самими двома числами, ця таблиця — правдоподібна відповідь.
Штраф до або після температури. Віднімання штрафу і потім ділення на дає ефективний штраф ; спочатку ділення, а потім віднімання дає . З presence penalty 1,0, застосованим до провідного token:
| температура | спершу штраф, потім температура | спершу температура, потім штраф |
|---|---|---|
| 0,5 | 99,858 % | 99,948 % |
| 1,0 | 89,839 % | 89,839 % |
| 2,0 | 10,783 % | 6,830 % |
Ідентично при , як і має бути. Різниця в 1,58 раза при . «Presence penalty 1,0» не є добре визначеною величиною штрафу, якщо ви також не знаєте, де застосовується температура, і жоден API цього не документує.
Показати подробиці
Опційно: весь pipeline, у наведеному вище порядку.
Шістнадцять рядків — і все з цього розділу в них є. Це те саме обчислення, яке виконує віджет, але на реальному векторі logits замість десяти фіксованих чисел.
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 непомітно стане трохи жорсткішим зрізом, ніж у кожній іншій реалізації.
Це одне з небагатьох місць у другій половині курсу, де Python є правильною мовою, і причина структурна, а не стилістична: у кожному рядку вище треба мати в руках повний вектор logits, а через HTTP API такого вектора не існує. Ви можете надіслати temperature і top_p provider; ви не можете реалізувати їх самі й не можете побачити, що вони зробили.
Універсального sampling API не існує
Посилання на розділ: Універсального sampling API не існуєКожен provider приймає іншу підмножину цих регуляторів, з іншими діапазонами, і мовчки ігнорує решту. Це не абстрактна скарга. Будь-який застосунок, що пропонує вибір моделі, мусить десь записати відмінності, і файл, у якому він це робить, є картою несумісності. Ось що один такий каталог декларує для одного параметра в дев’яти текстових джерелах, які він підтримує:
| заявлений діапазон температури | джерела |
|---|---|
| 0–1 | Anthropic, Google, Meta, Cerebras, PaLM |
| 0–1,5 | Mistral |
| 0–2 | OpenAI, DeepSeek, xAI |
Слово те саме; шкала — ні. «Температура 1» в одного — незмінений розподіл, а в іншого — максимально дозволене тепло, і половина каталогу не може виразити значення, яке інша половина вважає нейтральним-плюс-трохи. Решта ручок так само нерівні: записи OpenAI, DeepSeek і xAI приймають presence і frequency penalties та не приймають topK; записи Google, Meta, Cerebras і PaLM приймають topK і не приймають штрафів; Anthropic приймає topK, topP і stop sequences та не приймає штрафів; і рівно один із дев’яти — Mistral — приймає seed. Надсилання параметра, який provider не реалізує, зазвичай не дає жодної помилки: запит успішний, ручка нічого не робить, і ви робите висновок, що налаштування не має ефекту.
І зверніть увагу, чим є такий файл: твердженням про чужий API, написаним одного конкретного дня, яке потім ніхто не перевіряє. Каталог, що каже 0–1 для provider, який тепер приймає 0–2, мовчки обріже кожен запит.
Ще два регулятори належать до тієї самої родини. logprobs, там, де він пропонується, повертає log-probabilities вибраного token і часто кілька top alternatives — єдине вікно в розподіл, про який цей розділ, і основу кожної евристики впевненості на закритій моделі. А 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, який виробляє ваша модель, не є математичним об’єктом; це результат мільярдів додавань із рухомою комою, а в них є порядок.
Розділ 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 розбиває редукцію між більшою чи меншою кількістю паралельних блоків, він робить саме це. А 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Запущена сама, модель ідеально детермінована — двадцять проходів, ідентичні до біта. Помістіть той самий prompt у batch із нерелевантними запитами — і 97 % її logits змінюються. У вашому запиті нічого не змінилося. Прийшов чийсь інший запит.
Тепер чесна частина, бо це зазвичай подають так, ніби на цьому історія закінчується. Зміна на змінює вивід лише якщо два кандидати tokens були на відстані не більшій за це. За 717 кроків генерації на дванадцяти prompts найменший розрив між двома top logits був — у сто разів більший за збурення — і жоден крок не був достатньо близьким, щоб перекинутися. Отже, на цій моделі, у 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 біт мантиси, тому біля величини logit 16 представні значення стоять на відстані 0,125 — 16,0, потім 16,125, потім 16,25 — і округлення може зсунути logit на до 0,0625. Тим часом 4,7 % виміряних вище кроків генерації мали top-two gap нижче 0,1. Це вся різниця між двома експериментами: у float32 збурення було в сто разів меншим за найближче рішення, а в bfloat16 воно того самого масштабу. Production inference працює в 16-bit, на hardware з fused kernels і порядками редукції, які ніхто не обіцяє зберігати. Чи є «числовий шум незначним» — це питання точності й hardware, а не моделі.
Отже, чотири причини, каталогізовані, як обіцяв розділ 9:
Додавання з рухомою комою не асоціативне
Посилання на розділ: Додавання з рухомою комою не асоціативнеБлок із розділу 2. Порядок суми змінює її значення, тому будь-яка зміна того, як розбито редукцію, змінює logits. Це субстрат; інші три — способи змінити порядок.
Dynamic batching групує ваш запит із чужими
Посилання на розділ: Dynamic batching групує ваш запит із чужимиContinuous batching із розділу 13 — причина, чому inference доступний за ціною, і він означає, що форма матриць, крізь які проходять ваші tokens, залежить від трафіку. Виміряно вище: 147 321 logits зрушили через зміну batch size.
Mixture-of-experts routing залежить від batch
Посилання на розділ: Mixture-of-experts routing залежить від batchБлок із розділу 9 уже це сказав. router робить дискретний вибір на token на шар, з урахуванням лімітів місткості на expert, обчислених по batch. Token, який на самоті пішов би до expert 7, у компанії йде до expert 12. Це не різниця округлення; це інший набір ваг.
Модель за назвою змінюється
Посилання на розділ: Модель за назвою змінюєтьсяРядок версії на кшталт -latest — це вказівник, а вказівники перенаправляють. Providers також оновлюють serving stack під фіксованим ідентифікатором версії. Ні те, ні інше не оголошують із такою деталізацією, щоб ви могли співвіднести це зі зміною власного виводу.
Параметр OpenAI seed чесний щодо цього єдиним можливим способом: він поставляється поруч із полем system_fingerprint, яке ідентифікує конфігурацію backend, а документація каже, що детермінізм — best-effort і що змінений fingerprint означає, що результати можуть відрізнятися. Читайте це як те, чим воно є: provider каже вам, що він контролює всі чотири причини вище, що ви не контролюєте жодної, і що єдине, що він може запропонувати, — повідомити вам після факту, що щось зрушило.
Куди це веде далі
Посилання на розділ: Куди це веде даліУсе тут було про одну ручку та її наслідки. Відступіть на рівень вище — і з’явиться складніша проблема: об’єкт, який ми налаштовували, є розподілом імовірностей, а розподіли ймовірностей не мають інтерфейсу.
Function call має інтерфейс. Рядок бази даних має інтерфейс. Handler POST, який очікує JSON body з трьома обов’язковими полями, має інтерфейс і відхилить усе інше. Між моделлю та кожним іншим компонентом вашої системи стоїть контракт, про який одна сторона не може давати обіцянок: модель створить щось, витягнуте з розподілу, який ви сформували, але не зафіксували, а коду з іншого боку потрібне значення відомого типу, інакше він впаде.
Міст між цими двома світами будується з матеріалу цього розділу, а не з parsing і retries. Якщо token зламає потрібну структуру, ви не семплюєте його з надією — ви встановлюєте його logit у до того, як softmax взагалі його побачить. Constrained decoding — це mask над тим самим вектором, який ми весь розділ переформовували, і він перетворює «будь ласка, відповідай у JSON» із прохання на гарантію.
Розділ 18 — про цей контракт: tool calling, JSON Schema, structured outputs і що потрібно, щоб deterministic system було безпечно будувати поверх probabilistic one.
Джерела й метод
Посилання на розділ: Джерела й методУсі вимірювання в цьому розділі виконано з Qwen/Qwen2.5-0.5B-Instruct на CPU, float32, якщо не вказано інше, із sampling, реалізованим так, як написано в опційному розділі, а не делегованим бібліотеці. Це мала модель, і конкретні значення — її; механізми — ні. Von Platen, How to generate text with different decoding methods (Hugging Face, 2020) — стаття, відносно якої виміряно цю, і досі найкращий короткий вступ до того самого матеріалу. Для розділу про детермінізм: нотатки PyTorch про відтворюваність описують, що 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), де температура в softmax походить зі статистичної фізики. Hinton, G., Vinyals, O. and Dean, J., Distilling the Knowledge in a Neural Network, arXiv:1503.02531 (2015), розділ 2, — місце, де той самий параметр знову з’являється в сучасному deep learning як спосіб показати повний розподіл учителя, тобто 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 scaling підбирає одне значення на валідаційному наборі, щоб упевненість моделі відповідала її точності; це post-hoc метод калібрування, застосований до виходів класифікатора. 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, той, який ділить. ↩