コンテンツへスキップ
17/30第17章 / 全30章

Temperature、Top-p、そして手に入らない決定性

temperature は softmax の前に logits を割ります。この事実だけで「創造性ダイヤル」という説明は崩れます。同じ greedy 呼び出しでも答えは2つ。

このページの内容

同じリクエストを同じモデルに5回送ります。同じ重み、同じ prompt、同じマシン、同じランダムシードです。変わるのは1つの数値だけです。

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項目の語彙に対するモデル自身の確率分布から正当に引かれたものです。変わった数値は temperature と呼ばれ、ほとんどのドキュメントでは創造性のダイヤルとして説明されています。そしてその説明は、この章で断言ではなく実演できる形で間違っています。

この章は、以前の3つの約束を回収する章でもあります。第4章では logit を定義しましたが、まだほとんど使っていませんでした。第2章の浮動小数点のボックスは、ある指示で終わっていました — 第17章で、同じ prompt、モデル、シードなのに異なる tokens が出る理由を問うときに、これを思い出してください第9章の mixture-of-experts のボックスは、非決定性の4つの原因のカタログを約束していました。以下で、その3つすべてが登場します。

第4章では、logit をクラスごとに1つある、正規化されていない実数値スコアとして導入しました。第8章では、言語モデルが語彙項目ごとにそれを1つ生成するようにしました。softmax はそのベクトル z\mathbf{z} を確率に変換します。

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

temperature はここに入ります — 名前は統計物理学から借りたもので、同じパラメータがボルツマン分布を低エネルギー状態へどれだけ鋭く集中させるかを制御します1 — そして 指数関数の前に logits を割ります

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

この位置が仕組みのすべてであり、なぜ他の場所ではあり得ないのかは、2行の代数で見る価値があります。仮に temperature を確率に適用しようとしたとします — 1/T1/T 倍して正規化し直すのです。すると得られるのは

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

定数は相殺されます。確率をスケールしても何も起きません。分布は変わらず戻ってきます。temperature が効果を持つのは、指数に作用するからです。指数化する前に TT で割ることは、各確率を 1/T1/T 乗するのと同じで、項目間の共通スケールではなく 比率 を変える非線形な再形成になります。

その位置から、両端の極限も追加の作業なしに従います。T0T \to 0 では最大の logit が他を引き離し、pp は最もスコアの高い単一 token に潰れます。これが greedy decoding です。TT が大きくなるにつれて、すべての zi/Tz_i/T はゼロへ向かい、すべての指数は1へ向かい、分布は語彙全体の一様分布へ平坦化します。ちょうど T=0T = 0 では式がゼロ除算になるため、すべての実装はそれを算術的な最大値として特別扱いします。下のウィジェットも同じで、T0.001T \le 0.001 では argmax に切り替わります。

名前の衝突が本当に混乱を生むので、1つ注意します。機械学習には temperature と呼ばれる別の無関係なものがあります。temperature scaling です。これは分類器の信頼度が精度に合うように、検証セットで1つの値をフィットするキャリブレーション手法です。2 式は同じですが、生成とは関係ありません。論文で「temperature」と言う場合はこちらを意味することがよくあります。この章では決してその意味では使いません。

その分布を、計算と一緒に見てみましょう。logits は固定されたもっともらしい値なので、下の本文に出てくる数値は表示内容と照合できます。

  • ␣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 の続き候補10個。temperature 1、カットなしです。␣Paris は質量の 96.90 % を持っています。最下位で logit が 2.6-2.6␣banana は 0.00 % です。temperature を0にスライドすると、1つの token だけが 100 % で残ります。2にスライドすると、␣Paris は 69.81 % へ下がり、␣banana0.17 % へ上がります。モデルが拒否した token に、読者が回したノブによって実際の確率が与えられたのです。

␣banana という数値が、議論全体を縮図にしています。temperature を上げても、モデルが持っていなかったアイデアを与えることはできません。 logits はすでに計算され、順位はすでに固定されており、temperature はその順位を完全に保ちます。どれだけ熱しても、低くスコアされた token が高くスコアされた token を追い越すことはありません。temperature が行うのは、モデル自身が作った順位の下方へ質量を再分配することだけです。高い temperature はモデルをより発明的にするのではありません。モデルが 悪いとスコアした tokens を出しやすくするのです。

実際の語彙では、これは珍しさではなく、高 temperature の出力が使い物にならない理由になります。Qwen/Qwen2.5-0.5B-Instruct で測定したものです。1回の forward pass、上の prompt で、確率質量の一定割合を蓄積するのに必要な token 数を数えています。

