सामग्री पर जाएँ
17/30अध्याय 17 / 30

Temperature, top-p और वह determinism जो आपके पास नहीं है

Temperature softmax से पहले logits को विभाजित करता है—यही बात creativity-dial वाली सोच को गलत साबित करती है।

इस पेज पर

एक ही request को एक ही model पर पाँच बार भेजा गया। वही weights, वही prompt, वही machine, वही random seed। केवल एक number बदला।

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 ('$ספטמבר..."

कुछ टूटा नहीं। आख़िरी पंक्ति का हर token मॉडल की अपनी 151,936-entry vocabulary पर probability distribution से वैध रूप से निकला था। जो number बदला उसे temperature कहा जाता है; ज़्यादातर documentation में इसे creativity dial बताया जाता है, और यह description इस तरह गलत है जिसे यह chapter साबित करके दिखा सकता है।

यही वह chapter भी है जहाँ पहले किए गए तीन वादे पूरे होते हैं। अध्याय 4 ने logit को परिभाषित किया था और फिर सच में उसका उपयोग नहीं किया। अध्याय 2 के floating-point box का अंत एक निर्देश से हुआ था — इसे याद रखना जब अध्याय 17 पूछे कि वही prompt, model और seed अलग tokens क्यों पैदा कर सकते हैं। और अध्याय 9 के mixture-of-experts box ने non-determinism के चार कारणों की सूची का वादा किया था। तीनों नीचे आते हैं।

अध्याय 4 ने logit को एक unnormalised real-valued score के रूप में पेश किया था, हर class के लिए एक। अध्याय 8 ने language model से vocabulary की हर entry के लिए एक score बनवाया। softmax उस vector z\mathbf{z} को probabilities में बदलता है:

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

Temperature यहाँ आता है — नाम statistical physics से लिया गया है, जहाँ यही parameter control करता है कि Boltzmann distribution अपने low-energy states पर कितनी sharply concentrate करता है1 — और यह exponential से पहले logits को विभाजित करता है:

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

यही placement पूरा mechanism है, और दो lines की algebra से दिखता है कि यह कहीं और हो ही नहीं सकता था। मान लीजिए आपने temperature को probabilities पर apply करने की कोशिश की — उन्हें 1/T1/T से scale किया और renormalise किया। आपको मिलेगा

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

Constant cancel हो जाता है। Probabilities को scale करने से कुछ भी नहीं होता; distribution unchanged लौट आता है। Temperature का effect केवल इसलिए होता है क्योंकि यह exponent पर act करता है, जहाँ exponentiate करने से पहले TT से divide करना हर probability को 1/T1/T power पर raise करने जैसा है — एक nonlinear reshaping जो entries के बीच ratios बदलती है, उनका common scale नहीं।

उस placement से दोनों limits बिना और मेहनत के follow करती हैं। जैसे T0T \to 0, सबसे बड़ा logit बाकियों से दूर भागता है और pp अकेले सबसे high-scoring token पर collapse हो जाता है: greedy decoding। जैसे TT बढ़ता है, हर zi/Tz_i/T zero की ओर जाता है, हर exponential 1 की ओर जाता है, और distribution पूरी vocabulary पर uniform की ओर flatten होता है। ठीक T=0T = 0 पर formula zero से divide करता है, इसलिए हर implementation इसे arithmetic maximum के रूप में special-case करता है — नीचे का widget भी, जो T0.001T \le 0.001 पर argmax पर switch करता है।

एक warning, क्योंकि नामों की टक्कर सचमुच confusion पैदा करती है। machine learning में temperature नाम की दूसरी, unrelated चीज़ भी है: temperature scaling, एक calibration method जो validation set पर एक value fit करता है ताकि classifier का confidence उसकी accuracy से match करे।2 वही formula, generation से कोई लेना-देना नहीं। Papers में “temperature” लिखा हो तो अक्सर वही मतलब होता है; इस chapter में कभी नहीं।

यह रहा वह distribution, arithmetic आपके सामने। logits fixed और plausible हैं, इसलिए नीचे prose में दिए numbers आप जो देखते हैं उससे check किए जा सकते हैं:

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

10 में से 10 टोकन कट के बाद बचते हैं और प्रायिकता बाँटते हैं।

डेटा को तालिका में देखें
टोकनlogitतापमान के बादकट के बाद
␣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%
सैंपलिंग: तापमान, top-p और top-k

The capital of France is की दस candidate continuations, temperature 1 पर बिना cutting के। ␣Paris mass का 96.90 % रखता है; ␣banana, bottom पर 2.6-2.6 logit के साथ, 0.00 % पाता है। Temperature को 0 पर slide करें और एक token 100 % के साथ बचता है। इसे 2 पर slide करें और ␣Paris 69.81 % पर गिरता है जबकि ␣banana 0.17 % तक चढ़ता है — मॉडल का rejected token, reader द्वारा घुमाए knob से real probability पाते हुए।

␣banana number पूरा तर्क miniature में है: temperature बढ़ाने से model को ऐसा idea नहीं मिलता जो उसके पास था ही नहीं। logits पहले ही compute हो चुके हैं, ranking पहले ही fixed है, और temperature उसे बिल्कुल preserve करता है — कितनी भी heat lower-scored token को higher-scored token से ऊपर नहीं ले जाती। यह केवल model द्वारा बनाई ranking में नीचे की ओर mass redistribute करता है। High temperature model को अधिक inventive नहीं बनाता; यह उसे वे tokens emit करने की संभावना बढ़ाता है जिन्हें उसने bad score किया था

Real vocabulary पर यह curiosity नहीं रहता, बल्कि high-temperature output unusable होने का कारण बन जाता है। Qwen/Qwen2.5-0.5B-Instruct पर measured, एक forward pass, ऊपर वाला prompt, और यह count करते हुए कि probability mass का given share accumulate करने में कितने tokens लगते हैं:

temperaturetop-1 probabilityentropy80 % रखने वाले tokens90 %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

नीचे वाली row धीरे पढ़िए। T=2T = 2 पर, ऐसे question में जिसका exactly one correct answer है, 32,966 अलग tokens probability mass के top 90 % को share करते हैं। यह wider creative space नहीं है। यह ऐसा model है जिसे arithmetic ने कहा है कि A: के बाद आने वाले word के लिए Korean particle और C++ identifier को live options माने। Opening block का garbage इसका सीधा परिणाम है, और यह model या library का bug नहीं है — request ने यही माँगा था।

Useful range संकरी है और taste के बजाय task पर depend करती है। Factual question पर answer एक token है और लगभग 1.2 से ऊपर की कोई भी heat बेवजह error inject करती है। Open-ended question पर सचमुच एक से ज़्यादा good continuations होती हैं, और कुछ heat ऐसी variety खरीदती है जो fluent रहती है:

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..."

1.3 पर model ने proper name और ऐसी sentence invent की है जो parse नहीं होती। “हर बार identical” और “incoherent” के बीच की band इस model पर इस task के लिए लगभग 0.6 से 1.1 है, और ईमानदार सलाह यह है कि इसे अपने task पर measure करके खोजिए, किसी blog post से number copy करके नहीं।

इस सबके नीचे एक obvious question छिपा है: अगर model के पास probability distribution है और एक token सबसे likely है, तो हमेशा वही क्यों न लें? Greedy decoding free है, reproducible है और parameters नहीं चाहिए।

क्योंकि result यह है:

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 %

आठ sentences, एक sentence। लगभग दस में से नौ four-token windows पहले ही उसी output में आ चुकी थीं। यह neural text degeneration है, जिसे Holtzman et al. ने उस paper में नाम दिया और explain किया जिसने top-p introduce किया।3 Model टूटा नहीं है; sequence probability maximize करना open-ended text के लिए बस गलत objective है। Human writing words की most likely sequence नहीं होती — उसमें surprise होता है, उसकी per-token probability भटकती, गिरती और recover करती है — जबकि maximum-probability path एक fixed point है जिसमें एक बार enter होने के बाद छोड़ने की कोई वजह नहीं होती।

इसीलिए sampling मौजूद है। और यह बात भी, जो अक्सर छूट जाती है, universal law नहीं हैअध्याय 12 ने plain greedy decoding के साथ two-step word problems पर 24 में से 24 correct measure किए, और temperature 0.8 पर sampling ने उसे 81 % तक गिरा दिया; self-consistency ने फिर six times tokens खर्च करके वापस वहीं पहुँचने की कोशिश की जहाँ greedy पहले से था। दोनों बातें एक साथ सच हैं:

Open-ended generation। कोई single right continuation नहीं है, इसलिए सबसे likely one trap है — वह loop करता है, और उसका 87.6 % खुद से copied है। Sample करें।

एक सही answer वाले tasks। एक single correct continuation है, इसलिए कुछ और draw करना error draw करना है। इसी वजह से अध्याय 12 का 100 % 81 % बना। Sample न करें।

Most production prompts दूसरी तरह के होते हैं और पहली तरह की तरह configured होते हैं, क्योंकि temperature वही छोड़ दिया गया जो example code ने use किया था।

Cut करने के दो तरीके, और उनमें से केवल एक adapt करता है

सेक्शन का लिंक: Cut करने के दो तरीके, और उनमें से केवल एक adapt करता है

Full distribution से sampling वह नहीं है जो कोई सचमुच करता है, क्योंकि tail enormous और nonsense से भरी है। कुछ cut करना पड़ता है। दो classical answers हैं और वे एक respect में अलग हैं जो सब कुछ तय करता है।

Top-k fixed number of candidates रखता है। Probability के हिसाब से sort करें, पहले kk रखें, बाकी discard करें, renormalise करें।4 Top-p, जिसे nucleus sampling भी कहते हैं, fixed amount of mass रखता है: descending order में tokens लें जब तक उनकी cumulative probability pp तक न पहुँच जाए, और stop करें।3 Formally, nucleus सबसे छोटा set VpV_p है जिसके साथ

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

Difference cosmetic लगता है और है नहीं, क्योंकि एक ही minute में भेजे गए दो prompts की distribution shapes पूरी तरह अलग होती हैं। ये दोनों वही model हैं temperature 1 पर:

Q: What is the capital of France?\nA:Once upon a time,
top-1 probability96.01 %25.39 %
mass का 90 % रखने वाले tokens1467
top-k = 40 रखता हैmass का 99.61 %mass का 78.87 %
ranks 2 से 40 में mass3.61 %53.48 %
rank 40 पर token␣Av, 0.0093 %␣Dr, 0.128 %

एक fixed kk, opposite directions में दो failures। Factual prompt पर, k=40k = 40 ऐसे 39 tokens admit करता है जो मिलकर 3.6 % के हैं — यह rubbish अंदर आने दे रहा है, जिसमें percent के नौ thousandths वाला candidate भी है, क्योंकि rule slots count करता है, evidence नहीं। Story prompt पर वही k=40k = 40 model द्वारा genuinely assigned mass का 21 % फेंक देता है, क्योंकि वहाँ real nucleus 467 tokens wide है।

Top-p एक ही number से दोनों jobs कराता है। p=0.9p = 0.9 set करें और यह पहले prompt पर 1 token रखता है और दूसरे पर 467, क्योंकि यह distribution के बारे में question पूछ रहा है, उस पर count impose नहीं कर रहा। उस adaptation को सीधे देखें — वही cut, चार temperatures:

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

10 में से 3 टोकन कट के बाद बचते हैं और प्रायिकता बाँटते हैं।

डेटा को तालिका में देखें
टोकनlogitतापमान के बादकट के बाद
␣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%
सैंपलिंग: तापमान, top-p और top-k

Top-p 0.90 पर, temperature 1.5 के साथ: दस में से तीन tokens survive करते हैं और mass share करते हैं, ␣Paris renormalised होकर 91.10 %। अब केवल temperature बदलें। 0.7 पर वही 0.90 एक survivor छोड़ता है — इतना narrow nucleus अलग नाम पहने greedy decoding है। 2.0 पर यह पाँच छोड़ता है। Cut कभी नहीं बदला; उसके नीचे की shape बदली।

वह widget एक misconception भी settle करता है जिसका नाम लेना ज़रूरी है, क्योंकि इससे लोगों का real money खर्च होता है। Confident distribution पर, top_p = 0.9 “थोड़ी variety” नहीं है। यह greedy है। Temperature 1 पर leading token यहाँ 96.90 % रखता है, जो पहले से 0.9 से ऊपर है, इसलिए nucleus एक token wide है और कुछ और कभी draw नहीं हो सकता। Teams top_p को 0.9 set करती हैं सोचकर कि उन्होंने कुछ loosen किया है, फिर सोचती हैं हर response identical क्यों है।

इसके बजाय top-k set करें और opposite failure उतना ही visible है:

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

10 में से 5 टोकन कट के बाद बचते हैं और प्रायिकता बाँटते हैं।

डेटा को तालिका में देखें
टोकनlogitतापमान के बादकट के बाद
␣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%
सैंपलिंग: तापमान, top-p और top-k

Top-k 5 पर, no top-p। हर temperature पर पाँच tokens survive करते हैं, क्योंकि पाँच ही माँगा गया था। Temperature 1 पर, जैसा दिखाया गया, ␣Paris के नीचे चार candidates मिलकर 2.79 % के हैं। 0.7 पर drop करें और वही चार 0.38 % के हैं — cut theatre है, और model effectively greedy है। 2.0 पर raise करें और वे 22.54 % के हैं। Identical setting, identical survivor count, तीन completely different behaviours, और request में कुछ नहीं बताता कि आपको कौन-सा मिल रहा है।

Penalties, formulas के साथ, क्योंकि इन्हें confuse करना endemic है

सेक्शन का लिंक: Penalties, formulas के साथ, क्योंकि इन्हें confuse करना endemic है

Similar names के नीचे तीन अलग mechanisms चलते हैं, वे अलग काम करते हैं, और difference measurable है। मान लें cic_i वह count है जितनी बार token ii पहले आ चुका है।

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

किसी भी token से जो कभी भी आया है, एक constant subtract करें। एक बार आना और चालीस बार आना identically penalised हैं। यह switch है, dial नहीं।

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

Count के proportion में subtract करें। चार बार use हुआ token एक बार use हुए token से चार गुना hard penalised है, और text बढ़ने के साथ pressure compound होता है।

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}

