在AI模型部署到生产环境后,我们经常会遇到模型在训练数据分布之外(Out-of-Distribution, OOD)表现急剧下降的问题,这通常表现为模型鲁棒性(Robustness)的缺失。鲁棒区域的扩展是提升模型稳定性的关键。主动学习(Active Learning, AL)提供了一种高效的方法,通过智能地选择最有价值的未标注数据进行标注,从而集中增强模型在不确定区域的性能。
本文将聚焦一种高实操性的AL策略:基于Monte Carlo Dropout (MCD) 的不确定性采样。MCD允许我们在不修改模型结构太多的情况下,为深度学习模型引入贝叶斯特性,从而量化模型的预测不确定性(Epistemic Uncertainty)。
1. Monte Carlo Dropout (MCD) 与不确定性量化
传统的深度学习模型在推理阶段会关闭Dropout。然而,当我们在推理阶段保持Dropout开启并进行多次前向传播(T次)时,每次前传的结果都会有所不同。这T次结果的集合可以视为对后验分布的采样。通过分析这些采样的统计特性(如熵、方差),我们可以判断模型在当前输入上的不确定程度。
核心思路: 高不确定性的样本,往往位于模型的决策边界附近,或者是模型从未见过的“鲁棒性漏洞”区域。通过主动学习将这些样本加入训练集,能够有效加固模型的边界。
2. 实践:MCD 不确定性采样查询策略
我们将使用PyTorch实现一个简单的MCD查询机制。我们选择预测熵(Predictive Entropy)作为度量标准,熵值越高,模型对该样本的类别预测越不确定。
步骤一:准备模型和MCD配置
为了使用MCD,我们需要确保模型中包含Dropout层,并在推理时强制其处于训练模式(即启用Dropout)。
import torch
import torch.nn as nn
import torch.nn.functional as F
import numpy as np
from scipy.stats import entropy
# 1. 模拟一个简单的分类器
class SimpleClassifier(nn.Module):
def __init__(self, input_dim, num_classes):
super().__init__()
self.fc1 = nn.Linear(input_dim, 64)
# 关键:引入Dropout
self.dropout = nn.Dropout(p=0.3)
self.fc2 = nn.Linear(64, num_classes)
def forward(self, x):
x = F.relu(self.fc1(x))
x = self.dropout(x)
x = self.fc2(x)
return x
# 2. 启用MCD模式的函数
def enable_dropout(model):
"""强制将模型中的所有Dropout层设置为训练模式"""
for m in model.modules():
if m.__class__.__name__.startswith('Dropout'):
m.train()
# 实例化模型 (假设输入维度10, 类别3)
model = SimpleClassifier(input_dim=10, num_classes=3)
# 假设模型已经经过训练...
步骤二:实现不确定性查询函数
查询函数需要对每个未标注样本进行T次MCD前传,计算平均概率分布,并求其熵。
def query_uncertain_samples(model, unlabeled_data_loader, T=50, k_samples=10):
"""
使用MCD预测熵查询最不确定的k个样本。
:param model: 训练好的PyTorch模型
:param unlabeled_data_loader: 未标注数据的DataLoader
:param T: MCD采样次数
:param k_samples: 本轮需要查询的样本数量
:return: 最不确定的样本在原始数据集中的全局索引和对应的不确定性分数
"""
model.eval()
enable_dropout(model) # 开启MCD模式
all_uncertainty_scores = []
all_data_indices = []
with torch.no_grad():
for batch_idx, (data, indices) in enumerate(unlabeled_data_loader):
# 存储 T 次预测结果 [T, Batch_Size, Num_Classes]
batch_predictions = []
for _ in range(T):
output = F.softmax(model(data), dim=1)
batch_predictions.append(output.cpu().numpy())
# 转换为 [Batch_Size, T, Num_Classes]
predictions_stack = np.stack(batch_predictions, axis=1)
# 计算平均预测概率 [Batch_Size, Num_Classes]
mean_probabilities = np.mean(predictions_stack, axis=1)
# 计算预测熵 (信息熵越高,不确定性越高)
# 对每个样本的平均概率分布计算熵
mean_probabilities = np.clip(mean_probabilities, 1e-9, 1.0)
# axis=1 表示对每个样本的类别维度计算熵
uncertainty_scores = entropy(mean_probabilities.T)
all_uncertainty_scores.extend(uncertainty_scores)
all_data_indices.extend(indices.numpy()) # 假设DataLoader返回了样本的原始索引
# 3. 选择k个不确定性最高的样本索引
all_uncertainty_scores = np.array(all_uncertainty_scores)
all_data_indices = np.array(all_data_indices)
# argsort 升序排列,取最后k个
sorted_indices_in_list = np.argsort(all_uncertainty_scores)[-k_samples:]
query_indices = all_data_indices[sorted_indices_in_list]
query_scores = all_uncertainty_scores[sorted_indices_in_list]
return query_indices, query_scores
3. 部署主动学习循环 (AL Loop)
AI基础设施中的主动学习部署是一个迭代循环,通常包含以下关键步骤:
- 初始化: 使用少量标注数据训练初始模型 $M_0$。
- 查询(Query): 使用MCD查询策略,从大量的未标注数据 $U$ 中选出 $k$ 个不确定性最高的样本 $Q_i$。
- 标注(Label): 将 $Q_i$ 送入人工标注系统(Human-in-the-Loop)。
- 更新(Retrain): 将新标注的样本 $L_{new}$ 加入当前标注集 $L$,重新训练模型 $M_{i+1}$。
- 重复: 重复步骤 2-4,直到达到预算限制或模型性能收敛。
通过这种循环,每次迭代都集中解决了模型在不确定区域的知识盲点,从而系统性地增强了模型的鲁棒区域,使其在接近决策边界的输入上也能给出稳定且可靠的预测。
汤不热吧