Tutorial 4: 知识表示与推理

什么是知识表示?

知识表示 是将现实世界的知识编码成计算机可以处理的形式。

为什么重要?

  • 让 AI 能够”理解”世界

  • 支持推理和问题求解

  • 实现知识的存储和检索

现实世界的知识         知识表示          推理系统
┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│ 苏格拉底是人 │ ──► │ human(苏格拉底)│ ──► │ 苏格拉底会死 │
│ 人都会死    │     │ ∀x human(x)→ │     │             │
│             │     │   mortal(x) │     │             │
└─────────────┘     └─────────────┘     └─────────────┘

知识表示方法

1. 命题逻辑

最简单的逻辑系统,使用命题和逻辑连接词。

class PropositionalLogic:
    """命题逻辑"""

    def __init__(self):
        self.knowledge_base = set()

    def tell(self, proposition):
        """添加知识"""
        self.knowledge_base.add(proposition)

    def ask(self, query):
        """查询"""
        return query in self.knowledge_base

# 示例
kb = PropositionalLogic()
kb.tell("下雨")
kb.tell("下雨 → 带伞")

print(kb.ask("下雨"))  # True

逻辑运算符:

  • ¬ (NOT): 非

  • ∧ (AND): 与

  • ∨ (OR): 或

  • → (IMPLIES): 蕴含

  • ↔ (IFF): 当且仅当

# 简单的命题逻辑推理
class SimpleReasoner:
    def __init__(self):
        self.facts = {}
        self.rules = []

    def add_fact(self, name, value):
        self.facts[name] = value

    def add_rule(self, condition, conclusion):
        """添加规则: condition → conclusion"""
        self.rules.append((condition, conclusion))

    def infer(self):
        """前向推理"""
        changed = True
        while changed:
            changed = False
            for condition, conclusion in self.rules:
                if self.evaluate(condition) and conclusion not in self.facts:
                    self.facts[conclusion] = True
                    changed = True
                    print(f"推理得出: {conclusion}")

    def evaluate(self, condition):
        """评估条件"""
        if isinstance(condition, str):
            return self.facts.get(condition, False)
        elif condition[0] == 'AND':
            return all(self.evaluate(c) for c in condition[1:])
        elif condition[0] == 'OR':
            return any(self.evaluate(c) for c in condition[1:])
        return False

# 使用示例
reasoner = SimpleReasoner()
reasoner.add_fact("下雨", True)
reasoner.add_rule("下雨", "地湿")
reasoner.add_rule(("AND", "地湿", "没带伞"), "会淋湿")
reasoner.add_fact("没带伞", True)

reasoner.infer()
# 输出:
# 推理得出: 地湿
# 推理得出: 会淋湿

2. 一阶谓词逻辑

比命题逻辑更强大,支持变量、量词和谓词。

class FirstOrderLogic:
    """一阶谓词逻辑(简化版)"""

    def __init__(self):
        self.facts = []      # 事实
        self.rules = []      # 规则
        self.constants = set()  # 常量

    def add_fact(self, predicate, *args):
        """添加事实: predicate(arg1, arg2, ...)"""
        self.facts.append((predicate, args))
        self.constants.update(args)

    def add_rule(self, conditions, conclusion):
        """添加规则: conditions → conclusion"""
        self.rules.append((conditions, conclusion))

    def query(self, predicate, *args):
        """查询事实"""
        for fact_pred, fact_args in self.facts:
            if fact_pred == predicate:
                if self.unify(args, fact_args):
                    return True
        return False

    def unify(self, args1, args2):
        """简单的合一算法"""
        if len(args1) != len(args2):
            return False
        for a1, a2 in zip(args1, args2):
            if a1.startswith('?'):  # 变量
                continue
            if a2.startswith('?'):  # 变量
                continue
            if a1 != a2:
                return False
        return True

    def forward_chain(self):
        """前向链推理"""
        changed = True
        while changed:
            changed = False
            for conditions, (pred, args) in self.rules:
                # 尝试找到满足条件的绑定
                bindings = self.find_bindings(conditions)
                for binding in bindings:
                    new_args = tuple(binding.get(a, a) for a in args)
                    if not self.query(pred, *new_args):
                        self.add_fact(pred, *new_args)
                        changed = True
                        print(f"推理: {pred}{new_args}")

    def find_bindings(self, conditions):
        """找到满足条件的变量绑定"""
        if not conditions:
            return [{}]

        pred, args = conditions[0]
        rest = conditions[1:]

        results = []
        for fact_pred, fact_args in self.facts:
            if fact_pred == pred and len(args) == len(fact_args):
                binding = {}
                match = True
                for a, fa in zip(args, fact_args):
                    if a.startswith('?'):
                        binding[a] = fa
                    elif a != fa:
                        match = False
                        break

                if match:
                    # 递归处理剩余条件
                    for rest_binding in self.find_bindings(rest):
                        combined = {**binding, **rest_binding}
                        results.append(combined)

        return results

