Ves al contingut
17/30Capítol 17 de 30

Temperature, top-p i el determinisme que no tens

Temperature divideix els logits abans del softmax: això desmunta la idea del dial de creativitat i explica per què el mateix prompt pot variar.

En aquesta pàgina

Aquí tens la mateixa petició enviada al mateix model cinc vegades. Els mateixos pesos, el mateix prompt, la mateixa màquina, la mateixa llavor aleatòria. L’únic que canvia és un número.

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

No s’ha trencat res. Cada token de l’última línia s’ha extret legítimament de la distribució de probabilitat pròpia del model sobre el seu vocabulari de 151.936 entrades. El número que ha canviat s’anomena temperature, en la majoria de documentació es descriu com un dial de creativitat, i aquesta descripció és errònia d’una manera que aquest capítol pot demostrar en lloc d’afirmar.

Aquest també és el capítol en què es compleixen tres promeses anteriors. El capítol 4 va definir el logit i gairebé no el va gastar. La caixa de coma flotant del capítol 2 acabava amb una instrucció: recorda això quan el capítol 17 pregunti per què el mateix prompt, model i seed poden produir tokens diferents. I la caixa de mixture-of-experts del capítol 9 prometia un catàleg de quatre causes de no-determinisme. Totes tres arriben a continuació.

El capítol 4 va introduir el logit com una puntuació real no normalitzada, una per classe. El capítol 8 va fer que un model de llenguatge en produís una per cada entrada del vocabulari. El softmax converteix aquest vector z\mathbf{z} en probabilitats:

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

La temperature entra aquí —el nom es pren de la física estadística, on el mateix paràmetre controla com de fortament una distribució de Boltzmann es concentra en els seus estats de baixa energia1— i divideix els logits abans de l’exponencial:

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

Aquesta ubicació és tot el mecanisme, i val la pena veure amb dues línies d’àlgebra per què no podria ser enlloc més. Suposa que intentessis aplicar la temperature a les probabilitats —escalar-les per 1/T1/T i renormalitzar. Obtindries

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

La constant es cancel·la. Escalar probabilitats no fa absolutament res; la distribució torna sense canvis. La temperature només té efecte perquè actua sobre l’exponent, on dividir per TT abans d’exponenciar equival a elevar cada probabilitat a la potència 1/T1/T: una remodelació no lineal que canvia les ràtios entre entrades, no pas la seva escala comuna.

D’aquesta ubicació se’n deriven tots dos límits sense cap feina addicional. Quan T0T \to 0, el logit més gran s’allunya de la resta i pp col·lapsa sobre l’únic token amb la puntuació més alta: greedy decoding. Quan TT creix, cada zi/Tz_i/T tendeix cap a zero, cada exponencial tendeix cap a 1, i la distribució s’aplana cap a una uniforme sobre tot el vocabulari. Exactament a T=0T = 0, la fórmula divideix per zero, així que cada implementació ho tracta com un cas especial i fa servir el màxim aritmètic —inclòs el giny de sota, que canvia a argmax a T0.001T \le 0.001.

Un avís, perquè la col·lisió de noms provoca confusió real. Hi ha una segona cosa, no relacionada, que s’anomena temperature en machine learning: temperature scaling, un mètode de calibratge que ajusta un valor en un conjunt de validació perquè la confiança d’un classificador coincideixi amb la seva precisió.2 Mateixa fórmula, res a veure amb la generació. Quan els articles diuen "temperature", sovint volen dir aquesta altra; aquest capítol no ho fa mai.

Aquí tens aquesta distribució, amb l’aritmètica al davant. Els logits són fixos i plausibles, així que els números del text de sota es poden comprovar amb el que veus:

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

10 de 10 tokens sobreviuen al tall i es reparteixen la probabilitat.

Mostra les dades en una taula
TokenlogitDesprés de la temperaturaDesprés del tall
␣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%
Mostreig: temperatura, top-p i top-k

