简介:这份百度人工智能经典考试试题文档面向准备课程期末、考研复试或AI岗位面试的学习者,围绕人工智能基础概念与经典题型组织内容。文档为单一doc文件,压缩包约279KB,包含选择题、填空题、简答题与应用题四类,覆盖语义网络、归结推理、知识表示、机器学习、自然语言处理等核心知识点。题目配有参考答案,应用题涉及语义网络建模、α-β剪枝、谓词逻辑祖孙关系推理,简答计算题包含三值逻辑表、产生式语义、子句集求解步骤与MGU计算,便于读者按题型查漏补缺。文末附知识点总结与详细说明,可辅助梳理概念边界和答题思路。已有1588人学习,适合需要系统刷题、快速回顾人工智能基础知识体系的学习者。
1. 一份 AI 笔试题为什么值得翻:它把“人工智能”拆成了能考的名词
很多人第一次翻人工智能的笔试题,反应都差不多:这些词好像都见过,可真要开口解释,一个都说不清。它有点像考驾照的科目一——题目本身不难,却逼你把“待转区”“导向车道”“让行标志”这些天天路过、从没细想的东西,一个一个对上号。这套流传很广的经典试题干的就是这件事:它不问你会不会调模型、会不会写训练脚本,只问你能不能把归结原理、产生式、语义网络、可信度这些地基概念讲明白。把这些词捋顺了,再看今天那些热闹的 AI 新闻,才分得清哪些是新技术,哪些只是老概念换了身衣服重新出场。
2. 归结原理凭什么能“自动证明”:空子句、合一与 MGU 三件套
2.1 反证法加空子句:机器证明定理的固定套路
人证明一个定理,可以画辅助线、可以构造反例、可以灵光一闪。机器不行,机器只会做符号操作,所以它需要一套“闭着眼睛也能走完”的流程。归结原理(也叫消解)就是这套流程里最经典的一条:把要证的结论先否定掉,扔进前提堆里,然后两两配对、不断化简,直到推出一个空子句。
空子句记作 □,它没有任何文字,意思是“矛盾”。一旦推出来,就说明“结论的否定”和前提打架了,结论必然成立。这也是选择题里那句“当前归结式是空子句时定理得证”的由来——不是永真式,不是包孕式,就是空子句。永真式只能说明这一支没矛盾,包孕式只是子句之间可以互相吸收,都不能宣告证明结束。
归结的动作本身很朴素。假设有两个子句,一个形如 C1 = L ∨ C1',另一个形如 C2 = ¬L ∨ C2',它们含有一对互补的文字 L 和 ¬L。把这对互补文字消掉,剩下的部分拼起来,就是归结式 C = C1'σ ∨ C2'σ。这里的 σ 是一个替换,负责把两个子句里的变量对齐。通俗地说,就是“你有‘天下雨’,我有‘没下雨’,这两句不能同时成立,那剩下的部分总得有人负责”。
拿试题里那道祖孙关系的题举例。已知“如果 x 是 y 的父亲,y 又是 z 的父亲,那么 x 是 z 的祖父”,再已知“甲是乙的父亲”“乙是丙的父亲”,问谁是谁的祖父。把它写成子句:
① ¬F(x,y) ∨ ¬F(y,z) ∨ G(x,z) 从规则变换 ② F(甲,乙) 从事实变换 ③ F(乙,丙) 从事实变换 ④ ¬G(u,v) 结论的否定 ⑤ ¬F(乙,z) ∨ G(甲,z) ①②归结,置换 {甲/x, 乙/y} ⑥ G(甲,丙) ③⑤归结,置换 {丙/z} ⑦ □ ④⑥归结,置换 {甲/u, 丙/v}第七步推出空子句,说明“甲是丙的祖父”这个结论成立。注意每一步置换都写清楚了谁替换谁,这不是仪式感,而是因为机器必须知道自己在对哪个变量动手。
反证法这套路子有它的好处:不需要人类式的直觉,规则统一,步骤可检查。代价是搜索空间会膨胀,子句一多,两两配对的数量就爆炸,所以才有了后面各种归结策略——删除策略、支持集策略、线性归结、单元归结,本质上都是在给这场“配对游戏”限流。
提示:归结证明只回答“成立不成立”,不直接给出“具体是谁”。想要机器把答案也算出来,得在结论的否定里留一个变量,最后靠重言式把那一步接回来,试题里祖父那道题的后半段做的就是这个动作。
2.2 MGU 到底在求什么:把两个式子“对齐”的最小代价
归结能不能进行,前提是两个子句里存在一对互补文字;而这对文字要真能消掉,形状必须完全一致。比如 P(f(x), y) 和 ¬P(f(z), g(z)),一个第二项是 y,另一个是 g(z),不处理就没法配对。合一(unification)就是找一组替换,让两个原子公式变成同一个东西。
问题在于,能让它们一样的替换往往不止一个。{f(A)/x, g(A)/y, A/z} 可以,{f(w)/x, g(w)/y, w/z} 也可以,甚至把变量全换成常量也能对上。但归结要的是“最省、最通用”的那个,因为替换越具体,丢失的信息越多,后面越难继续推。这个最通用的替换就叫最一般合一,英文缩写 MGU。
所谓“最一般”,通俗讲就是:别的合一替换都能由它再复合一层得到,而它自己不能再被简化。它是这批替换里的“最小公倍数”。
求 MGU 的经典办法是差异集法,一步一步来:
- 令 k = 0,初始替换 δ₀ 为空,集合 S₀ 为原始公式集。
- 如果 S 已经是单元素集,结束,δ 就是 MGU。
- 否则找出第一个差异集 D,看里面有没有“变量 + 不含该变量的项”这种组合。
- 用项替换变量,得到新替换和新集合,k 加一,回到第 2 步。
试题里那道 S = {P(f(x), y, g(y)), P(f(x), z, g(x))} 就是标准演练。第一次比较,第一个参数都是 f(x),第三个参数 g(y) 和 g(x) 有差异,但先看第二个参数 y 和 z,差异集是 {y, z},把 z 替换 y,得 S₁ = {P(f(x), z, g(z)), P(f(x), z, g(x))}。再比,差异集 {z, x},把 z 替换 x,得 S₂ = {P(f(z), z, g(z))},单元素了,MGU = {z/y, z/x}。
这个流程写成代码大概长这样:
def unify(a, b, subst=None): """极简合一的骨架:a、b 是两个项,subst 记录已有替换""" if subst is None: subst = {} if a == b: return subst if is_var(a): # a 是变量 return bind(a, b, subst) if is_var(b): # b 是变量 return bind(b, a, subst) if is_compound(a) and is_compound(b): if a.name != b.name: # 谓词名不同,直接失败 return None for x, y in zip(a.args, b.args): # 逐参数递归合一 subst = unify(x, y, subst) if subst is None: return None return subst return None # 常量不同,失败 def bind(var, term, subst): if var in subst: # 变量已有绑定,先展开再比 return unify(subst[var], term, subst) if occurs(var, term): # 出现检查,防止 x = f(x) 这种循环 return None new = dict(subst) new[var] = term return new这段代码的重点不在语法,而在两个判断。一个是“谓词名不同直接失败”,因为 P 和 Q 永远合不到一起;另一个是出现检查,如果不做,遇到 x = f(x) 这种就会无限展开,程序当场卡死。这也是手算时容易漏掉的一步,考试里写成“y 不在 z 中出现”就是在补这个条件。
2.3 子句集化的八个步骤:顺序为什么不能乱
归结的输入必须是子句集,也就是一堆“文字的析取”组成的集合。现实中的命题往往带蕴含、带等价、带量词,所以得先做一次标准化,这就是试题里反复出现的“谓词公式 G 化成子句集 S 的八个步骤”。
顺序是这样的:先消去蕴含式和等价式;再把否定词往里推,一直推到只作用于原子公式;然后给变元改名,保证不同量词用的变元名不重复;接着消去存在量词,用 Skolem 函数代替;再消去全称量词;化成合取范式;子句之间再改一次名;最后去掉合取符号,用逗号分隔,得到子句集。
这里面最容易翻车的是第四步。消去存在量词时,如果它前面没有全称量词,就直接换成一个新常量;如果它前面有全称量词 ∀y,就必须换成一个关于 y 的函数 f(y),而不是常量。原因很直白:那个“存在的 x”是依赖 y 的,不同的 y 可能对应不同的 x,写成常量就把这种依赖关系抹平了,后面推出来的东西自然不对。这就是 Skolem 函数的由来,试题里专门有一道选择题考它。
第七步的改名也常被忽略。两个子句各自有一个 x,不做区分的话,归结时一替换就把两边污染了。改完名,每个子句的变量互不相干,配对才安全。
注意:这八步是有依赖关系的,不能跳着做。先 Skolem 化再消全称量词,依赖关系就丢了;先化范式再改名,量词辖域已经看不出来了。
3. 产生式系统与语义网络:两种“把知识塞进机器”的方式
3.1 产生式系统的三件套:规则库、数据库、推理机
机器要有智能,先得有知识。可知识在纸上是一段话,在机器里必须变成能匹配、能执行的结构。产生式系统就是最早、也最直白的一种结构,它把知识写成一条条“如果……那么……”的规则。
一条产生式的基本形式是 P → Q,读作“如果前提 P 成立,那么结论 Q 成立”或者“如果条件 P 满足,那么执行动作 Q”。P 叫前件,负责判断这条规则能不能用;Q 叫后件,负责给出结论或者动作。它的语义非常朴素:前提被满足,就推出结论或执行操作。没有概率,没有权重,就是一条硬规则。
整个系统由三部分组成。规则库是领域知识的集合,装着所有产生式;综合数据库(也叫工作内存)存放当前已知的事实,包括初始事实、外部输入、推理中间结果和最终结果;推理机是控制系统,负责拿数据库里的事实去规则库里匹配,挑出可触发的规则,执行它,再把新结论写回数据库,循环往复。
这里有个细节常被考到:当多条规则同时可触发时,推理机得选一条执行,被选中的那条叫触发规则。选谁不选谁,靠冲突消解策略,比如优先级高的先来、新事实先来、规则顺序靠前的先来。这个机制看着不起眼,实际影响很大——同样的规则库,换个消解顺序,可能得到不同的推理路径。
拿试题里那道“1 到 9 排个序列,使前 n 位组成的整数能被 n 整除”来说,它就是把人类解题时的直觉写成了规则:
r1: IF 序列长度 = 4 THEN 第五位放 5 r2: IF 长度为偶数 且 n ∈ {1,3,7,9} THEN 追加 n r3: IF 长度为奇数 且 n ∈ {2,4,6,8} THEN 追加 n第一条来自“第五位必须是 5”这个数学事实,第二、三条来自“偶数位放偶数、奇数位放奇数”的整除性质。规则本身很短,但每一条背后都是人对问题的理解。产生式系统的价值就在这儿:它把“怎么想”显式写了出来,机器照着做就行,做错了也能回头查是哪条规则写歪了。
3.2 正向推理和反向推理差在哪:从事实出发还是从目标倒推
有了规则库,推理机还得决定往哪个方向走。这就是正向推理、反向推理、双向推理的区别,也是选择题里的常客。
正向推理叫数据驱动。它从已知事实出发,拿事实去规则库里找前件能被满足的规则,推出新结论,新结论再加进事实堆,继续推,直到推出目标或者推不动为止。它的思路是“我手里有什么,就能算出什么”。
反向推理叫目标驱动。它从要证的结论出发,看哪条规则的后件能产出这个结论,然后把那条规则的前件变成新的子目标,再去找能推出这些子目标的规则,一路往下拆,直到子目标全部能和已知事实对上。它的思路是“我要这个结果,那得先满足什么条件”。
| 对比项 | 正向推理 | 反向推理 |
|---|---|---|
| 起点 | 已知事实 | 待证目标 |
| 驱动方式 | 数据驱动 | 目标驱动 |
| 适合场景 | 事实多、结论少,比如监控告警 | 目标明确、分支多,比如故障诊断 |
| 主要开销 | 可能推出大量无关结论 | 可能反复拆解无关子目标 |
| 典型问法 | “这些数据能说明什么” | “这个结论需要什么条件” |
举个例子。一个设备诊断系统,如果传感器一上来就报了十几条异常,那适合正向推理,从这些异常往下推可能的原因。反过来,如果运维只想知道“是不是电源模块坏了”,那适合反向推理,从“电源故障”这个假设出发,去找它需要哪些证据,再逐条核对。方向选错,不是算不出来,是算得特别慢,把时间浪费在无关分支上。
双向推理则是两头一起走,在中间碰头。它省时间,但控制复杂,得判断什么时候两边接上了。
3.3 语义网络里的 AKO 和 ISA:为什么它天生会“继承”
产生式把知识写成规则,语义网络换了个思路:把知识画成图。节点代表概念或实体,有向边代表关系,一张图就是一片知识。
这张图里最常出现的两种边是 ISA 和 AKO。ISA 表示“是一个”,通常连接个体和它所属的类,比如“麻雀 ISA 鸟”;AKO 是 a-kind-of 的缩写,表示“是一种”,连接子类和父类,比如“鸟 AKO 动物”。两者的区别在于层级:ISA 更偏向个体归入类别,AKO 更偏向类别归入更大的类别。试题里那句“有向弧 AKO 链、ISA 链是用来表达节点知识的继承性”,说的就是它们最核心的作用——下层节点自动拥有上层节点的属性。
为什么能自动拥有?因为语义网络的结构本身就是按分类组织的。你在“鸟”这个节点上挂一条“有羽毛”,那么所有 ISA 指向“鸟”的节点,都默认有羽毛,不需要在每个节点上重复写一遍。这就是继承性,也是语义网络比一堆孤立事实好用的地方:知识可以层层复用,改一处,下面全跟着变。
动物 ↑ AKO 鸟 ——有→ 羽毛 ↑ ISA 麻雀 ——颜色→ 棕色在这个小图里,麻雀沿着 ISA 往上找到鸟,再沿着 AKO 往上找到动物,一路把“有羽毛”“会动”这些属性继承下来。查询的时候不用逐条比对,顺着边走就行。
但继承性也是它的坑。鸟会飞,这条属性挂在“鸟”上没问题,可企鹅也是鸟,企鹅不会飞。这时候要么给企鹅单独加一条“不会飞”覆盖掉继承来的值,要么把“鸟”拆成“会飞的鸟”和“不会飞的鸟”两个子类。前一种做法省事,但覆盖规则一多,图就变得难维护;后一种做法干净,但类目会膨胀。这个取舍到今天还在知识图谱的工程里反复出现,只是换了个说法叫“本体设计”。
4. 常见误区:这几个概念最容易被混成一锅粥
4.1 误区一:把“正向推理”当成“正向搜索”
这两个词都带“正向”,初学者很容易当成一回事。会混是因为它们都表现为“从起点往下走”,看起来方向一致。
区别在于处理的对象不同。正向推理处理的是知识和事实,它的每一步是“匹配规则、推出新结论”,产出的是新命题;正向搜索处理的是状态空间,每一步是“从当前状态转移到下一个状态”,产出的是路径。前者关心“能推出什么”,后者关心“怎么走到目标”。判断方法很简单:如果这一步的结果是一条可以判断真假的知识,那是推理;如果结果是一个新的局面或位置,那是搜索。
4.2 误区二:MGU 就是“随便找个替换”
手算合一的时候,很多人凑出一组能对上号的替换就收工了,觉得反正都是合一,差不多。会混是因为“合一”这个词本身就指“让两边一致”,看起来任何能一致的替换都合格。
差别在于后续还能不能继续推。一个过于具体的替换会把变量钉死成常量,虽然当下对上了,但别的子句再想用这个变量就没办法了。MGU 要求的是最一般,也就是“能覆盖所有其他合一方案”的那个。检验办法是看这组替换能不能再被泛化:如果还能把某个常量换回变量而依然合一,那它就不是 MGU。
4.3 误区三:可信度 CF 当成概率用
CF 是可信度因子,取值在 -1 到 1 之间,正数表示相信,负数表示不相信,0 表示不知道。概率取值在 0 到 1 之间。两者长得像,用起来也都在描述“有多大把握”,所以经常被混着用。
问题出在运算规则上。CF 的合取取最小值、析取取最大值、否定取相反数,这套规则是人工定的经验公式,不满足概率的公理体系;概率的乘法、加法公式是从公理推出来的,两者不能互换。把 CF 当概率算,最常见的翻车是几个证据一叠加,算出来的数超过 1 或者出现明显违背直觉的结果。看到 -1 到 1 这个区间,就该反应过来这是可信度,不是概率。
4.4 误区四:α-β 剪枝是“少算一些节点所以结果会变”
听名字容易以为剪枝是一种近似,为了快牺牲准确性。会混是因为日常说的“剪枝”经常带着“砍掉不重要的部分”的意思。
实际上 α-β 剪枝剪掉的都是“无论算出什么都不会影响上层选择”的分支,最终倒推值和完整搜索一模一样。它不是近似算法,是等价优化。判断依据是剪枝条件本身:只有当某个节点已经不可能改变祖先节点的决策时,才停止往下搜。如果剪完发现结果和完整搜索对不上,那不是剪枝的锅,是估值或者节点顺序写错了。
4.5 误区五:语义网络和知识图谱是一回事
两者都是“节点加边”的结构,都能表达实体和关系,视觉上几乎一样,所以常被直接画等号。
差在规模和严谨度上。语义网络是上世纪的一种知识表示方法,结构相对自由,边的含义靠约定;知识图谱是工程化的产物,通常有本体约束、有实体对齐、有大规模存储和查询体系。可以说知识图谱吸收了语义网络的思想,但不能说语义网络就是知识图谱。判断方法看场景:一道考题里画的几个节点几张弧,那是语义网络;一套带 schema、带实体消歧的系统,那是知识图谱。
5. 搜索与博弈里的三个硬骨头:状态图、启发函数与 α-β 剪枝
5.1 状态空间图:先把问题画成一张图
走迷宫、下棋、八数码,这些问题表面各不相同,抽象之后是同一个东西:一个初始状态,一组可以施加的操作,一个或一组目标状态。把状态当节点,把操作当有向边,整个问题就变成了一张图,叫状态空间图。
这一步抽象很关键。不画图,你面对的是“怎么把这块挪过去”的直觉;画了图,你面对的是“从哪个节点走到哪个节点”的路径问题,广度优先、深度优先、有界深度优先这些算法才有地方落脚。试题里问“如果问题存在最优解,哪种搜索必然能得到”,答案落在广度优先上,原因就是它按层扩展,第一次碰到目标时走的一定是最短步数。
代价也很明显。广度优先要把同一层的节点全存下来,空间开销随分支因子指数增长。八数码还能扛,真实棋盘就不行了。所以才需要下面两个东西。
5.2 启发函数:给搜索装一个“方向感”
无信息的搜索是瞎子摸象,每个方向都试一遍。启发式搜索多做一件事:给每个待扩展节点算一个估值,优先扩展看起来最有希望的那个。这个估值函数就叫启发函数。
估值通常写成 f(n) = g(n) + h(n)。g(n) 是从起点走到当前节点的实际代价,h(n) 是从当前节点到目标还有多远的估计。g 负责“已经花了多少”,h 负责“还差多少”。h 选得好,搜索会直奔目标;h 选得差,等于没有方向感,退化成普通搜索。
启发函数有个重要性质叫可采纳性:h(n) 永远不能高估真实剩余代价。一旦高估,算法可能错过真正的最优路径,因为它以为某条路更贵,其实那条路更便宜。这个约束在考试里经常以“哪个启发函数是可采纳的”形式出现,判断标准就是拿它和真实距离比一比,看有没有超。
提示:h(n) 全部取 0,启发式搜索就退化成一致代价搜索;h(n) 取得越大越快,但超过真实代价就不再保证最优。这是速度和正确性之间的老取舍。
5.3 α-β 剪枝:剪掉的是重复劳动,不是正确答案
二人博弈里最经典的分析方法是极小极大法:先把博弈树展开到叶子,叶子给出估值,然后一层取最大、一层取最小,倒推回根节点,选出最好的一步。问题是树太大,国际象棋的分支因子几十,展开几层就是天文数字。
α-β 剪枝的思路是:一边倒推一边判断,某些分支根本不用看完。α 表示极大值层当前已知的最好选择,β 表示极小值层当前已知的最好选择。搜索过程中,如果某个极小值节点的 β 已经小于等于它祖先极大值节点的 α,说明这个节点再往下算出什么都不可能被祖先选中,直接停;反过来,如果某个极大值节点的 α 已经大于等于祖先极小值节点的 β,也同样停。
α 剪枝:α(先辈层) ≥ β(后继层) → 停掉这个 MIN 节点以下的搜索 β 剪枝:α(后继层) ≥ β(先辈层) → 停掉这个 MAX 节点以下的搜索这两条条件看着绕,本质是一句话:当一层已经发现“无论这个分支多好,上一层都不会选它”时,就没必要再算了。剪掉的分支不会改变根节点的倒推值,所以结果是精确的。
剪枝的效率高度依赖节点展开顺序。理想顺序下,复杂度能从指数级降到大约分支因子的平方根级别;最坏顺序下,一条都剪不掉,白忙一场。这也是为什么工程实现里要先做走法排序,把看起来好的走法排在前面——排序本身要花时间,但省下来的搜索量通常远大于这点开销。
6. 这套老知识什么时候用得上,什么时候别硬套
先说结论:这套东西不是用来应付考试的摆设,它是一套“把模糊问题变清楚”的语言。你在设计一个规则引擎、排查一个推荐策略、跟人解释为什么某个模型给出了奇怪结论时,这些概念会突然变得很好用。
什么时候适合拿它出来:
| 场景 | 用哪个概念 | 为什么合适 |
|---|---|---|
| 要把一堆业务规则写成可执行逻辑 | 产生式系统 | 规则和事实分离,改规则不动代码 |
| 要验证某个结论在给定前提下是否必然成立 | 归结原理 | 反证法步骤可检查,出错能定位到某一步 |
| 要表达一批有层级关系的实体 | 语义网络 / 本体 | 继承性省掉大量重复描述 |
| 要在有限时间内挑一步棋或一个方案 | α-β 剪枝 | 剪掉不影响决策的分支,结果不变 |
| 要给不确定的证据打分 | 可信度 CF | 比概率轻,适合人工定规则的场景 |
什么时候别硬套:
第一,数据量大、规律藏在样本里的时候。产生式系统靠人写规则,规则一多就互相打架,维护成本比训练一个模型还高。这种场景该让机器学习上场,规则只留少量兜底。
第二,需要精确概率推断的时候。CF 那套 min/max 运算适合快速判断,不适合算“得病的概率是多少”。要概率就老老实实建贝叶斯网络,别拿 CF 凑。
第三,实体关系规模上来之后。语义网络那种自由画法,几百个节点还能看,几万个节点就开始出现同名不同义、同义不同名,必须有本体约束和实体对齐,那就进入知识图谱的工程范畴了。
第四,实时性要求极高的搜索。α-β 剪枝在棋类上表现好,是因为估值函数便宜、分支可控。换成状态空间巨大、估值又贵的场景,得换蒙特卡洛树搜索之类的思路。
我自己的习惯是:拿到一个说不清的问题,先逼自己用这套语言描述一遍——事实是什么、规则是什么、目标是什么、哪一步不确定。描述完通常会发现,问题要么被拆小了,要么根本不是我一开始以为的那个问题。有一次排查一个策略冲突,写了两页规则,最后发现真正的原因是两条规则的优先级没定义,跟算法一点关系都没有。从那以后,我每次碰这类问题都强制先写事实和规则,写完再决定要不要上模型。
这套经典试题连同答案整理成了一份资料,选择题、填空题、简答计算、应用题四类都有,需要的话可以拿去对着过一遍,比自己凭印象猜要靠谱得多。希望帮到你。
本文还有配套的精品资源,点击获取