目次を表示する

LLM完全解説:スクラッチから理解する大規模言語モデル

確率と情報理論 ── エントロピー・交差エントロピー・ソフトマックス

確率と情報理論 ── エントロピー・交差エントロピー・ソフトマックス

LLM との関係:LLM の出力は「次のトークンの確率分布」。訓練の目的関数は「交差エントロピー損失の最小化」。この章はその数学的基盤。


確率と情報理論 — ソフトマックス・交差エントロピー・Perplexity

この章で何ができるようになるか:ソフトマックス関数がなぜ確率分布を作れるのか、交差エントロピー損失がなぜ「予測の良さ」を測れるのかを数式とコードで説明できる。


確率分布:LLM の出力の正体

LLM の出力は語彙全体に対する確率分布だ。

import numpy as np

# LLM の最終層の出力(logits):生のスコア
logits = np.array([2.0, 1.0, 0.1, 5.0, 3.0])
# 語彙: ["the", "a", "is", "Tokyo", "in"]
# → "Tokyo" のスコアが最も高い

# これは確率ではない(合計が1でない、負の値もありうる)
# → ソフトマックスで確率に変換する

ソフトマックス関数:スコアを確率に変換する

$$\text{softmax}(z_i) = \frac{e^{z_i}}{\sum_{j=1}^{K} e^{z_j}}$$

def softmax(logits):
    """ロジットを確率分布に変換する"""
    # 数値安定性のために最大値を引く(結果は同じ)
    shifted = logits - np.max(logits)
    exp_vals = np.exp(shifted)
    return exp_vals / np.sum(exp_vals)

logits = np.array([2.0, 1.0, 0.1, 5.0, 3.0])
probs = softmax(logits)
# → [0.047, 0.017, 0.007, 0.936, 0.127] ではなく...
# 実際: [0.046, 0.017, 0.007, 0.843, 0.114] ← ちゃんと合計1.0

vocab = ["the", "a", "is", "Tokyo", "in"]
for word, prob in zip(vocab, probs):
    print(f"  {word}: {prob:.3f}")
# → Tokyo: 0.843(最も確率が高い → 出力される)

ソフトマックスの性質

  • 全ての出力が正(exp のおかげ)
  • 合計が 1(正規化のおかげ)
  • 大きいスコアの差がさらに強調される(exp は差を指数的に広げる)

Temperature:確率分布の「鋭さ」を制御する

def softmax_with_temperature(logits, temperature=1.0):
    """
    temperature < 1: 分布が鋭くなる(最も確率の高いトークンに集中)
    temperature > 1: 分布がなだらかになる(ランダム性が増す)
    temperature → 0: 最大確率のトークンだけが選ばれる(greedy)
    temperature → ∞: 一様分布(完全ランダム)
    """
    scaled = logits / temperature
    return softmax(scaled)

logits = np.array([2.0, 1.0, 0.1, 5.0, 3.0])

print("T=0.5 (sharp):", softmax_with_temperature(logits, 0.5))
# → Tokyo: 0.977(ほぼ確定)

print("T=1.0 (normal):", softmax_with_temperature(logits, 1.0))
# → Tokyo: 0.843

print("T=2.0 (flat):", softmax_with_temperature(logits, 2.0))
# → Tokyo: 0.494(他のトークンも選ばれやすい)

API パラメータの temperature はまさにこれ。 創造的な文章には高い temperature、事実に基づく回答には低い temperature を使う。


エントロピー:「驚き」の平均

情報理論の中核概念。確率分布の「不確実さ」を数値化する。

$$H(p) = -\sum_{i} p_i \log p_i$$

def entropy(probs):
    """確率分布のエントロピー(不確実性)"""
    # log(0) を避けるため、0 に近い値にクリップ
    probs = np.clip(probs, 1e-10, 1.0)
    return -np.sum(probs * np.log(probs))

# 確定的な分布(1つのトークンが確実)
certain = np.array([1.0, 0.0, 0.0, 0.0, 0.0])
print(entropy(certain))  # ≈ 0(不確実性なし)

# 一様分布(全トークン等確率)
uniform = np.array([0.2, 0.2, 0.2, 0.2, 0.2])
print(entropy(uniform))  # ≈ 1.61(最大の不確実性)

# LLM の出力(Tokyo が高確率)
llm_output = np.array([0.046, 0.017, 0.007, 0.843, 0.087])
print(entropy(llm_output))  # ≈ 0.61(やや不確実)

直感:「次に来る単語がほぼ確定しているとき」エントロピーは低い。「どの単語が来てもおかしくないとき」エントロピーは高い。


交差エントロピー損失:LLM の目的関数