Deu continuacions candidates de The capital of France is, a temperature 1 sense cap retall. ␣Paris conserva 96,90 % de la massa; ␣banana, al fons amb un logit de 2.6-2.6, rep 0,00 %. Mou la temperature a 0 i un sol token sobreviu amb el 100 %. Mou-la a 2 i ␣Paris cau fins al 69,81 %, mentre ␣banana puja fins a 0,17 %: el token rebutjat pel model, dotat de probabilitat real per un botó que el lector ha girat.

El número ␣banana és tot l’argument en miniatura: apujar la temperature no pot donar al model una idea que no tenia. Els logits ja estan calculats, el rànquing ja està fixat, i la temperature el preserva exactament: cap quantitat de calor no mou mai un token amb puntuació inferior per sobre d’un token amb puntuació superior. L’únic que fa és redistribuir massa cap avall en el rànquing que el mateix model ha produït. Una temperature alta no fa que un model sigui més inventiu; fa que sigui més probable que emeti els tokens que ell mateix ha puntuat com a dolents.

En un vocabulari real, això deixa de ser una curiositat i es converteix en la raó per la qual la sortida amb temperature alta és inutilitzable. Mesurat a Qwen/Qwen2.5-0.5B-Instruct, una passada forward, el prompt anterior, comptant quants tokens calen per acumular una part determinada de la massa de probabilitat:

temperatureprobabilitat top-1entropiatokens que contenen el 80 %90 %95 %99 %
0,599,98 %0,00 nats1111
0,799,65 %0,03 nats1111
1,096,01 %0,30 nats11114
1,288,20 %0,88 nats1213252
1,562,83 %3,07 nats293532.67226.787
2,016,62 %8,19 nats13.51632.96655.231101.205

Llegeix l’última fila a poc a poc. A T=2T = 2, en una pregunta amb exactament una resposta correcta, 32.966 tokens diferents comparteixen el 90 % superior de la massa de probabilitat. Això no és un espai creatiu més ampli. És un model al qual l’aritmètica ha dit que tracti una partícula coreana i un identificador de C++ com a opcions vives per a la paraula després de A:. La brossa del bloc inicial n’és la conseqüència directa, i no és un bug del model ni de la biblioteca: és el que la petició ha demanat.

El rang útil és estret i depèn de la tasca més que del gust. En una pregunta factual, la resposta és un token i qualsevol calor per sobre d’aproximadament 1,2 injecta error de franc. En una de final obert, realment hi ha més d’una bona continuació, i una mica de calor compra varietat que continua sent fluida:

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

A 1,3, el model ha inventat un nom propi i una frase que no s’analitza bé. La franja entre "idèntic cada vegada" i "incoherent" és aproximadament de 0,6 a 1,1 per a aquest model en aquesta tasca, i el consell honest és que la trobis mesurant sobre la teva tasca, no copiant un número d’un article de blog.

Hi ha una pregunta òbvia amagada sota tot això: si el model té una distribució de probabilitat i un token és el més probable, per què no prendre’l sempre? El greedy decoding és gratuït, reproduïble i no necessita paràmetres.

Perquè el resultat és això:

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 %

Vuit frases, una frase. Gairebé nou de cada deu finestres de quatre tokens ja havien aparegut abans en la mateixa sortida. Això és degeneració de text neural, anomenada i explicada per Holtzman et al. a l’article que va introduir top-p.3 El model no està trencat; maximitzar la probabilitat de seqüència és simplement l’objectiu equivocat per al text de final obert. L’escriptura humana no és la seqüència de paraules més probable: porta sorpresa, amb una probabilitat per token que vaga, baixa i es recupera, mentre que el camí de màxima probabilitat és un punt fix que, un cop s’hi entra, no té cap motiu per sortir-ne.

