454 lines
14 KiB
Markdown
Executable File
454 lines
14 KiB
Markdown
Executable File
# 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 关键关系的覆盖率。 |