Original, CTRL paper से।7 यह subtract करने के बजाय divide करता है, sign case इसलिए चाहिए क्योंकि negative logit को divide करने से वह larger हो जाएगा। इसलिए इसकी strength logit के magnitude पर depend करती है, यानी वही ρ\rho उसी sentence में अलग points पर अलग hit करता है।

पहले वाली वही degenerate continuation, हर एक applied के साथ। “Steps altered” count करता है कि 120 generation steps में से कितनों ने unpenalised model की तुलना में अलग token चुना। यहाँ run 120 steps है, ऊपर block में 140 के मुकाबले, इसलिए unpenalised baseline 87.6 % के बजाय 85.5 % पढ़ता है:

settingrepeated 4-gramssteps altered
nothing85.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

तीन बातें निकलती हैं। Presence 0.5 ने 120 में से तीन decisions बदले और repetition को quarter तक काट दिया — loop handful of tokens से जुड़ा था। Frequency 0.5 ने चार गुना ज़्यादा decisions बदले और बहुत बड़ा effect दिया, क्योंकि count multiplier बढ़ता रहता है जबकि presence constant नहीं। और widely-copied 1.2 value पर CTRL penalty ने 120 में से 35 decisions rewrite किए, जो nudge नहीं है; यह अलग model है।

