🔬 深度学习中的不确定性测量详解
Uncertainty Measurement in Deep Learning
🧠 一、什么是不确定性(Uncertainty)?
在深度学习中,不确定性是指模型对其预测结果的“信心程度”。一个模型不仅要输出预测,还要知道自己有多确定这个预测是对的。特别在如下任务中尤为重要:
-
图像分类(Image Classification)
-
图像分割(Semantic Segmentation)
-
医疗影像分析、自动驾驶、金融风险评估等关键应用场景
简单理解:预测是“猫”的概率是 0.95,模型很有信心;如果是 0.51,那说明模型不太确定,存在不确定性。
🎯 二、不确定性的分类
不确定性主要分为以下三种类型:
1. Predictive Uncertainty(预测不确定性)
-
指模型最终对预测结果整体的不确定性。
-
是以下两种不确定性的叠加:
Predictive Uncertainty=Aleatoric+Epistemic\text{Predictive Uncertainty} = \text{Aleatoric} + \text{Epistemic}
2. Aleatoric Uncertainty(数据不确定性)
-
来源:输入数据中固有的噪声或模糊性
-
示例:
-
医学图像模糊不清,无法判断器官边界
-
拍摄条件差,图像中信息不足
-
-
特点:
-
无法通过增加数据或改善模型来完全消除
-
可以通过最大概率或**熵(entropy)**来度量
-
3. Epistemic Uncertainty(模型不确定性)
-
来源:模型自身的未知性或知识盲区
-
示例:
-
模型训练集中从未见过某类输入
-
模型容量不足或过拟合
-
-
特点:
-
可以通过增加训练数据、优化模型结构来减小
-
常用MC Dropout 与 预测方差(Predictive Variance) 来估算
-
📊 三、Aleatoric 不确定性测量方法
✅ 方法一:1 - Maximum Probability(1 - MP)
-
模型的输出为一组类别概率 [p1,p2,...,pC][p_1, p_2, ..., p_C]
-
最大概率值表示模型最信心的预测:
pmax=max(p1,...,pC)p_{\text{max}} = \max(p_1, ..., p_C) -
不确定性定义为:
Uncertainty=1−pmax\text{Uncertainty} = 1 - p_{\text{max}}
🔎 理解:
-
最大概率接近 1 → 模型很有信心 → 不确定性低
-
最大概率接近 0.5 → 模型犹豫不决 → 不确定性高
✅ 方法二:熵(Entropy)
熵衡量整个概率分布的混乱程度:
H=−∑i=1Cpilog(pi)H = - \sum_{i=1}^{C} p_i \log(p_i)
-
如果模型集中在某个类别上(例如 [0.99, 0.005, 0.005]),熵很低 → 确信
-
如果模型预测均衡(如 [0.33, 0.33, 0.34]),熵很高 → 很困惑
📈 最大熵出现在所有类别概率接近均匀分布的时候。
🧠 四、Epistemic 不确定性测量方法
Epistemic 不确定性是模型对自身的预测能力没有信心,尤其是面对未知输入或训练集覆盖不足时。
✅ 核心方法:MC Dropout + 多次预测 + 方差估计
✳️ 步骤解析:
Step 1:启用 Dropout
-
正常测试阶段关闭 Dropout
-
这里需要打开 Dropout以引入模型结构的随机性
在 PyTorch 中通过
model.train()
来启用 Dropout
Step 2:对同一个输入进行 N 次前向传播(Forward Passes)
-
每次由于 Dropout 结构不同 → 预测结果略有差异
-
得到 N 个概率预测集合:
{pi1,pi2,...,piN}\{p_{i1}, p_{i2}, ..., p_{iN}\}
Step 3:计算预测方差(Predictive Variance)
对于第 ii 类别的概率,计算方差如下:
Var(pi)=1N∑k=1Npik2−(1N∑k=1Npik)2\text{Var}(p_i) = \frac{1}{N} \sum_{k=1}^{N} p_{ik}^2 - \left( \frac{1}{N} \sum_{k=1}^{N} p_{ik} \right)^2
-
第一项:平方的平均值
-
第二项:平均值的平方
方差越大 → 模型每次预测结果波动大 → 不确定性高
Step 4:所有类别平均 → 得到整体不确定性
Uncertainty=1C∑i=1CVar(pi)\text{Uncertainty} = \frac{1}{C} \sum_{i=1}^{C} \text{Var}(p_i)
最终得到每个输入样本的整体模型不确定性。
💻 五、代码实现(PyTorch)
def compute_epistemic_uncertainty(model, input_tensor, num_passes=20):
model.train() # 打开 Dropout
softmax_outputs = []
with torch.no_grad():
for _ in range(num_passes):
outputs = model(input_tensor) # 模型输出 logits
probs = F.softmax(outputs, dim=1)
softmax_outputs.append(probs.unsqueeze(0)) # shape: [1, B, C]
stacked = torch.cat(softmax_outputs, dim=0) # shape: [N, B, C]
mean_probs = stacked.mean(dim=0) # shape: [B, C]
var_probs = stacked.var(dim=0) # shape: [B, C]
epistemic_uncertainty = var_probs.mean(dim=1) # shape: [B]
return mean_probs, epistemic_uncertainty
🔁 六、Aleatoric 与 Epistemic 不确定性的对比
属性 | Aleatoric | Epistemic |
---|---|---|
来源 | 数据噪声、模糊性 | 模型对知识掌握不足 |
是否可被学习降低 | ❌ 否 | ✅ 是 |
常用方法 | 1 - MP, 熵 | MC Dropout, 预测方差 |
举例 | 图像模糊,听不清语音 | 新类别输入、训练不足 |
🧪 七、实际案例与应用场景
应用场景 | 推荐评估方法 | 原因 |
---|---|---|
医疗图像分类 | 熵 + MC Dropout | 风险高、需可信预测 |
自动驾驶 | MC Dropout | 安全性极高需求 |
文本情感分析 | 熵 | 多分类概率分布解释性强 |
零样本学习 | MC Dropout | 检测 unseen 类别更有效 |
✅ 八、总复习:核心公式与重点记忆
Aleatoric:
-
1 - MP:
Uncertainty=1−max(p1,...,pC)\text{Uncertainty} = 1 - \max(p_1, ..., p_C) -
熵:
H=−∑pilog(pi)H = - \sum p_i \log(p_i)
Epistemic:
-
预测方差:
Var(pi)=1N∑pik2−(1N∑pik)2\text{Var}(p_i) = \frac{1}{N} \sum p_{ik}^2 - \left( \frac{1}{N} \sum p_{ik} \right)^2 -
总不确定性:
Uncertainty=1C∑i=1CVar(pi)\text{Uncertainty} = \frac{1}{C} \sum_{i=1}^{C} \text{Var}(p_i)
📦 模型设置与优化配置
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.0001)
✅ 解释:
-
nn.CrossEntropyLoss()
:用于多分类任务的损失函数,衡量模型预测与真实标签的差距。 -
optim.Adam(...)
:优化器,采用自适应学习率算法,能够有效更新模型参数。 -
lr=0.0001
:设置较小学习率以确保训练稳定。
🎯 Practice 0: 混淆矩阵评估模型预测准确性
from sklearn.metrics import confusion_matrix
model.eval()
all_predictions = []
all_labels = []
with torch.no_grad():
for images, labels in tqdm(test_loader, desc="Evaluating"):
images, labels = images.to(device), labels.to(device)
outputs = model(images)
_, predicted = torch.max(outputs, 1)
all_predictions.extend(predicted.cpu().numpy())
all_labels.extend(labels.cpu().numpy())
conf_matrix = confusion_matrix(all_labels, all_predictions)
print("Confusion Matrix:")
print(conf_matrix)
✅ 分析:
-
模型设为 eval 模式,关闭 Dropout、BatchNorm 训练特性。
-
遍历测试集,记录预测结果与真实标签。
-
使用 sklearn 生成 混淆矩阵,评估每类分类效果,发现模型在哪些类别上容易混淆。
🔎 Practice 1: 查看模型输出(logits & 概率)
model.eval()
for images, labels in test_loader:
images, labels = images.to(device), labels.to(device)
outputs = model(images)
print(outputs.shape)
print(outputs)
probabilities = torch.softmax(outputs, dim=1)
print(probabilities)
break
✅ 分析:
-
查看模型对一批样本的输出:
-
outputs
是 logits,未归一化的类别得分。 -
softmax(outputs)
转换为概率分布。 -
每个样本的输出维度为
[num_classes]
,用于多分类预测。
-
📊 Practice 2: Aleatoric 不确定性评估 —— 最大概率法
# 1 - max probability
all_mp = []
with torch.no_grad():
for images, labels in tqdm(test_loader, desc="Evaluating"):
images, labels = images.to(device), labels.to(device)
outputs = model(images)
probabilities = torch.softmax(outputs, dim=1)
max_prob, _ = torch.max(probabilities, dim=1)
max_prob = 1 - max_prob
all_mp.extend(max_prob.cpu().numpy())
all_mp = np.array(all_mp)
data_uncertainty = np.mean(all_mp)
print(f"Data Uncertainty: {data_uncertainty}")
✅ 分析:
-
对每个样本,找出模型预测的最大类别概率 pmaxp_{\text{max}}。
-
计算 1−pmax1 - p_{\text{max}} 作为不确定性得分。
-
所有样本的平均值即为数据不确定性(Aleatoric)。
📈 Practice 3: Aleatoric 不确定性评估 —— 熵方法
# Entropy
all_entropy = []
with torch.no_grad():
for images, labels in tqdm(test_loader, desc="Evaluating"):
images, labels = images.to(device), labels.to(device)
outputs = model(images)
probabilities = torch.softmax(outputs, dim=1)
entropy = -torch.sum(probabilities * torch.log(probabilities + 1e-8), dim=1)
all_entropy.extend(entropy.cpu().numpy())
all_entropy = np.array(all_entropy)
data_uncertainty = np.mean(all_entropy)
print(f"Data Uncertainty: {data_uncertainty}")
✅ 分析:
-
使用信息熵公式:
H=−∑pilog(pi)H = -\sum p_i \log(p_i) -
熵越大,模型越“困惑”;熵越小,模型越“自信”。
-
这是更细腻的 Aleatoric 估计方法。
🎛️ Practice 4: 模型结构修改 —— 添加 Dropout(用于 Epistemic 不确定性)
model.fc2 = nn.Sequential(
nn.Dropout(p=0.5),
nn.Linear(in_features=84, out_features=10)
)
✅ 分析:
-
替换输出层为
Dropout + Linear
,用于支持 MC Dropout。 -
Dropout 增加模型在推理时的随机性(前提是测试阶段强制开启 Dropout)。
🧩 Practice 5: 启用 Dropout 层(仅用于推理阶段)
def enable_dropout(model):
"""Function to enable dropout during inference."""
for module in model.modules():
if module.__class__.__name__.startswith('Dropout'):
module.train()
✅ 分析:
-
通常测试阶段 Dropout 是关闭的。
-
为了做 MC Dropout,只开启 Dropout 层而不影响 BatchNorm 等其他模块。
-
模拟“多个模型预测”,以计算 Epistemic 不确定性。
🔁 Practice 6: Epistemic Uncertainty via MC Dropout
model.eval()
enable_dropout(model)
num_mc_passes = 20
all_mean_probs = []
all_var_probs = []
with torch.no_grad():
for images, labels in tqdm(test_loader, desc="MC Dropout Inference"):
images = images.to(device)
mc_probs = []
for _ in range(num_mc_passes):
outputs = model(images)
probs = torch.softmax(outputs, dim=1)
mc_probs.append(probs.unsqueeze(0))
stacked_probs = torch.cat(mc_probs, dim=0)
mean_probs = stacked_probs.mean(dim=0)
var_probs = stacked_probs.var(dim=0)
epistemic_uncertainty = var_probs.mean(dim=1)
all_mean_probs.extend(mean_probs.cpu().numpy())
all_var_probs.extend(epistemic_uncertainty.cpu().numpy())
all_mean_probs = np.array(all_mean_probs)
all_var_probs = np.array(all_var_probs)
mean_uncertainty = np.mean(all_var_probs)
print(f"Epistemic Uncertainty (average): {mean_uncertainty:.6f}")
✅ 分析:
-
启用 Dropout,在推理阶段对每个样本预测多次。
-
堆叠预测 → 计算每个类别的均值和方差。
-
平均方差 = 样本的不确定性评分。
-
全体样本平均值即为模型整体的 Epistemic 不确定性。
🧠 结语:总结模型不确定性三种评估方式
方法 | 类型 | 特点 |
---|---|---|
1 - Max Prob | Aleatoric | 快速、粗略估计 |
Entropy | Aleatoric | 更细腻,考虑整体分布 |
MC Dropout + 方差 | Epistemic | 测试阶段多次推理,评估模型知识盲区 |