Per això existeix el mostreig. També és, i aquesta és la part que se sol ometre, no una llei universal. El capítol 12 va mesurar 24 de 24 correctes en problemes verbals de dos passos amb greedy decoding pla, i el mostreig a temperature 0,8 ho va fer baixar al 81 %; després, self-consistency va gastar sis vegades més tokens per tornar on el greedy ja era. Totes dues coses són certes alhora:

Generació de final obert. No hi ha una única continuació correcta, així que la més probable és una trampa: entra en bucle, i el 87,6 % se’n copia d’ella mateixa. Mostreja.

Tasques amb una única resposta correcta. Hi ha una única continuació correcta, així que extreure qualsevol altra cosa és extreure un error. El 100 % del capítol 12 es va convertir en 81 % exactament per això. No mostregis.

La majoria de prompts de producció són del segon tipus i es configuren com si fossin del primer, perquè la temperature s’ha deixat en el valor que feia servir el codi d’exemple.

Dues maneres de retallar, i només una s’adapta

Enllaç a la secció: Dues maneres de retallar, i només una s’adapta

Mostrejar de la distribució completa no és el que fa realment ningú, perquè la cua és enorme i plena de sense-sentit. Alguna cosa s’ha de retallar. Hi ha dues respostes clàssiques i difereixen en un aspecte que ho decideix tot.

Top-k conserva un nombre fix de candidats. Ordena per probabilitat, conserva els primers kk, descarta la resta i renormalitza.4 Top-p, també anomenat mostreig de nucli, conserva una quantitat fixa de massa: pren tokens en ordre descendent fins que la seva probabilitat acumulada arriba a pp, i s’atura.3 Formalment, el nucli és el conjunt més petit VpV_p amb

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

La diferència sona cosmètica i no ho és, perquè els dos prompts que envies en el mateix minut tenen formes de distribució completament diferents. Tots dos són el mateix model a temperature 1:

Q: What is the capital of France?\nA:Once upon a time,
probabilitat top-196,01 %25,39 %
tokens que contenen el 90 % de la massa1467
top-k = 40 conserva99,61 % de la massa78,87 % de la massa
massa en rangs 2 a 403,61 %53,48 %
token al rang 40␣Av, 0,0093 %␣Dr, 0,128 %

Un kk fix, dos fracassos en direccions oposades. En el prompt factual, k=40k = 40 admet 39 tokens que, entre tots, valen un 3,6 %: deixa passar porqueria, inclòs un candidat a nou mil·lèsimes d’un percentatge, perquè la regla compta llocs i no evidència. En el prompt narratiu, el mateix k=40k = 40 llença el 21 % de la massa que el model havia assignat de debò, perquè el nucli real allà té 467 tokens d’amplada.

Top-p fa que exactament un número faci totes dues feines. Defineix p=0.9p = 0.9 i conserva 1 token en el primer prompt i 467 en el segon, perquè fa una pregunta sobre la distribució en lloc d’imposar-li un recompte. Observa aquesta adaptació directament: el mateix tall, quatre temperatures:

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

3 de 10 tokens sobreviuen al tall i es reparteixen la probabilitat.

Mostra les dades en una taula
TokenlogitDesprés de la temperaturaDesprés del tall
␣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%
Mostreig: temperatura, top-p i top-k

Top-p a 0,90 amb la temperature a 1,5: tres dels deu tokens sobreviuen i comparteixen la massa, ␣Paris renormalitzat al 91,10 %. Ara mou només la temperature. A 0,7, el mateix 0,90 deixa un supervivent: un nucli tan estret és greedy decoding amb un altre nom. A 2,0 en deixa cinc. El tall no s’ha mogut; la forma que hi ha a sota sí.

Aquest giny també resol una idea equivocada que val la pena anomenar, perquè costa diners reals. En una distribució confiada, top_p = 0.9 no és "una mica de varietat". És greedy. A temperature 1, el token principal aquí conté el 96,90 %, que ja és per sobre de 0,9, així que el nucli té un sol token d’amplada i mai no es pot extreure res més. Els equips posen top_p a 0,9 creient que han afluixat alguna cosa i després es pregunten per què cada resposta és idèntica.