वह आख़िरी number उस failure का setup है जिसके बारे में कोई warn नहीं करता।

Penalties उस text के साथ क्या करती हैं जिसे repeat होना चाहिए

सेक्शन का लिंक: Penalties उस text के साथ क्या करती हैं जिसे repeat होना चाहिए

Code repeat होता है। Tables repeat होती हैं। Lists repeat होती हैं। Structured output definition से repeat होता है — वही structure है। Penalty stuck loop वाले model और table की fourth row correctly emit करते model में फर्क नहीं बता सकती, क्योंकि दोनों token के फिर आने जैसे दिखते हैं।

वही तीन tasks, तीन तरीकों से generated:

tasknothingfrequency 0.5repetition 1.2
markdown table, 6 rows0 / 56 steps altered0 / 562 / 62
Python function0 / 930 / 9310 / 110
bulleted list, 1 to 120 / 500 / 500 / 50

Frequency penalty 0.5 तीनों पर harmless निकली, जो useful और थोड़ा surprising result है, और यह precise बात कहता है: क्योंकि कोई decision नहीं बदला, structural tokens अपनी positions penalty द्वारा subtract किए गए amount से ज़्यादा margin से जीत रहे होंगे, पाँच और छह बार आने के बाद भी। CTRL penalty, जो इसके बजाय divide करता है, उन्हें dislodge करता है, और उसने यह बनाया:

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