# 经典推理示例
fol = FirstOrderLogic()

# 事实
fol.add_fact("human", "苏格拉底")
fol.add_fact("human", "柏拉图")

# 规则: ∀x human(x) → mortal(x)
fol.add_rule(
    [("human", ("?x",))],
    ("mortal", ("?x",))
)

print("初始事实:", fol.facts)
fol.forward_chain()
print("推理后:", fol.facts)

3. 语义网络

用图结构表示知识,节点表示概念,边表示关系。

class SemanticNetwork:
    """语义网络"""

    def __init__(self):
        self.nodes = {}  # 节点
        self.edges = []  # 边: (源, 关系, 目标)

    def add_node(self, name, properties=None):
        self.nodes[name] = properties or {}

    def add_edge(self, source, relation, target):
        self.edges.append((source, relation, target))

    def get_relations(self, node, relation=None):
        """获取节点的关系"""
        results = []
        for s, r, t in self.edges:
            if s == node:
                if relation is None or r == relation:
                    results.append((r, t))
        return results

    def is_a(self, node, category):
        """检查继承关系"""
        # 直接检查
        for r, t in self.get_relations(node, "is_a"):
            if t == category:
                return True
            # 递归检查
            if self.is_a(t, category):
                return True
        return False

    def get_property(self, node, property_name):
        """获取属性(支持继承)"""
        # 先检查自身
        if node in self.nodes and property_name in self.nodes[node]:
            return self.nodes[node][property_name]

        # 检查父类
        for r, parent in self.get_relations(node, "is_a"):
            result = self.get_property(parent, property_name)
            if result is not None:
                return result

        return None

# 示例:动物分类
net = SemanticNetwork()

# 添加概念
net.add_node("动物", {"能移动": True})
net.add_node("鸟", {"有翅膀": True, "能飞": True})
net.add_node("企鹅", {"能飞": False, "生活环境": "南极"})
net.add_node("麻雀")

# 添加关系
net.add_edge("鸟", "is_a", "动物")
net.add_edge("企鹅", "is_a", "鸟")
net.add_edge("麻雀", "is_a", "鸟")

# 查询
print(f"企鹅是动物吗? {net.is_a('企鹅', '动物')}")  # True
print(f"企鹅能飞吗? {net.get_property('企鹅', '能飞')}")  # False
print(f"麻雀能飞吗? {net.get_property('麻雀', '能飞')}")  # True (继承)

4. 知识图谱

现代的知识表示方法,使用三元组 (实体, 关系, 实体)。

class KnowledgeGraph:
    """简单的知识图谱"""

    def __init__(self):
        self.triples = []  # (头实体, 关系, 尾实体)
        self.entity_index = {}  # 实体索引
        self.relation_index = {}  # 关系索引

    def add_triple(self, head, relation, tail):
        """添加三元组"""
        triple = (head, relation, tail)
        self.triples.append(triple)

        # 建立索引
        if head not in self.entity_index:
            self.entity_index[head] = []
        self.entity_index[head].append(triple)

        if relation not in self.relation_index:
            self.relation_index[relation] = []
        self.relation_index[relation].append(triple)

    def query(self, head=None, relation=None, tail=None):
        """查询三元组"""
        results = []
        for h, r, t in self.triples:
            if (head is None or h == head) and \
               (relation is None or r == relation) and \
               (tail is None or t == tail):
                results.append((h, r, t))
        return results

    def get_neighbors(self, entity):
        """获取实体的邻居"""
        neighbors = []
        for h, r, t in self.triples:
            if h == entity:
                neighbors.append((r, t, "out"))
            if t == entity:
                neighbors.append((r, h, "in"))
        return neighbors

    def find_path(self, start, end, max_depth=3):
        """寻找两个实体之间的路径"""
        from collections import deque

        queue = deque([(start, [start])])
        visited = {start}

        while queue:
            current, path = queue.popleft()

            if len(path) > max_depth:
                continue

            for relation, neighbor, direction in self.get_neighbors(current):
                if neighbor == end:
                    return path + [(relation, direction), neighbor]

                if neighbor not in visited:
                    visited.add(neighbor)
                    queue.append((neighbor, path + [(relation, direction), neighbor]))

        return None

# 示例:构建简单的知识图谱
kg = KnowledgeGraph()