Configura top-k en lloc d’això i el fracàs oposat és igual de visible:

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

5 de 10 tokens sobreviuen al tall i es reparteixen la probabilitat.

Mostra les dades en una taula
TokenlogitDesprés de la temperaturaDesprés del tall
␣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%
Mostreig: temperatura, top-p i top-k

Top-k a 5, sense top-p. Cinc tokens sobreviuen a cada temperature, perquè cinc és el que s’ha demanat. A temperature 1, com es mostra, els quatre candidats sota ␣Paris valen 2,79 % entre tots. Baixa a 0,7 i els mateixos quatre valen 0,38 %: el tall és teatre, i el model és efectivament greedy. Puja a 2,0 i valen 22,54 %. Mateixa configuració, mateix nombre de supervivents, tres comportaments completament diferents, i res en la petició et diu quin obtindràs.

Les penalitzacions, amb les fórmules, perquè confondre-les és endèmic

Enllaç a la secció: Les penalitzacions, amb les fórmules, perquè confondre-les és endèmic

Tres mecanismes diferents viatgen sota noms semblants, fan coses diferents, i la diferència és mesurable. Sigui cic_i el nombre de vegades que el token ii ja ha aparegut.

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

Resta una constant a qualsevol token que hagi aparegut com a mínim una vegada. Aparèixer una vegada i aparèixer quaranta vegades es penalitzen de manera idèntica. És un interruptor, no un dial.

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

Resta en proporció al recompte. Un token usat quatre vegades es penalitza quatre vegades més fort que un token usat una vegada, i la pressió es compon a mesura que el text creix.

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}

L’original, del paper CTRL.7 Divideix en lloc de restar, amb el cas del signe necessari perquè dividir un logit negatiu el faria més gran. Per tant, la seva força depèn de la magnitud del logit, cosa que vol dir que el mateix ρ\rho colpeja diferent en punts diferents de la mateixa frase.

La mateixa continuació degenerada d’abans, amb cadascuna aplicada. "Passos alterats" compta quants dels 120 passos de generació han triat un token diferent del que hauria triat el model no penalitzat. La tirada aquí és de 120 passos contra 140 en el bloc anterior, i per això la línia base no penalitzada diu 85,5 % en lloc de 87,6 %:

configuració4-grams repetitspassos alterats
res85,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

En surten tres coses. Presence a 0,5 va canviar tres decisions de 120 i va reduir la repetició en una quarta part: el bucle s’aguantava per un grapat de tokens. Frequency a 0,5 va canviar quatre vegades més decisions amb un efecte molt més gran, perquè el multiplicador de recompte continua creixent mentre que la constant de presence no. I la penalització CTRL al valor tan copiat d’1,2 va reescriure 35 de 120 decisions, cosa que no és una empenta suau; és un model diferent.

Aquest últim número prepara el fracàs del qual ningú no t’avisa.

Què fan les penalitzacions al text que se suposa que s’ha de repetir

Enllaç a la secció: Què fan les penalitzacions al text que se suposa que s’ha de repetir

El codi repeteix. Les taules repeteixen. Les llistes repeteixen. La sortida estructurada repeteix per definició: això és l’estructura. Una penalització no pot distingir entre un model atrapat en un bucle i un model que emet correctament la quarta fila d’una taula, perquè totes dues coses semblen un token que torna a aparèixer.

Les mateixes tres tasques, generades de tres maneres:

tascaresfrequency 0,5repetition 1,2
taula markdown, 6 files0 / 56 passos alterats0 / 562 / 62
funció Python0 / 930 / 9310 / 110
llista amb vinyetes, 1 a 120 / 500 / 500 / 50

