このページの目次
トークンとサンプリング
モデルは文字列を読むのではなく、トークン単位でデコードします。入力側のサブワード分割がコストの基準を決め、出力側のサンプリング戦略が創造性と決定性(決定論的性質)を決めます。そして2026年、Claudeとローカルモデルの間でこの両端の挙動はすでに分岐しています。
概要
モデルは文字列を読まず、直接文章を吐き出すこともありません。入力側ではテキストが トークン に分割され、出力側ではモデルが一度に「次に来るトークンが語彙全体で持つ確率分布」を生成し、サンプリング戦略 によって1つが選ばれ、入力の続きとして結合され、自己回帰的に1つずつ生成されていきます。この両端は普段目に見えませんが、実際の開発で頻繁に直面する3つの事象を決定します:コストが正確に見積もれるか(課金単位はトークン)、「創造性」を適切に調整できるか(サンプリング戦略)、そしてなぜ同じプロンプトでも2回の実行で結果が変わるのか(非決定性)。
さらに重要なのは、この両端に関する「常識」が2026年に分岐したことです。オープンソース/ローカルモデル は依然として temperature/top_p/top_k を用いてサンプリングを調整します(ローカルで動作する Qwen など);一方、Claude のマネージド API は Opus 4.7 からこれらのパラメータを完全に削除しました——これらを送信すると即座に 400 エラーになります。同じ概念でありながら2つの世界があり、この記事では両端のメカニズムとこの分岐について深く解説します。
入力側:テキストがトークンに変換される仕組み
サブワード分割と BPE
現代の LLM は、文字単位でも単語単位でも分割せず、サブワード (subword) 単位で分割します。最も一般的なアルゴリズムは BPE (Byte Pair Encoding) です:トレーニング時は文字から始め、「最も頻繁に出現する隣接するトークンのペア」を新しいトークンとして繰り返しマージし、語彙サイズが設定値(通常は数万〜十几万)に達するまで行います。その結果、高頻度の単語は1つのトークンになり、低頻度の単語は複数のサブワードに分解されます。
現代の実装では byte-level BPE が多く採用されています:これは Unicode 文字ではなくバイト単位で BPE を実行します。利点はOOV(未登録語)が存在しないことです——どんな入力(珍しい文字、絵文字、バイナリ列)でもバイト単位でカバーできるため、絶対に分割できないことがありません。代償として、非ASCII文字(中国語、日本語など)は1文字が複数のバイト、複数のトークンを占めることになります。
直感に反するが重要なポイント
- トークン ≠ 単語 ≠ 文字。 英語では約 1トークン ≈ 4文字です。中国語では1文字が通常1〜2+トークンになり、コード内の句読点やインデントもトークンを消費します。文字数で長さを推定すると、系統的に過小評価されます。
- チャットテンプレートもトークンを消費します。 複数ターン対話では、
roleマーカー、メッセージ区切り文字、ツール定義のスキーマなどがすべて入力トークンに含まれます——本文だけが課金対象なのではありません。 - モデル間でトークンは互換性がなく、バージョンごとにドリフトします。 Claude のトークナイザー ≠ OpenAI のトークナイザーです。
tiktokenで Claude を見積もらないでください(15〜20% 過少評価され、コードや中国語ではさらに顕著です)。同じメーカーでも、モデル世代が変われば異なります:Opus 4.7 のトークナイザーは、同じテキストを前世代の 1.0〜1.35倍のトークン数に分割する可能性があります。モデルを変更する際は新しい基準で再評価し、古い倍率を適用しないでください。 - 正確に見積もるには API を使い、サードパーティの近似器を使わないでください:
# 単一セグメントのカウント
= .
# コストの見積もり(公式価格に基づく、変動あり): input_tokens × 単価
# 2つのバージョンの差分を比較: それぞれカウントし、引き算する——エンドポイントはステートレスなので、自身で差分を取る
count_tokens はステートレスです。「いくつのトークンが変わったか」を計算するには、それぞれをカウントして引き算します。
出力側:トークン単位でのサンプリング
モデルが各ステップで出力するのは単語ではなく、語彙全体のスコアベクトル (logits) です。これを softmax にかけて確率分布に変換し、戦略に従ってサンプリングし、入力の続きとして結合して再帰的に処理します——これを自己回帰 (autoregressive) と呼びます。
flowchart LR
T["テキスト"] --> TK["トークナイザー → トークン"]
TK --> M["モデル (Transformer)"]
M --> L["logits<br/>(語彙内の各トークンごとのスコア)"]
L --> SM["softmax(logits / T)<br/>→ 確率分布"]
SM --> S["サンプリング戦略<br/>top_p / top_k / greedy"]
S --> N["次のトークン"]
N -->|入力の続きとして結合, 自己回帰| M
サンプリングのノブ(サポートされているモデルの場合)
| 戦略 | メカニズム | 効果 |
|---|---|---|
| greedy (argmax) | 各ステップで確率が最も高いトークンを取得 | 最も決定論的だが、繰り返しや劣化に陥りやすい |
temperature T | softmax 前に logits を T で除算 | T>1 で分布を平坦化→よりランダム;T<1 で尖らせる→より集中;T=0 で greedy に退化 |
| top_p (nucleus) | 「累積確率が p に達する最小のトークン集合」のみでサンプリング | 動的に裾を切り捨て、分布の形状に応じて適応 |
| top_k | 確率が最も高い k 個のトークンのみでサンプリング | 固定された切り捨て |
| repetition / presence penalty | 既に出現したトークンの重みを低下 | 反復を抑制 |
| stop sequence | 指定された文字列に到達したら停止 | 出力の境界を制御 |
実務では temperature + top_p の組み合わせ が最も一般的です:温度でランダム度を制御し、nucleus で荒唐無稽な裾を切り落とします。top_k は比較的粗い制御です。
なぜチャット LLM でビームサーチを使わないのか
古典的な機械翻訳では ビームサーチ(複数の候補パスを同時に維持し、全体として最適なものを選択)が用いられます。チャット/オープン生成ではほぼ使用されません:それは高確率だが退屈で反復的なテキスト(「ニューラルテキストの劣化」)を生成する傾向があり、多様性と自然度を犠牲にします。サンプリング(top_p/温度)の方が、より人間らしく、情報量豊富なテキストを生成します——これが2020年の nucleus sampling 論文の核心的な結論です。
なぜ T=0 でもバイト単位の完全再現が保証されないのか
多くの人が「温度 0 = 再現可能」と思っています。できません。 その理由はサンプリングではなく、ハードウェアとスケジューリングにあります:
- 浮動小数点の非結合性: GPU 上の並列加算の累算順序は固定されておらず、浮動小数点演算では
(a+b)+c ≠ a+(b+c)が成立し得ます。極めて微小な差異が argmax の臨界点で選択を反転させることがあります。 - バッチ処理の構成: リクエストがどのバッチに組み込まれるか、カーネルのスケジューリング方法などが数値に影響を与えます。
- MoE ルーティング: MoE モデル のエクスパート選択は数値に敏感です。
「決定性」はエンジニアリング上の目標(固定プロンプト、低ランダム性、微小なドリフトを受け入れる)であり、特定のサンプリングパラメータが保証するものではありません。
分岐:Claude はサンプリングを削除し、ローカルモデルは依然として使用している
Claude Opus 4.7 / 4.8 / Fable 5 世代において、temperature、top_p、top_k が削除されました——これらを送信すると即座に 400 エラーになります。これは後退ではなく、設計思想の変化です:
- 強力な推論モデルにおいて、サンプリング温度で「創造性/決定性」を調整しても、その効果は限定的かつ制御不可能です。
- Anthropic は代わりにより高レベルな2つのツールを提供しています:
- プロンプティング——決定論的にしたい場合は指示を固定し、発散的にしたい場合はプロンプト内で多様性を明示的に要求する(例:フロントエンド設計のシナリオでモデルに「まず4つの異なる方向性を提示させてから実装する」よう指示する。詳細は移行ドキュメントの design セクションを参照);
- effort (
output_config.effort)——どの程度深く考え、どのくらいのトークンを消費するかを制御する(推論と思考 を参照)。
移行のポイント:旧コードの temperature=0.7 は新モデルで削除し、意図をプロンプト + effort に変換する——temperature=0(決定論的を希望)→ effort:"low" + 指示の固定;temperature=高(発散を希望)→ プロンプト内で「複数の異なる方向性を提示せよ」と明確に記述。この移行レシピは プロンプトエンジニアリングと構造化出力 で詳しく解説されています。
しかし他方で、サンプリングは依然として存続しています。 ローカル/オープンソースモデル(llama.cpp、vLLM)はこれらのパラメータを依然として受け付けます——ローカル LLM デプロイ でも設定されています。さらにモデルカードには推奨値が記載されています:Qwen-AgentWorld の公式推奨は temperature 0.6 / top_p 0.95 / top_k 20 です。つまり、「サンプリングの削除」は Claude マネージド API の設計思想であり、業界全体の標準ではありません——どの世界に属するかによって、どのルールを使うかが決まります。
ベストプラクティス
- 正確に見積もるには
count_tokensを使い、サードパーティの近似器を使わない。tiktokenで Claude を見積もると 15〜20% 過少評価され、中国語/コードではさらに顕著です;チャットテンプレートやツールスキーマもトークンを消費します。 - モデルを変更する際はトークン基準を再計算する。 トークナイザーはバージョンごとにドリフトするため、古い倍率を適用しないでください;2つのバージョンの差分を比較する場合は、それぞれカウントして引き算する。
- 属する世界に応じてルールを使う。 ローカル/オープンソースモデルでは
temperature/top_p/top_kを通常通り調整(モデルカードの推奨値に従う);Claude マネージド API はサンプリングを削除しているため、プロンプト + effort を使用する。 - ローカルモデルではモデルカードの推奨サンプリング値を設定する。 例えば AgentWorld の推奨値 0.6 / 0.95 / 20 を使用し、デフォルト値で発散度を賭けない。
T=0でバイト単位の完全再現を期待しない。 浮動小数点の累算、バッチ処理、MoE ルーティングが変動するため;厳密な再現が必要な場合は結果をキャッシュする。
トレードオフと失敗パターン
- 文字数でトークンを推定する: 中国語/コードで深刻な過小評価 →
count_tokensを使用し、チャットテンプレート/ツールスキーマを含める。 - モデル間でトークン数や古い温度を適用する: トークナイザーとサンプリングの両方が変化する → モデル変更時は基準を再計算し、移行時にサンプリングパラメータを削除する。
T=0での再現を期待する: バイト単位の完全一致は不可能 → 固定プロンプト + 低 effort で微小なドリフトを受け入れる;厳密な再現が必要な場合は結果をキャッシュする。- ローカルモデルで推奨サンプリング値を設定しない: デフォルト温度では発散しすぎたり、硬すぎたりする可能性がある → モデルカード(例:AgentWorld の 0.6/0.95/20)に従う。
参考
- Anthropic 公式ドキュメント: Token Counting、Models API、Migration Guide(サンプリングパラメータの削除)(platform.claude.com)
- 論文: "Neural Machine Translation of Rare Words with Subword Units" (BPE, Sennrich 2016)、「The Curious Case of Neural Text Degeneration」(nucleus sampling, Holtzman 2020)
- モデルカード: Qwen-AgentWorld-35B-A3B (推奨サンプリング値)
Keywords: tokenization, subword, BPE, byte-level BPE, vocabulary, OOV, chat template, token ≠ word, count_tokens, tiktoken, tokenizer drift, logits, softmax, autoregressive, greedy, temperature, top_p, nucleus sampling, top_k, repetition penalty, stop sequence, beam search, text degeneration, 決定性, floating-point non-associativity, サンプリングパラメータの削除, effort