一词多义是中文信息处理中一个特别常见的现象,经典的"苹果是水果还是公司"问题一直是困扰大家的一个实际问题,而就“苹果”而言,其义项多达19个,如“苹果产品公司”、“邓丽欣演唱歌曲”等,而在知识图谱构建过程当中,经常会遇到实体链接的问题。
上一篇文章从领域实体链接关键技术出发,对实体链接任务的定义、候选实体提及的生成、候选实体块的匹配、候选实体的消歧等环节进行介绍。
本次文章进行实践,借助在线百科知识库,输入句子以及待链接的实体,通过获取百科义项,完成链接项目的实践。
项目地址:
https://github.com/liuhuanyong/WordMultiSenseDisambiguation
完整的实体链接流程包括实体识别(实体指称识别)和实体链接两个部分,实体识别可以通过基于目标实体名称的精确匹配、基于目标名称与ES搜索排序的实体识别方法,基于序列标注识别等方式完成。
第二个阶段的实体链接是核心部分,为了简要地说明实体链接的流程,本项目将对这一问题进行尝试,实现了一个依靠在线百科知识库的特定句子下词语语义消歧的方法。
如上图所示:
首先,根据给定的待链接实体通过网络请求,获取百科中所记录的多个义项;
其次,获取每个义项对应的页面关键词以及描述信息构造目标上下文,接着,利用词向量相加求平均的方式对上下文进行向量化表示;
最后对词语上下文与义项语义表示相似度计算,并按照得分从大到小排序得到的份最高的实体,完成链接。
1)收集消歧或者链接词语的多个义项,使用百度百科查询的接口进行解析,返回多个义项的列表。
def collect_mutilsens(self, word):
url = "http://baike.baidu.com/item/%s?force=1" % parse.quote(word)
html = self.get_html(url)
selector = etree.HTML(html)
sens = [''.join(i.split(':')[1:]) for i in selector.xpath('//li[@class="list-dot list-dot-paddingleft"]/div/a/text()')]
sens_link = ['http://baike.baidu.com' + i for i in selector.xpath('//li[@class="list-dot list-dot-paddingleft"]/div/a/@href')]
sens_dict = {sens[i]:sens_link[i] for i in range(len(sens))}
return sens_dict
2)根据列表中的实体义项,获取每个义项对应的描述信息,作为该个义项的意义描述。
(1)获取描述信息
def extract_desc(self, link):
html = self.get_html(link)
selector = etree.HTML(html)
keywords = selector.xpath('//meta[@name="keywords"]/@content')
desc = selector.xpath('//meta[@name="description"]/@content')
return desc, keywords
(2)获取义项上下文
def collect_concepts(self, wd):
sens_dict = self.collect_mutilsens(wd)
for concept, links in sens_dict.items():
link = links[0]
desc, keywords = self.extract_desc(link)
context = ''.join(desc + [' '] + keywords)
concepts_dict[concept] = context
return concepts_dict
3)对实体义项的描述信息进行关键词提取,作为整个义项的一个语义特征表示。
def extract_keywords(self, sent):
keywords = [i for i in anse.extract_tags(sent, topK=20, withWeight=False)]
return keywords
4)调用预训练词向量文件,对实体义项上下文以及消歧或者链接词语的上下文进行向量化表示。
(1)加载词向量
def load_embedding(self, embedding_path):
embedding_dict = {}
for line in open(embedding_path):
line = line.strip().split(' ')
wd = line[0]
vector = np.array([float(i) for i in line[1:]])
embedding_dict[wd] = vector
return embedding_dict
(2)获取单个词的词向量
def get_wordvector(self, word):
return np.array(self.embdding_dict.get(word, [0]*self.embedding_size))
(3)基于wordvector,通过lookup table的方式找到句子的wordvector的表示
def rep_sentencevector(self, sentence):
word_list = self.extract_keywords(sentence)
unknown_embedding = np.zeros(self.embedding_size)
embedding = np.zeros(self.embedding_size)
for wd in word_list:
embedding += self.embdding_dict.get(wd, unknown_embedding)
return embedding/len(sent_len)
(4)利用cosine余弦相似度计算两个上下文的语义相似度,作为链接得分
def similarity_cosine(self, vector1, vector2):
cos1 = np.sum(vector1*vector2)
cos21 = np.sqrt(sum(vector1**2))
cos22 = np.sqrt(sum(vector2**2))
similarity = cos1/float(cos21*cos22)
if str(similarity) == 'nan':
return 0.0
else:
return similarity
(5)对链接的得分按照从大到小排序,返回最终实体链接结果
def detect_main(self, sent, word):
sent = sent.replace(word, '')
concept_dict = self.collect_concepts(word)
sent_vector = self.rep_sentencevector(sent)
concept_scores_sent = {}
for concept, desc in concept_dict.items():
concept_vector = self.rep_sentencevector(desc)
similarity_sent = self.similarity_cosine(sent_vector, concept_vector)
concept_scores_sent[concept] = similarity_sent
concept_scores_sent = sorted(concept_scores_sent.items(), key=lambda asd:asd[1],reverse=True)
return concept_scores_sent[:3]
我们以“苹果”作为待链接实体,并给出相应的上下文进行测试,可以得到如下结果。
1)上下文:苹果发布新产品了。待链接实体:苹果
结果:
[('公司', 0.4309597564421702), ('物品', 0.39608141793731144), ('歌曲', 0.37937766923800026)]
2)上下文:最近连降大雨,种苹果的果农损失惨重。待链接实体:苹果
结果:
[ ('果树', 0.23943442305363207), ('角色', 0.22535153116801097), ('歌曲', 0.21173595044037458)]
本文给出了一个基于在线百科知识库的实体链接实现项目,实体链接是当前实体应用的重要技术手段,我们需要从算法和工程两侧多加以关注。
关于该项目的实现源码,可访问:
https://github.com/liuhuanyong/WordMultiSenseDisambiguation
老刘,刘焕勇,NLP开源爱好者与践行者,主页:https://liuhuanyong.github.io。
老刘说NLP,将定期发布语言资源、工程实践、技术总结等内容,欢迎关注、转发、分享、收藏。