La frequency penalty a 0,5 va resultar ser inofensiva en totes tres, cosa que és un resultat útil i una mica sorprenent, i diu una cosa precisa: com que cap decisió no va canviar, els tokens estructurals devien estar guanyant les seves posicions per més del que restava la penalització, fins i tot després d’aparèixer cinc i sis vegades. La penalització CTRL, que divideix en canvi, sí que els desplaça, i això és el que va produir:

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

L’alineació es desfà: la quantitat de farciment dins de cada cel·la canvia de fila a fila, perquè la seqüència d’espais abans de la barra vertical de tancament és exactament el tipus de repetició que la penalització existeix per trencar. Cosmètic, i va costar sis tokens extra. El cas de Python no és cosmètic:

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,

La penalització va empènyer el model fora de total —ja usat al docstring— cap a total_sum, va farcir la sortida amb comentaris inventats per gastar el pressupost en tokens no usats, i després va entrar en un range de tres arguments amb un pas. El comentari diu incrementing by 2 each time, que és fals per a una suma de quadrats d’1 a nn. Una repetition penalty va produir codi incorrecte a partir d’un prompt que s’havia respost correctament sense ella.

La regla que se’n deriva és curta: les penalitzacions són per a prosa de final obert, i haurien d’estar desactivades per a codi, sortida estructurada, dades tabulars i qualsevol cosa amb un schema. El capítol 18 tracta exactament d’aquesta segona categoria.

L’ordre d’aplicació, i per què canvia la resposta

Enllaç a la secció: L’ordre d’aplicació, i per què canvia la resposta

Cada implementació real les aplica en una seqüència concreta:

penalitzacions → temperature → top-k → top-p → mostreig

Això no és comptabilitat arbitrària, i intercanviar dues etapes produeix distribucions genuïnament diferents. Dues mesures, totes dues sobre el prompt factual.

Retallar abans o després de la temperature. El nucli es calcula sobre qualsevol distribució que rep, i la temperature canvia aquesta distribució radicalment:

top-p 0,9 després de temperaturetop-p 0,9 abans de temperature
T=1.0T = 1.01 token1 token
T=1.5T = 1.5353 tokens1 token
T=2.0T = 2.032.966 tokens1 token

A T=2T = 2, la mateixa configuració nominal dona un conjunt candidat de 32.966 o d’1, depenent només de quina etapa s’executa primer. Si alguna vegada t’has preguntat per què apujar la temperature "no fa res" en un proveïdor i destrossa la sortida en un altre amb els mateixos dos números, aquesta taula és una resposta plausible.

Penalitzar abans o després de la temperature. Restar una penalització α\alpha i després dividir per TT dona una penalització efectiva de α/T\alpha/T; dividir primer i després restar dona α\alpha. Amb una presence penalty d’1,0 aplicada al token principal:

temperaturepenalitza, després temperatempera, després penalitza
0,599,858 %99,948 %
1,089,839 %89,839 %
2,010,783 %6,830 %

Idèntiques a T=1T = 1, com han de ser. Separades per un factor d’1,58 a T=2T = 2. "Presence penalty 1.0" no és una quantitat de penalització ben definida llevat que també sàpigues on s’aplica la temperature, i cap API ho documenta.

Mostra els detalls

Opcional: tota la pipeline, en l’ordre anterior.

Setze línies, i tot aquest capítol hi és. És el mateix càlcul que fa el giny, sobre un vector de logit real en lloc de deu números fixos.

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

El cumsum(0) - p de la línia de top-p és la massa acumulada excloent el token actual, que és el que fa que el nucli inclogui el token que creua el llindar en lloc d’aturar-se just abans. Equivoca’t aquí per un i top_p = 0.9 es converteix silenciosament en un tall una mica més estricte que el de qualsevol altra implementació.

