Files
PlantAI-rag/docs/history/PC_IMPLEMENTATION_PLAN.md
2026-07-10 17:59:59 +08:00

454 lines
14 KiB
Markdown
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# PC 实现方案详细设计
## PC 数据库特征分析
基于对 PC 数据库的观察,PC 的实现方式具有以下特征:
### 1. PC 的核心字段
```
- source: 源实体 (如 "32 SPL gene(s) (CiSPLs)")
- source type: 源实体类型 (如 "gene", "gene identifier")
- relationship: 关系 (如 "identified in")
- target: 目标实体 (如 "pecan genome")
- target type: 目标实体类型 (如 "organism")
- p_source: 章节来源 (如 "34966577_abstract")
- basis: 证据文本
- species: 物种
```
### 2. PC 的实体识别特点
**复合实体识别**:
- PC 识别 `"32 SPL gene(s) (CiSPLs)"` 作为一个整体
- PC 识别 `"SPL gene(s) family"` 作为群体实体
- PC 识别 `"nine segmentally duplicated gene(s) pairs"` 作为复合概念
**实体类型丰富**:
- `gene`: 单个基因
- `gene identifier`: 基因标识符
- `phenotype`: 表型
- `process`: 过程
- `organism`: 生物体
- `treatment`: 处理条件
### 3. PC 的关系抽取特点
**基于证据文本**:
- PC 的 `basis` 字段包含完整的证据句子
- 关系从证据句子中抽取
**章节溯源**:
- PC 记录关系来源的章节 (`p_source`)
- 如 `34966577_abstract`, `34966577_results1`
**关系类型标准化**:
- PC 使用标准化的关系类型
- 如 `identified in`, `collinear with`, `involved in`
---
## 最接近 PC 的详细实现方案
### 方案概述
采用 **"复合实体识别 + 基于证据的关系抽取 + 章节溯源"** 的三层架构
### 第一层:复合实体识别
#### 1.1 实体类型扩展
```python
ENTITY_TYPES = {
# 基础实体
'gene': r'\b[A-Z][a-z]{1,2}[A-Z]{1,4}\d{1,4}[a-z]?\b',
# 复合实体 - 数量+基因
'gene_group_count': r'(\d+)\s+([A-Z][a-z]{1,2}[A-Z]{1,4})\s+gene\(s\)',
# 复合实体 - 数量+基因+别名
'gene_group_with_alias': r'(\d+)\s+([A-Z][a-z]{1,2}[A-Z]{1,4})\s+gene\(s\)\s+\(\s*([A-Z][a-z]{1,2}[A-Z]{1,4})s\s*\)',
# 复合实体 - 基因家族
'gene_family': r'([A-Z][a-z]{1,2}[A-Z]{1,4})\s+gene\(s\)\s+family',
# 复合实体 - 重复基因对
'duplicated_gene_pairs': r'(\d+|\w+)\s+segmentally\s+duplicated\s+gene\(s\)?\s+pairs?',
# 复合实体 - 共线性基因对
'collinear_gene_pairs': r'(\d+)\s+collinear\s+gene\(s\)?\s+pairs?',
# 转录因子群体
'transcription_factor_group': r'([A-Z][a-z]{1,2}[A-Z]{1,4})\s+transcription\s+factors?',
}
```
#### 1.2 实体识别流程
```python
class CompositeEntityRecognizer:
def recognize(self, text):
entities = []
# 步骤1: 识别基础基因实体
for match in re.finditer(ENTITY_TYPES['gene'], text):
entities.append({
'name': match.group(),
'type': 'gene',
'start': match.start(),
'end': match.end()
})
# 步骤2: 识别复合实体
for match in re.finditer(ENTITY_TYPES['gene_group_with_alias'], text):
entities.append({
'name': f"{match.group(1)} {match.group(2)} gene(s) ({match.group(3)}s)",
'type': 'gene_group',
'start': match.start(),
'end': match.end(),
'count': match.group(1),
'family': match.group(2),
'alias': match.group(3)
})
# 步骤3: 实体链接和消歧
# 将 "CiSPLs" 链接到 "32 SPL gene(s) (CiSPLs)"
for entity in entities:
if entity['type'] == 'gene_group':
# 找到该群体中的所有单个基因
member_genes = self._find_member_genes(text, entity)
entity['member_genes'] = member_genes
return entities
def _find_member_genes(self, text, group_entity):
"""找到复合实体中的成员基因"""
# 例如从 "32 SPL gene(s) (CiSPLs)" 中找到所有 CiSPL 基因
alias = group_entity.get('alias', '')
pattern = rf'{alias}\d+[a-z]?'
return re.findall(pattern, text)
```
### 第二层:基于证据的关系抽取
#### 2.1 证据句子提取
```python
class EvidenceExtractor:
def extract_evidence_sentences(self, text, entity):
"""提取包含实体的证据句子"""
sentences = []
# 分割句子
sentence_pattern = r'[^.!?]+[.!?]+'
all_sentences = re.findall(sentence_pattern, text)
for sent in all_sentences:
# 检查句子是否包含实体
if entity['name'] in sent or self._entity_in_sentence(entity, sent):
sentences.append({
'text': sent.strip(),
'entity': entity,
'confidence': self._calculate_confidence(entity, sent)
})
return sentences
def _entity_in_sentence(self, entity, sentence):
"""检查实体是否在句子中(支持别名匹配)"""
# 基础名称匹配
if entity['name'] in sentence:
return True
# 别名匹配
if entity.get('alias') and entity['alias'] in sentence:
return True
# 成员基因匹配
if entity.get('member_genes'):
for gene in entity['member_genes']:
if gene in sentence:
return True
return False
```
#### 2.2 关系模式匹配(PC风格)
```python
PC_RELATION_PATTERNS = [
# identified in 模式
{
'relation': 'identified in',
'patterns': [
r'(\d+\s+[A-Z][a-z]+[A-Z]+\s+gene\(s\)\s+\([A-Z][a-z]+[A-Z]+s\))\s+were\s+identified\s+in\s+([^\.]+)',
r'(\d+\s+[A-Z][a-z]+[A-Z]+\s+gene\(s\))\s+were\s+identified\s+in\s+([^\.]+)',
r'([A-Z][a-z]+[A-Z]+\s+gene\(s\))\s+were\s+identified\s+in\s+([^\.]+)',
r'([A-Z][a-z]+[A-Z]+\d+[a-z]?)\s+was\s+identified\s+in\s+([^\.]+)',
],
'source_type': 'gene_group',
'target_type': 'organism'
},
# collinear with 模式
{
'relation': 'collinear with',
'patterns': [
r'([A-Z][a-z]+[A-Z]+\d+[a-z]?)\s+(?:is|was)\s+collinear\s+with\s+([A-Z][a-z]+[A-Z]+\d+[a-z]?)',
# 列表形式: "CiSPL3a/AtSPL3, CiSPL4b/AtSPL5"
r'([A-Z][a-z]+[A-Z]+\d+[a-z]?)/([A-Z][a-z]+[A-Z]+\d+[a-z]?)',
],
'source_type': 'gene',
'target_type': 'gene'
},
# involved in 模式
{
'relation': 'involved in',
'patterns': [
r'([A-Z][a-z]+[A-Z]+\s+gene\(s\)\s+family)\s+(?:is|are)\s+involved\s+in\s+([^\.]+)',
r'([A-Z][a-z]+[A-Z]+\s+gene\(s\))\s+(?:is|are)\s+involved\s+in\s+([^\.]+)',
r'([A-Z][a-z]+[A-Z]+\d+[a-z]?)\s+(?:is|are)\s+involved\s+in\s+([^\.]+)',
# 被动形式
r'([A-Z][a-z]+[A-Z]+\d+[a-z]?)\s+.*?\s+involved\s+in\s+([^\.]+)',
],
'source_type': 'gene',
'target_type': 'process'
},
# contain 模式
{
'relation': 'contain',
'patterns': [
r'([A-Z][a-z]+[A-Z]+\s+gene\(s\))\s+contain\s+([^\.]+)',
r'([A-Z][a-z]+[A-Z]+\d+[a-z]?)\s+contains?\s+([^\.]+)',
r'([A-Z][a-z]+[A-Z]+\s+gene\(s\))\s+had\s+([^\.]+)',
],
'source_type': 'gene',
'target_type': 'attribute'
},
]
```
### 第三层:章节溯源
```python
class SectionTracker:
def __init__(self):
self.section_markers = {
'abstract': r'ABSTRACT|Abstract',
'intro': r'INTRODUCTION|Introduction',
'methods': r'MATERIALS\s*&\s*METHODS|Methods',
'results': r'RESULTS|Results',
'discussion': r'DISCUSSION|Discussion',
'conclusion': r'CONCLUSION|Conclusion',
'references': r'REFERENCES|References',
}
def track_sections(self, text):
"""追踪文本中的章节"""
sections = []
current_pos = 0
for section_name, pattern in self.section_markers.items():
for match in re.finditer(pattern, text, re.IGNORECASE):
sections.append({
'name': section_name,
'start': match.start(),
'end': len(text) # 临时,后续会更新
})
# 排序并计算章节边界
sections.sort(key=lambda x: x['start'])
for i in range(len(sections) - 1):
sections[i]['end'] = sections[i + 1]['start']
return sections
def get_section_for_position(self, position, sections):
"""获取指定位置所属的章节"""
for section in sections:
if section['start'] <= position < section['end']:
return section['name']
return 'unknown'
### 第四层:关系抽取流程
```python
class PCKnowledgeExtractor:
"""PC 风格的知识抽取器"""
def __init__(self):
self.entity_recognizer = CompositeEntityRecognizer()
self.evidence_extractor = EvidenceExtractor()
self.section_tracker = SectionTracker()
def extract(self, text, pmid):
"""主抽取流程"""
relations = []
# 步骤1: 章节追踪
sections = self.section_tracker.track_sections(text)
# 步骤2: 实体识别
entities = self.entity_recognizer.recognize(text)
# 步骤3: 对每个实体抽取关系
for entity in entities:
# 提取证据句子
evidence_sentences = self.evidence_extractor.extract_evidence_sentences(text, entity)
for evidence in evidence_sentences:
# 匹配关系模式
for pattern_def in PC_RELATION_PATTERNS:
for pattern in pattern_def['patterns']:
matches = re.finditer(pattern, evidence['text'], re.IGNORECASE)
for match in matches:
# 确定源和目标
if entity['name'] in match.group(1) or match.group(1) in entity['name']:
source = entity['name']
target = match.group(2).strip()
else:
continue
# 确定章节
section = self.section_tracker.get_section_for_position(
evidence['text'].index(match.group(0)),
sections
)
# 构建关系
relation = {
'source': source,
'source_type': pattern_def['source_type'],
'relation': pattern_def['relation'],
'target': target,
'target_type': pattern_def['target_type'],
'evidence': evidence['text'],
'section': f"{pmid}_{section}",
'pmid': pmid,
'confidence': evidence['confidence']
}
relations.append(relation)
return relations
```
---
## 关键改进点
### 1. 复合实体识别
**PC 做法**: 识别 `"32 SPL gene(s) (CiSPLs)"` 作为一个整体实体
**V13 改进**:
```python
# 添加复合实体模式
r'(\d+)\s+([A-Z][a-z]+[A-Z]+)\s+gene\(s\)\s+\(\s*([A-Z][a-z]+[A-Z]+)s\s*\)'
# 识别结果
{
'name': '32 SPL gene(s) (CiSPLs)',
'type': 'gene_group',
'count': '32',
'family': 'SPL',
'alias': 'CiSPL',
'member_genes': ['CiSPL2a', 'CiSPL2b', ...] # 自动提取成员
}
```
### 2. 实体链接
**PC 做法**: 将 `"CiSPLs"` 链接到 `"32 SPL gene(s) (CiSPLs)"`
**V13 改进**:
```python
# 建立实体链接
for entity in entities:
if entity['type'] == 'gene_group':
# 找到所有别名引用
alias_pattern = rf"\b{entity['alias']}s?\b"
for match in re.finditer(alias_pattern, text):
# 链接到复合实体
entity['references'].append({
'text': match.group(),
'position': match.start()
})
```
### 3. 列表形式关系识别
**PC 做法**: 识别 `"CiSPL3a/AtSPL3, CiSPL4b/AtSPL5"` 中的共线性关系
**V13 改进**:
```python
# 列表形式模式
r'([A-Z][a-z]+[A-Z]+\d+[a-z]?)/([A-Z][a-z]+[A-Z]+\d+[a-z]?)'
# 从列表中提取多个关系
pairs = re.findall(pattern, "CiSPL3a/AtSPL3, CiSPL4b/AtSPL5, CiSPL6a/AtSPL6")
for source, target in pairs:
relations.append({
'source': source,
'relation': 'collinear_with',
'target': target
})
```
### 4. 灵活的模式匹配
**PC 做法**: 支持多种表达方式
- `"CiSPL gene(s) contain..."`
- `"CiSPL genes contained..."`
- `"The CiSPL genes had..."`
**V13 改进**:
```python
# 使用词干提取或模糊匹配
from nltk.stem import PorterStemmer
stemmer = PorterStemmer()
# 匹配 "contain", "contained", "contains", "had" 等
contain_stems = ['contain', 'contained', 'contains', 'had', 'have']
```
---
## 实施优先级
### 高优先级(立即实施)
1. **复合实体识别**: 识别 `"32 SPL gene(s) (CiSPLs)"` 等
2. **列表形式关系**: 识别 `"CiSPL3a/AtSPL3"` 共线性
3. **实体链接**: 将别名链接到复合实体
### 中优先级(后续实施)
4. **章节溯源**: 记录关系来源章节
5. **证据句子提取**: 提取完整证据文本
6. **词形还原**: 支持时态/词形变化
### 低优先级(可选)
7. **人工校验接口**: 支持人工确认关系
8. **机器学习优化**: 使用 NER 模型提升识别
---
## 预期效果
实施该方案后,预计:
| 指标 | 当前 | 预期 |
|:---|:---:|:---:|
| 覆盖率 | 19.9% | 35-40% |
| identified in | 0 | 15-18 |
| collinear with | 0 | 12-14 |
| involved in | 0 | 10-12 |
| contain | 0 | 5-6 |
**关键改进**: 能够识别复合实体和列表形式的关系,大幅提升 PC 关键关系的覆盖率。