📖 什么是事件抽取
事件抽取(Event Extraction)是从非结构化文本中识别事件的发生,并提取事件的各个要素(触发词、论元、时间、地点等),结构化为事件描述。事件比关系更复杂——关系是"二元"的,事件可以是"多元"的。
🎯 事件的核心要素
- 触发词(Trigger):标识事件发生的关键词,如"辞职"、"收购"、"地震"
- 事件类型(Event Type):事件的分类,如"人事变动"、"商业收购"、"自然灾害"
- 论元(Argument):事件的参与者,如"辞职者"、"收购方"、"震中"
- 时间(Time):事件发生的时间
- 地点(Location):事件发生的地点
📐 事件本体定义
from dataclasses import dataclass, field
from typing import List, Dict, Optional
import re
@dataclass
class EventSchema:
"""事件类型定义"""
event_type: str
trigger_words: List[str]
required_args: List[str]
optional_args: List[str]
description: str = ""
@dataclass
class ExtractedEvent:
"""抽取的事件实例"""
event_type: str
trigger: str
arguments: Dict[str, str]
text: str = ""
confidence: float = 1.0
def to_triple(self):
">>>转为三元组列表(事件中心化表示)"""
triples = []
event_id = f"event_{id(self)}"
triples.append((event_id, "类型", self.event_type))
triples.append((event_id, "触发词", self.trigger))
for role, value in self.arguments.items():
triples.append((event_id, role, value))
return triples
class EventExtractor:
"""基于规则的事件抽取器"""
def __init__(self):
self.schemas: Dict[str, EventSchema] = {}
self.arg_patterns: Dict[str, List] = {}
def register_event_type(self, schema):
self.schemas[schema.event_type] = schema
def add_arg_pattern(self, event_type, role, pattern_str):
"""添加论元抽取模式"""
self.arg_patterns.setdefault(event_type, []).append((role, re.compile(pattern_str)))
def extract(self, text):
"""从文本中抽取事件"""
events = []
for event_type, schema in self.schemas.items():
for trigger in schema.trigger_words:
if trigger in text:
arguments = {}
for role, pattern in self.arg_patterns.get(event_type, []):
match = pattern.search(text)
if match:
arguments[role] = match.group(1) if match.groups() else match.group()
missing = [a for a in schema.required_args if a not in arguments]
confidence = 1.0 - 0.2 * len(missing)
event = ExtractedEvent(
event_type=event_type,
trigger=trigger,
arguments=arguments,
text=text,
confidence=max(confidence, 0.1)
)
events.append(event)
return events
ee = EventExtractor()
ee.register_event_type(EventSchema(
event_type="人事变动",
trigger_words=["辞职", "离职", "就任", "任命", "升任"],
required_args=["人物"],
optional_args=["职位", "机构", "时间"],
description="人事变动事件"
))
ee.add_arg_pattern("人事变动", "人物", r'([一-鿿]{2,4})(辞职|离职|就任|任命|升任)')
ee.add_arg_pattern("人事变动", "职位", r'([一-鿿]{2,6})职位?')
ee.add_arg_pattern("人事变动", "机构", r'([一-鿿]{2,8})(公司|集团|大学|部门)')
ee.register_event_type(EventSchema(
event_type="商业收购",
trigger_words=["收购", "并购", "并购了", "收购了"],
required_args=["收购方", "被收购方"],
optional_args=["金额", "时间"],
))
ee.add_arg_pattern("商业收购", "收购方", r'([一-鿿]{2,6})(收购|并购)')
ee.add_arg_pattern("商业收购", "被收购方", r'(?:收购|并购)(?:了)?([一-鿿]{2,6})')
ee.add_arg_pattern("商业收购", "金额", r'(\d+[.亿万千百]+元?)')
test_texts = [
"张三辞职了公司CEO职位",
"阿里巴巴收购了优酷,金额为45亿美元",
"李四被任命为技术部门经理",
"腾讯并购了美团部分业务",
]
print("=== 事件抽取结果 ===")
for text in test_texts:
events = ee.extract(text)
for event in events:
print(f" 事件类型: {event.event_type}")
print(f" 触发词: {event.trigger}")
print(f" 论元: {event.arguments}")
print(f" 置信度: {event.confidence:.2f}")
print(f" 三元组: {event.to_triple()}")
print()
=== 事件抽取结果 ===
事件类型: 人事变动
触发词: 辞职
论元: {'人物': '张三'}
置信度: 0.80
三元组: [('event_...', '类型', '人事变动'), ('event_...', '触发词', '辞职'), ('event_...', '人物', '张三')]
事件类型: 商业收购
触发词: 收购
论元: {'收购方': '阿里巴巴', '被收购方': '优酷', '金额': '45亿美元'}
置信度: 1.00
三元组: [('event_...', '类型', '商业收购'), ('event_...', '触发词', '收购'), ...]
🔄 事件关系与事件图谱
class EventGraph:
"""事件图谱:事件间的关系网络"""
def __init__(self):
self.events = {}
self.relations = []
def add_event(self, event):
eid = f"e{len(self.events)}"
self.events[eid] = event
return eid
def add_relation(self, src, rel, tgt):
self.relations.append((src, rel, tgt))
def find_causal_chain(self, event_id, max_depth=5):
"""查找事件的因果链"""
chain = [event_id]
current = event_id
for _ in range(max_depth):
for src, rel, tgt in self.relations:
if src == current and rel == "导致":
chain.append(tgt)
current = tgt
break
else:
break
return chain
eg = EventGraph()
e1 = eg.add_event(ExtractedEvent("经济危机", "爆发", {"时间": "2008"}))
e2 = eg.add_event(ExtractedEvent("失业潮", "出现", {"规模": "大规模"}))
e3 = eg.add_event(ExtractedEvent("政策调整", "实施", {"类型": "刺激政策"}))
eg.add_relation(e1, "导致", e2)
eg.add_relation(e2, "引发", e3)
print("=== 事件因果链 ===")
chain = eg.find_causal_chain(e1)
for eid in chain:
ev = eg.events[eid]
print(f" → {ev.event_type}({ev.trigger})")
=== 事件因果链 ===
→ 经济危机(爆发)
→ 失业潮(出现)
→ 政策调整(实施)
📝 实战练习
练习1:自然灾害事件抽取
定义地震、洪水等自然灾害的事件Schema和论元模式,实现抽取。
练习2:事件共指
实现判断两个事件描述是否指同一事件的功能(基于触发词和论元重叠度)。
练习3:事件时序推理
根据事件的时间论元,建立事件的时序关系(先于/同时/后于)。
⚡
🏆 第8课成就解锁
事件抽取工程师
⚡ 触发词检测
📋 论元抽取
🔗 事件图谱
🔄 因果链