temperaturetop-1 probabilityentropytokens holding 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

最下段をゆっくり読んでください。T=2T = 2 では、正解が厳密に1つしかない問いに対して、32,966個の異なる tokens が上位90 %の確率質量を共有しています。これは創造空間が広がったのではありません。算術によって、A: の後の語として韓国語の助詞や C++ の識別子を有効な選択肢として扱うよう指示されたモデルです。冒頭のブロックのゴミはその直接の結果であり、モデルやライブラリのバグではありません。リクエストが求めたことそのものです。

有用な範囲は狭く、好みではなくタスクに依存します。事実質問では答えは1つの token であり、だいたい1.2を超える熱は無意味に誤りを注入します。オープンエンドな質問では、本当に良い続きが複数あり、いくらかの熱が流暢さを保ったまま多様性を買います。

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では、モデルは固有名詞と、構文解析できない文を発明しています。このモデルのこのタスクでは、「毎回同じ」と「支離滅裂」の帯域はおよそ0.6から1.1です。正直な助言は、ブログ記事の数値をコピーするのではなく、あなたの タスクで測って見つけることです。

ここまでの話の下には、明らかな疑問が隠れています。モデルに確率分布があり、最もありそうな token が1つあるなら、なぜ常にそれを取らないのでしょうか。greedy decoding は無料で、再現可能で、パラメータも不要です。

なぜなら結果がこうなるからです。

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 %

8文で、実質1文です。4-token の窓のほぼ9割が、同じ出力内で以前すでに出現していました。これは neural text degeneration であり、top-p を導入した論文で Holtzman らによって名付けられ説明されました。3 モデルが壊れているわけではありません。列の確率を最大化することが、オープンエンドなテキストに対する単純に間違った目的なのです。人間の文章は最もありそうな単語列ではありません。驚きを含み、token ごとの確率が揺れ、沈み、回復します。一方、最大確率の経路は固定点であり、一度入ると抜ける理由がありません。

それが sampling が存在する理由です。そして、見落とされがちなのは、これは普遍法則ではない ということです。第12章では、2段階の単語問題で単純な greedy decoding が24問中24問正解し、temperature 0.8の sampling は81 %まで落ちました。その後 self-consistency が6倍の tokens を使って、greedy がすでにいた場所へ戻りました。次の2つは同時に真です。

オープンエンド生成。 正しい続きは1つではないため、最もありそうなものは罠です。ループし、その87.6 %が自分自身からコピーされています。sample してください。

正解が1つのタスク。 単一の正しい続きが あります。それ以外を引くことは誤りを引くことです。第12章の100 %が81 %になった理由は、まさにこれです。sample しないでください。

本番の prompts の多くは後者なのに、前者のように設定されています。temperature がサンプルコードの値のまま放置されているからです。

完全な分布から sampling する人は実際にはいません。tail は巨大で、ナンセンスで満ちているからです。何かを切らなければなりません。古典的な答えは2つあり、すべてを決める1点で異なります。

Top-k は固定数の候補を残します。確率で並べ、先頭の kk 個を残し、残りを捨て、正規化し直します。4 Top-p は nucleus sampling とも呼ばれ、固定量の 質量 を残します。tokens を降順に取り、累積確率が pp に達したら止めます。3 形式的には、nucleus は次を満たす最小集合 VpV_p です。

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

この違いは表面的に聞こえますが、そうではありません。同じ1分の中で送る2つの prompts は、まったく異なる分布形状を持つからです。どちらも temperature 1 の同じモデルです。

Q: What is the capital of France?\nA:Once upon a time,
top-1 probability96.01 %25.39 %
tokens holding 90 % of the mass1467
top-k = 40 keeps99.61 % of the mass78.87 % of the mass
mass in ranks 2 to 403.61 %53.48 %
token at rank 40␣Av, 0.0093 %␣Dr, 0.128 %

固定された1つの kk が、正反対の2つの失敗を生みます。事実 prompt では、k=40k = 40 は合計3.6 %の価値しかない39個の tokens を入れてしまいます。ルールが証拠ではなく枠を数えるため、0.009 %の候補まで含めてゴミを通しているのです。物語 prompt では、同じ k=40k = 40 がモデルが本当に割り当てた質量の21 %を捨てます。そこにある本当の nucleus は467 tokens の幅だからです。