# 添加知识
kg.add_triple("北京", "是首都", "中国")
kg.add_triple("上海", "位于", "中国")
kg.add_triple("中国", "是", "国家")
kg.add_triple("李白", "出生于", "中国")
kg.add_triple("李白", "职业", "诗人")
kg.add_triple("杜甫", "职业", "诗人")
kg.add_triple("李白", "朋友", "杜甫")

# 查询
print("中国的相关知识:")
for triple in kg.query(head="中国"):
    print(f"  {triple}")

print("\n所有诗人:")
for h, r, t in kg.query(relation="职业", tail="诗人"):
    print(f"  {h}")

print("\n李白到中国的路径:")
path = kg.find_path("李白", "中国")
print(f"  {path}")

用 PyTorch 实现知识图谱嵌入

知识图谱嵌入将实体和关系映射到向量空间:

import torch
import torch.nn as nn
import torch.optim as optim

class TransE(nn.Module):
    """TransE 知识图谱嵌入模型

    核心思想: head + relation ≈ tail
    """

    def __init__(self, num_entities, num_relations, embedding_dim=50):
        super().__init__()
        self.entity_embeddings = nn.Embedding(num_entities, embedding_dim)
        self.relation_embeddings = nn.Embedding(num_relations, embedding_dim)

        # 初始化
        nn.init.xavier_uniform_(self.entity_embeddings.weight)
        nn.init.xavier_uniform_(self.relation_embeddings.weight)

    def forward(self, heads, relations, tails):
        """计算得分(距离越小越好)"""
        h = self.entity_embeddings(heads)
        r = self.relation_embeddings(relations)
        t = self.entity_embeddings(tails)

        # TransE: ||h + r - t||
        score = torch.norm(h + r - t, p=2, dim=1)
        return score

    def predict(self, head, relation, top_k=5):
        """预测尾实体"""
        h = self.entity_embeddings(head)
        r = self.relation_embeddings(relation)

        # 计算与所有实体的距离
        all_entities = self.entity_embeddings.weight
        scores = torch.norm(h + r - all_entities, p=2, dim=1)

        # 返回得分最低的k个
        _, indices = torch.topk(scores, top_k, largest=False)
        return indices

def train_transe(triples, num_entities, num_relations, epochs=100):
    """训练 TransE 模型"""
    model = TransE(num_entities, num_relations)
    optimizer = optim.Adam(model.parameters(), lr=0.01)
    margin = 1.0

    # 转换为张量
    heads = torch.tensor([t[0] for t in triples])
    relations = torch.tensor([t[1] for t in triples])
    tails = torch.tensor([t[2] for t in triples])

    for epoch in range(epochs):
        # 正样本得分
        pos_scores = model(heads, relations, tails)

        # 负采样(随机替换尾实体)
        neg_tails = torch.randint(0, num_entities, (len(triples),))
        neg_scores = model(heads, relations, neg_tails)

        # Margin-based loss
        loss = torch.mean(torch.relu(pos_scores - neg_scores + margin))

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        # 归一化实体嵌入
        with torch.no_grad():
            model.entity_embeddings.weight.data = \
                nn.functional.normalize(model.entity_embeddings.weight.data, dim=1)

        if (epoch + 1) % 20 == 0:
            print(f"Epoch {epoch+1}, Loss: {loss.item():.4f}")

    return model

# 示例
if __name__ == "__main__":
    # 简单的知识图谱
    # 实体: 0=北京, 1=中国, 2=上海, 3=亚洲
    # 关系: 0=首都, 1=位于
    triples = [
        (0, 0, 1),  # 北京 是首都 中国
        (0, 1, 1),  # 北京 位于 中国
        (2, 1, 1),  # 上海 位于 中国
        (1, 1, 3),  # 中国 位于 亚洲
    ]

    model = train_transe(triples, num_entities=4, num_relations=2, epochs=100)

    # 预测:上海 位于 ?
    predictions = model.predict(
        torch.tensor([2]),  # 上海
        torch.tensor([1]),  # 位于
        top_k=2
    )
    print(f"\n上海 位于 ? 预测: {predictions.tolist()}")
    # 期望输出 1 (中国) 排名靠前

关键概念总结

概念

解释

应用

命题逻辑

使用命题和逻辑连接词

简单推理

一阶逻辑

支持变量和量词

复杂推理

语义网络

图结构表示概念关系

知识组织

知识图谱

三元组存储知识

搜索引擎、问答

知识嵌入

将知识映射到向量

知识补全、推荐

下一步

在下一个教程中,我们将学习机器学习的基础知识。

Tutorial 5: 机器学习基础