🎯 本章要解决什么?从"知道事实"到"推出结论"
想象你有一个装满知识的"逻辑宝箱":
宝箱里有:
1. 所有人都会死
2. 苏格拉底是人
问题 :怎么自动得出"苏格拉底会死"?
这就是推断(Inference)------从已知逻辑语句推导出新语句的过程。
本章核心:一阶逻辑的推断比命题逻辑复杂得多,因为要处理:
- 变量(x, y, z...)
- 量词(∀, ∃)
- 函数(父亲(x), 加(2,3))
一、热身:命题推断 vs 一阶推断
📊 直观对比
| 推断类型 | 例子 | 复杂度 | 生活比喻 |
|---|---|---|---|
| 命题推断 | P→Q, P ⊢ Q | 相对简单 | 查菜谱做菜:按步骤来就行 |
| 一阶推断 | ∀x(P(x)→Q(x)), P(a) ⊢ Q(a) | 复杂得多 | 破案推理:要匹配线索、找嫌疑人 |
🔧 关键挑战:变量和量词
已知:∀x (猫(x) → 可爱(x))
猫(小花)
问:可爱(小花)?
步骤:
1. 从∀x (猫(x)→可爱(x)),可以推出对**任何具体对象**都成立
2. 用"小花"替换x:猫(小花)→可爱(小花)
3. 结合猫(小花),得到可爱(小花)
这个过程叫:全称实例化(Universal Instantiation)
二、约简为命题推断:把"一阶问题"降级
🎯 核心思想
既然命题推断算法(如归结)已经很成熟,能不能: 把一阶逻辑问题转换成命题逻辑问题,然后用现成工具解决?
📝 转换步骤
1. 去掉量词(全称实例化)
原知识库:
∀x (猫(x) → 哺乳动物(x))
猫(小花)
猫(小白)
转换后:
猫(小花) → 哺乳动物(小花)
猫(小白) → 哺乳动物(小白)
猫(小花)
猫(小白)
问题:如果论域无限(如所有整数),会生成无限多语句!
2. 函数项的处理更麻烦
原:∀x (加(x,0)=x)
论域:所有自然数 {0,1,2,3,...}
实例化:
加(0,0)=0
加(1,0)=1
加(2,0)=2
...
无限!
3. 存在量词∃的挑战
原:∃x (猫(x) ∧ 黑(x))
不能简单实例化为:猫(A) ∧ 黑(A)
因为不知道A具体是谁!
需要引入**新常量**(Skolem常量):
猫(Sk1) ∧ 黑(Sk1)
Sk1代表"那个存在的黑猫"
⚠️ 约简法的局限性
优点:可以利用成熟的命题逻辑算法
缺点:
1. 可能生成无限多命题(论域无限时)
2. 效率低下(生成太多实例)
3. 存在量词需要特殊处理
所以:需要专门为一阶逻辑设计的推断算法!
三、合一:一阶推断的"匹配引擎"
🤔 问题场景
已知规则:∀x (猫(x) → 可爱(x))
已知事实:猫(小花)
目标:证明可爱(小花)
我们需要把规则中的x和事实中的"小花"匹配起来
🔍 什么是合一(Unification)?
合一 :找到变量的替换,使两个逻辑表达式变得相同。
例子1:简单合一
表达式1:猫(x)
表达式2:猫(小花)
替换:{x/小花}
结果:猫(小花) = 猫(小花) ✓
例子2:多变量合一
表达式1:父亲(x,y)
表达式2:父亲(张三,z)
替换:{x/张三, y/z}
或:{x/张三, z/y}
结果:父亲(张三,y) = 父亲(张三,z)(y和z都代表同一个未知)
例子3:函数项合一
表达式1:父亲(父亲(x))
表达式2:父亲(y)
替换:{y/父亲(x)}
结果:父亲(父亲(x)) = 父亲(父亲(x)) ✓
❌ 不能合一的情况
1. 常量冲突:
猫(小花) vs 猫(小白) → 不能合一(小花≠小白)
2. 出现检查(Occurs Check):
x vs 父亲(x) → 不能合一(x不能等于包含x的表达式)
否则会出现:x = 父亲(x) = 父亲(父亲(x)) = ... 无限循环!
📋 合一算法(伪代码版)
function 合一(表达式1, 表达式2, 替换集):
if 替换集包含冲突: return 失败
if 表达式1 == 表达式2: return 替换集
if 表达式1是变量:
return 合一变量(表达式1, 表达式2, 替换集)
if 表达式2是变量:
return 合一变量(表达式2, 表达式1, 替换集)
if 表达式1和表达式2都是复合项:
return 合一(表达式1的各个部分, 表达式2的各个部分, 替换集)
return 失败
四、前向链接:从已知事实"向前推"
🚀 工作方式
前向链接 :像数据驱动的推理,不断用规则推出新事实,直到推出目标或无法再推。
生活比喻:破案时收集线索
已知线索(事实):
1. 现场有猫毛
2. 邻居听到猫叫
规则:
如果有猫毛→可能有猫
如果听到猫叫→可能有猫
推出:可能有猫(新事实)
再用新规则:如果有猫→调查宠物店记录...
📝 算法步骤
1. 初始化:所有已知事实放入"已证事实集"
2. 循环:
a. 遍历所有规则
b. 对每条规则:如果前提都能合一匹配到已证事实
c. 应用合一得到的替换,推出结论(新事实)
d. 新事实加入已证事实集
3. 直到:推出目标事实,或没有新事实产生
🌰 具体例子:家族关系
已知事实:
父母(张三, 李四)
父母(张三, 王五)
男性(张三)
女性(李四)
男性(王五)
规则:
1. ∀x∀y (父母(x,y) ∧ 男性(x) → 父亲(x,y))
2. ∀x∀y (父母(x,y) ∧ 女性(x) → 母亲(x,y))
3. ∀x∀y (父亲(x,y) → 子女(y,x))
前向链接过程:
步骤1:用规则1 + 事实父母(张三,李四)+男性(张三)
→ 父亲(张三,李四)(新事实)
步骤2:同理 → 父亲(张三,王五)
步骤3:用规则3 + 父亲(张三,李四)
→ 子女(李四,张三)
步骤4:同理 → 子女(王五,张三)
...
⚡ 高效前向链接的技巧
1. 索引化存储
不要每次遍历所有事实!
建立索引:
谓词"父母" → [父母(张三,李四), 父母(张三,王五)...]
谓词"男性" → [男性(张三), 男性(王五)...]
2. 增量更新
新事实产生时,只检查相关规则
而不是每次重新检查所有规则
3. 确定子句(Definite Clause)限制
规则必须是这种形式:
P₁ ∧ P₂ ∧ ... ∧ Pₙ → Q
其中Q是单个原子(不能是¬Q或P∨Q)
好处:推出的事实总是确定的(真/假)
✅ 前向链接适合的场景
- 需要推出所有可能结论(如监控系统)
- 数据不断到达(如传感器数据流)
- 规则前提都是具体事实(没有"不存在"这种否定前提)
五、反向链接:从目标"向后找"
🎯 工作方式
反向链接 :像目标驱动的推理,从目标出发,找能推出它的规则,再递归证明规则的前提。
生活比喻:医生诊断
目标:证明病人得流感
找规则:发烧∧咳嗽∧流鼻涕→流感
子目标1:证明发烧
子目标2:证明咳嗽
子目标3:证明流鼻涕
📝 算法步骤(递归版)
function 反向链接(目标, 知识库):
if 目标在已证事实中: return True
if 目标已被标记为失败: return False
标记目标为正在证明(防循环)
对每条能推出目标的规则:
对规则中的每个前提:
if not 反向链接(前提, 知识库):
跳出,试下一条规则
所有前提都成立 → 目标得证,return True
标记目标为失败
return False
🌰 具体例子:证明"李四是张三的子女"
知识库:
事实:父母(张三,李四)
规则:∀x∀y (父母(y,x) → 子女(x,y))
目标:子女(李四,张三)
反向链接过程:
1. 目标:子女(李四,张三)
2. 匹配规则:父母(张三,李四) → 子女(李四,张三)
(合一:{x/李四, y/张三})
3. 新子目标:父母(张三,李四)
4. 在事实中找到!✓
5. 目标得证 ✓
💻 逻辑编程:Prolog语言
Prolog就是基于反向链接的逻辑编程语言:
% 知识库(规则和事实)
父母(张三, 李四).
子女(X, Y) :- 父母(Y, X). % 规则:如果父母(Y,X)则子女(X,Y)
% 查询
?- 子女(李四, 张三). % Prolog回答:Yes
⚠️ 反向链接的陷阱
1. 冗余推断
规则1:A → B
规则2:B → A
查询:A?
会无限循环:A→需要B→需要A→需要B...
2. 无限循环
规则:祖先(X,Y) :- 父母(X,Z), 祖先(Z,Y).
事实:父母(张三,李四), 父母(李四,张三) % 循环关系!
查询祖先(张三,王五)可能无限递归
3. Prolog的数据库语义
Prolog使用封闭世界假设:
- 知识库没说为真 → 就认为假
- 使用深度优先搜索(可能陷入死胡同)
- 使用回溯(失败时尝试其他选择)
🔗 约束逻辑编程
传统反向链接:值在推理过程中确定
约束逻辑编程:先建立约束关系,最后一起求解
例:解方程
传统:X=1? 不行→X=2? 不行→X=3?...
约束:X>0, X<10, X是偶数 → 直接得到{2,4,6,8}
六、归结:一阶逻辑的"终极证明武器"
🎯 归结法回顾(命题逻辑版)
已知:P ∨ Q
¬P ∨ R
归结:Q ∨ R (消去P和¬P)
思想:如果P成立则R必须成立,如果P不成立则Q必须成立
所以要么Q成立,要么R成立
🔥 一阶逻辑归结:加入合一
已知:猫(x) ∨ 黑(x)
¬猫(小花)
不能直接归结!因为猫(x)和¬猫(小花)不完全相反
步骤:
1. 合一:{x/小花},使猫(x)=猫(小花)
2. 应用替换:猫(小花) ∨ 黑(小花)
3. 与¬猫(小花)归结:黑(小花)
📋 一阶归结算法步骤
1. 将知识库和目标否定转化为合取范式(CNF)
2. 不断归结,直到:
a. 推出空子句(□)→ 原目标成立
b. 无法再归结 → 原目标不成立
🌰 归结证明示例(简化版)
要证明:苏格拉底会死
知识库:
1. ∀x (人(x) → 会死(x))
2. 人(苏格拉底)
目标:会死(苏格拉底)
步骤:
1. 将知识库转化为CNF:
(1) ¬人(x) ∨ 会死(x)
(2) 人(苏格拉底)
2. 否定目标:¬会死(苏格拉底)
3. 归结:
(a) ¬人(x) ∨ 会死(x)
(b) 人(苏格拉底)
(c) ¬会死(苏格拉底)
用合一{x/苏格拉底}:
(a)变成:¬人(苏格拉底) ∨ 会死(苏格拉底)
与(b)归结:会死(苏格拉底)
与(c)归结:□(空子句,矛盾!)
4. 矛盾说明原目标成立:苏格拉底会死 ✓
📊 图9-10解析:证明"韦斯特有罪"
知识库包含:
American(x) ∨ ¬Weapon(y) ∨ ¬Sells(x,y,z) ∨ ¬Hostile(z) ∨ Criminal(x)
¬Criminal(West)
American(West)
Missile(x) ∨ Weapon(x)
...
(图中展示归结步骤,每一步消去一对互补文字)
最终推出空子句,证明Criminal(West)成立
🎯 归结策略:提高效率的技巧
1. 单元归结
优先使用单文字子句归结
例:有子句P和¬P∨Q,先归结这两个
2. 输入归结
每次归结至少有一个输入是原始子句(不是中间结果)
避免生成太多中间子句
3. 线性归结
保持线性推导链,每个新子句都和前一个子句归结
4. 集支持归结
优先归结涉及目标否定的子句
更快导向矛盾
✅ 归结的完备性
好消息:一阶归结是完备的!
- 如果语句逻辑蕴含结论,归结一定能证明
- 如果无法证明,要么结论不成立,要么一直运行下去(半可判定)
坏消息:可能非常慢,甚至无限循环(一阶逻辑不可判定)
七、等词的处理:说"两个东西相同"
🤔 等词(=)的特殊性
等词不是普通谓词!
普通谓词:父亲(张三,李四) 只是陈述事实
等词:父亲(小明)=张三 表示"是同一个对象"
🔧 等词公理
要在归结中使用等词,需要添加公理:
1. 自反性:∀x (x=x)
2. 对称性:∀x∀y (x=y → y=x)
3. 传递性:∀x∀y∀z (x=y ∧ y=z → x=z)
4. 替换性:∀x∀y (x=y → (P(x)↔P(y))) 对所有谓词P
⚡ 等词归结的特殊规则
参数化归结:允许用等词进行替换
例:已知P(a)和a=b,可以推出P(b)
八、实际应用与选择指南
📊 算法选择矩阵
| 场景 | 推荐算法 | 理由 |
|---|---|---|
| 实时监控(如入侵检测) | 前向链接 | 数据驱动,新事实立即触发推理 |
| 问答系统(如"谁是谁的父亲") | 反向链接 | 目标驱动,只求所需答案 |
| 定理证明(数学证明) | 归结 | 完备,能处理复杂逻辑 |
| 数据库查询 | 反向链接+索引 | 类似SQL查询优化 |
| 有函数和等词 | 归结+等词处理 | 其他算法处理等词困难 |
⚡ 性能优化技巧
- 索引化:为每个谓词建索引,快速查找
- 子句排序:简单子句先处理,复杂子句后处理
- 缓存结果:记住已证明的目标,避免重复计算
- 启发式:优先处理涉及常量的子句,减少变量匹配
⚠️ 一阶推断的现实限制
理论很美好,现实很骨感:
1. **计算复杂度**:一阶逻辑推断是半可判定的(可能永远算不完)
2. **知识工程难度**:把现实知识写成逻辑规则很难
3. **不确定性**:现实世界充满"可能"、"大概"
4. **规模问题**:知识库太大会组合爆炸
所以实际系统常:
- 使用受限子集(如确定子句)
- 结合概率(下一部分内容)
- 使用专门领域推理机
🧠 第9章思维升华
从"静态知识"到"动态推理"
- 知识表示 (第8章):建立逻辑世界模型
- 逻辑推断 (本章):让模型运转起来,产生新知识
推理的哲学
前向链接:经验主义------从观察出发,归纳规律
反向链接:理性主义------从假设出发,验证理论
归结法:归谬法------假设结论错,推出矛盾
实际系统设计启示
没有"最好"的算法,只有"最适合"的算法:
- 专家系统常用反向链接(Prolog风格)
- 业务规则引擎常用前向链接(数据触发)
- 数学证明器常用归结(追求完备性)
- 实际系统常混合使用
连接后续章节
本章:确定性逻辑推理
第13-14章:概率推理(处理不确定性)
第10章:知识表示扩展(处理默认、例外)
现实AI系统 = 逻辑推理 + 概率 + 学习 + 搜索
最后一句大实话 :
一阶逻辑推断就像"逻辑计算器"------给定规则和事实,能算出新结论。
但现实问题往往没有明确规则,或规则互相矛盾,或信息不全。
所以AI需要更灵活的工具:概率推理、机器学习、常识推理...