从理论到代码
前面几篇文章讲了神经符号AI的理论、范式和挑战。现在是时候动手了。这篇文章带你从零实现一个神经符号推理系统——一个能解决简单数学推理问题的混合系统。
系统设计
我们的系统将解决以下问题:给定一段文字描述和一个数学问题,输出答案和推理过程。
示例输入:
"小明有5个苹果,小红给了小明3个苹果,然后小明吃了2个苹果。小明现在有多少个苹果?"
期望输出:
推理过程:
1. 小明最初有5个苹果
2. 小红给了小明3个苹果 → 5 + 3 = 8
3. 小明吃了2个苹果 → 8 - 2 = 6
答案:6个苹果
系统架构:
- 神经网络模块(GPT-2):理解自然语言,提取关键信息
- 符号推理模块:执行数学运算,保证计算正确性
- 桥接模块:将自然语言信息转化为符号化的数学表达式
代码实现
import torch
import torch.nn as nn
from transformers import GPT2LMHeadModel, GPT2Tokenizer
import re
class NeuroSymbolicReasoner:
"""神经符号推理系统"""
def __init__(self):
# 神经网络模块:用于自然语言理解
self.tokenizer = GPT2Tokenizer.from_pretrained('gpt2')
self.lm = GPT2LMHeadModel.from_pretrained('gpt2')
# 符号推理模块:用于数学运算
self.symbolic_engine = SymbolicMathEngine()
# 桥接模块:将自然语言转化为符号表达式
self.bridge = NeuroSymbolicBridge()
def reason(self, problem_text):
# Step 1: 神经网络提取关键信息
entities = self.extract_entities(problem_text)
actions = self.extract_actions(problem_text)
# Step 2: 桥接模块将自然语言转化为符号表达式
symbolic_steps = self.bridge.convert_to_symbolic(
entities, actions
)
# Step 3: 符号推理引擎执行精确计算
result, reasoning_steps = self.symbolic_engine.execute(
symbolic_steps
)
return {
'answer': result,
'reasoning': reasoning_steps,
'confidence': self.estimate_confidence(entities, actions)
}
def extract_entities(self, text):
"""使用神经网络提取实体和数值"""
# 简化版:使用正则表达式提取数值
# 实际应用中应使用NER模型
numbers = re.findall(r'\d+', text)
names = re.findall(r'[小][明红]', text)
return {'numbers': numbers, 'names': names}
def extract_actions(self, text):
"""使用神经网络提取动作"""
# 简化版:关键词匹配
actions = []
if '给了' in text or '给' in text:
actions.append('add')
if '吃了' in text or '用了' in text:
actions.append('subtract')
return actions
class SymbolicMathEngine:
"""符号数学引擎:执行精确的数学运算"""
def __init__(self):
self.state = {} # 符号状态:存储变量的值
self.reasoning_steps = [] # 推理步骤记录
def execute(self, symbolic_steps):
"""执行符号化的推理步骤"""
for step in symbolic_steps:
operation = step['operation']
target = step['target']
value = step['value']
if operation == 'initialize':
self.state[target] = value
self.reasoning_steps.append(
f"{target}最初有{value}"
)
elif operation == 'add':
old_value = self.state.get(target, 0)
self.state[target] = old_value + value
self.reasoning_steps.append(
f"{target}增加了{value} → "
f"{old_value} + {value} = {self.state[target]}"
)
elif operation == 'subtract':
old_value = self.state.get(target, 0)
self.state[target] = old_value - value
self.reasoning_steps.append(
f"{target}减少了{value} → "
f"{old_value} - {value} = {self.state[target]}"
)
return self.state.get('result', 0), self.reasoning_steps
class NeuroSymbolicBridge:
"""桥接模块:将自然语言理解结果转化为符号表达式"""
def __init__(self):
# 动作映射表
self.action_map = {
'给了': {'operation': 'add', 'direction': 'receive'},
'吃了': {'operation': 'subtract', 'direction': 'self'},
'用了': {'operation': 'subtract', 'direction': 'self'},
}
def convert_to_symbolic(self, entities, actions):
"""将自然语言理解结果转化为符号化的操作步骤"""
steps = []
numbers = [int(n) for n in entities['numbers']]
# 初始化
steps.append({
'operation': 'initialize',
'target': 'result',
'value': numbers[0]
})
# 执行操作
for i, action in enumerate(actions):
if action == 'add':
steps.append({
'operation': 'add',
'target': 'result',
'value': numbers[i + 1]
})
elif action == 'subtract':
steps.append({
'operation': 'subtract',
'target': 'result',
'value': numbers[i + 1]
})
return steps
# 使用示例
reasoner = NeuroSymbolicReasoner()
problem = "小明有5个苹果,小红给了小明3个苹果,然后小明吃了2个苹果。小明现在有多少个苹果?"
result = reasoner.reason(problem)
print("推理过程:")
for step in result['reasoning']:
print(f" {step}")
print(f"答案:{result['answer']}")
代码解析
为什么这个系统是神经符号的?
神经网络部分(
extract_entities和extract_actions):负责处理自然语言的模糊性和多样性。在实际应用中,这部分会使用更强大的NER模型和关系抽取模型。符号推理部分(
SymbolicMathEngine):负责执行精确的数学运算。这保证了计算结果的正确性——不会出现LLM常见的"5+3=7"这种错误。桥接部分(
NeuroSymbolicBridge):负责将神经网络的"模糊"输出转化为符号引擎的"精确"输入。这是神经符号系统中最关键也最难设计的部分。
扩展这个系统
这个简单的系统可以扩展到更复杂的场景:
扩展一:更强大的自然语言理解
替换extract_entities和extract_actions为基于BERT或GPT的NER和关系抽取模型。
扩展二:更丰富的符号推理
扩展SymbolicMathEngine以支持更多数学运算(乘除、方程式、几何推理),甚至支持逻辑推理(if-then-else、量词)。
扩展三:可解释性增强
记录完整的推理链,包括神经网络的中间决策和符号引擎的计算步骤。
扩展四:失败回退机制
当符号推理引擎无法处理时,回退到纯神经网络(如LLM)进行推理。
实际应用中的注意事项
1. 桥接模块的鲁棒性
桥接模块是神经符号系统中最脆弱的环节。如果神经网络提取的信息有误,符号引擎会基于错误的信息进行推理。你需要:
- 对桥接输出进行置信度评估
- 当置信度低时,请求人类确认或回退到LLM
2. 符号推理的覆盖范围
符号引擎只能处理你预先编码的推理规则。你需要持续扩展符号引擎的能力,覆盖更多的推理场景。
3. 性能优化
符号推理可能很慢(特别是搜索空间大时)。你需要:
- 使用神经网络引导搜索剪枝
- 缓存常用的推理结果
- 设置推理超时,超时后回退到LLM
写在最后
从零实现一个神经符号推理系统,没有想象中那么难。核心是三个模块:神经网络(感知)、符号引擎(推理)、桥接模块(转换)。难点在于桥接模块的设计——如何让神经网络的模糊输出和符号引擎的精确输入无缝对接。
完整的代码和更多示例可以在GitHub上找到。欢迎Fork和Star。
你动手实现了神经符号系统吗?遇到了什么问题?欢迎在评论区交流。