Aquest és un dels pocs llocs de la segona meitat del curs on Python és el llenguatge adequat, i la raó és estructural més que estilística: cada línia anterior necessita tenir a les mans tot el vector de logits, i sobre una API HTTP aquest vector no existeix. Pots enviar temperature i top_p a un proveïdor; no els pots implementar, i no pots veure què han fet.

Cada proveïdor accepta un subconjunt diferent d’aquests controls, amb rangs diferents, i ignora la resta en silenci. Això no és una queixa abstracta. Qualsevol aplicació que ofereixi una tria de model ha d’escriure les diferències en algun lloc, i el fitxer on ho fa és un mapa de la incompatibilitat. Això és el que un catàleg d’aquest tipus declara per a un sol paràmetre a través de les nou fonts de text que admet:

rang de temperature declaratfonts
0 a 1Anthropic, Google, Meta, Cerebras, PaLM
0 a 1,5Mistral
0 a 2OpenAI, DeepSeek, xAI

La paraula és la mateixa; l’escala no. Una "temperature de 1" és la distribució no modificada en un cas i la calor màxima permesa en un altre, i mig catàleg no pot expressar el valor que l’altra meitat tracta com a neutral-més-una-mica. La resta de botons són igual de desiguals: les entrades d’OpenAI, DeepSeek i xAI accepten presence i frequency penalties i cap topK; les entrades de Google, Meta, Cerebras i PaLM accepten topK i cap penalització; Anthropic accepta topK, topP i seqüències de parada i cap penalització; i exactament una de les nou —Mistral— accepta una seed. Enviar un paràmetre que un proveïdor no implementa normalment no produeix cap error: la petició té èxit, el botó no fa res, i conclous que la configuració no té cap efecte.

I fixa’t què és un fitxer així: una afirmació sobre l’API d’algú altre, escrita un dia concret, que després res no verifica. Un catàleg que diu 0 a 1 per a un proveïdor que ara accepta 0 a 2 limitarà silenciosament cada petició.

Dos controls més pertanyen a la mateixa família. logprobs, quan s’ofereix, retorna les log-probabilitats del token triat i sovint les poques alternatives principals: l’única finestra que tens sobre la distribució de què tracta aquest capítol, i la base de cada heurística de confiança construïda sobre un model tancat. I maximum tokens més seqüències de parada acaben la generació sense referència a la probabilitat: un límit dur i una coincidència de cadena. Tots dos apareixen com el finish_reason del capítol 14, on length vol dir que la teva resposta s’ha tallat a mitja frase per un pressupost, no que el model l’hagi acabada.

Defineix una seed i el mostreig esdevé reproduïble. Aquesta part és real, i és fàcil de verificar:

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

Idèntic byte per byte dins d’una seed, diferent entre seeds, exactament com s’anuncia. Així que el que fixa la seed és l’extracció aleatòria de l’última línia d’aquella funció sample: quin token es tria donada una distribució.

El que no fixa és la distribució. I aquí és on hi ha el problema, perquè el vector de logits que produeix el teu model no és un objecte matemàtic; és la sortida de milers de milions de sumes de coma flotant, i aquestes tenen un ordre.

El capítol 2 va deixar aquest experiment preparat. El mateix milió de números float32, sumats en agrupacions diferents:

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

Mira l’última línia. El nombre de chunks canvia la resposta. Això no és una curiositat sobre numpy; és el mecanisme, perquè quan un servidor d’inferència divideix una reducció entre més o menys unitats paral·leles, està fent exactament això. I un servidor divideix segons quantes peticions està servint.

Aquí tens aquest efecte sobre el model mateix. El mateix prompt, la mateixa passada forward, l’única diferència és quantes altres peticions hi havia al batch en aquell moment:

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

Executat sol, el model és perfectament determinista: vint passades, idèntiques fins al bit. Posa el prompt idèntic en un batch amb peticions no relacionades i el 97 % dels seus logits canvien. No ha canviat res de la teva petició. Ha arribat la petició d’algú altre.

