词义消岐简介

  词义消岐,英文名称为Word Sense Disambiguation,英语缩写为WSD,是自然语言处理(NLP)中一个非常有趣的基本任务。

  那么,什么是词义消岐呢?通常,在我们的自然语言中,不管是英语,还是中文,都有多义词存在。这些多义词的存在,会让人对句子的意思产生混淆,但人通过学习又是可以正确地区分出来的。

  以“小米”这个词为例,如果仅仅只是说“小米”这个词语,你并不知道它实际指的到底是小米科技公司还是谷物。但当我们把词语置于某个特定的语境中,我们能很好地区分出这个词语的意思。比如,

雷军是小米的创始人。

在这个句子中,我们知道这个“小米”指的是小米科技公司。比如

我今天早上喝了一碗小米粥。

在这个句子中,“小米”指的是谷物、农作物。

  所谓词义消岐,指的是在特定的语境中,识别出某个歧义词的正确含义。

  那么,词义消岐有什么作用呢?词义消岐可以很好地服务于语言翻译和智能问答领域,当然,还有许多应用有待开发~

词义消岐实现

  在目前的词义消岐算法中,有不少原创算法,有些实现起来比较简单,有些想法较为复杂,但实现的效果普遍都不是很好。比较经典的词义消岐的算法为Lesk算法,该算法的想法很简单,通过对某个歧义词构建不同含义的语料及待判别句子中该词语与语料的重合程度来实现,具体的算法原理可参考网址:https://en.wikipedia.org/wiki/Lesk_algorithm .

  在下面的部分中,笔者将会介绍自己想的一种实现词义消岐的算法,仅仅是一个想法,仅供参考。

  我们以词语“火箭”为例,选取其中的两个义项(同一个词语的不同含义):NBA球队名燃气推进装置 ,如下:

获取语料

  首先,我们利用爬虫爬取这两个义项的百度百科网页,以句子为单位,只要句子中出现该词语,则把这句话加入到这个义项的预料中。爬虫的完整Python代码如下:

