深層学習モデルは高い予測精度を実現しますが、「その予測がどれくらい信頼できるか」を定量化することは容易ではありません。予測の不確実性(Uncertainty)を理解し定量化することは、自動運転や医療診断などの安全性が求められる応用において極めて重要です。
予測の不確実性は、Aleatoric Uncertainty(データ固有の不確実性)と Epistemic Uncertainty(モデルの不確実性)の2種類に分類されます。
本記事の内容
- 2種類の不確実性の定義と直感的理解
- 数学的な定式化
- MCドロップアウトによるEpistemic Uncertaintyの推定
- Pythonでの実装と可視化
前提知識
この記事を読む前に、以下の記事を読んでおくと理解が深まります。
2種類の不確実性
Aleatoric Uncertainty(偶然的不確実性)
データそのものに内在するノイズや変動性に起因する不確実性です。
イメージとしては、同じ条件でもサイコロの目が変わるように、観測データに含まれるノイズは本質的に除去できません。データを増やしても減少しません。
例: * センサーの測定ノイズ * 画像のラベリングの曖昧さ * 対象の本質的なランダム性
Epistemic Uncertainty(認識的不確実性)
モデルの知識不足に起因する不確実性です。
イメージとしては、訓練データが少ない領域では「モデルが自信を持てない」状態です。データを増やすことで減少できます。
例: * 訓練データが存在しない入力領域 * モデルの表現力の限界 * パラメータの不確定性
数学的な定式化
ベイズ推定の枠組みで、予測の不確実性を分解します。
入力 $\bm{x}^*$ に対する予測値 $y^*$ の予測分散は、
$$ \text{Var}[y^*] = \underbrace{E_{\bm{w}}[\text{Var}[y^* | \bm{w}]]}_{\text{Aleatoric}} + \underbrace{\text{Var}_{\bm{w}}[E[y^* | \bm{w}]]}_{\text{Epistemic}} $$
この式は 全分散の法則(Law of Total Variance) に基づいています。
Aleatoric項
$$ E_{\bm{w}}[\text{Var}[y^* | \bm{w}]] $$
パラメータ $\bm{w}$ が固定された状態でのデータノイズ由来の分散の期待値です。
Epistemic項
$$ \text{Var}_{\bm{w}}[E[y^* | \bm{w}]] $$
パラメータ $\bm{w}$ 自体の不確定性に由来する予測平均の分散です。
MCドロップアウト
Gal & Ghahramani (2016) は、ドロップアウトを推論時にも適用する MCドロップアウト(Monte Carlo Dropout) が、ベイズ近似推論として解釈できることを示しました。
$T$ 回の推論で異なるドロップアウトマスクを適用し、
$$ \hat{y}_t^* = f(\bm{x}^*; \hat{\bm{w}}_t), \quad t = 1, \dots, T $$
予測平均:
$$ E[y^*] \approx \frac{1}{T}\sum_{t=1}^{T} \hat{y}_t^* $$
Epistemic Uncertainty:
$$ \text{Var}_{\text{epistemic}} \approx \frac{1}{T}\sum_{t=1}^{T}(\hat{y}_t^* – E[y^*])^2 $$
MCドロップアウトでは、各推論でランダムにニューロンが無効化されるため、異なるサブネットワークの予測を平均化していると解釈できます。予測のばらつきが大きい領域は、モデルが不確かな領域です。
Aleatoric Uncertaintyのモデル化
Aleatoric Uncertaintyは、ネットワークの出力として直接モデル化できます。予測平均 $\hat{\mu}(\bm{x})$ と予測分散 $\hat{\sigma}^2(\bm{x})$ を同時に出力するようにネットワークを設計します。
$$ \text{Network}: \bm{x} \to (\hat{\mu}, \hat{\sigma}^2) $$
損失関数は負の対数尤度:
$$ L = \frac{1}{2n}\sum_{i=1}^{n}\left[\frac{(y_i – \hat{\mu}_i)^2}{\hat{\sigma}_i^2} + \log \hat{\sigma}_i^2\right] $$
Pythonでの実装
MCドロップアウトによるEpistemic Uncertaintyの推定を実装します。
import numpy as np
import matplotlib.pyplot as plt
import torch
import torch.nn as nn
torch.manual_seed(42)
np.random.seed(42)
# --- データ生成(データの少ない領域を作る)---
n = 100
X_train = np.concatenate([
np.random.uniform(-4, -1, n // 2),
np.random.uniform(1, 4, n // 2)
])
y_train = np.sin(X_train) + np.random.randn(n) * 0.2 * (1 + np.abs(X_train) * 0.2)
X_train_t = torch.FloatTensor(X_train).unsqueeze(1)
y_train_t = torch.FloatTensor(y_train).unsqueeze(1)
# --- MCドロップアウト付きネットワーク ---
class MCDropoutNet(nn.Module):
def __init__(self, dropout_rate=0.1):
super().__init__()
self.fc1 = nn.Linear(1, 64)
self.fc2 = nn.Linear(64, 64)
self.fc3 = nn.Linear(64, 1)
self.dropout = nn.Dropout(p=dropout_rate)
def forward(self, x):
x = torch.relu(self.fc1(x))
x = self.dropout(x) # ドロップアウト
x = torch.relu(self.fc2(x))
x = self.dropout(x) # ドロップアウト
return self.fc3(x)
# --- 学習 ---
model = MCDropoutNet(dropout_rate=0.1)
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)
criterion = nn.MSELoss()
for epoch in range(1000):
model.train()
optimizer.zero_grad()
output = model(X_train_t)
loss = criterion(output, y_train_t)
loss.backward()
optimizer.step()
# --- MC Dropout推論 ---
X_test = np.linspace(-6, 6, 300)
X_test_t = torch.FloatTensor(X_test).unsqueeze(1)
T = 100 # MCサンプル数
predictions = []
model.train() # ドロップアウトをアクティブに保つ
with torch.no_grad():
for _ in range(T):
pred = model(X_test_t).numpy().ravel()
predictions.append(pred)
predictions = np.array(predictions) # (T, n_test)
# 予測統計量
mean_pred = predictions.mean(axis=0)
std_pred = predictions.std(axis=0) # Epistemic Uncertainty
# データノイズの推定(Aleatoric Uncertaintyの近似)
aleatoric = 0.2 * (1 + np.abs(X_test) * 0.2)
# Total Uncertainty
total_std = np.sqrt(std_pred**2 + aleatoric**2)
# --- 可視化 ---
fig, axes = plt.subplots(1, 3, figsize=(18, 5))
# Epistemic Uncertainty
ax1 = axes[0]
ax1.scatter(X_train, y_train, c='black', s=10, alpha=0.3, label='Data')
ax1.plot(X_test, mean_pred, 'b-', label='Mean prediction')
ax1.fill_between(X_test, mean_pred - 2*std_pred, mean_pred + 2*std_pred,
alpha=0.3, color='blue', label='Epistemic (2$\\sigma$)')
ax1.set_xlabel('x')
ax1.set_ylabel('y')
ax1.set_title('Epistemic Uncertainty (MC Dropout)')
ax1.legend(fontsize=8)
ax1.grid(True, alpha=0.3)
# Aleatoric Uncertainty
ax2 = axes[1]
ax2.scatter(X_train, y_train, c='black', s=10, alpha=0.3, label='Data')
ax2.plot(X_test, np.sin(X_test), 'k--', alpha=0.5, label='True function')
ax2.fill_between(X_test, np.sin(X_test) - 2*aleatoric,
np.sin(X_test) + 2*aleatoric,
alpha=0.3, color='orange', label='Aleatoric (2$\\sigma$)')
ax2.set_xlabel('x')
ax2.set_ylabel('y')
ax2.set_title('Aleatoric Uncertainty (Data Noise)')
ax2.legend(fontsize=8)
ax2.grid(True, alpha=0.3)
# 不確実性の比較
ax3 = axes[2]
ax3.plot(X_test, std_pred, 'b-', label='Epistemic')
ax3.plot(X_test, aleatoric, 'r-', label='Aleatoric')
ax3.plot(X_test, total_std, 'k--', label='Total')
ax3.axvspan(-1, 1, alpha=0.1, color='gray', label='No training data')
ax3.set_xlabel('x')
ax3.set_ylabel('Standard Deviation')
ax3.set_title('Uncertainty Comparison')
ax3.legend()
ax3.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
print(f"データなし領域のEpistemic Uncertainty: {std_pred[100:200].mean():.4f}")
print(f"データあり領域のEpistemic Uncertainty: {std_pred[:50].mean():.4f}")
Epistemic Uncertainty(青帯)は訓練データがない $[-1, 1]$ の領域で大きくなり、データが豊富な領域では小さくなることが確認できます。一方、Aleatoric Uncertainty(オレンジ帯)はデータのノイズ特性に依存し、データ量に関係なく一定です。
まとめ
本記事では、深層学習における予測の不確実性について解説しました。
- 予測の不確実性はAleatoric(データ固有)とEpistemic(モデルの知識不足)の2種類に分類される
- 全分散の法則により、全体の分散 = Aleatoric + Epistemic と分解できる
- MCドロップアウトは推論時にドロップアウトを適用することでEpistemic Uncertaintyを近似的に推定する
- Aleatoric Uncertaintyはネットワークの出力として直接モデル化可能
次のステップとして、以下の記事も参考にしてください。