Top-p は、まったく1つの数値で両方の仕事をします。p=0.9p = 0.9 に設定すると、最初の prompt では1 token、2つ目では467 tokens を残します。個数を押し付けるのではなく、分布についての質問をしているからです。その適応を直接見てください。同じカット、4つの 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

temperature 1.5 で top-p 0.90: 10個の tokens のうち 3つ が残って質量を共有し、␣Paris は 91.10 % に正規化されます。今度は temperature だけを動かします。0.7では、同じ0.90でも生存者は 1つ です。そこまで狭い nucleus は、別名をまとった greedy decoding です。2.0では 5つ 残ります。カットは動いていません。その下の形が変わったのです。

このウィジェットは、名指ししておく価値のある誤解も解きます。実際に人々にお金を失わせる誤解です。確信度の高い分布では、top_p = 0.9 は「少し多様性」ではありません。greedy です。 temperature 1 で、ここでは先頭 token が 96.90 %を持ち、すでに0.9を超えています。したがって nucleus は1 token 幅で、他のものが引かれることはありません。チームは top_p を0.9に設定して、何かを緩めたと信じ、その後なぜすべての応答が同じなのかと不思議がります。

代わりに top-k を設定すると、反対側の失敗も同じくらいはっきり見えます。

  • ␣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、top-p なし。どの temperature でも5つの tokens が残ります。5つを求めたからです。示したように、temperature 1では ␣Paris の下の4候補は合計2.79 %の価値があります。0.7に下げると、同じ4つは 0.38 % です。カットは演出であり、モデルは実質 greedy です。2.0に上げると、それらは 22.54 % になります。同じ設定、同じ生存数、まったく異なる3つの挙動。そしてリクエストの中には、自分がどれを得るのかを教えてくれるものは何もありません。

似た名前で運ばれる3つの異なる仕組みがあります。それぞれ別のことを行い、その違いは測定できます。cic_i を token ii がすでに出現した回数とします。

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

一度でも出現した token から 定数 を引きます。1回出た場合も40回出た場合も、ペナルティは同じです。ダイヤルではなくスイッチです。

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

回数に比例して 引きます。4回使われた token は1回使われた token の4倍強く罰せられ、テキストが長くなるにつれて圧力は複利的に増えます。

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}

CTRL 論文に由来する元祖です。6 これは引くのではなく 割ります。負の logit を割ると 大きく なってしまうため、符号の場合分けが必要です。したがってその強さは logit の大きさに依存します。つまり同じ ρ\rho でも、同じ文の中の別の地点で異なる効き方をします。

先ほどの退化した続きに、それぞれを適用します。「変更されたステップ」は、120回の生成ステップのうち、ペナルティなしのモデルが選んだ token と異なる token を選んだ回数です。ここでの実行は上のブロックの140ステップではなく120ステップなので、ペナルティなしのベースラインは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

3つのことが分かります。presence 0.5 は120の判断のうち3つを変え、反復を4分の1減らしました。ループは一握りの tokens で支えられていました。frequency 0.5 は4倍の判断を変え、はるかに大きな効果を出しました。回数の乗数は増え続ける一方、presence の定数は増えないからです。そして広くコピーされている1.2という値の CTRL penalty は、120の判断のうち35を 書き換えました。これは軽い後押しではありません。別のモデルです。

その最後の数値が、誰も警告しない失敗への導入です。

コードは繰り返します。表は繰り返します。リストは繰り返します。構造化出力は定義上繰り返します。それが構造だからです。ペナルティは、ループにはまったモデルと、表の4行目を正しく出しているモデルを区別できません。どちらも token が再び現れたように見えるからです。

同じ3つのタスクを3通りに生成しました。

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 は3つすべてで無害でした。これは有用で少し意外な結果であり、正確なことを示しています。判断が1つも変わらなかったので、構造 token は、5回や6回出現した後でも、差し引かれたペナルティを上回ってその位置を勝ち取っていたはずです。一方、引くのではなく 割る CTRL penalty はそれらを押しのけます。結果はこうです。

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

整列が崩れます。各セル内のパディング量が行ごとに変わります。閉じるパイプの前のスペース列は、まさにペナルティが壊すために存在する種類の反復だからです。見た目の問題で、しかも6個余分な tokens を消費しました。Python の場合は見た目の問題ではありません。

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,

ペナルティはモデルを、docstring ですでに使われていた total から total_sum へ押し出し、未使用 token に予算を使うために発明したコメントで出力を水増しし、その後 stride つきの3引数 range に入りました。コメントには 毎回2ずつ増やす とありますが、これは1から nn までの平方和として間違っています。repetition penalty は、ペナルティなしなら正しく答えられた prompt から誤ったコードを生みました。

