166

不确定性估计:模型什么时候应该说“我不知道”

模型什么时候该说「我不知道」?这篇讲清不确定性的来源、估计方法、校准检验与在决策中的用法。

一个只给出预测值而不给出置信度的模型,在实际决策中价值有限——因为你无法区分「模型很确定这个分子活性高」与「模型在瞎猜」。不确定性估计是把预测变成决策依据的关键一步。

两类不确定性

偶然不确定性(aleatoric) 认知不确定性(epistemic)
来源 数据本身的噪声 模型知识的不足
例子 实验测定误差、批次差异 这个化学类型训练集里没有
能否减少 不能(除非改进实验) (增加数据)
对决策的含义 这是精度的下限 这里需要做实验
主动学习中 选它没用 正是要选的(见 165《主动学习 Active Learning》

区分这两者很重要:如果一个分子的不确定性主要是偶然的(实验本身就测不准),再做实验也不会改善模型;而认知不确定性高的分子,做实验的信息价值最大

估计方法

# ---- 1) 集成(最推荐)----
# 用不同随机种子训练 N 个模型
# 预测的均值作为估计,标准差作为不确定性
#
# 优点:
#   - 简单,任何模型都能用
#   - 【实践中校准通常最好】
#   - 主要捕捉认知不确定性
# 缺点:训练与推理成本 × N
#
# 建议:N = 5~10 通常够用

import numpy as np
import lightgbm as lgb

def train_ensemble(X, y, n=5):
    return [lgb.LGBMRegressor(n_estimators=800, learning_rate=0.05,
                              subsample=0.8, colsample_bytree=0.8,
                              random_state=s, verbose=-1).fit(X, y)
            for s in range(n)]

def predict_ensemble(models, X):
    P = np.array([m.predict(X) for m in models])
    return P.mean(axis=0), P.std(axis=0)

# ---- 2) 分位数回归 ----
# 直接预测分位数,给出预测区间
# 优点:直接给区间,不需要正态假设
#      主要捕捉【偶然不确定性】
q_models = {q: lgb.LGBMRegressor(objective="quantile", alpha=q,
                                 n_estimators=800, verbose=-1).fit(X, y)
            for q in (0.1, 0.5, 0.9)}

# ---- 3) 保形预测(conformal prediction)----
# 【有统计保证的方法】
# 用一个校准集确定区间宽度,
# 保证区间的覆盖率达到指定水平

def conformal_interval(model, X_cal, y_cal, X_test, alpha=0.1):
    """分裂保形预测:保证约 (1-alpha) 的覆盖率"""
    residuals = np.abs(y_cal - model.predict(X_cal))
    n = len(residuals)
    q_level = min(1.0, np.ceil((n + 1) * (1 - alpha)) / n)
    q = np.quantile(residuals, q_level, method="higher")
    pred = model.predict(X_test)
    return pred - q, pred + q

# 【保形预测的优势】:
#   在分布不变的假设下,覆盖率有【有限样本的保证】
#   → 不依赖模型是否「校准良好」
#   缺点:区间宽度对所有样本相同(除非用自适应版本)

# ---- 4) MC Dropout ----
# 推理时保持 dropout 开启,多次前向传播
# 优点:便宜
# 缺点:【校准通常不好】,不确定性常被低估

# ---- 5) 高斯过程 ----
# 不确定性有理论基础
# 缺点:难以扩展到大数据;核函数的选择影响大

校准:必须检验的性质

# 「模型说 90% 置信区间」,
# 那么真实值应该有 90% 的时候落在里面
# → 这就是【校准】
#
# 【未校准的模型给出的不确定性是误导性的】

def calibration_curve(y_true, y_pred, y_std, n_bins=10):
    """检验不确定性是否校准"""
    from scipy import stats
    expected, observed = [], []
    for p in np.linspace(0.1, 0.9, n_bins):
        z = stats.norm.ppf(0.5 + p / 2)
        within = np.abs(y_true - y_pred) <= z * y_std
        expected.append(p)
        observed.append(within.mean())
    # 理想情况:observed ≈ expected
    # observed < expected → 【过度自信】(最常见)
    # observed > expected → 过度保守
    return np.array(expected), np.array(observed)

def error_vs_uncertainty(y_true, y_pred, y_std, n_bins=5):
    """更直观的检验:不确定性大的样本,误差是否真的大?"""
    errors = np.abs(y_true - y_pred)
    order = np.argsort(y_std)
    bins = np.array_split(order, n_bins)
    print("不确定性分箱  平均预测不确定性  平均实际误差")
    for i, b in enumerate(bins):
        print(f"  第 {i+1} 箱      {y_std[b].mean():.3f}        "
              f"{errors[b].mean():.3f}")
    # 【两列应该同向增长】
    # 如果不增长,说明不确定性估计没有意义

# 【这个检验是使用不确定性的前提】
#   不做这个检验就用不确定性做决策,
#   与直接用点估计没有本质区别

# 【校准的修正】:
#   如果发现系统性的过度自信,
#   可以用一个校准集拟合一个缩放因子:
#     y_std_calibrated = a * y_std + b
#   或直接改用保形预测

适用域:更简单也很有效的判据

# 除了模型内部的不确定性,
# 还应该用【与训练数据的距离】做判断
#
# 最简单的方法:最大 Tanimoto 相似度

from rdkit import Chem, DataStructs
from rdkit.Chem import rdFingerprintGenerator

gen = rdFingerprintGenerator.GetMorganGenerator(radius=2, fpSize=2048)

def applicability_domain(query_smiles, train_fps):
    mol = Chem.MolFromSmiles(query_smiles)
    if mol is None:
        return {"max_sim": 0.0, "verdict": "无效结构"}
    fp = gen.GetFingerprint(mol)
    sims = DataStructs.BulkTanimotoSimilarity(fp, train_fps)
    max_sim = max(sims)
    top5 = float(np.mean(sorted(sims, reverse=True)[:5]))
    verdict = ("域内" if max_sim > 0.6 else
               "边缘" if max_sim > 0.4 else "域外")
    return {"max_sim": max_sim, "top5_mean": top5, "verdict": verdict}

# 【经验判据】:
#   max_sim > 0.6   预测相对可靠
#   0.4 ~ 0.6       谨慎
#   < 0.4           【预测基本不可信,不应驱动决策】
#
# 【为什么这个简单方法有用】:
#   它直接回答了「模型见过类似的东西吗」
#   而这正是认知不确定性的本质
#
# 【最佳实践】:同时使用集成方差与适用域
#   两者都好 → 可信
#   任一不好 → 谨慎
#   都不好   → 不可信

在决策中怎么用

场景 用法
决定合成哪个分子 高预测值 + 低不确定性 → 优先
主动学习选样本 高不确定性 → 优先(见 165《主动学习 Active Learning》
虚拟筛选 用 UCB 而非纯预测值排序
风险控制 不确定性超过阈值 → 拒绝预测,交给实验
模型监控 不确定性整体上升 → 可能有数据漂移
与专家沟通 展示区间比展示点估计更诚实

关键要点

  • 区分偶然与认知不确定性——只有后者能通过增加数据减少,也只有它值得做实验;
  • 集成方法在实践中校准通常最好;MC Dropout 常低估不确定性;
  • 必须做「不确定性大的样本误差是否真的大」的检验——不做就用等于没用;
  • 同时用集成方差与适用域(最大 Tanimoto 相似度)两个判据。

延伸资源