语义消歧

可以看作分类问题。一个词W有K个含义,对W消歧 就是确定W在特定句子中究竟使用了哪一个含义,即把W分到K类中的一个。

分类的依据则是和W邻近的词,即W的上下文C。

歧义可以分为两类:一类是词的语义有多种,如“bank”,可以是银行,也可以是河岸;另一类是词本身的词性也是多样的,如predicate,既能作为名字,也能作为动词。

对于前者,可能需要与W相隔较远的其他词参与消歧,而对于后者,往往通过邻近的词汇就能确定W的词性了。

常用的消歧方法:

一、有监督消歧

1. 贝叶斯分类

s = argmax p(Sk|c), Sk 是W可能包含的语义,C是歧义词的上下文,而s是 使该概率最大的语义,即消歧后确定的语义。

2. 基于信息论的方法。

以W包含2个语义为例,基本思想是最大化 互信息 I(P,Q),P是W的语义集,Q是W的指示器取值集(指示器 即能区分W不同语义的关键邻近词)。

例:法语“ prendre”的含义是take或make,其指示器可以是 decision,note,example,measure。P划分为 p1 = {take,}和p2={make,},

Q分为Q1 = {note,example,measure,}和Q2 = {decision}, 如果W的指示器为note,出现在Q1中,那么W对应的语义应该对应地出现在P1中,即take。

在这里,P和Q的集合划分的原则是最大化 I(P,Q)。

该方法感觉和贝叶斯分类本质上类似,还是基于邻近词,根据概率判决,只是具体的公式不一样。

二、 基于词典的消歧(本质上也是无监督消歧的一种)

1. 基于语义定义的消歧。

如果词典中对W的 第i种定义 包含 词汇Ei,那么如果在一个包含W的句子中,同时也出现了Ei,那么就认为 在该句子中 W的语义应该取词典中的第i种定义。

2. 基于类义辞典的消歧。

词的每个语义 都定义其对应的主题或范畴(如“网球”对应的主题是“运动”),多个语义即对应了多个主题。如果W的上下文C中的词汇包含多个主 题,则取其频率最高的主题,作为W的主题,确定了W的主题后,也就能确定其对应的语义。

3. 基于双语对比的消歧。

这种方法比较有创意,即把一种语言作为另一种语言的定义。

例如,为了确定“interest”在英文句子A中的含义,可以利用句子A的中文表达,因为 interest的不同语义在中文的表达是不同的。

如果句子A对应中文包含“存款利率”,那么“interest”在句子A的语义就是“利率”。

如果句子A的对应中文是“我对英语没有兴趣”,那么其语义就是“兴趣”。

ps: 这里说实在的,只是将歧义消除的复杂度转移到了翻译而已。本质并没有被解决。

三、无监督消歧

主要是使用 EM 算法对W的上下文C进行无监督地聚类,也就是对W的语义进行了分类。(当然,该分类的结果不见得就是和词典中对该词的定义分类是匹配的)。

总结

马克思说:”人是社会关系的总和”,那么词的含义就是其上下文关系的体现,所有消歧方法都必须依赖于词的上下文信息,不同方法的区别在于对上下文信息的挖掘程度和方式。

也可以看出,消歧成功与否的关键在于能否充分挖掘和利用上下文信息,上下文可以既可以在广度上扩展:从邻近词汇 ,到跨句,甚至可以到段落,也可以在深度上扩展:从字面语义,到高层的抽象概念。

词义消除歧义NLP项目实验

本项目主要使用 https://github.com/alvations/pywsd 中的pywsd库来实现词义消除歧义

目前,该库一部分已经移植到了nltk中,为了获得更好的性能WSD,而不是使用的NLTK模块pywsd库。

一般来说,从pywsd的simple_lesk()比NLTK的lesk好。当我有空时,我会尽量更新NLTK模块。在本文档中主要介绍原pywsd库的使用。

一、使用的技术:

  • Lesk 算法

Original Lesk (Lesk, 1986) Adapted/Extended Lesk (Banerjee and Pederson, 2002/2003) Simple Lesk (with definition, example(s) and hyper+hyponyms) Cosine Lesk (use cosines to calculate overlaps instead of using raw counts)

  • 最大化相似度 (see also, Pedersen et al. (2003))

Path similarity (Wu-Palmer, 1994; Leacock and Chodorow, 1998)

Information Content (Resnik, 1995; Jiang and Corath, 1997; Lin, 1998)

  • 基线

Random sense

First NLTK sense

Highest lemma counts

二、使用方法:

  • 安装:
  [plaintext]
1
2
3
pip install -U nltk python -m nltk.downloader 'popular' pip install -U pywsd
  • 使用:
  [py]
1
2
3
4
5
from pywsd.lesk import simple_lesk #引入pywsd库 sent = 'I went to the bank to deposit my money' #设定包含具有多义的词的句子 ambiguous = 'bank' #设定多义的词语 answer = simple_lesk(sent, ambiguous, pos='n') #设置answer的参数,将句子与词进行判断 print (answer.definition()) #打印出答案

三、原理

词义消岐,英文名称为Word Sense Disambiguation,英语缩写为WSD,LESK算法是词义消歧的主要算法。

LESK算法是以一种以TF-IDF为权重的频数判别算法,主要流程可以简述为:

  1. 去掉停用词

  2. 统计出该词以外的TF-IDF值

  3. 累加起来,比较多个义项下这个值的大小,值越大说明是该句子的义项

下面以NBA火箭队为示例来简要实现一下lesk算法:

  [py]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
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))

结果如下:

  [plaintext]
1
2
3
4
5
6
7
8
9
10
['赛季', '初', '时候', '众望所归', '西部', '决赛', '球队'] 火箭_燃气推进装置: 初, 频数为: 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球队名 .

四、改进

对于代码本身,可以做到一点点进步的优化,算法上的优化可以做到更大的跨越,如 http://www.doc88.com/p-9959426974439.html 这篇文章提到的lesk算法的改进。

对于lesk算法的缺点,释义的判断很容易被相同TF-IDF的值误扰,即权值相同的情况。

拓展阅读

贝叶斯

信息论

句法分析树

参考资料

语义歧义消除

词义消除歧义NLP项目实验

词义消歧(WSD)是NLP应用的瓶颈

词义消除歧义NLP项目实验

NLP自动机及其应用,文稿自动校正,歧义消除