機械学習の分類問題で「交差エントロピー損失を最小化する」というフレーズをよく耳にします。しかし、なぜ「交差」で「エントロピー」なのでしょうか。単なるお決まりの損失関数ではなく、その背後には情報理論という美しい数学的体系があります。
天気予報を例に考えてみましょう。「明日は70%の確率で晴れ、30%の確率で雨」という予報があるとき、実際に晴れたならば、この予報はどれだけ「良い」予報だったのでしょうか。交差エントロピーは、まさにこの「予測した確率分布が実際の分布にどれだけ近いか」を定量化する指標です。
交差エントロピーを情報理論から理解すると、以下のような洞察が得られます。
- なぜ対数を使うのか: 情報量の加法性と確率の乗法性の関係
- なぜ交差エントロピーが最小化対象として適切なのか: KLダイバージェンスとの関係
- 交差エントロピーの下限は何か: エントロピーが汎化の限界を決める
- 数値計算で何に注意すべきか: log-sum-expトリックと安定な実装
本記事の内容
- 自己情報量とエントロピーの定義
- KLダイバージェンスと交差エントロピーの関係
- 分類問題への適用と損失関数としての性質
- 数値安定な実装のテクニック
- Pythonでの実装と可視化
前提知識
この記事を読む前に、以下の記事を読んでおくと理解が深まります。
- 損失関数の全体像 — 損失関数の基本的な役割
- 最尤推定 — 対数尤度との関係
- ソフトマックス関数の定義・性質・数値安定な実装 — 多クラス分類での出力関数
情報理論の基礎 — 自己情報量
「驚き」を数値化する
情報理論の出発点は、「ある事象がどれだけ情報を持つか」を定量化することです。
直感的に考えると、珍しい事象ほど多くの情報を伝えます。「太陽が東から昇った」という情報はほぼ価値がありませんが、「隕石が落ちた」という情報は大きな驚きと情報量を持ちます。
シャノンは、確率 $p$ の事象が起きたときの情報量(自己情報量, self-information)を次のように定義しました。
$$ \begin{equation} I(x) = -\log p(x) \end{equation} $$
対数の底は通常2(ビット)か $e$(ナット)を使います。機械学習では自然対数 $\ln$ を用いることが多いです。
なぜ対数なのか
なぜ確率の「負の対数」なのでしょうか。情報量が満たすべき直感的な性質から導かれます。
性質1(非負性): 情報量は非負である。$p(x) \leq 1$ より $-\log p(x) \geq 0$
性質2(確実な事象の情報量は0): $p(x) = 1$ ならば $I(x) = 0$。確実に起きることは何の情報も持たない
性質3(加法性): 独立な2つの事象の情報量は加法的である。$I(x, y) = I(x) + I(y)$
性質3が対数を選ぶ決定的な理由です。独立事象の同時確率は $p(x, y) = p(x)p(y)$ と乗法的ですが、情報量は足し算で扱いたい。$-\log(p(x)p(y)) = -\log p(x) – \log p(y)$ と、対数が乗法を加法に変換してくれるのです。
この情報量の概念を使って、確率分布全体の「平均的な情報量」を定義するのがエントロピーです。
エントロピー
定義と直感
離散確率分布 $p = (p_1, p_2, \ldots, p_K)$ のエントロピー(entropy)は、自己情報量の期待値として定義されます。
$$ \begin{equation} H(p) = -\sum_{k=1}^K p_k \ln p_k \end{equation} $$
エントロピーは不確実性の度合いを表します。
- 確実な分布(例: $p = (1, 0, 0)$)のエントロピーは0。結果がわかっているので不確実性はない
- 一様分布(例: $p = (1/3, 1/3, 1/3)$)のエントロピーは最大。どの結果になるか最も予測しにくい
エントロピーの性質
エントロピーは以下の重要な性質を持ちます。
非負性: $H(p) \geq 0$。等号は確定的な分布のときのみ成立
最大値: $K$ 個の状態の場合、$H(p) \leq \ln K$。等号は一様分布 $p_k = 1/K$ のとき成立
この最大値の性質は、ラグランジュの未定乗数法で示せます。$\sum_k p_k = 1$ の制約のもとで $H(p)$ を最大化すると
$$ \frac{\partial}{\partial p_k}\left[-\sum_j p_j \ln p_j – \lambda\left(\sum_j p_j – 1\right)\right] = -\ln p_k – 1 – \lambda = 0 $$
全ての $k$ について同じ方程式なので $p_k$ はすべて等しく、制約条件より $p_k = 1/K$ が得られます。
import numpy as np
import matplotlib.pyplot as plt
# 二値分布のエントロピー
p = np.linspace(0.001, 0.999, 500)
entropy = -p * np.log(p) - (1 - p) * np.log(1 - p)
fig, axes = plt.subplots(1, 2, figsize=(14, 5.5))
# (a) 二値エントロピー関数
ax = axes[0]
ax.plot(p, entropy, "b-", linewidth=2.5)
ax.axvline(0.5, color="red", linestyle="--", linewidth=1.5, alpha=0.7)
ax.set_xlabel("$p$ (probability of class 1)", fontsize=13)
ax.set_ylabel("$H(p)$", fontsize=13)
ax.set_title("Binary Entropy Function", fontsize=14)
ax.grid(True, alpha=0.3)
ax.annotate("Maximum at p = 0.5\n(most uncertain)", xy=(0.5, np.log(2)),
xytext=(0.7, 0.5), fontsize=10,
arrowprops=dict(arrowstyle="->", color="red"), color="red")
# (b) K値分布の最大エントロピー
ax = axes[1]
K_values = np.arange(2, 21)
max_entropy = np.log(K_values)
ax.bar(K_values, max_entropy, color="steelblue", alpha=0.8, edgecolor="gray")
ax.set_xlabel("Number of classes $K$", fontsize=13)
ax.set_ylabel("Maximum entropy $\\ln K$", fontsize=13)
ax.set_title("Maximum Entropy for K Classes", fontsize=14)
ax.grid(True, alpha=0.3, axis="y")
plt.tight_layout()
plt.savefig("entropy_basics.png", dpi=150, bbox_inches="tight")
plt.show()
このグラフから、エントロピーの基本的な性質が読み取れます。
-
左図(二値エントロピー): $p = 0.5$ のとき最大値 $\ln 2 \approx 0.693$ をとり、$p = 0$ や $p = 1$ に近づくと0に減少します。コインの表裏が等確率のときが最も不確実で、偏ったコインほど予測しやすい(エントロピーが低い)ことを反映しています
-
右図(最大エントロピー): クラス数 $K$ が増えるほど最大エントロピー $\ln K$ が増加します。選択肢が多いほど不確実性の上限が高くなるという直感に合っています
エントロピーは「真の分布が持つ本質的な不確実性」を表しますが、実際にはモデルが出力する予測分布 $q$ は真の分布 $p$ と異なります。この「ずれ」を測るのが、次に導入するKLダイバージェンスと交差エントロピーです。
KLダイバージェンスと交差エントロピー
KLダイバージェンス — 2つの分布の「距離」
真の分布 $p$ と近似分布 $q$ の間の「距離」を測る指標として、KLダイバージェンス(Kullback-Leibler divergence)が定義されます。
$$ \begin{equation} D_{\text{KL}}(p \| q) = \sum_{k=1}^K p_k \ln \frac{p_k}{q_k} \end{equation} $$
KLダイバージェンスは厳密には「距離」ではありません(対称性 $D_{\text{KL}}(p \| q) \neq D_{\text{KL}}(q \| p)$ が成り立たない)が、$p$ と $q$ がどれだけ異なるかを測る尺度として機能します。
ギブスの不等式: $D_{\text{KL}}(p \| q) \geq 0$。等号は $p = q$ のときのみ成立
この不等式はイェンセンの不等式から示せます。$\ln$ は凹関数なので
$$ D_{\text{KL}}(p \| q) = -\sum_k p_k \ln \frac{q_k}{p_k} \geq -\ln\left(\sum_k p_k \cdot \frac{q_k}{p_k}\right) = -\ln\left(\sum_k q_k\right) = -\ln 1 = 0 $$
交差エントロピーの定義
KLダイバージェンスを展開すると
$$ D_{\text{KL}}(p \| q) = \sum_k p_k \ln p_k – \sum_k p_k \ln q_k = -H(p) + H(p, q) $$
ここで
$$ \begin{equation} H(p, q) = -\sum_{k=1}^K p_k \ln q_k \end{equation} $$
が交差エントロピー(cross-entropy)です。
この関係式から、交差エントロピーは次のように分解されます。
$$ H(p, q) = H(p) + D_{\text{KL}}(p \| q) $$
$H(p)$ は真の分布のエントロピーで定数です。したがって、交差エントロピー $H(p, q)$ を最小化することは KLダイバージェンス $D_{\text{KL}}(p \| q)$ を最小化することと等価です。
つまり、交差エントロピーを最小化するモデルは、予測分布 $q$ を真の分布 $p$ に最も近づけるモデルなのです。
交差エントロピーの下限
$$ H(p, q) = H(p) + D_{\text{KL}}(p \| q) \geq H(p) $$
交差エントロピーの最小値は真の分布のエントロピー $H(p)$ であり、$q = p$ のときに達成されます。
これは重要な意味を持ちます。データに本質的なノイズ(ラベルの曖昧さ)がある場合、そのエントロピーが交差エントロピー損失の理論的下限を決めます。どんなに良いモデルを作っても、この下限以下にはなれないのです。
ここまでで交差エントロピーの理論的な基盤を理解しました。次に、これが分類問題でどのように損失関数として機能するかを見ていきましょう。
分類問題への適用
二値分類の場合
二値分類で真のラベルが $y \in \{0, 1\}$、モデルの予測確率が $\hat{p} = P(Y=1|\bm{x})$ のとき、真の分布は点分布(one-hot)$p = (1-y, y)$、予測分布は $q = (1-\hat{p}, \hat{p})$ です。
交差エントロピーは
$$ H(p, q) = -[y \ln \hat{p} + (1-y) \ln(1-\hat{p})] $$
これは二値交差エントロピー損失(Binary Cross-Entropy, BCE)そのものです。
多クラス分類の場合
$K$ クラスの分類で真のラベルがone-hotベクトル $\bm{y} = (y_1, \ldots, y_K)$(正解クラス $c$ のみ $y_c = 1$)、予測確率がソフトマックス出力 $\hat{\bm{p}} = (\hat{p}_1, \ldots, \hat{p}_K)$ のとき
$$ H(\bm{y}, \hat{\bm{p}}) = -\sum_{k=1}^K y_k \ln \hat{p}_k = -\ln \hat{p}_c $$
正解クラスの予測確率の負の対数をとるだけです。$\hat{p}_c$ が大きいほど(正解クラスに高い確率を割り当てるほど)損失は小さくなります。
最尤推定との一致
交差エントロピー損失の最小化が最尤推定と一致することを確認しましょう。
$n$ 個のデータの尤度は
$$ \mathcal{L}(\bm{w}) = \prod_{i=1}^n \prod_{k=1}^K \hat{p}_k(\bm{x}_i; \bm{w})^{y_{ik}} $$
負の対数尤度は
$$ -\ln \mathcal{L}(\bm{w}) = -\sum_{i=1}^n \sum_{k=1}^K y_{ik} \ln \hat{p}_k(\bm{x}_i; \bm{w}) = \sum_{i=1}^n H(\bm{y}_i, \hat{\bm{p}}_i) $$
データ数 $n$ で割れば、交差エントロピー損失の平均になります。したがって、交差エントロピーの最小化はカテゴリカル分布の最尤推定と完全に等価です。
勾配の計算
ソフトマックス関数 $\hat{p}_k = \frac{e^{z_k}}{\sum_j e^{z_j}}$ と交差エントロピーの組み合わせの勾配を計算しましょう。
損失は $L = -\ln \hat{p}_c = -z_c + \ln \sum_j e^{z_j}$ なので、ロジット $z_k$ に関する偏微分は
$$ \frac{\partial L}{\partial z_k} = -\delta_{kc} + \frac{e^{z_k}}{\sum_j e^{z_j}} = \hat{p}_k – y_k $$
ここで $\delta_{kc}$ はクロネッカーのデルタです。
この結果は驚くほどシンプルで、予測確率と真のラベルの差が勾配になります。ソフトマックスの複雑な微分が交差エントロピーの微分と打ち消し合って、きれいな形になるのです。
これがMSEに対する交差エントロピーの最大の利点です。MSEの場合、シグモイド/ソフトマックスの微分項が残り、飽和領域で勾配が消失しますが、交差エントロピーではこの問題が起きません。
import numpy as np
import matplotlib.pyplot as plt
# ソフトマックス + 交差エントロピー vs ソフトマックス + MSE の勾配比較
z = np.linspace(-6, 6, 500)
# 正解ラベル y=1 の場合
# ソフトマックスの出力(二値の場合はシグモイド)
p = 1.0 / (1.0 + np.exp(-z))
# 交差エントロピーの勾配: dp/dz * dL/dp = p(1-p) * (-1/p) = -(1-p) → dL/dz = p - 1
grad_ce = p - 1 # y=1の場合
# MSEの勾配: dL/dz = -2(1 - p) * p * (1 - p) = -2p(1-p)^2
grad_mse = -2 * p * (1 - p) ** 2
fig, axes = plt.subplots(1, 2, figsize=(14, 5.5))
# (a) 損失関数
ax = axes[0]
loss_ce = -np.log(p + 1e-15)
loss_mse = (1 - p) ** 2
ax.plot(z, loss_ce, "b-", linewidth=2.5, label="Cross-Entropy: $-\\ln p$")
ax.plot(z, loss_mse, "r-", linewidth=2.5, label="MSE: $(1-p)^2$")
ax.set_xlabel("Logit $z$", fontsize=13)
ax.set_ylabel("Loss (for $y=1$)", fontsize=13)
ax.set_title("Loss Functions vs Logit", fontsize=14)
ax.legend(fontsize=11)
ax.grid(True, alpha=0.3)
ax.set_ylim(-0.5, 8)
# (b) 勾配
ax = axes[1]
ax.plot(z, np.abs(grad_ce), "b-", linewidth=2.5, label="|CE gradient| = $1-p$")
ax.plot(z, np.abs(grad_mse), "r-", linewidth=2.5, label="|MSE gradient| = $2p(1-p)^2$")
ax.set_xlabel("Logit $z$", fontsize=13)
ax.set_ylabel("|Gradient| (for $y=1$)", fontsize=13)
ax.set_title("Gradient Magnitude vs Logit", fontsize=14)
ax.legend(fontsize=11)
ax.grid(True, alpha=0.3)
ax.annotate("MSE gradient vanishes\nwhen $z \\ll 0$ (wrong prediction)",
xy=(-5, 0.01), xytext=(-3, 0.5), fontsize=10,
arrowprops=dict(arrowstyle="->", color="red"), color="red")
plt.tight_layout()
plt.savefig("ce_vs_mse_gradient.png", dpi=150, bbox_inches="tight")
plt.show()
このグラフから、交差エントロピーとMSEの勾配の違いが明確に読み取れます。
-
左図(損失関数): $z \ll 0$(誤分類方向)のとき、交差エントロピー(青)は線形に増加し続けるのに対し、MSE(赤)は最大値1で飽和します。交差エントロピーは誤分類に対して際限なくペナルティを課すため、強い修正信号を送ります
-
右図(勾配の大きさ): 交差エントロピーの勾配は $z \ll 0$ のとき最大値(1に近い値)をとり、間違った予測に対して最も大きな修正を行います。一方、MSEの勾配は $z \ll 0$ でも $z \gg 0$ でも0に近づきます。特に $z \ll 0$(モデルが間違った方向に確信を持つ)での勾配消失は、学習を著しく遅くします
この勾配の性質こそが、分類問題で交差エントロピーが標準的に使われる最大の理由です。
数値安定な実装
log-sum-expトリック
ソフトマックスと交差エントロピーを素朴に実装すると、$e^{z_k}$ が非常に大きくなりオーバーフローする危険があります。
ソフトマックスの安定な計算にはlog-sum-expトリックを使います。$c = \max_k z_k$ として
$$ \ln \sum_k e^{z_k} = c + \ln \sum_k e^{z_k – c} $$
$z_k – c \leq 0$ なので $e^{z_k – c} \leq 1$ となり、オーバーフローが防げます。
交差エントロピーの安定な計算
交差エントロピー $L = -z_c + \ln \sum_k e^{z_k}$ を直接ロジットから計算することで、ソフトマックスの計算を経由せず数値的に安定な実装が可能です。
import numpy as np
def softmax_stable(z):
"""数値安定なソフトマックス"""
c = np.max(z, axis=-1, keepdims=True)
exp_z = np.exp(z - c)
return exp_z / np.sum(exp_z, axis=-1, keepdims=True)
def cross_entropy_from_probs(y_onehot, probs):
"""確率からの交差エントロピー(素朴な実装)"""
# log(0) を防ぐためにクリップ
probs_clipped = np.clip(probs, 1e-15, 1 - 1e-15)
return -np.sum(y_onehot * np.log(probs_clipped), axis=-1)
def cross_entropy_from_logits(y_onehot, logits):
"""ロジットからの交差エントロピー(数値安定版)"""
# log-sum-exp トリック
c = np.max(logits, axis=-1, keepdims=True)
log_sum_exp = c.squeeze() + np.log(np.sum(np.exp(logits - c), axis=-1))
correct_logit = np.sum(y_onehot * logits, axis=-1)
return -correct_logit + log_sum_exp
# テスト
np.random.seed(42)
K = 5
n = 10
logits = np.random.randn(n, K) * 10 # 大きなロジット
# one-hotラベルの生成
y_true = np.random.randint(0, K, n)
y_onehot = np.eye(K)[y_true]
# 2つの方法で計算
probs = softmax_stable(logits)
loss_from_probs = cross_entropy_from_probs(y_onehot, probs)
loss_from_logits = cross_entropy_from_logits(y_onehot, logits)
print("Cross-entropy from probabilities:", loss_from_probs[:5])
print("Cross-entropy from logits: ", loss_from_logits[:5])
print("Max difference:", np.max(np.abs(loss_from_probs - loss_from_logits)))
# 極端なロジットでの比較
extreme_logits = np.array([[1000, 0, 0, 0, 0]])
y_extreme = np.array([[1, 0, 0, 0, 0]])
print("\n--- Extreme logits [1000, 0, 0, 0, 0] ---")
probs_extreme = softmax_stable(extreme_logits)
print("Softmax output:", probs_extreme)
print("CE from logits:", cross_entropy_from_logits(y_extreme, extreme_logits))
print("CE from probs: ", cross_entropy_from_probs(y_extreme, probs_extreme))
この実装の出力から、数値安定性の重要さが確認できます。通常のロジット値では両方の計算方法がほぼ同じ結果を返しますが、極端に大きなロジットでは、確率を経由する方法はアンダーフローの影響を受ける可能性があります。ロジットから直接計算する方法は、log-sum-expトリックにより常に安定した結果を返します。
PyTorchやTensorFlowでは CrossEntropyLoss や tf.nn.softmax_cross_entropy_with_logits がこの安定な実装を内部で使っているため、ユーザは確率ではなくロジットを渡すべきです。
交差エントロピーの幾何学的解釈
確率単体上の可視化
$K = 3$ の場合、確率分布は2次元の確率単体(probability simplex)上の点として表現できます。確率単体とは $p_1 + p_2 + p_3 = 1$, $p_k \geq 0$ を満たす領域で、正三角形として可視化できます。
交差エントロピーの等高線を確率単体上に描くと、真の分布からの「距離」の構造が見えてきます。
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon
def ternary_to_cartesian(p):
"""3クラスの確率を2次元座標に変換"""
x = p[1] + 0.5 * p[2]
y = np.sqrt(3) / 2 * p[2]
return x, y
# 確率単体上の交差エントロピー
fig, axes = plt.subplots(1, 3, figsize=(16, 5.5))
# 真の分布を3パターン
true_dists = [
np.array([1.0, 0.0, 0.0]),
np.array([0.5, 0.3, 0.2]),
np.array([1/3, 1/3, 1/3]),
]
titles = [
"True dist: (1, 0, 0)\nOne-hot label",
"True dist: (0.5, 0.3, 0.2)\nSoft label",
"True dist: (1/3, 1/3, 1/3)\nUniform"
]
for ax, p_true, title in zip(axes, true_dists, titles):
# 確率単体の三角形を描画
triangle = Polygon(
[ternary_to_cartesian([1,0,0]),
ternary_to_cartesian([0,1,0]),
ternary_to_cartesian([0,0,1])],
fill=False, edgecolor="black", linewidth=2
)
ax.add_patch(triangle)
# グリッド上で交差エントロピーを計算
resolution = 200
ce_values = np.full((resolution, resolution), np.nan)
for i in range(resolution):
for j in range(resolution):
p1 = i / (resolution - 1)
p2 = j / (resolution - 1)
p3 = 1 - p1 - p2
if p3 >= 0.001 and p1 >= 0.001 and p2 >= 0.001:
q = np.array([p1, p2, p3])
ce = -np.sum(p_true * np.log(q + 1e-15))
x, y = ternary_to_cartesian(q)
# プロット座標に変換
xi = int(x * (resolution - 1))
yi = int(y * (resolution - 1))
if 0 <= xi < resolution and 0 <= yi < resolution:
ce_values[yi, xi] = ce
# 等高線プロット
x_range = np.linspace(0, 1, resolution)
y_range = np.linspace(0, np.sqrt(3)/2, resolution)
X, Y = np.meshgrid(x_range, y_range)
levels = np.linspace(0, 4, 20)
cs = ax.contourf(X, Y, ce_values, levels=levels, cmap="RdYlBu_r", alpha=0.8)
ax.contour(X, Y, ce_values, levels=levels, colors="gray", linewidths=0.5, alpha=0.5)
# 真の分布の位置をマーク
tx, ty = ternary_to_cartesian(p_true)
ax.scatter(tx, ty, color="red", s=100, zorder=5, edgecolors="black", linewidth=1.5)
# 頂点ラベル
corners = [[1,0,0], [0,1,0], [0,0,1]]
labels = ["(1,0,0)", "(0,1,0)", "(0,0,1)"]
offsets = [(-0.08, -0.05), (0.02, -0.05), (0.02, 0.03)]
for corner, label, offset in zip(corners, labels, offsets):
cx, cy = ternary_to_cartesian(corner)
ax.annotate(label, xy=(cx, cy), xytext=(cx+offset[0], cy+offset[1]),
fontsize=8, ha="center")
ax.set_title(title, fontsize=12)
ax.set_xlim(-0.1, 1.1)
ax.set_ylim(-0.1, 1.0)
ax.set_aspect("equal")
ax.axis("off")
plt.tight_layout()
plt.savefig("cross_entropy_simplex.png", dpi=150, bbox_inches="tight")
plt.show()
この可視化から、交差エントロピーの幾何学的構造が読み取れます。
-
左図(one-hotラベル): 真の分布が頂点 $(1, 0, 0)$ にあるとき、交差エントロピーの等高線は頂点を中心に放射状に広がります。この頂点に予測分布を近づけるほど損失が下がります。分類問題のほとんどのケースはこのパターンです
-
中央図(ソフトラベル): 真の分布が $(0.5, 0.3, 0.2)$ のとき、交差エントロピーの最小点がこの確率に対応する位置にあります。ラベルスムージングや知識蒸留ではこのようなソフトなターゲット分布が使われます
-
右図(一様分布): 真の分布が一様分布のとき、三角形の中心が最小点となります。交差エントロピーの最小値はエントロピー $\ln 3 \approx 1.099$ であり、これが到達不可能な下限です
ラベルスムージングと交差エントロピー
ラベルスムージングの動機
通常の交差エントロピーでは真の分布がone-hot($y_c = 1$, 他は0)ですが、ラベルスムージング(label smoothing)ではone-hotの代わりに
$$ y_k^{\text{smooth}} = \begin{cases} 1 – \varepsilon + \frac{\varepsilon}{K} & k = c \\ \frac{\varepsilon}{K} & k \neq c \end{cases} $$
を使います。$\varepsilon$ は通常0.1程度です。
ラベルスムージングの効果を直感的に説明すると、one-hotラベルは「正解クラスに確率1を割り当てよ」という極端な要求です。これを達成するにはロジットを $\pm \infty$ に発散させる必要があり、モデルが過度に確信的になります(overconfidence)。ラベルスムージングはこれを緩和し、モデルの出力確率を適度に「柔らかく」保つ効果があります。
import numpy as np
import matplotlib.pyplot as plt
# ラベルスムージングの効果
K = 5
n_epochs = 200
lr = 0.5
def softmax(z):
c = np.max(z)
exp_z = np.exp(z - c)
return exp_z / np.sum(exp_z)
def train_with_smoothing(epsilon, n_epochs, lr):
"""ラベルスムージング付きで1サンプルの学習をシミュレート"""
z = np.zeros(K) # ロジットの初期値
true_class = 0
# スムージングされたラベル
y_smooth = np.full(K, epsilon / K)
y_smooth[true_class] = 1 - epsilon + epsilon / K
logit_history = []
prob_history = []
for epoch in range(n_epochs):
p = softmax(z)
logit_history.append(z.copy())
prob_history.append(p.copy())
# 勾配: p - y
grad = p - y_smooth
z -= lr * grad
return np.array(logit_history), np.array(prob_history)
fig, axes = plt.subplots(1, 2, figsize=(14, 5.5))
# (a) 正解クラスの確率の推移
ax = axes[0]
for eps, color, label in [(0.0, "blue", "$\\varepsilon=0$ (no smoothing)"),
(0.1, "green", "$\\varepsilon=0.1$"),
(0.3, "red", "$\\varepsilon=0.3$")]:
_, probs = train_with_smoothing(eps, n_epochs, lr)
ax.plot(probs[:, 0], color=color, linewidth=2, label=label)
ax.set_xlabel("Epoch", fontsize=12)
ax.set_ylabel("$\\hat{p}_{\\text{correct}}$", fontsize=12)
ax.set_title("Predicted Probability of Correct Class", fontsize=13)
ax.legend(fontsize=10)
ax.grid(True, alpha=0.3)
# (b) ロジットの推移
ax = axes[1]
for eps, color, label in [(0.0, "blue", "$\\varepsilon=0$"),
(0.1, "green", "$\\varepsilon=0.1$"),
(0.3, "red", "$\\varepsilon=0.3$")]:
logits, _ = train_with_smoothing(eps, n_epochs, lr)
# 正解と不正解クラスのロジット差
ax.plot(logits[:, 0] - np.mean(logits[:, 1:], axis=1),
color=color, linewidth=2, label=label)
ax.set_xlabel("Epoch", fontsize=12)
ax.set_ylabel("Logit gap (correct - avg others)", fontsize=12)
ax.set_title("Logit Difference: Correct vs Others", fontsize=13)
ax.legend(fontsize=10)
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig("label_smoothing.png", dpi=150, bbox_inches="tight")
plt.show()
このグラフから、ラベルスムージングの効果が読み取れます。
-
左図(正解クラスの確率): ラベルスムージングなし($\varepsilon=0$、青)では正解クラスの確率が1に漸近し続けます。$\varepsilon=0.1$(緑)では $1 – \varepsilon + \varepsilon/K = 0.92$ 付近に収束し、$\varepsilon=0.3$(赤)ではさらに低い値に収束します。モデルの確信度が適度に抑制されていることがわかります
-
右図(ロジット差): ラベルスムージングなしではロジット差が際限なく増大し、ソフトマックスの出力が飽和します。ラベルスムージングありではロジット差が有限値に収束するため、ソフトマックスの出力が過度に尖ることがなく、汎化性能の向上に寄与します
フォーカル損失 — クラス不均衡への対処
動機
クラス不均衡(例: 陽性1%、陰性99%)のデータでは、交差エントロピーは多数派クラスの「簡単な」サンプルからの損失で支配され、少数派クラスの学習が不十分になりがちです。
Lin et al. (2017) が提案したフォーカル損失(focal loss)は、簡単なサンプルの損失を抑制し、難しいサンプルに焦点を当てる損失関数です。
$$ \begin{equation} L_{\text{focal}}(y, \hat{p}) = -\alpha_t (1 – \hat{p}_t)^\gamma \ln \hat{p}_t \end{equation} $$
ここで $\hat{p}_t$ は正解クラスの予測確率、$\gamma \geq 0$ はフォーカルパラメータ、$\alpha_t$ はクラス重みです。
$\gamma = 0$ のとき通常の交差エントロピーに一致します。$\gamma > 0$ のとき、$(1 – \hat{p}_t)^\gamma$ が調整因子として働きます。
- $\hat{p}_t \approx 1$(正しく分類、簡単なサンプル): $(1 – \hat{p}_t)^\gamma \approx 0$ → 損失がほぼ0に抑制
- $\hat{p}_t \approx 0$(誤分類、難しいサンプル): $(1 – \hat{p}_t)^\gamma \approx 1$ → 通常の交差エントロピーに近い損失
import numpy as np
import matplotlib.pyplot as plt
p_t = np.linspace(0.001, 0.999, 500)
fig, ax = plt.subplots(figsize=(10, 6))
gammas = [0, 0.5, 1, 2, 5]
colors = plt.cm.viridis(np.linspace(0, 0.9, len(gammas)))
for gamma, color in zip(gammas, colors):
focal = -(1 - p_t) ** gamma * np.log(p_t)
label = f"$\\gamma={gamma}$" + (" (CE)" if gamma == 0 else "")
ax.plot(p_t, focal, color=color, linewidth=2.5, label=label)
ax.set_xlabel("Predicted probability of correct class $\\hat{p}_t$", fontsize=13)
ax.set_ylabel("Focal Loss", fontsize=13)
ax.set_title("Focal Loss for Different $\\gamma$ Values", fontsize=14)
ax.legend(fontsize=11)
ax.grid(True, alpha=0.3)
ax.set_ylim(-0.5, 5)
ax.annotate("Easy examples\n(loss suppressed)", xy=(0.85, 0.1), fontsize=10,
color="darkgreen")
ax.annotate("Hard examples\n(loss preserved)", xy=(0.1, 3.5), fontsize=10,
color="darkred")
plt.tight_layout()
plt.savefig("focal_loss.png", dpi=150, bbox_inches="tight")
plt.show()
このグラフから、フォーカル損失の仕組みが明確に読み取れます。
-
$\gamma = 0$(通常の交差エントロピー): $\hat{p}_t = 0.9$ でも損失は約0.1あり、既に正しく分類されたサンプルも損失に寄与しています
-
$\gamma$ が大きくなるほど: $\hat{p}_t$ が大きい領域(簡単なサンプル)の損失が急速に0に抑制されます。例えば $\gamma = 2$ では $\hat{p}_t = 0.9$ での損失は約0.001と、通常の交差エントロピーの100分の1以下です
-
$\hat{p}_t$ が小さい領域(難しいサンプル): $\gamma$ の値によらず損失はほぼ同じです。つまりフォーカル損失は難しいサンプルの情報を失わずに、簡単なサンプルの影響だけを選択的に抑制します
まとめ
本記事では、交差エントロピーを情報理論の基礎から丁寧に導出し、分類問題での役割を解説しました。
- 自己情報量 $I(x) = -\ln p(x)$ は「驚き」の度合いを表し、対数の加法性が確率の乗法性と対応する
- エントロピー $H(p)$ は分布の「平均的な不確実性」であり、一様分布で最大、確定分布で最小になる
- 交差エントロピー $H(p, q) = H(p) + D_{\text{KL}}(p \| q)$ の最小化は、KLダイバージェンスの最小化と等価であり、予測分布を真の分布に近づけるのと同じ
- 最尤推定との一致: 交差エントロピー損失の最小化はカテゴリカル分布の最尤推定と完全に等価
- ソフトマックスとの組み合わせで勾配が $\hat{p} – y$ という単純な形になり、勾配消失が起きない
- 数値安定性: log-sum-expトリックを用い、ロジットから直接計算することでオーバーフローを防ぐ
- ラベルスムージング: one-hotラベルを「柔らかく」することでモデルのoverconfidenceを抑制する
- フォーカル損失: クラス不均衡問題で簡単なサンプルの影響を抑制し、難しいサンプルに焦点を当てる
次のステップとして、以下の記事も参考にしてください。
- ソフトマックス関数の定義・性質・数値安定な実装 — 交差エントロピーと組み合わせる出力関数
- 損失関数の全体像 — 他の損失関数との比較
- 勾配降下法の基礎 — 損失関数の最適化手法