Alignment टूट जाता है: हर cell के अंदर padding की amount row to row बदलती है, क्योंकि closing pipe से पहले spaces की run ठीक वही repetition है जिसे penalty break करने के लिए बनी है। Cosmetic, और इसने छह extra tokens खर्च किए। Python case cosmetic नहीं है:

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,

Penalty ने model को total से — जो docstring में पहले से use था — total_sum पर धकेला, output को invented comments से pad किया ताकि unused tokens पर budget खर्च हो, और फिर stride के साथ three-argument range में चला गया। Comment कहता है हर बार 2 से incrementing, जो 1 से nn तक sum of squares के लिए wrong है। Repetition penalty ने ऐसे prompt से incorrect code produced किया जिसका answer उसके बिना correct था।

इसके बाद का rule छोटा है: penalties open-ended prose के लिए हैं, और code, structured output, tabular data और schema वाली किसी भी चीज़ के लिए off होनी चाहिए। अध्याय 18 ठीक उसी दूसरी category के बारे में है।

Application का order, और यह answer क्यों बदलता है

सेक्शन का लिंक: Application का order, और यह answer क्यों बदलता है

हर real implementation इन्हें एक specific sequence में apply करता है:

penalties → temperature → top-k → top-p → sample

यह arbitrary bookkeeping नहीं है, और दो stages swap करने से सचमुच अलग distributions बनते हैं। दो measurements, दोनों factual prompt पर।