ここから従うルールは短いです。ペナルティはオープンエンドな散文のためのものであり、コード、構造化出力、表形式データ、schema を持つものではオフにすべきです。第18章はまさにその2つ目のカテゴリを扱います。

実際のすべての実装は、これらを特定の順序で適用します。

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

これは恣意的な帳簿付けではありません。2つの段階を入れ替えると、本当に異なる分布が生まれます。どちらも事実 prompt での2つの測定です。

temperature の前に切るか後に切るか。 nucleus は渡された分布に対して計算され、temperature はその分布を大きく変えます。

top-p 0.9 after temperaturetop-p 0.9 before temperature
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 では、同じ名目設定が、どの段階を先に実行するかだけで、32,966個の候補集合にも1個の候補集合にもなります。temperature を上げてもあるプロバイダーでは「何も起きず」、同じ2つの数値で別のプロバイダーでは出力が壊れるのはなぜか、と考えたことがあるなら、この表はあり得る答えです。

temperature の前にペナルティをかけるか後にかけるか。 ペナルティ α\alpha を引いてから TT で割ると、有効なペナルティは α/T\alpha/T になります。先に割ってから引くと α\alpha です。leading token に presence penalty 1.0 を適用すると、こうなります。

temperaturepenalise, then tempertemper, then penalise
0.599.858 %99.948 %
1.089.839 %89.839 %
2.010.783 %6.830 %

T=1T = 1 では必然的に同一です。T=2T = 2 では1.58倍離れます。「presence penalty 1.0」は、temperature がどこで適用されるかも知らない限り、明確に定義されたペナルティ量ではありません。そしてどの API もこれを文書化していません。

詳細を表示

任意: 上の順序でのパイプライン全体。

16行で、この章のすべてが入っています。ウィジェットが行うのと同じ計算を、10個の固定数ではなく実際の logit ベクトルに対して行っています。

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 行の cumsum(0) - p は現在の token を 除いた 累積質量です。これにより、threshold を越える token を nucleus に含め、直前で止まらないようにしています。ここを1つずらすと、top_p = 0.9 は他のすべての実装よりわずかに厳しいカットへ、静かに変わってしまいます。

コース後半で Python が正しい言語になる数少ない箇所の1つです。理由は文体ではなく構造にあります。上の各行では logits の完全なベクトルを手元に持つ必要がありますが、HTTP API 越しにはそのベクトルは存在しません。temperaturetop_p をプロバイダーに送ることはできます。しかし自分で実装することはできず、彼らが何をしたのかも見えません。

各プロバイダーは、これらの制御の異なるサブセットを、異なる範囲で受け取り、残りを黙って無視します。これは抽象的な不満ではありません。モデルの選択肢を提供するアプリケーションは、その差分をどこかに書き留めなければなりません。そしてそれを書いたファイルは非互換性の地図になります。そうしたカタログの1つが、対応する9つのテキストソースに対して、1つのパラメータについて宣言している内容はこうです。

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

単語は同じでも、尺度は同じではありません。あるところでは「temperature 1」は未変更の分布であり、別のところでは許可される最大の熱です。カタログの半分は、もう半分が中立プラス少しとして扱う値を表現できません。残りのノブも同じくらい不均一です。OpenAI、DeepSeek、xAI の項目は presence penalty と frequency penalty を受け取り、topK は受け取りません。Google、Meta、Cerebras、PaLM の項目は topK を受け取り、ペナルティは受け取りません。Anthropic は topKtopP、stop sequences を受け取り、ペナルティは受け取りません。そして9個のうち ちょうど1つ、Mistral だけがシードを受け取ります。プロバイダーが実装していないパラメータを送っても、通常はエラーになりません。リクエストは成功し、ノブは何もせず、あなたはその設定には効果がないと結論します。

そして、そのようなファイルが何であるかにも注目してください。ある特定の日に書かれ、その後それを検証するものが何もない、他人の API についての 主張 です。現在は0から2を受け付けるプロバイダーについてカタログが0から1と書いていれば、すべてのリクエストは黙って上限に丸められます。

