一个只给出预测值而不给出置信度的模型,在实际决策中价值有限——因为你无法区分「模型很确定这个分子活性高」与「模型在瞎猜」。不确定性估计是把预测变成决策依据的关键一步。
两类不确定性
| 偶然不确定性(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 相似度)两个判据。
延伸资源
- 主动学习:165《主动学习 Active Learning》;模型外推性:167《模型外推性》;
- 活性预测:155《AI 活性预测模型》;ADMET:156《AI ADMET 预测模型》;Chemprop:131《Chemprop / D-MPNN 论文精读》。