命名实体识别

命名实体识别

一、概述

1. 命名实体。 文本中有一些描述实体的词汇。比如人名、地名、组织机构名、股票基金、医学术语等,称为命名实体。具有以下共性:

  • 数量无穷。比如宇宙中的恒星命名、新生儿的命名不断出现新组合。
  • 构词灵活。比如中国工商银行,既可以称为工商银行,也可以简称工行。
  • 类别模糊。有一些地名本身就是机构名,比如“国家博物馆”

2. 命名实体识别。 识别出句子中命名实体的边界与类别的任务称为命名实体识别。由于上述难点,命名实体识别也是一个统计为主、规则为辅的任务。 对于规则性较强的命名实体,比如网址、E-mail、IBSN、商品编号等,完全可以通过正则表达式处理,未匹配上的片段交给统计模型处理。 命名实体识别也可以转化为一个序列标注问题。具体做法是将命名实体识别附着到{B,M,E,S}标签,比如, 构成地名的单词标注为“B/ME/S- 地名”,以此类推。对于那些命名实体边界之外的单词,则统一标注为O( Outside )。

命名实体识别实际上可以看作分词与词性标注任务的集成: 命名实体的边界可以通过{B,M,E,S}确定,其类别可以通过 B-nt 等附加类别的标签来确定。

二、 基于感知机序列标注的命名实体识别

感知机模型,详细见: 感知机分类与序列标注

感知机模型词性标注(自动下载 PKU 语料库): perceptron_ner.py

from  pyhanlp import *
import zipfile
import os
from pyhanlp.static import download, remove_file, HANLP_DATA_PATH

def test_data_path():
    """
    获取测试数据路径,位于$root/data/test,根目录由配置文件指定。
    :return:
    """
    data_path = os.path.join(HANLP_DATA_PATH, 'test')
    if not os.path.isdir(data_path):
        os.mkdir(data_path)
    return data_path



## 验证是否存在 MSR语料库,如果没有自动下载
def ensure_data(data_name, data_url):
    root_path = test_data_path()
    dest_path = os.path.join(root_path, data_name)
    if os.path.exists(dest_path):
        return dest_path

    if data_url.endswith('.zip'):
        dest_path += '.zip'
    download(data_url, dest_path)
    if data_url.endswith('.zip'):
        with zipfile.ZipFile(dest_path, "r") as archive:
            archive.extractall(root_path)
        remove_file(dest_path)
        dest_path = dest_path[:-len('.zip')]
    return dest_path

## 指定 PKU 语料库
PKU98 = ensure_data("pku98", "http://file.hankcs.com/corpus/pku98.zip")
PKU199801 = os.path.join(PKU98, '199801.txt')
PKU199801_TRAIN = os.path.join(PKU98, '199801-train.txt')
PKU199801_TEST = os.path.join(PKU98, '199801-test.txt')
POS_MODEL = os.path.join(PKU98, 'pos.bin')
NER_MODEL = os.path.join(PKU98, 'ner.bin')

## ===============================================
## 以下开始 感知机 命名实体识别
NERTrainer = JClass('com.hankcs.hanlp.model.perceptron.NERTrainer')
PerceptronNERecognizer = JClass('com.hankcs.hanlp.model.perceptron.PerceptronNERecognizer')
PerceptronSegmenter = JClass('com.hankcs.hanlp.model.perceptron.PerceptronSegmenter')
PerceptronPOSTagger = JClass('com.hankcs.hanlp.model.perceptron.PerceptronPOSTagger')
Sentence = JClass('com.hankcs.hanlp.corpus.document.sentence.Sentence')
AbstractLexicalAnalyzer = JClass('com.hankcs.hanlp.tokenizer.lexical.AbstractLexicalAnalyzer')
Utility = JClass('com.hankcs.hanlp.model.perceptron.utility.Utility')

def train(corpus, model):
    trainer = NERTrainer()
    return PerceptronNERecognizer(trainer.train(corpus, model).getModel())

def test(recognizer):
    # 包装了感知机分词器和词性标注器的词法分析器
    analyzer = AbstractLexicalAnalyzer(PerceptronSegmenter(), PerceptronPOSTagger(), recognizer)
    print(analyzer.analyze("华北电力公司董事长谭旭光和秘书胡花蕊来到美国纽约现代艺术博物馆参观"))
    scores = Utility.evaluateNER(recognizer, PKU199801_TEST)
    Utility.printNERScore(scores)

if __name__ == '__main__':
    recognizer = train(PKU199801_TRAIN, NER_MODEL)
    test(recognizer)

    ## 支持在线学习
    # 创建了感知机词法分析器
    analyzer = PerceptronLexicalAnalyzer(PerceptronSegmenter(), PerceptronPOSTagger(), recognizer)  # ①
    # 根据标注样本的字符串形式创建等价的 Sentence对象
    sentence = Sentence.create("与/c 特朗普/nr 通/v 电话/n 讨论/v [太空/s 探索/vn 技术/n 公司/n]/nt")  # ②
    # 测试词法分析器对样本的分析结果是否与标注一致,若不一致重复在线学习,直到两者一致。
    while not analyzer.analyze(sentence.text()).equals(sentence):  # ③
        analyzer.learn(sentence)

结果如下:

华北电力公司/nt 董事长/n 谭旭光/nr 和/c 秘书/n 胡花蕊/nr 来到/v [美国纽约/ns 现代/ntc 艺术/n 博物馆/n]/ns 参观/v

三、 自定义领域命名实体识别

以上都是通用领域上的语料库,所含的命名实体仅限于人名、地名、机构名等。假设我们想要识别专门领域中的命名实体,这时,我们就要自定义领域的语料库了。

标注领域命名实体识别语料库。首先我们需要收集一些文本, 作为标注语料库的原料,称为生语料。生语料的量级越大越好,一般最低不少于数千个句子。

生语料准备就绪后,就可以开始标注了。对于命名实体识别语料库,若以词语和词性为特征的话,还需要标注分词边界和词性。不过我们不必从零开始标注,而可以在HanLP的标注基础上进行校正。

参考资料

HanLP

留下评论