Temperature से पहले या बाद cut करना। Nucleus जिस distribution को दिया जाता है उसी पर compute होता है, और temperature उस distribution को radically बदलता है:

temperature के बाद top-p 0.9temperature से पहले top-p 0.9
T=1.0T = 1.01 token1 token
T=1.5T = 1.5353 tokens1 token
T=2.0T = 2.032,966 tokens1 token

T=2T = 2 पर वही nominal setting 32,966 या 1 का candidate set देती है, purely इस पर depend करते हुए कि कौन-सा stage पहले चलता है। अगर आपने कभी सोचा है कि एक provider पर temperature बढ़ाने से “कुछ नहीं होता” और दूसरे पर वही दो numbers output destroy कर देते हैं, तो यह table plausible answer है।

Temperature से पहले या बाद penalise करना। Penalty α\alpha subtract करके फिर TT से divide करना effective penalty α/T\alpha/T देता है; पहले divide करके फिर subtract करना α\alpha देता है। Leading token पर 1.0 presence penalty applied के साथ:

temperaturepenalise, फिर tempertemper, फिर penalise
0.599.858 %99.948 %
1.089.839 %89.839 %
2.010.783 %6.830 %

T=1T = 1 पर identical, जैसा होना ही चाहिए। T=2T = 2 पर factor 1.58 apart। “Presence penalty 1.0” penalty की well-defined amount नहीं है जब तक आपको यह भी न पता हो कि temperature कहाँ applied है, और कोई API इसे document नहीं करता।

विवरण दिखाएँ

Optional: पूरा pipeline, ऊपर वाले order में।

Sixteen lines, और इस chapter की हर चीज़ उनमें है। यह वही computation है जो widget करता है, दस fixed numbers के बजाय real logit vector पर।

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)])

Top-p line में cumsum(0) - p current token को exclude करती cumulative mass है, इसी से nucleus वह token include करता है जो threshold cross करता है, उससे ठीक पहले stop नहीं करता। इसे off by one कर दें और top_p = 0.9 silently हर दूसरी implementation की तुलना में थोड़ा tighter cut बन जाता है।

यह course के दूसरे half में उन कुछ जगहों में से एक है जहाँ Python सही language है, और वजह stylistic नहीं structural है: ऊपर की हर line को आपके हाथ में logits का full vector चाहिए, और HTTP API पर वह vector मौजूद नहीं होता। आप provider को temperature और top_p भेज सकते हैं; आप उन्हें implement नहीं कर सकते, और आप देख नहीं सकते कि उन्होंने क्या किया।

हर provider इन controls का अलग subset लेता है, अलग ranges के साथ, और बाकी को चुपचाप ignore करता है। यह abstract complaint नहीं है। कोई भी application जो model choice offer करता है उसे differences कहीं लिखने पड़ते हैं, और वह file जहाँ वह ऐसा करता है incompatibility का map है। ऐसा ही एक catalogue अपने supported नौ text sources में एक single parameter के लिए यह declare करता है:

