专家系统是规则系统穿上工程外衣的样子:规则库、推理机、解释器、界面,四块拼起来, 卖给了医院和工厂。它在商业上真赚到了钱,是 1980 年代 AI 唯一有客户的形态。 这一节的重点不在结构,在「不确定」怎么处理。 MYCIN 那套置信度公式只有三行,但它做对了一件事、也留下了两个坑。
上一节的规则系统只会说「是」或「不是」。 可医生从来不说「是」,他说「八成是」。这一节补的就是这「八成」。 CF 这套东西后来被贝叶斯网络取代,第 6 章会讲贝叶斯那一支怎么算。
一、四个零件拼成一个能对话的系统
专家系统和上一节的规则系统,零件几乎一样,但多了一个关键角色。
| 零件 | 干什么 | 少了它会怎样 |
|---|---|---|
| 知识库 | 存规则和事实 | 什么都推不出 |
| 推理机 | 匹配规则、推进结论 | 知识是死的 |
| 解释器 | 回答「你怎么得出这个结论」 | 没人敢用 |
| 人机界面 | 用医生的词问、用医生的词答 | 医生不愿用 |
| 第三块是专家系统真正的卖点。用户问「为什么说要用抗生素」,系统能把整条链摊开: | ||
| 「因为白细胞升高、咳嗽有痰,所以判断细菌感染;因为细菌感染,所以建议用抗生素。」 | ||
| 这一段话今天看很朴素,但它是深度网络给不出的东西。一个几十层的网络告诉你「这张片子有问题」, | ||
| 你没法问它「你凭什么这么看」。 | ||
| 第四块也常被低估。 | ||
| MYCIN 的问诊顺序、用词、甚至「不知道」怎么说,都是按临床习惯设计的。 | ||
| 一个准确率 90% 但要求医生填 60 个字段的系统,没人会用。 |
二、不确定从三个地方来
第一,输入本身不确定。病人说「有点头晕」,这个「有点」没法量化; 体温 37.8 度是刚量的还是两小时前量的,意义不一样。 第二,规则本身不确定。专家说「咳嗽有痰多半是细菌感染」, 这个「多半」是他二十年经验的一个模糊总结,不是统计数字。 第三,检查有误差。白细胞计数有仪器误差,培养皿可能被污染。 三条加起来,一个只会说「是/不是」的系统根本没法用。它必须能输出「我有七成把握」。 早期试过用概率。 但用概率要写出 P(症状|疾病),还得保证疾病之间互斥且完备,临床上根本拿不到这些数。1975 年, 肖特利夫和布坎南在 MYCIN 里换了一套自己的记账法,叫置信度因子 CF。
三、CF 的三行公式
CF 取值 -1 到 +1。 +1 是确定成立,-1 是确定不成立,0 是毫无信息。规则带一个 CF,事实也带一个 CF, 推的时候两者相乘。 多条证据指向同一个结论时,要合并。公式分三个分支:
同为正: c = c1 + c2 * (1 - c1)
同为负: c = c1 + c2 * (1 + c1)
一正一负:c = (c1 + c2) / (1 - min(|c1|, |c2|))
第一个分支最好理解:新证据只能填补「剩下的不确定」。已经有 0.6 的把握,剩下 0.4 不确定,
新证据最多填 0.4 的 60%,于是变成 0.84。
第三个分支是「正负相抵」。分母那个 1 - min(|c1|, |c2|) 是「两边重合的那部分不算数」,
所以抵消得比简单相加更彻底。
这一套的关键性质是:它可交换、可结合。三条证据不管按什么顺序合并,
结果一样(Demo 里六种排列全是 0.75)。
这不是巧合,是公式特意凑出来的——不然推理结果会依赖规则的书写顺序,
那专家系统的答案就没法复现。
四、反证的权重不对称地大
Demo 第四段那张表是这一节最该记住的。
一条 -0.7 的反证,能把 0.3、0.5、0.6 的正证全部翻成负数;正好 0.7 才打平;0.8 以上才压得住。
分界线为什么是 0.7?
因为异号那个公式的分子是 c1 + c2,要让它大于 0,就得 c1 大于 |c2|。就这么直白。
这个性质在医学上是对的。一条「培养出病毒」的证据,
确实应该压过好几条「症状像细菌感染」的证据——后者是间接的,前者是直接的。
但它也埋了一个坑:CF 没有「证据之间是否独立」的概念。三条其实来自同一个检验的「证据」,
会被当成三条独立证据累加,把 CF 推到 0.94 以上。这在临床上就是过度自信。
五、为什么被统计方法取代
CF 有两个硬伤。 第一,那些数字是人给的,不是从数据里估的。「咳嗽有痰 → 细菌感染,CF 0.7」这个 0.7 是专家拍的, 换一个专家可能拍 0.6,换一家医院可能拍 0.5。系统继承了专家的判断,也继承了专家的偏差。 第二,CF 不是概率,不能拿来做决策。想知道「用抗生素的期望收益」,得算 P(细菌感染), 而 CF 换算不出这个概率。它只能排序,不能算账。 1988 年,珀尔把贝叶斯网络系统化, 让「不确定」第一次有了严格的概率语义,而且可以从数据里学。加上 1990 年代统计学习起来, CF 就退到教科书里了。 但别把这段读成「CF 是错的」。它解决的是「在拿不到先验概率的年代, 怎么让系统输出一个带程度的答案」。那个年代只有这一条路,它跑通了,而且真上了临床。 这一段的历史位置值得记住:规则系统教会机器「按人的逻辑推」,CF 教会机器「说自己有多确定」。 下一节讲推荐引擎,它把这两件事一起交给了数据。
六、动手 Demo:一套上呼吸道感染的规则,和 MYCIN 那三行合并公式
六条规则、四个症状、两个病、一个处置。规则和事实都带 CF,推理时规则 CF 乘以前提 CF,
同一结论的多条证据按 MYCIN 公式合并。
代码里 cf_add 就是那三行公式,combine 只是把它串起来。
没有训练、没有数据,所有数字都是人手填的。
def cf_add(c1, c2):
"""MYCIN 的置信度合并:同正、同负、异号,三个分支。"""
if c1 >= 0 and c2 >= 0:
return c1 + c2 * (1.0 - c1) # 剩下的不确定再打个折
if c1 < 0 and c2 < 0:
return c1 + c2 * (1.0 + c1)
return (c1 + c2) / (1.0 - min(abs(c1), abs(c2))) # 正负相抵
def combine(cs):
"""一串证据从 0 起累加。"""
v = 0.0
for c in cs:
v = cf_add(v, c)
return v
# 规则 =(前提, 结论, 置信度)
RULES = (((u'发烧',), u'感染', 0.6),
((u'咳嗽', u'有痰'), u'细菌感染', 0.7),
((u'咳嗽', u'无痰'), u'病毒感染', 0.6),
((u'白细胞升高',), u'细菌感染', 0.5),
((u'细菌感染',), u'用抗生素', 0.8),
((u'病毒感染',), u'用抗生素', -0.7))
print(combine([0.6] * 3)) # 0.936
print(cf_add(0.6, -0.7)) # -0.25,一条反证就翻过去
实跑输出:
--------------------------------------------------------------------
专家系统的置信度
--------------------------------------------------------------------
==============================================================
[1] 规则不只是「若 A 则 B」,还带一个可信程度
前提 结论 CF
发烧 感染 +0.6
咳嗽 且 有痰 细菌感染 +0.7
咳嗽 且 无痰 病毒感染 +0.6
白细胞升高 细菌感染 +0.5
细菌感染 用抗生素 +0.8
病毒感染 用抗生素 -0.7
CF 取值 -1 到 +1:+1 是确定成立,-1 是确定不成立,0 是毫无信息。
它和概率不是一回事——CF 不满足概率公理,它只是一个「可信程度」的记账法。
[2] 一次推理:从症状到用药建议
症状 细菌感染 病毒感染 用抗生素
只有发烧 +0.00 +0.00 +0.00
发烧 + 咳嗽有痰 +0.70 +0.00 +0.56
再加白细胞升高 +0.85 +0.00 +0.68
换成干咳 +0.00 +0.60 -0.42
干咳 + 白细胞升高 +0.50 +0.60 -0.03
第一行全是 0.00——0 在这里的意思是「毫无信息」,不是「确定不成立」。
只有发烧时,系统只知道「感染」,「感染」推不出细菌还是病毒,只能停在 0。
第三行是最典型的:细菌感染的证据累到 +0.85,
于是「用抗生素」被推到 +0.68。
第四行换了干咳,病毒感染的证据上来,抗生素的 CF 立刻被拉低。
[3] 同向证据叠加:越多越接近 1,但永远到不了 1
证据条数 CF 离 1 还差
1 0.6000 4.00e-01
2 0.8400 1.60e-01
3 0.9360 6.40e-02
5 0.9898 1.02e-02
10 0.9999 1.05e-04
20 1.0000 1.10e-08
每加一条 0.6 的证据,剩下的不确定就乘以 0.4。
20 条之后还差 1.10e-08——数学上永远填不满,
这跟贝叶斯不一样:贝叶斯里证据足够多,后验可以任意逼近 0 或 1。
CF 这么设计是有意的——它承认「没见到反证」不等于「确定成立」。
[4] 一条反证,要几条正证才压得住
正证 CF 叠加 -0.7 之后 结果
+0.3 -0.5714 翻成负数
+0.5 -0.4000 翻成负数
+0.6 -0.2500 翻成负数
+0.7 +0.0000 刚好抵消
+0.8 +0.3333 还是正的
+0.9 +0.6667 还是正的
分界线正好在 0.7:正证小于 0.7,一条 -0.7 就把它整个翻过去。
这是这一节最该记住的一条:反证的权重不对称地大。
一条「病毒感染」的证据,能顶掉好几条「细菌感染」的证据。
[5] 换个顺序算,结果一样吗
三条证据 +0.7、+0.5、-0.4,六种排列全部算一遍:
得到的最终 CF 有 1 种,值 = 0.750000
合并公式被设计成可交换、可结合的——不然推理结果会依赖规则的书写顺序,
那专家系统的答案就没法复现了。
这一点值得和上一节对比:那个规则系统里,规则顺序不影响结果,
是因为前向链是单调的;这里的 CF 公式能保证顺序无关,是数学上特意凑出来的。
[6] 和「非黑即白」比一比
写法 结论 遇到缺失信息时
布尔规则 是/否 推不出来,沉默
置信度 -1 到 +1 之间 给一个偏低的分数,还能用
上一节说的「系统不会报错,它只是沉默」,在 CF 这里缓解了一半:
证据不全时它给出的不是沉默,是一个带符号的分数:
表里最后那个 -0.03,意思是「两边的证据几乎抵消了」,
这比「推不出来」多了一点信息——它至少告诉你「有人提了反证」。
但代价是:那个 0.3 从哪来,谁也说不清。
CF 是人给的,不是从数据里估的——这正是专家系统后来被统计方法取代的原因之一。
第一段先把规则摆出来。注意最后一条的 CF 是负的——「病毒感染 → 用抗生素」给 -0.7, 意思是「如果是病毒感染,就别用抗生素」。规则系统靠「加一条负规则」来表达反对。 第二段跑五个病例。第一行全是 0.00,这个 0 的意思是「毫无信息」,不是「确定不成立」。 只有发烧时,系统只知道「感染」,推不出细菌还是病毒,只能停在 0。 第三行是标准案例:有痰 + 白细胞升高,细菌感染累到 0.85,抗生素被推到 0.68。 这两个数的关系是 0.8 乘 0.85——规则 CF 乘以前提 CF,这就是「不确定会往下传」。 最后一行最有意思:干咳 + 白细胞升高,细菌感染 0.50、病毒感染 0.60,两边几乎抵消, 抗生素只剩 -0.03。系统给出的不是「不知道」,是「两边都有证据,我倾向于不用」。 这个细微差别,布尔规则给不出来。 第三段量叠加。一条 0.6 是 0.6,两条 0.84,三条 0.936,五条 0.9898。 每加一条,剩下的不确定就乘 0.4。 表格最后两行的「离 1 还差」是 1.05e-04 和 1.10e-08。 数学上永远填不满。这跟贝叶斯不一样——贝叶斯里证据足够多,后验可以任意逼近 0 或 1。 CF 这么设计是有意的:它承认「没见到反证」不等于「确定成立」。 第四段是这一节的重点。正证 0.3、0.5、0.6 遇上 -0.7 全部翻负;0.7 正好抵消;0.8 才留 0.3333。 分界线就在 0.7,因为异号公式的分子是 c1 + c2。 第五段验证可交换性。三条 CF 六种排列,结果都是 0.75,只有 1 种值。 这一步在工程上是必需的:不然同一个病例换个录入顺序,结论就变了,没人敢用。 第六段做个收尾对比。布尔规则遇到缺失信息是沉默,CF 给一个带符号的分数。 多出来的信息是真的,但那个分数从哪来,谁也说不清。这是它后来被取代的根子。
30 秒实验
- 把最后那条规则的 CF 从 -0.7 改成 -0.4,重跑第二段。 看第五行怎么变——原来 -0.03 应该变成明显的正数。这就是「反证有多硬」对结论的影响。
- 给
combine传[0.6, 0.6, 0.6, 0.6, 0.6]和[combine([0.6]*5)],看两者是否相等。 前者是逐条累加,后者是「已经合并好的结果再合并」,结果应该一样——这就是结合律。 - 构造
combine([-0.6, -0.6, -0.6]),看负向叠加对称不对称。 对称的,值应该是 -0.936。不对称就说明公式抄错了。
常见误区
- 以为 CF 是概率。CF 不满足概率公理:三条 CF 0.6 的证据合并成 0.936, 可如果你按概率算「至少一条成立」, 那是 1 - 0.4 的三次方 = 0.936——两个结果碰巧一样,但含义完全不同。CF 说的是「我的把握」, 概率说的是「事件发生的频率」。
- 以为反证和正证权重一样。异号公式的分母把「两边重合的部分」扣掉了, 所以抵消得比简单相加更彻底。 一条 -0.7 能压过 0.6,这在直觉上是对的:一条直接证据比三条间接证据硬。
- 以为专家系统的失败是因为准确率不够。MYCIN 在测试里准确率约 69%,超过当时的一般医生。 它失败在「搬到第二个科室」,而且规则库维护成本随条数平方长。准确率不是问题。
- 以为 CF 已经死了。它的思路活在很多地方:搜索引擎的相关度打分、风控模型的规则分数、 甚至大模型输出的置信度。「给个带符号的分数」这件事,就是从 CF 这一支传下来的。
自测三问(点开对答案)
1. MYCIN 为什么不用概率,改用 CF? —— 因为用概率要写 P(症状|疾病), 还得保证所有疾病互斥且完备—— **这些数字在 1970 年代的临床上拿不到,而且疾病清单会变。** CF 只需要专家给一个「有多大把握」,一个人就能拍板,代价是丢失了概率语义, 不能拿来做期望收益计算。 2. 为什么一条 -0.7 能压过 0.6 的正证? —— 因为异号合并的公式是 `(c1 + c2) / (1 - min(|c1|, |c2|))`, **分子是简单相加,所以 c1 必须大于 |c2| 才能保持正号。**0.6 + (-0.7) = -0.1,负的; 0.7 + (-0.7) = 0,刚好抵消。**分母只是把幅度放大,不改变符号。** 3. CF 的两个硬伤是什么? —— 一,**数字是人给的,不是从数据估的**——换个专家、换家医院,同一个 CF 就变了, 系统继承判断也继承偏差。 二,**它不是概率,不能算期望**——只能给结论排序,不能回答「这么做划不划算」。 这两条合起来,就是它被贝叶斯网络取代的原因。专家系统把「怎么推」和「有多确定」都解决了,可它还是得有人写规则。 下一节讲推荐引擎——它是第一类「规则从数据里自己长出来」的系统。
- ☐ (本节完,下一节 2.4 推荐引擎)