一、命名实体识别基本概念
信息有多种表现形式,一个重要的形式就是结构化数据:即实体和关系的规范和可预测的组织。而现实生活中大多数自然语言句子是非结构化数据,为从文本获得其意义,我们首先需要将自然语言数据转化为结构化数据,然后利用强大的查询工具,如sql。这种从文本获取意义的方法被称为信息提取。
文本信息提取处理的顺序是:首先,使用句子分割器将该文档的原始文本分割成句,然后使用分词器将每个句子进一步细分为词,之后对每个句子进行词性标注,对每个标注过的句子进行命名实体识别,最后使用关系识别搜索文本中不同实体间可能的关系。所以在对文档进行命名实体识别之前必须对文档进行分句,分词和词性标注。在命名实体识别中,我们需要分割和标注可能组成具有某种关系的实体,通常是名词短语。
命名实体识别(Named Entity Recognition)主要是识别出文本中出现的专有名称和有意义的数量短语并进行分类。命名实体(Named Entity )主要包括实体(组织名,人名,地名)、时间表达式(日期、时间)和数字表达式(货币值、百分数等)。其中,对时间表达式和数字表达式的识别相对于对组织名,人名和地名这些实体的识别来说要简单些,因为时间表达式和数字表达式在形式上有规律可循,而针对组织名,人名和地名,由于其具有开放性和发展性的特点,识别难度比较大。
目前已有的命名实体识别的方法主要分为两大类:基于规则的方法和基于统计的方法。
基于规则的方法主要是根据待识别的命名实体的语言学上的表现形式,人为设定一些规则来识别命名实体的方法。这类方法实现的效果很大程度上依赖于规则的设定且需要大量的专业知识,而且因为不同领域内的实体具有不同的规则,所以对每个新领域的文本处理都要重新设定规则。使用基于规则的方法来进行命名实体识别比较消耗时间和消耗人力。
基于统计的方法主要利用原始的或经过加工的(人工标注的)语料进行训练,其语料的加工(标注)不需要非常多的语言学的知识,而且小规模的语料可以在可接受的时间和人力代价下完成,且基于统计的方法实现的命名实体识别在新的领域使用时可以不作改动或者做较少的改动,只需要利用新领域的语料进行训练即可。但是由于基于统计的方法获取的概率知识不如基于规则的方法所具有的专家的语言学知识的可靠性,所以基于统计的命名实体识别系统的性能要比基于规则的命名实体识别的性能要低。用于命名实体识别的基于统计的方法主要有:N元模型、隐马尔克夫模型(HMM)、最大熵模型(ME)、决策树(Decision Tree)等等。目前评价性能最好的是隐马尔克夫模型。
二、命名实体识别实现
目前国内有多个开源的中文语言处理工具可供直接调用实现命名实体识别,比如复旦大学研发的fudanNLP,中科院的NLPIR分词系统(又名ICTCLAS2013)和哈工大的LTP。
因为从一开始接触自然语言处理就是用Python进行基本的操作,恰好哈工大的LTP提供python接口,所以就直接用python调用其封装成的pyltp的模块实现命名实体识别。
具体安装配置使用详情请参见哈工大ltp官方文档。参考的博客:python下的自然语言处理利器——pyltp学习手札。
# -*- coding: utf-8 -*-
import codecs
from pyltp import SentenceSplitter
from pyltp import Segmentor
from pyltp import Postagger
from pyltp import NamedEntityRecognizer
#读取待处理文本
'''
读取的文本格式是encoding参数值,codecs函数将其转化为unicode格式。
'''
news_files = codecs.open('news_content.txt','r',encoding='utf8')
news_list = news_files.readlines()
#type(news_list[1].encode('utf-8'))
#分句
'''
此函数参数输入的格式必须为str格式,所以直接获取的list里的参数值需要
通过encode('utf-8'),从Unicode转换为str
'''
def sentence_splitter(sentence):
sents = SentenceSplitter.split(sentence)
print '\n'.join(sents)
sents_list = list(sents)
return sents_list
#分词
def segmentor(sentence):
segmentor = Segmentor()
segmentor.load('E:\\ltp-data-v3.3.1\\ltp_data\\cws.model')#加载模型
words = segmentor.segment(sentence) #分词
#默认可以这样输出
#print '\t'.join(words)
#可以转化成List输出
word_list = list(words)
segmentor.release()#释放模型
return word_list
#词性标注
def posttagger(words):
postagger = Postagger()
postagger.load('E:\\ltp-data-v3.3.1\\ltp_data\\pos.model')
posttags=postagger.postag(words) #词性标注
postags = list(posttags)
postagger.release() #释放模型
#print type(postags)
return postags
#命名实体识别
def ner(words,postags):
print '命名实体开始!'
recognizer = NamedEntityRecognizer()
recognizer.load('E:\\ltp-data-v3.3.1\\ltp_data\\ner.model') #加载模型
netags = recognizer.recognize(words,postags) #命名实体识别
for word,ntag in zip(words,netags):
print word+'/'+ ntag
recognizer.release() #释放模型
nerttags = list(netags)
return nerttags
#新建一个txt文件保存命名实体识别的结果
out_file = codecs.open('out_nerfile.txt','w',encoding='utf8')
sents = sentence_splitter(news_list[1].encode('utf-8'))
for sent in sents:
words=segmentor(sent)
tags = posttagger(words)
nertags = ner(words,tags)
for word,nertag in zip(words,nertags):
out_file.write(word.decode('utf-8')+'/'+nertag.decode('utf-8')+' ')
out_file.close()
out_file.txt的结果是:
三、命名实体提取
在成功进行命名实体识别后,要把文本中的命名实体识别给提取出来,就需要把组成命名实体的词给单个提取出来且根据标记连接短语词组。在这里,主要是提取命名实体三大类(组织、人名和地名)中的组织名。我主要利用正则表达式把命名实体的每个词(词的形式为:词语/命名实体标注)给提取出来,再利用().join函数把单个词给连接起来。直接上代码。
# -*- coding: utf-8 -*-
"""
提取的命名实体类型是组织
"""import codecsimport re
#读取命名实体识别的结果
file=codecs.open('out_nerfile.txt','r',encoding='utf8')file_content = file.read()
#以空格为切割标志,使每个命名实体标注词组为列表的一个元素file_list = file_content.split()
#创建两个空列表,分别保存命名实体和构成命名实体短语词组的各个词ner_list=[]phrase_list=[]
#使用正则表达式,判断单个词是否是命名实体for word in file_list: if(re.search('Ni$',word)): print '提取出的命名实体为:',word word_list=word.split('/')
#判断是否单个词是否是命名实体 if re.search(r'^S',word_list[1]): ner_list.append(word_list[0])
#判断多个词构成的命名实体,并对其进行连词处理 elif re.search(r'^B',word_list[1]): phrase_list.append(word_list[0])
elif re.search(r'^I',word_list[1]): phrase_list.append(word_list[0])
else: phrase_list.append(word_list[0]) #把list转换为字符串. ner_phrase=''.join(phrase_list) ner_list.append(ner_phrase) phrase_list=[]
#输出命名实体识别的结果for ner in ner_list: print ner输出的结果如下图所示:
虽然初步经历一系列的关于自然语言处理的学习、借助各种工具、搜索相关博客勉勉强强把命名实体提取给成功抽取出来了,但是仍然不清楚其内在的机理,在后续的学习和实践中会注重对其本质的理解分析,还要继续啃《python自然语言处理》这本小红书哪~另外,在实践操作过程中,遇到很多bug,后续再慢慢整理吧。