交差エントロピー
交差エントロピー $H(p,q)$ は、真の分布 $p$ に従うデータを、分布 $q$ に最適化した符号で符号化したときに必要な平均ビット数です。
$q$ の情報量(符号長)$-\log_2 q(x)$ を、真の確率 $p(x)$ で重み付けして平均をとったものになります。
期待値を使って以下のように定義できます。
\[H(p,q) = \mathbb{E}_{x \sim p}[-\log_2 q(x)]\]$p$ を固定したとき、$H(p,q)\ge H(p)$ が成り立ち、等号は $p=q$ のときに限ります。
- $p=q$ なら、割り当てた符号長がその事象の真の分布と合っているので、平均符号長は最小の $H(p)$ になります($H(p,q)=H(p)$)
- $p \neq q$ だと、符号長の割り当てが実際の起こりやすさと乖離しているため平均符号長は $H(p)$ より大きくなります。その差が KLダイバージェンス($D_{\text{KL}}(p\parallel q)$)です
交差エントロピー $H(p,q)$ は KLダイバージェンス($D_{\text{KL}}(p\parallel q)$)を用いて以下のように表すことができます。
\[\begin{aligned} H(p, q) = H(p) + D_{\text{KL}}(p \parallel q) \\ D_{\text{KL}}(p \parallel q) = H(p,q) - H(p) \geq 0 \end{aligned}\]$p=q$ の場合は KLダイバージェンスが $0$ になり、交差エントロピーは $p$ 自身のエントロピーに一致します。
\[p=q \Rightarrow D_{\text{KL}}(p\parallel q) = 0,\qquad H(p,q) = H(p) + 0 = H(p)\]NOTE
$H(p)$ 自体は一般に $0$ ではありません($H(p)=0$ になるのは one-hot のような決定的な分布の場合だけです)。$p=q$ で $0$ になるのは $D_{\text{KL}}$ の方です。
$q(x)=0$ の場合
$p(x)>0$ なのに $q(x)=0$ となる $x$ があると、$-\log_2 q(x)=\infty$ なので $H(p,q)=\infty$($D_{\text{KL}}=\infty$)になります。
「$q$ が起こりうる事象に確率 $0$ を割り当てる」ことに対する強いペナルティであり、後述のコードで微小値 $10^{-7}$ を足している理由にも関係します。
連続確率変数の場合
連続版は微分エントロピーを含むため、$H(p,q)$ 自体は負の値を取り得ます。一方、KLダイバージェンス $D_{\text{KL}}(p\parallel q)\ge0$ は連続でも成り立ちます。
交差エントロピー誤差(損失関数・誤差関数)
交差エントロピーは、2 つの確率分布の乖離を評価できるため、深層学習 の分類問題の損失(誤差)関数(Loss Function)として使用されます(誤差関数を強調する意味で交差エントロピー誤差(Cross-Entropy Error / Cross-Entropy Loss)と呼ぶことが多いようです)。
- $p(x)$(現実の分布):正解データ(正解ラベル)の分布
- $q(x)$(モデルの分布):モデルが出力した予測(Softmax 関数を通した確率)の分布
NOTE
深層学習 の実装(PyTorchなど)では、情報理論で一般的な底が $2$ の対数($\log_2$)ではなく、自然対数($\log_e$ または $\ln$)が使われます。
本節「交差エントロピー誤差」以降の式は、底を明示せず $\log$ と書き、自然対数を想定します(底を変えても $H(p,q)=H(p)+D_{\text{KL}}(p\parallel q)$ などの関係は、すべての項で同じ底を使えば成り立ちます)。
コンテキスト長 $C$ 内の「ある 1 つの位置(トークン位置)$t$」に注目したときの、深層学習 のモデルの分布と正解データの関係性は以下の通りです。
1. モデルの出力(予測確率分布: $q_t$)
各位置 $t$ において、モデルは次に続くトークンの予測として、語彙数(Vocabulary Size: $V$)と同じ次元数を持つ確率分布を出力します。
\[q_t = \bigl(q_t(1),\ q_t(2),\ \dots,\ q_t(V)\bigr), \quad \sum_{i=1}^V q_t(i) = 1\]2. 正解データ(one-hotベクトル: $p_t$)
正解データ(次に実際に来た単語)を、語彙数 $V$ の次元を持つ one-hotベクトル(正解の単語のインデックスだけが 1 で、他はすべて 0 のベクトル)として表現します。
3. 交差エントロピー誤差($L_t$)の計算
\[L_t = - \sum_{i=1}^{V} p_t(i) \log q_t(i)\]正解の one-hotベクトル $p_t$ は正解ラベル($k_t$ 番目)以外すべて $0$ になるため、正解トークンの項だけが残ります。
\[L_t = - \log q_t(k_t)\]4. コンテキスト(系列長 C )全体の損失
各 $t$ の交差エントロピー誤差を算出して平均をとります。
\[L_{C} = - \frac{1}{C} \sum_{t=1}^{C} \log q_t(k_t)\]系列長 $C$ 全体の尤度 $\prod_t q_t(k_t)$ の対数を取り符号を反転させたもの(負の対数尤度、NLL)は次のとおりです。
\[-\log \prod_{t=1}^{C} q_t(k_t) = -\sum_{t=1}^{C} \log q_t(k_t)\]$L_C$ はこの負の対数尤度を $C$ で割った(トークンあたりの平均にした)ものです。値は $C$ 倍ずれますが、最小化する対象としては同じです。
ref. 系列長 C の尤度関数 / 対数尤度関数 と損失関数
5. 全体(バッチとコンテキスト)での損失の集約
LLM の学習は、入力データ(形状 $N\times C$ のトークン ID)のバッチサイズ $N$ とコンテキスト長 $C$ について損失を一括で計算します。
- すべての位置での計算: バッチ内の各サンプル $n$($1$ から $N$)の、各トークン位置 $t$($1$ から $C$)のすべてにおいて、上記の一連の交差エントロピー損失 $L_{n, t}$ を計算します。
- 平均値の算出: すべての位置の損失の平均値をとり、全体の総損失(Total Loss)とします。
つまり総損失(Total Loss)を交差エントロピー誤差の平均値と定義します。
\[\text{Total Loss} = \frac{1}{N \times C} \sum_{n=1}^{N} \sum_{t=1}^{C} L_{n, t} = \frac{1}{N \times C} \sum_{n=1}^{N} \sum_{t=1}^{C} -\log q_{n,t}(k_{n,t})\]($q_{n,t}$:サンプル $n$ の位置 $t$ でのモデルの予測分布、$k_{n,t}$:その位置の正解トークンのインデックス)
この全体の平均損失(Total Loss)が小さくなるように、バックプロパゲーション(誤差逆伝播法)で Transformer 内部のパラメータ(重み)に関する勾配を計算し、その勾配を使って SGD や Adam などの最適化手法がパラメータを更新していきます。
モンテカルロ積分によるクロスエントロピーの近似
クロスエントロピー $H(p,q) = - \int p(x) \log q(x) dx$ を解析的に求めることが困難な場合、モンテカルロ積分を用いて近似します(この節は底を限定せず、$\log_2$ でも $\ln$ でも同じ底を使えば成り立ちます)。
モンテカルロ積分では、真の分布 $p(x)$ による期待値を、真の分布から独立に抽出されたデータ $D={x_{1},\ldots,x_{n}}$(ただし $x_i \sim p(x)$)の標本平均に置き換えます。
\[H(p,q) \approx - \frac{1}{n} \sum_{i=1}^n \log q(x_i)\]データ数 $n$ で割ることで、サンプルの平均(期待値の近似値)を求めます。
$n$ が大きいほど真の値に近づくのは 大数の法則 によるもので、$-\log q(x)$ の期待値が存在することが前提です。
対数の性質を利用すると、以下のように変形することもできます。
\[\begin{aligned} H(p,q) &\approx - \frac{1}{n} \sum_{i=1}^n \log q(x_i) \\ &= - \frac{1}{n} \log \prod_{i=1}^n q(x_i) \end{aligned}\]LLM の損失との関係
上の式は、$q$ が 1 つの固定された分布である場合の書き方です。LLM では、各位置 $t$ の予測分布が文脈 $c=x_{<t}$ によって変わります($q_t = q(\cdot\mid c)$)。そのため、サンプルを「文脈と正解トークンのペア」$(c, x)$ として、$q(x\mid c)$ を使って書き直します。
近似の対象
近似したいのは、文脈 $c$ ごとの交差エントロピーを、文脈の分布 $p(c)$ で平均したものです。
\[\mathbb{E}_{(c,x)\sim p}\bigl[-\log q(x\mid c)\bigr] = \mathbb{E}_{c\sim p(c)}\Bigl[\, H\bigl(p(\cdot\mid c),\ q(\cdot\mid c)\bigr) \Bigr]\]- 内側:文脈 $c$ を固定したときの交差エントロピー $H\bigl(p(\cdot\mid c),\ q(\cdot\mid c)\bigr)=\mathbb{E}_{x\sim p(\cdot\mid c)}[-\log q(x\mid c)]$
- 外側:文脈が $p(c)$ に従って変わることによる平均
標本平均による近似
真の分布 $p$(データ)から $n$ 個のペア $(c_i, x_i)\sim p$ を取り出し、標本平均で置き換えます。
\[\mathbb{E}_{(c,x)\sim p}\bigl[-\log q(x\mid c)\bigr] \;\approx\; -\frac{1}{n}\sum_{i=1}^{n}\log q(x_i\mid c_i)\]サンプルごとに文脈 $c_i$ が異なるため、$q(\cdot\mid c_i)$ もサンプルごとに異なります。同じ文脈で $q$ を何度計算しても値は変わらないので、繰り返すのはモデルの予測ではなく、データからのサンプリングです。
LLM の損失との対応
| モンテカルロの式 | LLM の損失 |
|---|---|
| サンプル数 $n$ | $N\times C$(バッチ内の全トークン位置) |
| サンプル $(c_i, x_i)$ | 文脈 $x_{<t}$ と正解トークン $k_{n,t}$ のペア |
| $q(x_i\mid c_i)$ | $q_{n,t}(k_{n,t})$ |
1 系列だけを見た $L_C=-\frac{1}{C}\sum_t\log q_t(k_t)$ も、同じ近似を $C$ 個のサンプルで行ったものです。
one-hot の $p_t$ との関係
各文脈 $c$ について、本来の交差エントロピーは $\mathbb{E}_{x\sim p(\cdot\mid c)}[-\log q(x\mid c)]$ ですが、データにはその文脈の次のトークンが 1 つしか付いていません。one-hot の $p_t$ は、この 1 サンプルの経験分布とみなせます。
\[L_t = -\log q_t(k_t)\]1 サンプルではブレが大きいため、多数の文脈にわたって平均することで、全体の期待値に近づけています。
注意点
- 2 段階の近似になっています。ミニバッチの平均は訓練データ全体の平均損失の推定値であり、訓練データ全体の平均は真の分布 $p$ に対する期待値の推定値です
- 同じ系列内の位置 $t$ どうしは独立ではないため、厳密な i.i.d. サンプルではありません。実用上は、多数の系列にわたる平均として近似的に扱います
- 前節の $-\frac{1}{n}\log\prod_i q(x_i)$ という積の形は、i.i.d. を仮定した固定分布 $q$ の場合の書き方です。LLM で系列全体の尤度を考えるときは、連鎖律による $\prod_t q(x_t\mid x_{
Python コード
\[L = -\sum_{k=1}^{V} t_k \log y_k\]($t_k$:正解(one-hot)、$y_k$:モデルの予測確率。本文の $p_t(i)$, $q_t(i)$ に対応します。)
import numpy as np
def cross_entropy_error(y, t):
if y.ndim == 1:
t = t.reshape(1, t.size)
y = y.reshape(1, y.size)
# 正解データがone-hot-vectorの場合、正解ラベルのインデックスに変換
if t.size == y.size:
t = t.argmax(axis=1)
batch_size = y.shape[0]
# np.log(0) は RuntimeWarning とともに -inf を返します(計算自体は止まりません)。
# 損失が inf になると、その後の勾配が nan になって学習が壊れてしまいます。
# それを防ぐために、ごく小さな値(1e-7)を足して、log の引数が 0 にならないようにしています。
return -np.sum(np.log(y[np.arange(batch_size), t] + 1e-7)) / batch_size
出典:ゼロから作るDeep Learning ❷ ―自然言語処理編 斎藤康毅 (著)
使用上の注意
- 上記のコードは
yの形状が(バッチサイズ, クラス数)の 2 次元、かつ softmax 後の確率であることを前提にしています - LLM の出力(形状
(N, C, V))を渡すには、(N*C, V)にフラット化し、正解も(N*C,)にして節 5 の $\frac{1}{N\times C}$ の平均と対応させます - PyTorch の
F.cross_entropyは softmax 後の確率ではなく logits を受け取り、内部で数値的に安定な log-softmax を行います。softmax を二重にかけないよう注意してください
import torch.nn.functional as F
# logits: (N, C, V), targets: (N, C)
loss = F.cross_entropy(logits.view(-1, logits.size(-1)), targets.view(-1))