ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

不确定性推理之C-F模型:原理、案例与Python实践

不确定性推理之C-F模型:原理、案例与Python实践 1. 为什么传统逻辑推理处理不了医疗诊断这类问题C-F模型要解决的痛点如果你是学人工智能的八成在《不确定性推理》这一章见过C-F模型的名字。人工智能里的不确定性推理核心要回答的是证据不完整、规则不绝对时结论到底该信几分的问题而C-F模型Certainty Factor Model可信度模型是这类方法中最经典、最容易被低估的一个。这篇文章既不打算只贴公式也不打算回避推导。我会从它解决的问题讲起把规则强度、证据合成、阈值判断全部拆开最后给出一套可以直接跑起来的Python实现再聊聊我在实际项目中踩过的坑。适合三类人看准备考试却背不住公式的学生、正在做专家系统大作业的开发者、想快速搭一个规则推理原型的工程师。1.1 三段论在真实世界里的尴尬传统产生式系统里知识被写成如果A那么B的形式。比如如果白细胞计数高于正常值那么存在感染。这种规则看起来简洁但一到临床场景就会出问题。患者说有点发热体温是37.6摄氏度算不算发热发热对感染的提示作用有多大如果患者用了退烧药体温正常了能不能排除感染这些问号不是逻辑上真/假能表达的。逻辑推理要求前提为真结论才为真可现实世界几乎没有哪个证据是百分百确定的也没有哪条医学规则敢说自己在所有患者身上都成立。规则一多甚至会出现相互矛盾的情况一条规则推出来的结论支持感染另一条规则却支持其他诊断。这个时候仅仅依靠布尔逻辑做推理要么输出一个武断答案要么直接崩溃。1.2 概率论很完美但专家系统不敢直接用有些人会想既然不确定性那用概率不就行了概率论确实严谨但它在早期专家系统里并不好用。要计算在症状E出现情况下患H病的概率P(H|E)至少得有患病先验概率还要知道症状在不同人群中的条件概率。医学统计资料未必覆盖每一个细分人群专家也很难一张口就给出所有概率。更麻烦的是诊断过程通常涉及几十个症状和候选疾病完整条件概率表的规模是指数级的计算和知识获取都扛不住。C-F模型之所以在1976年MYCIN系统里被提出就是因为Shortliffe团队发现医生根本不按贝叶斯公式思考他们习惯说这个症状强烈支持某种细菌感染这条证据略微反对那个诊断。既然如此不如让专家直接给一个数值表达支持/反对的程度剩下的计算尽量简单可解释。1.3 C-F模型的本质用可信度替代真/假C-F模型对每个断言不管是证据、假设还是规则都挂一个可信度值CF范围在[-1, 1]之间。CF大于0表示偏向支持等于0表示不知道或不影响小于0表示反对。然后通过一组固定的算术规则把多条证据和规则组合起来最终算出目标假设的可信度。整个过程不需要先验概率也不需要复杂参数知识工程师只需要问领域专家如果这个前提完全成立你对结论的信心是多少这种直觉化的知识获取方式让它成了早期专家系统普及的重要推手。后面你会看到整个模型的计算量其实很小甚至手工就能算这恰恰是它长盛不衰的原因之一。2. C-F模型的数学内核可信度、证据组合与规则强度的来龙去脉要理解C-F模型不能只背合并公式。它背后有一套从概率论出发、又刻意避开概率约束的设计逻辑。理解了这套逻辑你在考试和项目中才不会用错。2.1 从MB/MD到CF一个区间[-1,1]的度量最初的C-F模型用两个指标表示一条证据对假设的支持和反对。支持度MB(H,E)表示观察到证据E后对假设H相信程度的增量反对度MD(H,E)表示不相信程度的增量。模型定义CF(H,E)MB(H,E)-MD(H,E)取值范围正是[-1,1]。之所以不直接用概率是因为概率必须满足归一性而可信度没有这个负担。假设H为患者感染肠球菌医生可以给出CF0.7表示在证据齐全时比较支持但他没必要说明P(H|E)精确等于多少也不关心某个候选假设占用多少概率。MB/MD的另一个作用是处理证据矛盾同一假设可以被一个证据支持同时被另一个证据反对CF通过正负抵消把这两股力量合并成净可信度。这比单纯的真假集合灵活得多。2.2 规则强度知识工程师如何给规则打分知识库里的每一条规则都带一个规则强度CF(H,E)它回答的问题是当规则前提完全为真时结论的可信度是多少。这个值通常由领域专家给出。比如右下腹转移性疼痛强烈支持急性阑尾炎可以写成规则强度0.8病程超过一周无加重则不太支持阑尾炎可以写成-0.5。注意规则强度跟概率有本质区别。概率不允许负值但规则强度允许负值概率在[0,1]内规则强度在[-1,1]内。专家如果说80%可能不能直接写成0.8因为概率0.8意味着还有0.2概率不是而CF0.8是一种信心强度不代表剩余0.2。实际知识获取时我会让专家对一组典型病例排序哪些症状组合强烈支持疾病哪些只是微弱支持哪些反而否定疾病然后为每组规则标定一个区间值。这个过程需要反复校验不是一次性定死的。2.3 证据传播与多条规则合并的计算公式现在看计算。一条简单规则的推理公式是CF(H) CF(H, E) * max(0, CF(E))其中CF(E)是当前证据的可信度。如果证据可信度小于等于0说明前提不成立或起反作用这条规则就不触发结论可信度记为0。若前提是一个复合条件比如发热E2且白细胞升高E3则先组合前提可信度AND取最小值OR取最大值NOT取1-CF(E)。用min表达AND逻辑上就是短板效应整个复合前提的可信度不会超过其中最弱的一项。用max表达OR表示只要有最强的那个证据成立整体前提就成立。这样做虽然粗糙但计算代价低也符合直觉。当多个不同规则得到同一个结论时需要合并。设同一结论H由两条规则分别算出CF1和CF2合并后的CF12分为三种情况# 两者同为正 CF12 CF1 CF2 - CF1 * CF2 # 两者同为负 CF12 CF1 CF2 CF1 * CF2 # 一正一负 CF12 (CF1 CF2) / (1 - min(|CF1|, |CF2|))这个合并公式是C-F模型最核心的部分。两个同向证据合成会让可信度向同一个方向增加但上限被控制在1以内反向证据则互相抵消抵消程度与较弱一方有关。需要特别指出公式假设多条规则的证据之间是独立的如果证据强相关合成结果会偏乐观这一点在第五章还会重点展开。3. 一步一步手算一个临床诊断案例完整走一遍推理流程公式看得再多不如手算一遍。我用一个经典的急性阑尾炎诊断场景带你完整跑一遍尽量少跳步。3.1 案例知识库与初始证据假设知识库里有三条支持急性阑尾炎H的规则和一条反对规则R1: IF 右下腹转移性疼痛(E1) THEN 急性阑尾炎(H) CF 0.8 R2: IF 发热(E2) AND 白细胞升高(E3) THEN 急性阑尾炎(H) CF 0.6 R3: IF 右下腹压痛(E4) THEN 急性阑尾炎(H) CF 0.7 R4: IF 病程超过一周且无明显加重(E5) THEN 急性阑尾炎(H) CF -0.5当前病例给出的初始证据可信度如下证据含义可信度E1右下腹转移性疼痛0.9E2发热0.8E3白细胞升高0.7E4右下腹压痛0.5E5病程超过一周无加重0.4注意这里证据可信度来自医生对观察结果的确信程度比如E1给0.9表示病人描述典型但不是完全典型。做完这步你就把症状是否出现从布尔值换成连续值了。3.2 逐条规则计算中间结论先算三条支持规则的结论。R1单前提CF10.8max(0,0.9)0.72。R2是AND复合前提前提可信度取min(0.8,0.7)0.7所以CF20.60.70.42。R3单前提CF30.70.50.35。三条规则都指向同一结论H需要两两合并。先合并CF1和CF2都是正数套公式CF120.720.42-0.720.421.14-0.30240.8376。再合并CF12和CF3CF1230.83760.35-0.8376*0.351.1876-0.293160.89444。可以看出三条规则合起来把急性阑尾炎的可信度推到了0.894明显高于任意一条单独规则。这就是多证据融合的意义单一症状可能没有决定性多条线索汇聚在一起后结论的置信度会显著上升。3.3 加入反例证据后的合成效果如果患者病程已经超过一周且没有明显加重医生对E5的把握是0.4那么R4这条反对规则给出的结论可信度是负的CF4-0.5*max(0,0.4)-0.2。现在要把CF4并入之前的0.89444。这是一个一正一负的合并套异号公式CF (0.89444 (-0.2)) / (1 - min(0.89444, 0.2)) 0.69444 / 0.8 0.86755最终可信度从0.894降到了0.868。反例会削弱结论但不会把整个结论推翻因为反例本身的规则强度和证据可信度都不高。这个部分可信的结果正是真实世界的写照证据支持强度不等于百分百确诊决策者还要结合阈值来判断。如果系统里设定的治疗阈值为0.7那么0.868可以启动下一步处置如果阈值是0.9就还不能行动需要补充检查或继续观察。阈值本身不是C-F模型的一部分但一个可用的专家系统必须显式设计它。3.4 链式传播从诊断结论到处置建议C-F模型不仅支持横向合并还支持纵向传播。上面的H已经算出可信度0.868如果知识库里还有一条上层规则R5: IF 急性阑尾炎(H) THEN 需急诊手术(S) CF 0.9那么S的可信度约等于0.9*0.8680.781。注意这里H在上一层里作为证据出现它的CF就是0.868。如果环节增多可信度会逐层衰减特别是在规则强度和证据可信度都小于1的情况下。因此实际知识库设计时最好不要让推理链拉得太长否则深层结论的可信度会低到无法通过阈值。急诊手术这个例子也说明C-F模型适合表达倾向性结论而不是给出一个严格的最优决策它输出的是候选行动的可信度排序。4. 从公式到代码用Python把C-F模型封装成可复用的推理工具考试和手算理解的是原理工程上你得把它变成能跑的代码。C-F模型计算量很小几十行就能实现一个最小引擎。4.1 一个最小可运行的CF推理引擎核心就三个函数规则传播、前提组合、合并。我用Python写了一个最简版本def infer_rule(cf_rule, cf_evidence): 单规则推理CF(H) CF(H,E) * max(0, CF(E)) if cf_evidence 0: return 0.0 return cf_rule * cf_evidence def combine_premise(operator, *cfs): 复合前提可信度AND取minOR取maxNOT取补 if operator AND: return min(cfs) if operator OR: return max(cfs) if operator NOT: return 1 - cfs[0] raise ValueError(unknown operator) def combine_cf(cf1, cf2): 同一结论来自两条规则的合并 if cf1 0 and cf2 0: return cf1 cf2 - cf1 * cf2 if cf1 0 and cf2 0: return cf1 cf2 cf1 * cf2 denom 1 - min(abs(cf1), abs(cf2)) if denom 0: return 0.0 return (cf1 cf2) / denominfer_rule负责把规则强度和证据可信度相乘combine_premise处理AND/OR/NOTcombine_cf处理两条同结论规则的合成。这个代码已经能够支撑大部分考试题了。实际部署时建议把combine_cf改成支持无限条规则的版本也就是从第一条开始循环合并因为合并规则满足结合律。4.2 用上面的临床案例跑一遍把第三章的案例翻译成代码cf_e1, cf_e2, cf_e3, cf_e4, cf_e5 0.9, 0.8, 0.7, 0.5, 0.4 # R1 cf1 infer_rule(0.8, cf_e1) # R2: E2 AND E3 e_and combine_premise(AND, cf_e2, cf_e3) cf2 infer_rule(0.6, e_and) # R3 cf3 infer_rule(0.7, cf_e4) # 合并三条正向规则 cf_h combine_cf(cf1, cf2) cf_h combine_cf(cf_h, cf3) print(合并三条正向规则后的CF(H):, cf_h) # 0.89444 # R4: 反对规则 cf4 infer_rule(-0.5, cf_e5) cf_h combine_cf(cf_h, cf4) print(并入反例后的CF(H):, cf_h) # 0.86755 # R5: 链式传播 cf_s infer_rule(0.9, cf_h) print(急诊手术结论CF(S):, cf_s) # 0.780795输出与手算一致。代码里有一个容易被忽略的细节combine_cf在异号时要求分母不能为0如果cf11, cf2-1公式会出现除零。虽然标准C-F模型里可信度一般取不到边界工程上还是得加保护否则一个脏数据就能让整个推理进程崩溃。4.3 工程化扩展把规则放到配置文件里真实知识库不可能把规则硬编码在Python函数里。我的习惯是把规则以JSON或者YAML形式维护定义成一个列表每条规则记录前提类型、前提条件、结论和规则强度然后写一个加载器循环执行。比如JSON可以长这样{ threshold: 0.7, rules: [ {id: R1, premise: E1, type: single, then: H, cf: 0.8}, {id: R2, premise: [E2, E3], type: and, then: H, cf: 0.6}, {id: R3, premise: E4, type: single, then: H, cf: 0.7}, {id: R4, premise: E5, type: single, then: H, cf: -0.5}, {id: R5, premise: H, type: single, then: S, cf: 0.9} ] }加载之后引擎需要根据规则依赖关系决定执行顺序先算底层证据再逐层向上。如果知识库里有循环依赖还需要做拓扑排序或设置最大迭代次数。到这里一个最小可用的CF推理引擎就成型了输入是初始证据CF字典输出是每个中间假设和最终结论的CF列表。用这种方式知识工程师可以只改配置文件不改代码不断调整规则强度。5. 踩坑记录规则强度、阈值设定与多个证据传播的常见问题公式很简单但真正把C-F模型用到项目里坑比想象中多。以下是我和组员在几个不同项目里踩过的真实问题拿出来当反面教材。5.1 坑一把规则强度当概率用第一次接触这个模型的人特别容易把CF(H,E)0.8理解成结论发生的概率是80%。这会导致两个连锁错误第一专家报概率时写0.8但概率有严格的补集约束可信度没有第二合成时总想着归一化把多个候选结论做相加等于1的处理。实际上C-F模型允许两个互斥假设的可信度都很高比如肺炎CF0.85肺结核CF0.8。如果你强行归一化等于给模型增加了不存在的假设。正确做法是把CF当作排序分值最终决策还要结合阈值和业务规则而不是当作概率分布。5.2 坑二阈值设太高深层结论全部不可信我见过一个做故障诊断的原型系统规则链有5层每层规则强度大概0.7到0.9初始证据可信度平均0.8。系统把决策阈值定在0.8结果绝大多数测试用例都判为无法确认根本没法用。当时我接到反馈说系统经常判定不出来了第一反应不是调阈值而是先把知识库里所有规则的中间CF打印出来看看问题到底出在哪一层。排查步骤很简单第一步打印第一层规则输出证据CF都在0.8左右看起来正常第二步打印第二层因为传播时要乘规则强度输出掉到0.6第三步第三层掉到0.42第四层只有0.29第四步第五层基本就剩0.2永远过不了0.8的阈值。根因是推理链过长导致CF逐层衰减而非某条规则写错。后来把阈值降到0.6同时把中间三层规则合并成一条高阶规则结果才稳定下来。经验是推理链超过3层时阈值不宜高于0.6初始证据本身不确定时更要把阈值调低。C-F模型不擅长处理过长推理链这是它的结构性短板不是调参能根治的。5.3 坑三相关性证据被当成独立证据反复叠加合并公式假设证据之间相互独立但现实里很多证据高度相关。比如发热白细胞升高C反应蛋白升高都指向炎症它们不是独立证据。如果三条规则都用上合成后的CF会明显偏高可能把原本只是可能的结论推到几乎确诊。我们当时用C-F模型做疾病筛查时就出现过一批假阳性根因就是相关规则过多。排查时我做了个对照实验把所有指向同一结论的规则按明显相关/弱相关分组分别重新计算发现去掉强相关规则后CF从0.96降到了0.78这才是更合理的置信度。解决思路有两种一是做证据聚类把强相关的低层证据先合并成一个炎症证据组再参与诊断二是提高每条规则的强度要求并主动限制同一假设的规则数量。知识库不是规则越多越好尤其不能让一堆相似证据同时出现。5.4 坑四合并公式的符号细节和未知证据处理combine_cf的三种情况看着简单但在浮点数世界里容易翻车。比如cf10.0时按正数公式算没问题但如果出现-0.0Python里cf1 0为False可能走入异号公式的分支产生意外结果。更常见的问题是未知证据的处理。很多新手把未知证据的CF设为0这会把结论可信度往0拖。但0在C-F模型里表示不影响而未知应该是不参与推理。设计推理引擎时证据值应该区分True、False、Unknown三种状态未知证据不进合成而不是当成CF0。这个细节直接决定了系统面对缺失数据时的鲁棒性。我在代码里会用一个None表示未知只有等到证据值变成数值时才参与计算。6. C-F模型在现实系统里的定位和贝叶斯网络、模糊推理怎么选很多人学完C-F模型会问一句现在还有人在用吗我的答案是纯C-F模型确实不像深度学习那么光鲜但它从未消失在推理工具链里而且常被拿来和更现代的模型做对比。选型时你要清楚它处在哪块生态位上。6.1 三张牌的对比C-F / 贝叶斯网络 / 模糊推理同样处理不确定性C-F模型、贝叶斯网络和模糊推理的出发点是不同的方法核心输入不确定性表达计算特点典型场景C-F模型规则强度和证据可信度正负可信度[-1,1]简单、透明、手算可行专家系统、决策支持、教学实验贝叶斯网络先验概率和条件概率表概率分布需要图结构和参数学习有历史数据、依赖关系复杂的诊断模糊推理隶属度函数和模糊规则隶属度[0,1]适合连续变量、控制模糊控制、智能家居、自动驾驶辅助C-F模型最大的优势是前提条件便宜请专家打分比收集统计概率数据容易得多一条规则就是一个可解释的如果...那么...。贝叶斯网络虽然数学上更漂亮但完整条件概率表的知识获取成本很高当数据不足时强行上贝叶斯网络只会变成拼凑参数。模糊推理则擅长处理高温较大这类模糊语言变量它把边界模糊化但对证据的正反支持不如C-F模型直观。所以三者不是完全替代关系而是面向不同问题的工具。6.2 什么情况下我会坚持用C-F模型根据我的实际项目经验以下情况优先选择C-F模型领域专家能清晰表达规则但拿不出足够统计数据系统需要快速搭建、立即向用户解释推理过程规则规模在几十到几百条推理链不要过长。典型例子包括企业内部的设备故障初步诊断、教学演示用的植物病虫害识别、早期医疗预检分诊、学术竞赛里的专家系统原型。这类场景要的不是一个黑箱概率模型而是一个人能看懂、能现场调整的决策逻辑。C-F模型的规则强度可以直接由专家在知识库管理界面中修改改完立刻生效这种敏捷迭代能力是数据驱动模型很难给你的。6.3 从C-F模型升级的路线图如果你在做的东西演进到数据量充足、关系复杂再继续硬套C-F模型会力不从心。我建议走升级路线先保留C-F模型做规则骨架和生产环境的可解释层同时用历史数据统计条件概率表把关键节点升级成贝叶斯网络或者用随机森林/逻辑回归生成候选结果再用C-F规则做最后的风险评分。这种混合架构既保留了规则的透明性又吸收了统计模型的效果。不少商用规则引擎的置信度计算里仍然能看到CF合并公式的影子只是外面包了一层更复杂的决策框架。最后聊点个人判断。C-F模型现在很少单独出现在工业级系统里但它的思想渗透在很多规则引擎和决策支持系统中。如果你正在做专家系统相关的毕业设计我建议先用C-F模型快速跑通端到端流程再根据数据情况决定要不要向贝叶斯网络迁移。这个看起来过时的模型反而能帮你把不确定性推理整个链条理解得更扎实。要是你手头正好有一个需要解释性的业务场景不妨先拿C-F模型搭一个最小版本让业务专家看两天他们给出的修改意见往往比任何论文里的参数都更接地气。
返回列表