从理论到代码

前面几篇文章讲了神经符号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']}")

代码解析

为什么这个系统是神经符号的?

  1. 神经网络部分extract_entitiesextract_actions):负责处理自然语言的模糊性和多样性。在实际应用中,这部分会使用更强大的NER模型和关系抽取模型。

  2. 符号推理部分SymbolicMathEngine):负责执行精确的数学运算。这保证了计算结果的正确性——不会出现LLM常见的"5+3=7"这种错误。

  3. 桥接部分NeuroSymbolicBridge):负责将神经网络的"模糊"输出转化为符号引擎的"精确"输入。这是神经符号系统中最关键也最难设计的部分。

扩展这个系统

这个简单的系统可以扩展到更复杂的场景:

扩展一:更强大的自然语言理解

替换extract_entitiesextract_actions为基于BERT或GPT的NER和关系抽取模型。

扩展二:更丰富的符号推理

扩展SymbolicMathEngine以支持更多数学运算(乘除、方程式、几何推理),甚至支持逻辑推理(if-then-else、量词)。

扩展三:可解释性增强

记录完整的推理链,包括神经网络的中间决策和符号引擎的计算步骤。

扩展四:失败回退机制

当符号推理引擎无法处理时,回退到纯神经网络(如LLM)进行推理。

实际应用中的注意事项

1. 桥接模块的鲁棒性

桥接模块是神经符号系统中最脆弱的环节。如果神经网络提取的信息有误,符号引擎会基于错误的信息进行推理。你需要:

  • 对桥接输出进行置信度评估
  • 当置信度低时,请求人类确认或回退到LLM

2. 符号推理的覆盖范围

符号引擎只能处理你预先编码的推理规则。你需要持续扩展符号引擎的能力,覆盖更多的推理场景。

3. 性能优化

符号推理可能很慢(特别是搜索空间大时)。你需要:

  • 使用神经网络引导搜索剪枝
  • 缓存常用的推理结果
  • 设置推理超时,超时后回退到LLM

写在最后

从零实现一个神经符号推理系统,没有想象中那么难。核心是三个模块:神经网络(感知)、符号引擎(推理)、桥接模块(转换)。难点在于桥接模块的设计——如何让神经网络的模糊输出和符号引擎的精确输入无缝对接。

完整的代码和更多示例可以在GitHub上找到。欢迎Fork和Star。


你动手实现了神经符号系统吗?遇到了什么问题?欢迎在评论区交流。