declared temperature rangesources
0 to 1Anthropic, Google, Meta, Cerebras, PaLM
0 to 1.5Mistral
0 to 2OpenAI, DeepSeek, xAI

Word वही है; scale नहीं। “temperature of 1” एक पर unmodified distribution है और दूसरे पर maximum permitted heat, और catalogue का आधा हिस्सा वह value express नहीं कर सकता जिसे दूसरा आधा neutral-plus-a-bit मानता है। बाकी knobs उतने ही uneven हैं: OpenAI, DeepSeek और xAI entries presence और frequency penalties लेती हैं और कोई topK नहीं; Google, Meta, Cerebras और PaLM entries topK लेती हैं और कोई penalties नहीं; Anthropic topK, topP और stop sequences लेता है और कोई penalties नहीं; और नौ में exactly one — Mistral — seed लेता है। Provider द्वारा implement न किया गया parameter भेजना generally कोई error नहीं देता: request succeed होती है, knob कुछ नहीं करता, और आप conclude करते हैं कि setting का कोई effect नहीं।

और ध्यान दें ऐसी file क्या है: किसी और के API के बारे में एक claim, किसी particular day पर लिखा हुआ, जिसे बाद में कुछ verify नहीं करता। Provider के लिए 0 to 1 कहने वाला catalogue, जबकि वह अब 0 to 2 accept करता है, silently हर request cap कर देगा।

दो और controls उसी family के हैं। logprobs, जहाँ offered हो, chosen token की log-probabilities और अक्सर top few alternatives लौटाता है — इस chapter वाले distribution पर आपकी एकमात्र खिड़की, और closed model पर बने हर confidence heuristic का basis। और maximum tokens plus stop sequences probability की परवाह किए बिना generation end करते हैं: hard cap और string match। दोनों अध्याय 14 के finish_reason के रूप में surface होते हैं, जहाँ length का मतलब है आपका answer budget से mid-sentence cut हुआ, model द्वारा finished नहीं।

Seed set करें और sampling reproducible हो जाती है। यह part real है, और verify करना आसान है:

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\"."

Seed के भीतर byte-identical, seeds के बीच different, exactly as advertised। तो seed जो fix करता है वह उस sample function की last line में random draw है — distribution given होने पर कौन-सा token picked होता है।

जो यह fix नहीं करता वह distribution है। और problem वहीं है, क्योंकि आपके model द्वारा produced logits का vector mathematical object नहीं है; यह billions of floating-point additions का output है, और उन additions का order होता है।

अध्याय 2 ने यह experiment ready छोड़ा था। वही million float32 numbers, अलग groupings में summed:

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

Last line देखिए। Chunks की संख्या answer बदल देती है। यह numpy के बारे में curiosity नहीं है; यही mechanism है, क्योंकि जब inference server reduction को ज़्यादा या कम parallel units में split करता है, वह precisely यही कर रहा होता है। और server split इस पर करता है कि वह कितनी requests serve कर रहा है।

Model पर यही effect। वही prompt, वही forward pass, केवल difference यह कि batch में कितनी other requests थीं:

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

Alone run करें तो model perfectly deterministic है — twenty passes, bit तक identical। Identical prompt को unrelated requests के batch में डालें और उसके 97 % logits change हो जाते हैं। आपकी request में कुछ नहीं बदला। किसी और की request आ गई।

अब honest part, क्योंकि इसे आमतौर पर ऐसे बताया जाता है जैसे story यहीं खत्म हो। 2.5×1052.5 \times 10^{-5} का change केवल output को बदलता है अगर दो candidate tokens उसके भीतर हों। बारह prompts पर 717 generation steps में, top two logits के बीच smallest gap 2.5×1032.5 \times 10^{-3} था — perturbation से hundred times larger — और कोई step flip होने के लिए close enough नहीं था। तो इस model पर, float32 में, laptop पर, batching ने हर logit move किया और कोई token नहीं बदला।

यह favourable conditions का description है, reassurance नहीं, और उन conditions में एक change काफी है:

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."