Ara la part honesta, perquè això normalment s’explica com si fos el final de la història. Un canvi de 2.5×1052.5 \times 10^{-5} només altera la sortida si dos tokens candidats estaven dins d’aquest marge. En 717 passos de generació a través de dotze prompts, l’escletxa més petita entre els dos logits principals va ser 2.5×1032.5 \times 10^{-3} —cent vegades més gran que la pertorbació— i cap pas no era prou proper per capgirar-se. Així que en aquest model, en float32, en un portàtil, el batching va moure cada logit i no va canviar cap token.

Això és una descripció de condicions favorables, no una tranquil·lització, i n’hi ha prou amb un canvi d’aquestes condicions:

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

Sis de vuit respostes divergeixen, i una es degrada molt. La taula del capítol 2 diu per què: bfloat16 conserva 7 bits de mantissa, així que a prop d’una magnitud de logit de 16, els valors representables estan separats per 0,125 —16,0, després 16,125, després 16,25— i l’arrodoniment pot moure un logit fins a 0,0625. Mentrestant, el 4,7 % dels passos de generació mesurats abans tenien una escletxa entre els dos principals per sota de 0,1. Aquesta és tota la diferència entre els dos experiments: en float32 la pertorbació era cent vegades més petita que la decisió més propera, i en bfloat16 és de la mateixa mida. La inferència de producció s’executa en 16 bits, en maquinari amb kernels fusionats i ordres de reducció que ningú no promet mantenir. Si "el soroll numèric és negligible" és una pregunta sobre precisió i maquinari, no sobre el model.

Així doncs, les quatre causes, catalogades com prometia el capítol 9:

La caixa del capítol 2. L’ordre d’una suma canvia el seu valor, així que qualsevol canvi en com es divideix una reducció canvia els logits. Aquest és el substrat; les altres tres són maneres de canviar l’ordre.

El batching dinàmic agrupa la teva petició amb les d’estranys

Enllaç a la secció: El batching dinàmic agrupa la teva petició amb les d’estranys

El batching continu, del capítol 13, és per què la inferència és assequible, i vol dir que la forma de les matrius per on flueixen els teus tokens depèn del trànsit. Mesurat abans: 147.321 logits es van moure perquè va canviar la mida del batch.

El routing de mixture-of-experts depèn del batch

Enllaç a la secció: El routing de mixture-of-experts depèn del batch

La caixa del capítol 9 ja ho deia. El router fa una tria discreta per token i per capa, subjecta a límits de capacitat per expert calculats sobre el batch. Un token que sol hauria anat a l’expert 7 va a l’expert 12 en companyia. Això no és una diferència d’arrodoniment; és un conjunt de pesos diferent.

Una cadena de versió com -latest és un punter, i els punters es reorienten. Els proveïdors també actualitzen la pila de servei sota un identificador de versió fix. Cap de les dues coses s’anuncia amb la granularitat que et permetria correlacionar-la amb el canvi de la teva sortida.

El paràmetre seed d’OpenAI és honest sobre això de l’única manera que pot ser-ho: s’envia juntament amb un camp system_fingerprint que identifica la configuració del backend, i la documentació afirma que el determinisme és best-effort i que un fingerprint canviat vol dir que els resultats poden diferir. Llegeix-ho pel que és: un proveïdor dient-te que controla les quatre causes anteriors, que tu no en controles cap, i que l’única cosa que pot oferir és dir-te després del fet que alguna cosa s’ha mogut.

Tot això ha anat sobre un botó i les seves conseqüències. Fes un pas enrere i apareix el problema més difícil: l’objecte que hem estat ajustant és una distribució de probabilitat, i les distribucions de probabilitat no tenen interfície.

Una function call en té. Una fila de base de dades en té. Un handler POST que espera un cos JSON amb tres camps obligatoris en té, i rebutjarà qualsevol altra cosa. Entre el model i qualsevol altre component del teu sistema hi ha un contracte sobre el qual una banda no pot fer promeses: el model produirà alguna cosa, extreta d’una distribució que has modelat però no fixat, i el codi de l’altra banda necessita un valor d’un tipus conegut o llença un error.

