神经网络像缓存层 + 模糊索引:从海量数据里学出统计直觉,又快又能泛化,但偶尔"命中错条目"且说不清为什么。符号系统像数据库的约束 + 事务引擎:规则精确、可审计、保证一致性,但只认你显式写下的逻辑,遇到没见过的输入就僵住。神经符号 AI = 把两者分层组合——感知/直觉交给神经网络,验证/推理交给符号引擎,正如你不会用 Redis 当唯一存储、也不会让每个查询都打穿到主库。
纯神经网络(包括 LLM)有两个结构性短板:不可靠的多步推理(会编造看似合理的中间步骤)和不可验证(无法保证输出满足硬约束,如"日程不冲突""化学式配平")。纯符号系统则学不会感知——你没法手写规则识别一张猫的照片。神经符号的核心洞察:这两类系统的强弱恰好互补,对应 Kahneman 的 System 1(快、直觉、神经)与 System 2(慢、逻辑、符号)。
Henry Kautz 的 6 型分类法(Garcez & Lamb 在《Neurosymbolic AI: The 3rd Wave》中系统化)把整个设计空间排成一条"耦合松紧"的光谱:
这条光谱说明一件重要的事:"神经符号"不是单一架构,而是一族 trade-off。今天落地最多的是松耦合(LLM + 工具调用即其最朴素形态),研究前沿在紧耦合。
# 最朴素的神经符号:LLM(神经) 把自然语言翻成符号约束, # 再交给求解器(符号) 求精确解 —— 各干各擅长的事 from anthropic import Anthropic from z3 import Ints, Solver, sat # pip install z3-solver client = Anthropic() q = "我有3个孩子,年龄和是13,老大比老二大2岁,老二是老三的2倍。各几岁?" # 神经端:只负责把语义抽成约束(它不擅长精确算术,但擅长理解语言) spec = client.messages.create(model="claude-opus-4-8", max_tokens=300, messages=[{"role":"user", "content":f"把这题翻成 z3 约束代码,只输出代码:{q}"}]).content[0].text a, b, c = Ints("a b c"); s = Solver() s.add(a+b+c==13, a==b+2, b==2*c, a>0, b>0, c>0) # 符号端:保证精确 print(s.check()==sat, s.model()) # → 可验证的唯一解,不会"算错"
知识图谱就是一张图数据库:节点是实体,边是关系,存成无数 (头, 关系, 尾) 三元组——和 (用户, 关注, 用户) 这种关系表一模一样。但图数据库只能查已存在的边。知识图谱嵌入做的事,是把每个实体和关系映射成一个向量,让"关系"变成向量空间里的几何操作——于是缺失的边可以被几何推断出来,等于给你的关系表加了一个"自动补全缺失外键"的引擎。
真实知识图谱(Freebase、Wikidata、企业知识库)永远不完整——大量该有的关系没被录入。任务叫 链接预测(link prediction):给定 (北京, 是首都, ?),推断尾实体。手写规则不可行(关系成千上万)。嵌入方法的奠基者是 TransE(Bordes et al., NeurIPS 2013),它的设计直觉极其优雅:
把"关系"建模成向量的平移(translation)。 若三元组 (h, r, t) 成立,就让 head 向量 + relation 向量 ≈ tail 向量,即 h + r ≈ t。训练目标是最小化成立三元组的 ‖h+r−t‖、同时拉大错误三元组的距离。
为什么能泛化?因为相似关系被压到相近的几何变换。但 TransE 有名的软肋:处理不了对称关系("朋友"是对称的,h+r≈t 和 t+r≈h 没法同时满足)。后续 RotatE(Sun et al., ICLR 2019)把平移换成复数空间里的旋转,一举能表达对称、反对称、求逆、组合等关系模式——这是从"位移"到"旋转"的关键升级。
⚠️ 注意边界:这是统计式软推理,给的是"可能性排名",不是符号逻辑那种"可证硬结论"——和概念 1 的符号端互补,不是替代。
# 用纯 numpy 演示 TransE 的核心:h + r ≈ t 的几何直觉 import numpy as np np.random.seed(0) # 实体/关系都是低维向量(真实场景由训练学出,这里手设演示) ent = {"北京":np.array([0.,0.]), "中国":np.array([2.,1.]), "东京":np.array([5.,3.]), "日本":np.array([7.,4.])} rel_capital = np.array([2.,1.]) # "是首都"这个关系的平移向量 def predict_tail(head, rel): target = ent[head] + rel # h + r # 在所有实体里找离 target 最近的(最近邻 = 预测的尾实体) return min(ent, key=lambda e: np.linalg.norm(ent[e]-target)) print(predict_tail("东京", rel_capital)) # → 日本(训练集没这条边也能推)
传统逻辑推理像 Prolog / SQL 的规则引擎:规则要么命中要么不命中,是离散的开关,没法"微调"也没法从数据里学。可微分推理做的事,是把这些硬规则改写成可以反向传播的连续计算图——相当于给每条 if-else 规则挂上一个可学习的置信权重,让整条推理链能被梯度下降优化。等于把硬编码的 business rule 变成"能被训练数据慢慢调准"的软规则。
概念 1 的"松耦合"把神经和符号当两个黑盒拼接,缺点是不能联合训练——神经端的错误传不到符号端去纠正。可微分推理追求紧耦合:让逻辑推理本身可微,于是感知和推理能用同一个梯度一起学。核心难点是逻辑是离散的(真/假、命中/不命中),梯度无从谈起。两条主流破解思路:
这个例子的威力在于:你只用"和=12"这种高层弱标签,逻辑结构就自动把监督信号分解到了每张图的数字识别上——符号知识充当了"归纳偏置(inductive bias)",大幅降低了所需的标注量。这是纯神经网络做不到的样本效率。
# 可微"逻辑与"的最小演示:用乘法松弛 AND,让规则可导 import torch # 两个神经谓词输出的"为真概率"(真实场景由网络给出) p_digit_a = torch.tensor(0.9, requires_grad=True) # P(A是7) p_digit_b = torch.tensor(0.8, requires_grad=True) # P(B是5) # 规则 "A∧B 都成立" 的硬逻辑版是 and(真,真)=真; # 概率松弛版:P(A∧B) = P(A)*P(B),可微 p_rule = p_digit_a * p_digit_b target = torch.tensor(1.0) # 我们知道这条规则该成立 loss = (p_rule - target)**2 loss.backward() # 梯度能一路传回两个谓词 print(p_digit_a.grad, p_digit_b.grad) # → 非零:逻辑结果可指导神经端学习
程序合成像 property-based testing 反过来跑:测试是"给实现,验证它对所有输入满足性质";合成是"给一组输入→输出的例子,反推出满足它们的程序"。再叠一层 DreamCoder 式的库学习(library learning),就像你在重构中不断把重复代码提炼成公共函数库——只不过这套提炼是机器在搜索中自动完成、并越攒越强的。
程序合成是神经符号的"圣杯"任务:程序本身就是符号结构(可执行、可验证、可组合),而搜索程序的空间又大到必须靠神经网络来引导。它直击 LLM 的软肋——LLM 写代码靠记忆模式,遇到需要真正搜索 + 验证的新问题就不可靠;而程序一旦合成出来,运行它就能精确验证对不对。
代表作 DreamCoder(Ellis et al., 2020/PLDI 2021)的精妙之处在 wake-sleep(醒-睡)循环,它同时成长两种知识:
论文里 DreamCoder 从最基本的原语出发,自己重新发现了函数式编程的基础构件、向量代数乃至经典物理(牛顿、库仑定律的形式)。这正是符号表示的红利:学到的不是一团权重,而是人能读懂、能复用的函数库。2026 年看,LLM 成了更强的"神经搜索引导器",但"生成→执行→反馈纠错"这个把神经生成和符号验证闭环起来的骨架,正是 DreamCoder 思路的延续,也是当下代码 Agent 可靠性的来源。
# 程序合成最小内核:在"程序空间"里枚举,用输入输出例子做验证 from itertools import product # 例子:找一个把输入变成输出的程序 f(x) = x * a + b examples = [(1, 5), (2, 8), (3, 11)] # 规约 = 一组 I/O 对 # 库 = 候选操作的搜索空间(DreamCoder 会自动让这个库成长) for a, b in product(range(-5,6), repeat=2): prog = lambda x, a=a, b=b: x*a + b # 符号验证:必须对所有例子都精确成立(这是 LLM 给不了的保证) if all(prog(x)==y for x, y in examples): print(f"找到程序: f(x) = x*{a} + {b}") # → x*3 + 2 break # 神经网络的作用:在巨大空间里"猜"先试哪些 a,b,把暴力枚举变成智能搜索