import requests
from bs4 import BeautifulSoup
from pyltp import SentenceSplitter class WebScrape(object):
def __init__(self, word, url):
self.url = url
self.word = word # 爬取百度百科页面
def web_parse(self):
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 \
(KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36'}
req = requests.get(url=self.url, headers=headers) # 解析网页,定位到main-content部分
if req.status_code == 200:
soup = BeautifulSoup(req.text.encode(req.encoding), 'lxml')
return soup
return None # 获取该词语的义项
def get_gloss(self):
soup = self.web_parse()
if soup:
lis = soup.find('ul', class_="polysemantList-wrapper cmn-clearfix")
if lis:
for li in lis('li'):
if '<a' not in str(li):
gloss = li.text.replace('▪', '')
return gloss return None # 获取该义项的语料,以句子为单位
def get_content(self):
# 发送HTTP请求
result = []
soup = self.web_parse()
if soup:
paras = soup.find('div', class_='main-content').text.split('\n')
for para in paras:
if self.word in para:
sents = list(SentenceSplitter.split(para))
for sent in sents:
if self.word in sent:
sent = sent.replace('\xa0', '').replace('\u3000', '')
result.append(sent) result = list(set(result)) return result # 将该义项的语料写入到txt
def write_2_file(self):
gloss = self.get_gloss()
result = self.get_content()
print(gloss)
print(result)
if result and gloss:
with open('./%s_%s.txt'% (self.word, gloss), 'w', encoding='utf-8') as f:
f.writelines([_+'\n' for _ in result]) def run(self):
self.write_2_file() # NBA球队名
#url = 'https://baike.baidu.com/item/%E4%BC%91%E6%96%AF%E6%95%A6%E7%81%AB%E7%AE%AD%E9%98%9F/370758?fromtitle=%E7%81%AB%E7%AE%AD&fromid=8794081#viewPageContent'
# 燃气推进装置
url = 'https://baike.baidu.com/item/%E7%81%AB%E7%AE%AD/6308#viewPageContent'
WebScrape('火箭', url).run()

利用这个爬虫,我们爬取了“火箭”这个词语的两个义项的语料,生成了火箭_燃气推进装置.txt文件和火箭_NBA球队名.txt文件,这两个文件分别含有361和171个句子。以火箭_燃气推进装置.txt文件为例,前10个句子如下:

火箭技术的飞速发展,不仅可提供更加完善的各类导弹和推动相关科学的发展,还将使开发空间资源、建立空间产业、空间基地及星际航行等成为可能。

火箭技术是一项十分复杂的综合性技术,主要包括火箭推进技术、总体设计技术、火箭结构技术、控制和制导技术、计划管理技术、可靠性和质量控制技术、试验技术,对导弹来说还有弹头制导和控制、

1903年,俄国的К.E.齐奥尔科夫斯基提出了制造大型液体火箭的设想和设计原理。

火箭有很多种,原始的火箭是用引火物附在弓箭头上,然后射到敌人身上引起焚烧的一种箭矢。

“长征三号丙”火箭是在 “长征三号乙”火箭的基础上, 减少了两个助推器并取消了助推器上的尾翼。

火箭与导弹有什么区别

为了能够在未来大规模的将人类送入太空,不可能依赖传统的火箭和飞船。

火箭V2火箭

探测高层大气的物理特征(如气压、温度、湿度等)和现象的探空火箭。

可一次发射一发至数十发火箭弹。

实现算法

  我们以句子为单位进行词义消岐,即输入一句话,识别出该句子中某个歧义词的含义。笔者使用的算法比较简单,是以TF-IDF为权重的频数判别。以句子

赛季初的时候,火箭是众望所归的西部决赛球队。

为例,对该句子分词后,去掉停用词(stopwords),然后分别统计除了“火箭”这个词以外的TF-IDF值,累加起来,比较在两个义项下这个值的大小即可。

  实现这个算法的完整Python代码如下:

import os
import jieba
from math import log2 # 读取每个义项的语料
def read_file(path):
with open(path, 'r', encoding='utf-8') as f:
lines = [_.strip() for _ in f.readlines()]
return lines # 对示例句子分词
sent = '赛季初的时候,火箭是众望所归的西部决赛球队。'
wsd_word = '火箭' jieba.add_word(wsd_word)
sent_words = list(jieba.cut(sent, cut_all=False)) # 去掉停用词
stopwords = [wsd_word, '我', '你', '它', '他', '她', '了', '是', '的', '啊', '谁', '什么','都',\
'很', '个', '之', '人', '在', '上', '下', '左', '右', '。', ',', '!', '?'] sent_cut = []
for word in sent_words:
if word not in stopwords:
sent_cut.append(word) print(sent_cut) # 计算其他词的TF-IDF以及频数
wsd_dict = {}
for file in os.listdir('.'):
if wsd_word in file:
wsd_dict[file.replace('.txt', '')] = read_file(file) # 统计每个词语在语料中出现的次数
tf_dict = {}
for meaning, sents in wsd_dict.items():
tf_dict[meaning] = []
for word in sent_cut:
word_count = 0
for sent in sents:
example = list(jieba.cut(sent, cut_all=False))
word_count += example.count(word) if word_count:
tf_dict[meaning].append((word, word_count)) idf_dict = {}
for word in sent_cut:
document_count = 0
for meaning, sents in wsd_dict.items():
for sent in sents:
if word in sent:
document_count += 1 idf_dict[word] = document_count # 输出值
total_document = 0
for meaning, sents in wsd_dict.items():
total_document += len(sents) # 计算tf_idf值
mean_tf_idf = []
for k, v in tf_dict.items():
print(k+':')
tf_idf_sum = 0
for item in v:
word = item[0]
tf = item[1]
tf_idf = item[1]*log2(total_document/(1+idf_dict[word]))
tf_idf_sum += tf_idf
print('%s, 频数为: %s, TF-IDF值为: %s'% (word, tf, tf_idf)) mean_tf_idf.append((k, tf_idf_sum)) sort_array = sorted(mean_tf_idf, key=lambda x:x[1], reverse=True)
true_meaning = sort_array[0][0].split('_')[1]
print('\n经过词义消岐,%s在该句子中的意思为 %s .' % (wsd_word, true_meaning))

输出结果如下:

['赛季', '初', '时候', '众望所归', '西部', '决赛', '球队']
火箭_燃气推进装置:
初, 频数为: 2, TF-IDF值为: 12.49585502688717
火箭_NBA球队名:
赛季, 频数为: 63, TF-IDF值为: 204.6194333469459
初, 频数为: 1, TF-IDF值为: 6.247927513443585
时候, 频数为: 1, TF-IDF值为: 8.055282435501189
西部, 频数为: 16, TF-IDF值为: 80.88451896801904
决赛, 频数为: 7, TF-IDF值为: 33.13348038429679
球队, 频数为: 40, TF-IDF值为: 158.712783770034 经过词义消岐,火箭在该句子中的意思为 NBA球队名 .

测试

  接着,我们对上面的算法和程序进行更多的测试。

输入句子为:

三十多年前,战士们在戈壁滩白手起家,建起了我国的火箭发射基地。

输出结果为:

['三十多年', '前', '战士', '们', '戈壁滩', '白手起家', '建起', '我国', '发射', '基地']
火箭_燃气推进装置:
前, 频数为: 2, TF-IDF值为: 9.063440958888354
们, 频数为: 1, TF-IDF值为: 6.05528243550119
我国, 频数为: 3, TF-IDF值为: 22.410959804340102
发射, 频数为: 89, TF-IDF值为: 253.27878721862933
基地, 频数为: 7, TF-IDF值为: 42.38697704850833
火箭_NBA球队名:
前, 频数为: 3, TF-IDF值为: 13.59516143833253
们, 频数为: 1, TF-IDF值为: 6.05528243550119 经过词义消岐,火箭在该句子中的意思为 燃气推进装置 .

输入句子为:

对于马刺这样级别的球队,常规赛只有屈指可数的几次交锋具有真正的意义,今天对火箭一役是其中之一。

输出结果为:

['对于', '马刺', '这样', '级别', '球队', '常规赛', '只有', '屈指可数', '几次', '交锋', '具有', '真正', '意义', '今天', '对', '一役', '其中', '之一']
火箭_燃气推进装置:
只有, 频数为: 1, TF-IDF值为: 7.470319934780034
具有, 频数为: 5, TF-IDF值为: 32.35159967390017
真正, 频数为: 2, TF-IDF值为: 14.940639869560068
意义, 频数为: 1, TF-IDF值为: 8.055282435501189
对, 频数为: 5, TF-IDF值为: 24.03677461028802
其中, 频数为: 3, TF-IDF值为: 21.16584730650357
之一, 频数为: 2, TF-IDF值为: 14.11056487100238
火箭_NBA球队名:
马刺, 频数为: 1, TF-IDF值为: 7.470319934780034
球队, 频数为: 40, TF-IDF值为: 158.712783770034
常规赛, 频数为: 14, TF-IDF值为: 73.4709851882102
只有, 频数为: 1, TF-IDF值为: 7.470319934780034
对, 频数为: 10, TF-IDF值为: 48.07354922057604
之一, 频数为: 1, TF-IDF值为: 7.05528243550119 经过词义消岐,火箭在该句子中的意思为 NBA球队名 .

输入句子为:

姚明是火箭队的主要得分手之一。

输出结果为:

['姚明', '火箭队', '主要', '得分手', '之一']
火箭_燃气推进装置:
主要, 频数为: 9, TF-IDF值为: 51.60018906552445
之一, 频数为: 2, TF-IDF值为: 14.11056487100238
火箭_NBA球队名:
姚明, 频数为: 18, TF-IDF值为: 90.99508383902142
火箭队, 频数为: 133, TF-IDF值为: 284.1437533641371
之一, 频数为: 1, TF-IDF值为: 7.05528243550119 经过词义消岐,火箭在该句子中的意思为 NBA球队名 .

输入的句子为:

从1992年开始研制的长征二号F型火箭,是中国航天史上技术最复杂、可靠性和安全性指标最高的运载火箭。

输出结果为:

['从', '1992', '年', '开始', '研制', '长征二号', 'F', '型', '中国', '航天史', '技术', '最', '复杂', '、', '可靠性', '和', '安全性', '指标', '最高', '运载火箭']
火箭_燃气推进装置:
从, 频数为: 6, TF-IDF值为: 29.312144604353264
1992, 频数为: 1, TF-IDF值为: 6.733354340613827
年, 频数为: 43, TF-IDF值为: 107.52982410441274
开始, 频数为: 5, TF-IDF值为: 30.27641217750595
研制, 频数为: 25, TF-IDF值为: 110.28565614316162
长征二号, 频数为: 37, TF-IDF值为: 159.11461253349566
F, 频数为: 7, TF-IDF值为: 40.13348038429679
中国, 频数为: 45, TF-IDF值为: 153.51418105769093
技术, 频数为: 27, TF-IDF值为: 119.10850863461454
最, 频数为: 2, TF-IDF值为: 7.614709844115208
、, 频数为: 117, TF-IDF值为: 335.25857156467714
可靠性, 频数为: 5, TF-IDF值为: 30.27641217750595
和, 频数为: 76, TF-IDF值为: 191.22539545388003
安全性, 频数为: 2, TF-IDF值为: 14.940639869560068
运载火箭, 频数为: 95, TF-IDF值为: 256.28439093389505
火箭_NBA球队名:
从, 频数为: 5, TF-IDF值为: 24.42678717029439
1992, 频数为: 2, TF-IDF值为: 13.466708681227654
年, 频数为: 52, TF-IDF值为: 130.0360663588247
开始, 频数为: 2, TF-IDF值为: 12.11056487100238
中国, 频数为: 4, TF-IDF值为: 13.64570498290586
最, 频数为: 3, TF-IDF值为: 11.422064766172813
、, 频数为: 16, TF-IDF值为: 45.847326025938756
和, 频数为: 31, TF-IDF值为: 77.99983235618791
最高, 频数为: 8, TF-IDF值为: 59.76255947824027 经过词义消岐,火箭在该句子中的意思为 燃气推进装置 .

输入句子为:

到目前为止火箭已经在休斯顿进行了电视宣传,并在大街小巷竖起广告栏。

输出结果为:

['到', '目前为止', '已经', '休斯顿', '进行', '电视', '宣传', '并', '大街小巷', '竖起', '广告栏']
火箭_燃气推进装置:
到, 频数为: 11, TF-IDF值为: 39.19772273088667
已经, 频数为: 2, TF-IDF值为: 13.466708681227654
进行, 频数为: 14, TF-IDF值为: 68.39500407682429
并, 频数为: 11, TF-IDF值为: 49.17351928258037
火箭_NBA球队名:
到, 频数为: 6, TF-IDF值为: 21.38057603502909
已经, 频数为: 2, TF-IDF值为: 13.466708681227654
休斯顿, 频数为: 2, TF-IDF值为: 14.940639869560068
进行, 频数为: 2, TF-IDF值为: 9.770714868117755
并, 频数为: 5, TF-IDF值为: 22.351599673900168 经过词义消岐,火箭在该句子中的意思为 燃气推进装置 .

总结

  对于笔者的这个算法,虽然有一定的效果,但是也不总是识别正确。比如,对于最后一个测试的句子,识别的结果就是错误的,其实“休斯顿”才是识别该词语义项的关键词,但很遗憾,在笔者的算法中,“休斯顿”的权重并不高。

  对于词义消岐算法,如果还是笔者的这个思路,那么有以下几方面需要改进:

  • 语料大小及丰富程度;
  • 停用词的扩充;
  • 更好的算法。

  笔者的这篇文章仅作为词义消岐的简介以及简单实现,希望能对读者有所启发~

注意:本人现已开通微信公众号: Python爬虫与算法(微信号为:easy_web_scrape), 欢迎大家关注哦~~

NLP入门(九)词义消岐(WSD)的简介与实现的更多相关文章

  1. cips2016+学习笔记︱NLP中的消岐方法总结(词典、有监督、半监督)

    歧义问题方面,笔者一直比较关注利用词向量解决歧义问题: 也许你寄希望于一个词向量能捕获所有的语义信息(例如run即是动车也是名词),但是什么样的词向量都不能很好地进行凸显. 这篇论文有一些利用词向量的 ...

  2. NLP入门资料

    <统计自然语言处理> 一些基础理论概念,涉及统计自然语言处理的基本概念.理论方法和新研究进展,内容包括形式语言与自动机及其在自然语言处理中的应用.语言模型.隐马尔可夫模型.语料库技术.汉语 ...

  3. 学习笔记CB008:词义消歧、有监督、无监督、语义角色标注、信息检索、TF-IDF、隐含语义索引模型

    词义消歧,句子.篇章语义理解基础,必须解决.语言都有大量多种含义词汇.词义消歧,可通过机器学习方法解决.词义消歧有监督机器学习分类算法,判断词义所属分类.词义消歧无监督机器学习聚类算法,把词义聚成多类 ...

  4. 网络编程懒人入门(九):通俗讲解,有了IP地址,为何还要用MAC地址?

    1.前言 标题虽然是为了解释有了 IP 地址,为什么还要用 MAC 地址,但是本文的重点在于理解为什么要有 IP 这样的东西.本文对读者的定位是知道 MAC 地址是什么,IP 地址是什么. (本文同步 ...

  5. NLP入门(五)用深度学习实现命名实体识别(NER)

    前言   在文章:NLP入门(四)命名实体识别(NER)中,笔者介绍了两个实现命名实体识别的工具--NLTK和Stanford NLP.在本文中,我们将会学习到如何使用深度学习工具来自己一步步地实现N ...

  6. NLP入门(一)词袋模型及句子相似度

      本文作为笔者NLP入门系列文章第一篇,以后我们就要步入NLP时代.   本文将会介绍NLP中常见的词袋模型(Bag of Words)以及如何利用词袋模型来计算句子间的相似度(余弦相似度,cosi ...

  7. NLP入门(八)使用CRF++实现命名实体识别(NER)

    CRF与NER简介   CRF,英文全称为conditional random field, 中文名为条件随机场,是给定一组输入随机变量条件下另一组输出随机变量的条件概率分布模型,其特点是假设输出随机 ...

  8. C#基础入门 九

    C#基础入门 九 集合 对于很多应用程序,需要创建和管理相关对象组,有两种方式可以将对象分组,一是创建对象数组,如 object[] obj=new object[3]{1,2.33,"st ...

  9. cesium编程入门(九)实体 Entity

    cesium编程入门(九)实体 Entity 在cesium编程入门(五)绘制形状提到过添加实体的方法,这一节聊一聊实体相关的一些内容: 先来看 Entity 的各个属性 id 唯一标志,如果没设置, ...

随机推荐

  1. throw和throws有什么区别

    1.Throw用于方法内部,Throws用于方法声明上 2.Throw后跟异常对象,Throws后跟异常类型 3.Throw后只能跟一个异常对象,Throws后可以一次声明多种异常类型 throw关键 ...

  2. html5 中video标签属性

      <video id="haoroomsvideo" src="haorooms.mp4" poster="images/haorooms.j ...

  3. [转]JVM参数使用手册

    内存分配相关 Xms 英文释义:Initial heap size(in bytes) 中文释义:堆区初始值 使用方法:-Xms2g 或 -XX:InitialHeapSize=2048m Xmx 英 ...

  4. java对象的实例化过程

    简单类对象的实例化过程 1.在方法区加载类: 2.在栈内存申请空间,声明变量P: 3.在堆内存中开辟空间,分配对象地址: 4.在对象空间中,对对象的属性进行默认初始化,类成员变量显示初始化: 5.构造 ...

  5. ProjectServer2010升级到ProjectServer2016,Sharepoint2010升级到Sharepoint2016第三章

    继续上一章,转换了身份认证模式后继续将WSS_Content备份还原到2016数据库服务器上升级,发现还是报错,报错截图如下: 查看日志,提到某些网站集还体验还是2010的,需要升级,可是我明明升级了 ...

  6. Dynamics 365中的Client API form context (formContext)

    适用于Dynamics 365 for Customer Engagement apps 9.x版本. 本文是一篇翻译,原文来源是微软官方文档. 本文链接:https://www.cnblogs.co ...

  7. 2019有赞中高级Java工程师面试题与解答

    说说JVM的内存分区 线程私有的区域 程序计数器:JVM中程序计数器相当于汇编语言中的CPU中的寄存器,保存程序当前执行的指令的地址. 虚拟机栈:Java方法执行的栈由许多个栈帧构成,每个栈帧对应一个 ...

  8. 微言Netty:分布式服务框架

    1. 前言 几年前,我就一直想着要设计一款自己的实时通讯框架,于是出来了TinySocket,她是基于微软的SocketAsyncEventArgs来实现的,由于此类提供的功能很简洁,所以当时自己实现 ...

  9. Spring 关于ResponseBody注解的作用

    //responseBody一般是作用在方法上的,加上该注解表示该方法的返回结果直接写到Http response Body中,常用在ajax异步请求中, //在RequestMapping中 ret ...

  10. 微信wxid搜索用户不存在,怎么加好友?

    点击上方↑↑↑蓝字[协议分析与还原]关注我们 " 根据wxid微信号找到用户的方法及周边资料大全." 最近经常会收到好友请求的消息提醒,但却不是通过搜索好友和群聊来添加的,因此研究 ...