4. 条件概率表归一化错误的检测与修正

问题定位:手工录入的 CPT 行和不为 1

条件概率表(CPT)的每一行对应父变量的一种取值组合,行内各子变量取值的概率之和必须等于 1。手工录入时常见的错误是:漏填某个取值、把 0.3 写成 0.03、或者把两个取值写反导致行和变成 0.9 或 1.1。下面这张图展示了一个 3 行 CPT 的校验流程——逐行求和、与 1 比较、定位偏差行、按比例修正并复验。

CPT 行归一化校验与修正流程 输入 CPT(示例) 父=晴: P(带伞)=0.2 父=晴: P(不带)=0.8 父=雨: P(带伞)=0.9 父=雨: P(不带)=0.3 ← 错 父=阴: P(带伞)=0.5 父=阴: P(不带)=0.5 逐行求和 晴: 0.2+0.8 = 1.0 ✓ 雨: 0.9+0.3 = 1.2 ✗ 阴: 0.5+0.5 = 1.0 ✓ 容差 |sum-1| > 1e-6 判错 行和 = 1 ? 是 通过 CPT 合法 否 按比例修正该行 p_i' = p_i / Σp 雨: 0.9/1.2=0.75, 0.3/1.2=0.25 新行和: 0.75+0.25 = 1.0 ✓ 复验 重新逐行求和 复验通过后输出修正后的 CPT 容差阈值 1e-6;修正采用按比例缩放,保持行内相对大小关系

校验脚本:逐行求和、定位、修正

下面用 Python 实现一个可复用的 CPT 校验器。输入是一个字典,键为父变量取值,值为子变量各取值的概率列表。脚本会逐行求和,标记偏差超过容差的行,并按比例缩放修正。

import math

def validate_cpt(cpt, tol=1e-6):
    """
    cpt: dict[str, list[float]]
        键 = 父变量取值,值 = 子变量各取值的概率列表
    返回: (是否全部合法, 问题行列表, 修正后的 cpt)
    """
    bad_rows = []
    fixed = {}
    for parent_val, probs in cpt.items():
        s = sum(probs)
        if abs(s - 1.0) > tol:
            bad_rows.append((parent_val, probs, s))
            # 按比例修正
            fixed[parent_val] = [p / s for p in probs]
        else:
            fixed[parent_val] = list(probs)
    return len(bad_rows) == 0, bad_rows, fixed

# 示例:手工录入的 CPT(父变量=天气,子变量=是否带伞)
cpt_raw = {
    "晴": [0.2, 0.8],
    "雨": [0.9, 0.3],   # 错误行:和 = 1.2
    "阴": [0.5, 0.5],
}

ok, bad, fixed = validate_cpt(cpt_raw)
print("全部合法:", ok)
for pv, probs, s in bad:
    print(f"问题行: 父={pv}, 概率={probs}, 行和={s:.4f}")

print("\n修正后 CPT:")
for pv, probs in fixed.items():
    print(f"  {pv}: {[round(p, 4) for p in probs]}, 行和={sum(probs):.6f}")

运行输出(示例预期结果):

全部合法: False
问题行: 父=雨, 概率=[0.9, 0.3], 行和=1.2000

修正后 CPT:
  晴: [0.2, 0.8], 行和=1.000000
  雨: [0.75, 0.25], 行和=1.000000
  阴: [0.5, 0.5], 行和=1.000000
修正策略选择:按比例缩放(p_i / Σp)保持行内各取值的相对大小关系,适合“录入笔误”类错误。如果怀疑是某个取值漏填或写错,应先核对原始数据再决定是否缩放,避免把真实错误“抹平”。对于二值变量,也可以只修正一个取值(如 p₂ = 1 − p₁),但需明确哪个取值更可信。

验证与边界检查

修正后必须复验:每行和与 1 的偏差不超过容差,且每个概率值落在 [0, 1] 区间。若原始数据中存在负值或大于 1 的值,按比例缩放无法修复,应直接报错并回溯录入源。

检查项条件处理方式
行和偏差|Σp − 1| > 1e-6标记问题行,按比例缩放
概率越界p < 0 或 p > 1报错,不自动修正,回溯录入源
空行概率列表为空报错,父变量取值缺少子变量分布
复验修正后所有行 |Σp − 1| ≤ 1e-6通过,输出修正后 CPT
常见陷阱:浮点累加误差可能让 0.1 + 0.2 + 0.7 得到 0.9999999999999999,此时不应判为错误。容差 1e-6 足以覆盖双精度浮点的累加误差,又不会放过真实的录入错误(通常偏差在 0.01 以上)。