3. 医学文本预处理技术:从分词到实体边界识别

文本预处理是整个NLP管线的“地基”。处理新闻评论,通用分词器就够用。但碰到医生写的病历,用通用工具很容易翻车。我最早做医学文本处理时,踩过不少这方面的坑。

3.1 医学分词:领域专有工具才是正解

分词是中文处理的起点。通用工具(比如jieba、HanLP)对“肌酸激酶同工酶”这类长医学实体,几乎必错。它们会切成“肌酸/激酶/同工酶”甚至更碎。

我建议你优先考虑下面几个医学分词工具:

工具名称特点适合场景
PySSP (Shenzhen)自带医学词典,支持多词性标注中文电子病历、检验报告
PKUSeg (医学版)北大开源,提供了医学领域预训练模型药品说明书、医学论文摘要
CCKS 评测词库每年评测提供高质量医学词表定制化项目,词典融合
BERT + CRF (微调版)效果最好,但推理慢对精度要求极高的场景

说白了,如果你只是做Demo,用jieba + 自定义医学词典也能顶一下。但要上线生产,我建议上PySSP或者微调一个BERT-CRF。我之前在一个药品不良反应项目里,用jieba切“盐酸肾上腺素注射液”,结果被切得七零八落,后处理代码写了快200行来补漏。换了PySSP之后,直接能正确分词,省了不少事。

# 使用 pkuseg 医学模型的示例
import pkuseg
seg = pkuseg.pkuseg(model_name='medicine')
text = '患者口服盐酸二甲双胍片后出现恶心症状'
seg_list = seg.cut(text)
print(seg_list)
# 输出: ['患者', '口服', '盐酸二甲双胍片', '后', '出现', '恶心', '症状']

3.2 词性标注:让模型知道名词和动词的区别

分完词,我们要给每个词打上词性标签。这步在药物实体关系抽取里尤其重要。

举个例子,“口服”是动词,“血压”是名词,“升高”是形容词/动词。模型如果不知道词性,很难判断“高血压”这个实体从哪开始。

我常用的词性标注方案:

  • 北大数据集 (PKU POS):32个词性标签,对医学文本够用
  • CTB (Chinese Treebank):更细粒度,但标注成本高
  • 自定义医学词性标签:比如给“药品名”专门加个标签 pos_drug

这里有个坑要提醒你。中文医学文本里,“黄芪”在中医语境下是名词,但“黄芪片”这种组合,通用标注器可能标错。我在实际项目中,发现通用标注器会把“黄芪片”里的“片”标成量词 (q),从而导致后续实体边界判断出错。

嗯,所以我的习惯是:先用通用工具标一遍,再用医学专有词表做后修正。

# 使用 PySSP 进行词性标注
import pyssp
annotator = pyssp.PySSP()
text = '服用阿莫西林后出现皮疹'
result = annotator.pos_tag(text)
for token in result:
    print(token.word, token.pos)
# 服用 v, 阿莫西林 n_drug, 后 p, 出现 v, 皮疹 n_symptom
小经验:如果项目中需要频繁出现“药品名”和“症状名”,我建议直接扩展词性标注体系,把 n_drug、n_symptom 加进去,而不是让模型自己去猜。

3.3 句法分析:理清“谁修饰谁”

句法分析帮我们弄懂句子的结构。在药物关系抽取里,它特别适合处理长修饰关系。你想想看,“口服盐酸二甲双胍片患者的血糖水平显著降低”,这里面“血糖水平”是被“口服盐酸二甲双胍片”修饰的。句法依赖能把这种关系清晰的画出来。

常用方法有:

  • 依存句法分析:画出一个词之间的“修饰树”。工具如 LTP、哈工大 LTP4
  • 成分句法分析:生成短语结构树。工具如 Berkeley Parser

我更喜欢依存句法。因为它直接给出词对之间的“头-修饰”关系,这和我们抽实体关系时需要的“驱动词-实体”模式很接近。

为什么会这样?因为药物关系大多是一个动作(口服、注射、引起)连接两个实体(药品、症状)。依存句法能帮你快速定位动作词的修饰范围。

# 使用 LTP 进行依存句法分析(伪代码示例)
from ltp import LTP
ltp = LTP()
seg, hidden = ltp.seg(['口服盐酸二甲双胍片后血糖降低'])
dep = ltp.dep(hidden)
# 输出: 口服 -> 二甲双胍片 (dobj 关系)
#       降低 -> 血糖 (nsubj 关系)

3.4 实体边界识别:分词的“最后一公里”

实体边界识别,本质上是把分词后可能切错的医学实体再拼回来。很多医学实体由多个词复合而成,比如“慢性/阻塞性/肺疾病”。如果分词阶段没把它们合并成一个整体,后面做关系抽取就会出错。

我常用的方法有三类:

方法原理优点局限
基于规则用正则或词典匹配边界快,零成本覆盖率低,需要大量人工规则
CRF (条件随机场)把边界识别当序列标注做 (BIO标注)效果稳定,适合医学文本依赖人工特征工程
基于BERT利用预训练模型直接预测实体边界效果最好,泛化强需要标注数据,推理慢

我曾经在一个中文药物文本上测试过,结果发现边界不对。比如“盐酸肾上腺素注射液”,BERT+CRF直接给打成了“盐酸肾上腺/注射液”。后来我在标注数据里补充了几百条这种例子,重新训练后才修正过来。

避坑指南:实体边界识别不要光看准确率。我建议你重点测那些“长实体”(4个字以上)和“嵌套实体”(比如“高血压病”嵌在“高血压病患者”里)。边界查准率低,会直接导致后续关系抽取的精度腰斩。我之前遇到过,bert输出的是“盐酸”和“肾上腺素”,但模型预测的边界是“盐酸肾上腺”,这种我一般会加一个规则修正。

3.5 本章流程总结:一个完整的预处理管线

下面这张图汇总了我平时搭建医学预处理管线的典型步骤。从原始文本输入,到经过分词、词性标注、句法分析、实体边界识别,最后输出结构化的、可供下游模型使用的数据。

药物实体关系抽取预处理流程 药物实体关系抽取预处理管线 原始医学文本输入 分词(医学细分词器) 词性标注 句法分析 实体边界识别(规则/CRF/BERT) 输出结构化的药物知识 说明 每一步的输出 都会作为下一步的输入 边界识别结果 直接影响后续 关系抽取的质量 建议每一步 都做人工验证

在实际项目中,我推荐优先保障“分词”和“实体边界识别”这两个环节。词性标注和句法分析可以视情况降低要求——如果你的关系抽取模型是基于BERT微调的,那它内部其实已经隐含了句法信息,不一定需要显式的句法分析输出。

要点总结:预处理不是一劳永逸的。我给你的建议是——建一个小的“领域错误库”,每次跑完预处理,把分词和边界识别出错的结果记录下来。迭代优化3-5轮后,整个管线的稳定性会大幅提升。具体做法:先跑一遍通用工具,再用规则修正常见错误,最后用领域数据微调一个轻量模型。这是我在实际项目中验证过的最优路径。