同じ仲間に属する制御があと2つあります。提供されている場合、logprobs は選ばれた token の log-probabilities と、多くの場合上位数個の代替を返します。これはこの章が扱っている分布を覗ける唯一の窓であり、クローズドモデル上に構築されるすべての信頼度ヒューリスティックの基盤です。そして maximum tokensstop sequences は確率を参照せずに生成を終了します。ハードキャップと文字列一致です。どちらも 第14章finish_reason として表面化します。そこでは length は、回答がモデルによって完了したのではなく、予算によって文の途中で切られたことを意味します。

シードを設定すると sampling は再現可能になります。その部分は本当で、簡単に検証できます。

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

同じシード内ではバイト単位で同一、シードが違えば異なる。宣伝どおりです。つまりシードが固定するのは、あの sample 関数の最後の行にあるランダムな抽選です。分布が与えられたとき、どの token が選ばれるかです。

固定しないのは分布です。 そして問題はそこにあります。モデルが生成する logits のベクトルは数学的な対象ではありません。数十億回の浮動小数点加算の出力であり、それらには順序があります。

第2章はこの実験を用意したままにしていました。同じ100万個の float32 数値を、異なるグループ分けで足し合わせます。

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

最後の行を見てください。chunks の数が答えを変えます。 これは numpy についての珍事ではありません。仕組みそのものです。inference server が reduction をより多い、または少ない並列ユニットに分けるとき、まさにこれを行っています。そしてサーバーは、処理しているリクエスト数に応じて分けます。

モデル自体に対するその効果がこちらです。同じ prompt、同じ forward pass、違いはバッチ内にそのとき偶然何件の他のリクエストがあったかだけです。

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

単独で実行すると、モデルは完全に決定的です。20回の pass がビット単位で同一です。同じ prompt を無関係なリクエストと同じバッチに入れると、logits の97 %が変わります。あなたのリクエストは何も変わっていません。誰か別のリクエストが到着したのです。

ここで正直な話をします。これはよく、話の終わりであるかのように語られます。2.5×1052.5 \times 10^{-5} の変化が 出力 を変えるのは、2つの候補 tokens がその差の範囲内にあった場合だけです。12個の prompts にわたる717生成ステップで、上位2つの logits の最小ギャップは 2.5×1032.5 \times 10^{-3} でした。摂動の100倍大きく、反転に十分近いステップはありませんでした。したがってこのモデルでは、float32、ラップトップ上では、batching はすべての logit を動かしましたが、token は1つも変えませんでした。

これは有利な条件の記述であって、安心材料ではありません。その条件を1つ変えるだけで十分です。

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

8つの答えのうち6つが分岐し、そのうち1つは大きく劣化します。第2章の表が理由を示しています。bfloat16 は仮数部を7ビット保持するため、logit の大きさが16付近では表現可能な値は0.125刻みです。16.0、次に16.125、次に16.25です。丸めは logit を最大0.0625動かせます。一方、上で測定した生成ステップの4.7 %は、上位2つのギャップが0.1未満でした。2つの実験の違いはそれだけです。float32 では摂動が最も近い判断の100分の1であり、bfloat16 では同じ大きさです。本番 inference は16-bit で、融合 kernel と、誰も維持を約束しない reduction 順序を持つハードウェア上で動きます。「数値ノイズは無視できるか」は、モデルについての問いではなく、精度とハードウェアについての問いです。

では、第9章が約束した4つの原因をカタログ化します。

第2章のボックスです。和の順序は値を変えるため、reduction の分割方法が変わると logits が変わります。これが基盤です。他の3つは順序を変える方法です。

第13章の continuous batching は inference を手頃にする理由です。そしてそれは、あなたの tokens が通る行列の形がトラフィックに依存することを意味します。上で測定したとおり、batch size が変わったために147,321個の logits が動きました。

第9章のボックスですでに述べました。router は token ごと、layer ごとに 離散的な 選択を行い、batch 全体で計算される expert ごとの容量制限の影響を受けます。単独なら expert 7 に行った token が、他と一緒なら expert 12 に行きます。これは丸めの違いではありません。異なる重みの集合です。

-latest のようなバージョン文字列はポインタであり、ポインタは差し替えられます。プロバイダーは固定されたバージョン識別子の下で serving stack も更新します。どちらも、自分の出力変化と相関できる粒度では発表されません。

OpenAI の seed パラメータは、可能な唯一のやり方でこのことに正直です。backend configuration を識別する system_fingerprint フィールドと一緒に提供され、ドキュメントは決定性が best-effort であり、fingerprint が変われば結果が異なる可能性があると述べています。これはそのまま読んでください。プロバイダーは上の4つの原因すべてを制御し、あなたはそのどれも制御していない。そして提供できる唯一のことは、何かが動いたことを 事後に 教えることだ、と言っているのです。