LLM の訓練は「交差エントロピー損失を最小化する」ことに尽きる。

$$\mathcal{L} = -\sum_{i} y_i \log \hat{y}_i$$

$y_i$ は正解ラベル(one-hot)、$\hat{y}_i$ はモデルの予測確率。

def cross_entropy_loss(predicted_probs, true_label_index):
    """
    predicted_probs: モデルの出力確率分布
    true_label_index: 正解トークンのインデックス

    正解トークンに割り当てた確率が高いほど、損失が小さい
    """
    prob_of_correct = predicted_probs[true_label_index]
    return -np.log(prob_of_correct + 1e-10)

# モデルが "Tokyo" に 84% の確率を割り当てた場合
probs = np.array([0.046, 0.017, 0.007, 0.843, 0.087])
true_label = 3  # "Tokyo" のインデックス
loss = cross_entropy_loss(probs, true_label)
print(f"Loss: {loss:.4f}")  # 0.1709(低い = 良い予測)

# モデルが "the" に高い確率を割り当ててしまった場合
bad_probs = np.array([0.7, 0.1, 0.05, 0.05, 0.1])
loss_bad = cross_entropy_loss(bad_probs, true_label)
print(f"Bad Loss: {loss_bad:.4f}")  # 2.9957(高い = 悪い予測)

なぜ交差エントロピーが「良い」損失関数なのか

1. 正解の確率が 1.0 に近づくと損失が 0 に近づく(-log(1) = 0)
2. 正解の確率が 0 に近づくと損失が無限大に近づく(-log(0) → ∞)
   → 「絶対に間違えてはいけない」というペナルティが強い
3. 微分が綺麗(ソフトマックス + 交差エントロピーの勾配がシンプル)
   → 勾配降下法と相性が良い

Perplexity:LLM の「困惑度」

交差エントロピー損失を直感的に解釈する指標。

$$\text{PPL} = e^{\text{CrossEntropyLoss}}$$

def perplexity(avg_cross_entropy_loss):
    return np.exp(avg_cross_entropy_loss)

# Loss = 3.0 → PPL = 20.1(20個の中から当てずっぽうに選ぶのと同程度の困惑)
# Loss = 2.0 → PPL = 7.4
# Loss = 1.0 → PPL = 2.7(2〜3個の選択肢で迷っている程度)

Perplexity = 20 は「次のトークンを20個の候補から均一に選ぶのと同じくらい迷っている」という意味。PPL が低いほど良いモデル。

GPT-4 クラスのモデルでは一般的な英語テキストに対して PPL ≈ 5〜15 程度。


Top-k サンプリングと Top-p(Nucleus)サンプリング

確率分布からどうトークンを選ぶか。

def top_k_sampling(logits, k=50, temperature=1.0):
    """上位 k 個のトークンだけを候補にし、その中からサンプリング"""
    probs = softmax_with_temperature(logits, temperature)
    top_k_indices = np.argsort(probs)[-k:]  # 上位 k 個
    top_k_probs = probs[top_k_indices]
    top_k_probs /= top_k_probs.sum()  # 再正規化
    chosen = np.random.choice(top_k_indices, p=top_k_probs)
    return chosen

def top_p_sampling(logits, p=0.9, temperature=1.0):
    """累積確率が p に達するまでのトークンを候補にする"""
    probs = softmax_with_temperature(logits, temperature)
    sorted_indices = np.argsort(probs)[::-1]
    sorted_probs = probs[sorted_indices]
    cumulative = np.cumsum(sorted_probs)

    # 累積確率が p を超える最初のインデックス
    cutoff = np.searchsorted(cumulative, p) + 1
    top_indices = sorted_indices[:cutoff]
    top_probs = probs[top_indices]
    top_probs /= top_probs.sum()

    return np.random.choice(top_indices, p=top_probs)

Top-p の利点:Top-k は固定数の候補しか見ないが、Top-p は「確率の分布に応じて」候補数が動的に変わる。確率が集中しているとき(確信があるとき)は少数の候補、分散しているときは多数の候補を見る。


まとめ

概念LLM での役割数式
ソフトマックスロジット → 確率分布$e^{z_i} / \Sigma e^{z_j}$
Temperature出力の多様性制御logits / T
エントロピー予測の不確実性$-\Sigma p \log p$
交差エントロピー訓練の損失関数$-\Sigma y \log \hat{y}$
Perplexityモデルの「困惑度」$e^{\text{CE Loss}}$
Top-k / Top-pサンプリング戦略候補の絞り込み

次章では、これらの数学的道具を使って実際にニューラルネットワークがどう「学習」するか──パーセプトロンから誤差逆伝播法までを見ていく。