आठ में से छह answers diverge करते हैं, और उनमें से एक badly degrade होता है। अध्याय 2 की table बताती है क्यों: bfloat16 में 7 mantissa bits हैं, इसलिए 16 logit magnitude के पास representable values 0.125 apart हैं — 16.0, फिर 16.125, फिर 16.25 — और rounding logit को 0.0625 तक move कर सकती है। Meanwhile ऊपर measured generation steps में 4.7 % का top-two gap 0.1 से below था। यही दोनों experiments के बीच पूरा difference है: float32 में perturbation closest decision से hundred times smaller था, और bfloat16 में यह same size है। Production inference 16-bit में चलता है, hardware पर fused kernels और reduction orders के साथ जिन्हें keep करने का promise कोई नहीं देता। “Numerical noise negligible है या नहीं” precision और hardware के बारे में question है, model के बारे में नहीं।

तो Chapter 9 के वादे के मुताबिक चार causes, catalogued:

अध्याय 2 का box। Sum का order उसकी value बदलता है, इसलिए reduction कैसे split हुआ इसमें कोई भी change logits बदलता है। यही substrate है; बाकी तीन order बदलने के तरीके हैं।

Dynamic batching आपकी request को strangers की requests के साथ group करता है

सेक्शन का लिंक: Dynamic batching आपकी request को strangers की requests के साथ group करता है

Continuous batching, अध्याय 13 से, inference को affordable बनाता है — और इसका मतलब है कि आपके tokens जिन matrices से गुजरते हैं उनकी shape traffic पर depend करती है। ऊपर measured: batch size बदलने से 147,321 logits moved।

अध्याय 9 के box ने पहले ही कहा था। Router प्रति token प्रति layer एक discrete choice करता है, per-expert capacity limits के अधीन जो batch पर compute होती हैं। अकेला होने पर expert 7 को जाने वाला token company में expert 12 को जाता है। यह rounding difference नहीं; weights का अलग set है।

-latest जैसी version string pointer है, और pointers repoint होते हैं। Providers fixed version identifier के नीचे serving stack भी update करते हैं। दोनों में से कोई भी उस granularity पर announce नहीं होता जिससे आप इसे अपने output बदलने से correlate कर सकें।

OpenAI का seed parameter इस बारे में उतना honest है जितना हो सकता है: यह backend configuration identify करने वाले system_fingerprint field के साथ ship करता है, और documentation कहती है कि determinism best-effort है और changed fingerprint का मतलब results differ कर सकते हैं। इसे वैसा ही पढ़िए — provider आपको बता रहा है कि ऊपर के चारों causes उसके control में हैं, आपका किसी पर control नहीं है, और वह जो एक चीज़ offer कर सकता है वह है बाद में बताना कि कुछ moved।

यहाँ सब कुछ एक knob और उसके consequences के बारे में था। एक level पीछे हटें और कठिन problem दिखती है: जिस object को हम tune कर रहे थे वह probability distribution है, और probability distributions का interface नहीं होता।

Function call का होता है। Database row का होता है। तीन required fields वाले JSON body की अपेक्षा करने वाले POST handler का होता है, और वह बाकी सब reject कर देगा। आपके system में model और हर दूसरे component के बीच एक contract बैठा है जिसके बारे में एक side promises नहीं कर सकती: model कुछ produce करेगा, उस distribution से drawn जिसे आपने shaped किया है पर fixed नहीं किया, और दूसरी side का code known type की value चाहता है वरना throw करता है।

उन दो worlds के बीच bridge parsing और retries से नहीं, इसी chapter की material से बनता है। अगर कोई token required structure तोड़ेगा, तो आप उसे sample करके hope नहीं करते — आप softmax के देखने से पहले उसका logit -\infty पर set करते हैं। Constrained decoding उसी vector पर mask है जिसे हम इस chapter भर reshape करते रहे हैं, और यह “कृपया JSON में reply करें” को request से guarantee में बदल देता है।

Chapter 18 वही contract है: tool calling, JSON Schema, structured outputs, और probabilistic one के ऊपर deterministic system safe build करने में क्या लगता है।