El pont entre aquests dos mons es construeix amb el material d’aquest capítol, no pas amb parsing i reintents. Si un token trencaria l’estructura requerida, no el mostreges i esperes: poses el seu logit a -\infty abans que el softmax el vegi. La descodificació constrained és una màscara sobre el mateix vector que hem estat remodelant tot aquest capítol, i converteix "si us plau, respon en JSON" d’una petició en una garantia.

El capítol 18 és aquest contracte: tool calling, JSON Schema, sortides estructurades, i què cal per fer que un sistema determinista sigui segur de construir damunt d’un de probabilístic.


Totes les mesures d’aquest capítol venen de Qwen/Qwen2.5-0.5B-Instruct en CPU, float32 llevat que s’indiqui el contrari, amb el mostreig implementat tal com s’ha escrit a la secció opcional en lloc de delegar-lo a una biblioteca. Són d’un model petit, i els valors concrets són seus; els mecanismes no. How to generate text with different decoding methods, de Von Platen (Hugging Face, 2020), és l’article amb què es contrasta aquest i continua sent la millor introducció breu al mateix material. Per a la secció de determinisme: les notes de reproductibilitat de PyTorch descriuen què fixa i què no fixa una seed en una sola màquina, la documentació d’OpenAI de seed i system_fingerprint descriu què pot prometre i què no pot prometre un proveïdor, i la discussió de Thinking Machines del 2025 sobre kernels invariants al batch és el relat públic més clar de per què arreglar-ho al nivell del servidor d’inferència és possible però no és gratuït.

  1. Ackley, D. H., Hinton, G. E. i Sejnowski, T. J. A Learning Algorithm for Boltzmann Machines. Cognitive Science 9(1), pp. 147–169 (1985), on la temperature en un softmax ve de la física estadística. Hinton, G., Vinyals, O. i Dean, J., Distilling the Knowledge in a Neural Network, arXiv:1503.02531 (2015), secció 2, és on el mateix paràmetre reapareix en deep learning modern: com una manera d’exposar la distribució completa d’un professor, que són les soft labels del capítol 13 i no pas el mostreig d’aquest capítol.

  2. Guo, C., Pleiss, G., Sun, Y. i Weinberger, K. Q. On Calibration of Modern Neural Networks. arXiv:1706.04599 (2017). No ho confonguis amb la temperature d’aquest capítol. Temperature scaling ajusta un únic valor en un conjunt de validació perquè la confiança del model coincideixi amb la seva precisió; és un mètode de calibratge post-hoc aplicat a les sortides d’un classificador. Temperature sampling és un control en temps d’execució sobre com un generador extreu tokens. Mateixa fórmula, propòsit diferent i cap valor compartit.

  3. Holtzman, A., Buys, J., Du, L., Forbes, M. i Choi, Y. The Curious Case of Neural Text Degeneration. arXiv:1904.09751 (2019). Introdueix el mostreig de nucli i la mesura que el decoding basat en maximització produeix text amb un perfil de probabilitat que no s’assembla gens al text humà. 2

  4. Fan, A., Lewis, M. i Dauphin, Y. Hierarchical Neural Story Generation. arXiv:1805.04833 (2018). L’article que va popularitzar el mostreig top-k.

  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. i Cotterell, R. Locally Typical Sampling. arXiv:2202.00666 (2022).

  7. Keskar, N. S., McCann, B., Varshney, L. R., Xiong, C. i Socher, R. CTRL: A Conditional Transformer Language Model for Controllable Generation. arXiv:1909.05858 (2019). La secció 4.1 és la repetition penalty original: la que divideix.

A punt per deixar que triï LIA?

Crea amb tots els models d'IA en un sol lloc — comença gratis avui mateix.