ここまでのすべては、1つのノブとその帰結についてでした。1段引いて見ると、より難しい問題が現れます。私たちが調整してきた対象は確率分布であり、確率分布にはインターフェースがありません。

function call にはあります。データベース行にはあります。3つの必須フィールドを持つ JSON body を期待する POST handler にはあり、それ以外は拒否します。モデルとシステム内の他のすべてのコンポーネントの間には、一方が約束を作れない契約が座っています。モデルは、あなたが形作ったが固定してはいない分布から引かれた 何か を生成します。そして反対側のコードは既知の型の値を必要とし、そうでなければ例外を投げます。

その2つの世界の橋は、解析とリトライではなく、この章の材料から作られます。ある token が必要な構造を壊すなら、それを sample して願うのではありません。softmax が見る前に、その logit を -\infty に設定します。Constrained decoding は、この章でずっと形を変えてきた同じベクトル上の mask であり、「JSON で返信してください」を依頼から保証へ変えます。

第18章はその契約です。tool calling、JSON Schema、structured outputs、そして確率的なものの上に安全に構築できる決定的システムを作るには何が必要かを扱います。


この章のすべての測定は、特に断らない限り CPU 上の Qwen/Qwen2.5-0.5B-Instruct、float32 で行い、sampling はライブラリに委ねるのではなく任意セクションに書いた通りに実装しました。小さなモデルであり、具体的な値はそのモデルのものです。しかし仕組みはそうではありません。Von Platen の How to generate text with different decoding methods (Hugging Face, 2020) は、この記事が測定対象としている記事であり、同じ材料への最良の短い入門として今も有効です。決定性のセクションについては、PyTorch の reproducibility notes が単一マシン上でシードが何を固定し、何を固定しないかを説明しています。OpenAI の seedsystem_fingerprint のドキュメントは、プロバイダーが何を約束でき、何を約束できないかを説明しています。そして Thinking Machines の2025年の batch-invariant kernels に関する議論は、inference-server レベルでこれを修正することが可能だが無料ではない理由について、最も明快な公開説明です。

  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 が統計物理学に由来することを示しています。Hinton, G., Vinyals, O. and Dean, J., Distilling the Knowledge in a Neural Network, arXiv:1503.02531 (2015) のセクション2では、同じパラメータが現代の deep learning に再登場します。ただしそれは教師の完全な分布を露出する方法であり、この章の sampling ではなく第13章の soft labels です。

  2. Guo, C., Pleiss, G., Sun, Y. and Weinberger, K. Q. On Calibration of Modern Neural Networks. arXiv:1706.04599 (2017). これをこの章の temperature と混同しないでください。 Temperature scaling は、モデルの信頼度が精度に一致するように検証セットで単一の値をフィットします。分類器の出力に適用される事後キャリブレーション手法です。Temperature sampling は、生成器が tokens をどう引くかに対する実行時の制御です。同じ式、異なる目的、共有される値はありません。

  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 を導入し、最大化ベースの decoding が、人間のテキストとはまったく異なる確率プロファイルのテキストを生むという測定を示した論文です。 2

  4. Fan, A., Lewis, M. and Dauphin, Y. Hierarchical Neural Story Generation. arXiv:1805.04833 (2018). top-k sampling を普及させた論文です。

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

  6. 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、つまり割るほうのペナルティです。


作成者

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.
jev読了15分

Jev AIモデルは文章ではなく意思決定のために作られている

TypeSafe AIのJevが注目されているのは、ソフトウェアの知能を確率の問題として扱うからです。適切な分岐を選び、信頼度を添え、コードが必要としているのが意思決定であるときに、LLMに文章を書かせるためのコストを避けます。

Abstract legal research workspace with documents, search nodes and governance controls.
openai読了14分

OpenAIのAstra for Lawは新モデルではなく、法律AIシステム

OpenAIの法律分野での発表の本質は、新しい基盤モデルそのものではなく、その周辺にあるシステムです。ドメイン検索、信頼できるツール、権限、ベンチマーク、レビュー経路が重要になります。

Abstract agent runtime sorting documents, memory blocks and pointer nodes inside a bounded context frame.
context-engineering読了12分

Context engineering for long-horizon AI agents

Long-running agents do not fail only because the window is small. They fail when files, tool outputs and stale history crowd out the task the agent was supposed to finish.

モデル選びは、LIAにおまかせ。

すべてのAIモデルをひとつの場所で。今日から無料で。