इस chapter की सारी measurements CPU पर Qwen/Qwen2.5-0.5B-Instruct से आती हैं, float32 unless stated, sampling को optional section में लिखे अनुसार implement करके, library को delegate नहीं करके। वे small model हैं, और specific values उसी की हैं; mechanisms नहीं। Von Platen का How to generate text with different decoding methods (Hugging Face, 2020) वह article है जिसके against यह measured है और यही material समझाने वाला अब भी best short introduction है। Determinism section के लिए: PyTorch के reproducibility notes बताते हैं कि single machine पर seed क्या fix करता है और क्या नहीं, OpenAI की seed और system_fingerprint documentation बताती है कि provider क्या promise कर सकता है और क्या नहीं, और Thinking Machines की 2025 batch-invariant kernels पर discussion सबसे साफ़ public account है कि inference-server level पर इसे fix करना possible है लेकिन free नहीं।

  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), जहाँ softmax में temperature statistical physics से आता है। Hinton, G., Vinyals, O. and Dean, J., Distilling the Knowledge in a Neural Network, arXiv:1503.02531 (2015), section 2, वह जगह है जहाँ वही parameter modern deep learning में फिर आता है — teacher की full distribution expose करने के तरीके के रूप में, जो इस chapter की sampling नहीं बल्कि Chapter 13 के soft labels हैं।

  2. Guo, C., Pleiss, G., Sun, Y. and Weinberger, K. Q. On Calibration of Modern Neural Networks. arXiv:1706.04599 (2017). इसे इस chapter के temperature से confuse न करें। Temperature scaling validation set पर single value fit करता है ताकि model का confidence उसकी accuracy से match करे; यह classifier outputs पर applied post-hoc calibration method है। Temperature sampling generator tokens कैसे draw करता है, इस पर runtime control है। Same formula, different purpose, और कोई shared value नहीं।

  3. Holtzman, A., Buys, J., Du, L., Forbes, M. and Choi, Y. The Curious Case of Neural Text Degeneration. arXiv:1904.09751 (2019). Nucleus sampling और वह measurement introduce करता है कि maximisation-based decoding ऐसा text produce करता है जिसका probability profile human text जैसा बिल्कुल नहीं होता। 2

  4. Fan, A., Lewis, M. and Dauphin, Y. Hierarchical Neural Story Generation. arXiv:1805.04833 (2018). वह paper जिसने top-k sampling को popularise किया।

  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). Section 4.1 original repetition penalty है — वही जो divide करता है।


निर्माता

David Vicente Campos

NeuraLIA Labs के संस्थापक और MyRealFood के सह-संस्थापक

मैं लेओन विश्वविद्यालय से कंप्यूटर इंजीनियर हूँ। मैंने MyRealFood की सह-स्थापना की, जहाँ CTO के रूप में मैंने वह ऐप बनाया जिसे लाखों लोग बेहतर खान-पान के लिए इस्तेमाल कर चुके हैं, और मैंने NeuraLIA Labs की स्थापना की, जहाँ मैं AI प्रोडक्ट्स बनाता हूँ। यहाँ मैं उन बातों के बारे में लिखता हूँ जो इस सफ़र में मुझे समझनी पड़ीं, उस तरह जिस तरह काश किसी ने मुझे समझाई होतीं।

लेखक के बारे में और जानें

NeuraLIA Labs द्वारा प्रकाशित।

नए पोस्ट अपने इनबॉक्स में पाएं

AI समाचार, गाइड और प्रोडक्ट अपडेट — जब भी हम कुछ उपयोगी प्रकाशित करें, एक छोटा ईमेल।

कोर्स सूची

Abstract software decision engine with branching paths, probability nodes, and glowing gates.
jev13 मिनट पढ़ें

Jev AI मॉडल गद्य के लिए नहीं, निर्णयों के लिए बना है

TypeSafe AI का Jev ध्यान खींच रहा है क्योंकि यह सॉफ्टवेयर इंटेलिजेंस को संभावना की समस्या मानता है: सही शाखा चुनें, भरोसे का स्तर जोड़ें, और जब कोड को निर्णय चाहिए तो LLM से टेक्स्ट लिखवाने पर खर्च न करें।

Abstract agent runtime sorting documents, memory blocks and pointer nodes inside a bounded context frame.
context-engineering14 मिनट पढ़ें

लॉन्ग-होराइजन AI एजेंट्स के लिए कॉन्टेक्स्ट इंजीनियरिंग

लंबे समय तक चलने वाले एजेंट सिर्फ इसलिए असफल नहीं होते कि विंडो छोटी है। वे तब असफल होते हैं जब फ़ाइलें, टूल आउटपुट और पुराना इतिहास उस काम को ही पीछे धकेल देते हैं जिसे एजेंट को पूरा करना था।

मॉडल चुनने का काम LIA पर छोड़ने के लिए तैयार हैं?

हर AI मॉडल एक ही जगह — आज ही मुफ़्त शुरू करें।