赞
踩
在自然语言处理领域,最著名的同义词词典是 WordNet。WordNet 是普林斯顿大学于1985年开始开发的同义词词典,迄今已用于许多研究,并活跃于各种自然语言处理应用中。使用 WordNet,可以获得单词的近义词,或者利用单词网络。使用单词网络,可以计算单词之间的相似度。
通过 Python 利用 WordNet,可以使用 NLTK(Natural Language Toolkit,自然语言处理工具包)这个库。NLTK 是用于自然语言处理的 Python 库,其中包含许多自然语言处理相关的便捷功能,比如词性标注、句法分析、信息抽取和语义分析等。
from nltk.corpus import wordnet # 1. 看单词 car 存在多少个不同的含义(获得 car 的同义词) print(wordnet.synsets('car')) out: [Synset('car.n.01'), Synset('car.n.02'), Synset('car.n.03'), Synset('car.n.04'), Synset('cable_car.n.01')] # 单词名称.属性(名词、动词等).簇的索引 # 2. 确认“car.n.01”这一标题词指定的同义词的含义 car = wordnet.synset('car.n.01') # 同义词簇 print(car.definition()) out: 'a motor vehicle with four wheels; usually propelled by an internal combustion engine' # 3. 使用 lemma_names() 方法,可以获得同义词簇中存在的单词名称 print(car.lemma_names()) out: ['car', 'auto', 'automobile', 'machine', 'motorcar']
同义词词典的问题:
- 难以顺应时代变化:随着时间的推移,新词不断出现,而那些落满尘埃的旧词不知哪天就会被遗忘。比如,“众筹”(crowdfunding)就是一个最近才开始使用的新词。另外,语言的含义也会随着时间的推移而变化。
- 人力成本高:制作词典需要巨大的人力成本。以英文为例,据说现有的英文单词总数超过 1000 万个。在极端情况下,还需要对如此大规模的单词进行单词之间的关联。
- 无法表示单词的微妙差异:同义词词典中将含义相近的单词作为近义词分到一组。但实际上,即使是含义相近的单词,也有细微的差别。比如,vintage 和 retro 虽然表示相同的含义,但是用法不同,而这种细微的差别在同义词词典中是无法表示出来的(让人来解释是相当困难的)。
语料库就是大量的文本数据。不过,语料库并不是胡乱收集数据,一般收集的都是用于自然语言处理研究和应用的文本数据。其中的文章都是由人写出来的。换句话说,语料库中包含了大量的关于自然语言的实践知识,即文章的写作方法、单词的选择方法和单词含义等。基于计数的方法的目标就是从这些富有实践知识的语料库中,自动且高效地提取本质。
def preprocess(text):
"""准备语料库(预处理)"""
text = text.lower()
text = text.replace('.', ' .')
words = text.split(' ')
word_to_id = {}
id_to_word = {}
for word in words:
if word not in word_to_id:
new_id = len(word_to_id)
word_to_id[word] = new_id
id_to_word[new_id] = word
corpus = np.array([word_to_id[w] for w in words])
# corpus:单词ID列表,word_to_id:单词到单词ID的字典,id_to_word:单词ID到单词的字典
return corpus, word_to_id, id_to_word
text = 'You say goodbye and I say hello.'
corpus, word_to_id, id_to_word = preprocess(text)
print(corpus) # [0 1 2 3 4 1 5 6]
print(id_to_word) # {0: 'you', 1: 'say', 2: 'goodbye', 3: 'and', 4: 'i', 5: 'hello', 6: '.'}
使用 RGB 这样的向量表示可以更准确地指定颜色,并且这种基于三原色的表示方式很紧凑,也更容易让人想象到具体是什么颜色。比如,即便不知道“深绯”是什么样的颜色,但如果知道它的 (R, G, B) = (201, 23, 30),就至少可以知道它是红色系的颜色。此外,颜色之间的关联性(是否是相似的颜色)也更容易通过向量表示来判断和量化。
关注能准确把握单词含义的向量表示。在自然语言处理领域,这称为分布式表示。
“某个单词的含义由它周围的单词形成”,这称为分布式假设(distributional hypothesis)。
分布式假设所表达的理念非常简单。单词本身没有含义,单词含义由它所在的上下文(语境)形成。的确,含义相同的单词经常出现在相同的语境中。比如“I drink beer.”和“We drink wine.”,drink 的附近常有饮料出现。另外,从“I guzzle beer.”和“We guzzle wine.”可知,guzzle 和 drink 所在的语境相似。进而我们可以推测出,guzzle 和 drink 是近义词。
上下文是指某个居中单词的周围词汇。将上下文的大小(即周围的单词有多少个)称为窗口大小(window size)。窗口大小为1,上下文包含左右各1个单词;窗口大小为2,上下文包含左右各2个单词。
以句子“you say goodbye and i say hello.”为例:
单词 you 的上下文仅有 say,可用如下表格表示单词 you 的上下文共现的单词的频数:
you | say | goodbye | and | i | hello | . | |
---|---|---|---|---|---|---|---|
you | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
这也意味着可以用向量 [0, 1, 0, 0, 0, 0, 0] 表示单词 you。
用表格汇总各个单词的上下文中共同出现的单词的频数如下:
you | say | goodbye | and | i | hello | . | |
---|---|---|---|---|---|---|---|
you | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
say | 1 | 0 | 1 | 0 | 1 | 1 | 0 |
goodbye | 0 | 1 | 0 | 1 | 0 | 0 | 0 |
and | 0 | 0 | 1 | 0 | 1 | 0 | 0 |
i | 0 | 1 | 0 | 1 | 0 | 0 | 0 |
hello | 0 | 1 | 0 | 0 | 0 | 0 | 1 |
. | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
这个表格的各行对应相应单词的向量。因为表格呈矩阵状,所以称为共现矩阵(co-occurence matrix)。
实现一个直接从语料库生成共现矩阵的函数:
def create_co_matrix(corpus, vocab_size, window_size=1): """直接从语料库生成共现矩阵的函数 corpus:单词 ID 列表,vocab_size:词汇个数,window_size:窗口大小 """ corpus_size = len(corpus) # 获取总单词个数 co_matrix = np.zeros((vocab_size, vocab_size), dtype=np.int32) # 初始化一个全零方阵 for idx, word_id in enumerate(corpus): # 获取每个单词的索引及ID for i in range(1, window_size + 1): # 根据窗口大小迭代将每个单词的上下文加1 left_idx = idx - i right_idx = idx + i if left_idx >= 0: left_word_id = corpus[left_idx] co_matrix[word_id, left_word_id] += 1 if right_idx < corpus_size: right_word_id = corpus[right_idx] co_matrix[word_id, right_word_id] += 1 return co_matrix
测量向量间的相似度有很多方法,其中具有代表性的方法有向量内积或欧式距离等。在测量单词的向量表示的相似度方面,余弦相似度(cosine similarity)是很常用的。
设有
x
=
(
x
1
,
x
2
,
x
3
,
⋅
⋅
⋅
,
x
n
)
\mathcal{x} = (x_1, x_2, x_3, ··· , x_n)
x=(x1,x2,x3,⋅⋅⋅,xn) 和
y
=
(
y
1
,
y
2
,
y
3
,
⋅
⋅
⋅
,
y
n
)
\mathcal{y} = (y_1, y_2, y_3, ··· , y_n)
y=(y1,y2,y3,⋅⋅⋅,yn) 两个向量,它们之间的余弦相似度的定义如下式:
s
i
m
i
l
a
r
i
t
y
(
x
,
y
)
=
x
⋅
y
∣
∣
x
∣
∣
∣
∣
y
∣
∣
=
x
1
y
1
+
⋯
+
x
n
y
n
x
1
2
+
⋯
+
x
n
2
y
1
2
+
⋯
+
y
n
2
similarity(\mathcal{x},\mathcal{y})=\frac{\mathcal{x}·\mathcal{y}}{||\mathcal{x}||||\mathcal{y}||}=\frac{x_1y_1+\dots+x_ny_n}{\sqrt{x_1^2+\dots+x_n^2}\sqrt{y_1^2+\dots+y_n^2}}
similarity(x,y)=∣∣x∣∣∣∣y∣∣x⋅y=x12+⋯+xn2
y12+⋯+yn2
x1y1+⋯+xnyn
余弦相似度的要点是先对向量进行正规化,再求它们的内积。直观地表示了“两个向量在多大程度上指向同一方向”。两个向量完全指向相同的方向时,余弦相似度为 1;完全指向相反的方向时,余弦相似度为 −1。
def cos_similarity(x, y, eps=1e-8): # eps微小值,防止零向量
"""计算余弦相似度"""
nx = x / (np.sqrt(np.sum(x**2)) + eps) # x的正规化
ny = y / (np.sqrt(np.sum(y**2)) + eps) # y的正规化
return np.dot(nx, ny)
当某个单词被作为查询词时,将与这个查询词相似的单词按降序显示出来。即相似度排序。
def most_similar(query, word_to_id, id_to_word, word_matrix, top=5): """单词相似度排序 query:查询词,word_matrix:单词向量矩阵, top=5:显示前几位 """ # 1、取出查询词 if query not in word_to_id: print('%s is not found' % query) return print('\n[query] ' + query) query_id = word_to_id[query] query_vec = word_matrix[query_id] # 2、计算余弦相似度 vocab_size = len(id_to_word) similarity = np.zeros(vocab_size) for i in range(vocab_size): similarity[i] = cos_similarity(word_matrix[i], query_vec) # 3、基于余弦相似度,按降序输出值 count = 0 for i in (-1 * similarity).argsort(): # argsort()方法可以按升序对 NumPy 数组进行排序,并返回排序后的索引 if id_to_word[i] == query: continue print(' %s: %s' % (id_to_word[i], similarity[i])) count += 1 if count >= top: return
考虑某个语料库中 the 和 car 共现的情况。在这种情况下,我们会看到很多“…the car…”这样的短语。因此,它们的共现次数将会很大。另外,car 和 drive 也明显有很强的相关性。但是,如果只看单词的出现次数,那么与 drive 相比,the 和 car 的相关性更强。这意味着,仅仅因为 the 是个常用词,它就被认为与 car 有很强的相关性。
为了解决这一问题,可以使用点互信息(Pointwise Mutual Information,PMI)这一指标。对于随机变量 x 和 y,它们的 PMI 定义如下:
P
M
I
(
x
,
y
)
=
log
2
P
(
x
,
y
)
P
(
x
)
P
(
y
)
PMI(x,y)=\log_2{\frac{P(x,y)}{P(x)P(y)}}
PMI(x,y)=log2P(x)P(y)P(x,y)
其中,P(x) 表示 x 发生的概率,P(y) 表示 y 发生的概率,P(x, y) 表示 x 和 y 同时发生的概率。PMI 的值越高,表明相关性越强。
为了解决两个单词的共现次数为 0 时,
l
o
g
2
0
=
−
∞
log_20 = −∞
log20=−∞ 问题,实践上我们会使用正的点互信息(Positive PMI,PPMI):
P
P
M
I
(
x
,
y
)
=
max
(
0
,
P
M
I
(
x
,
y
)
)
PPMI(x, y) = \max(0,PMI(x,y))
PPMI(x,y)=max(0,PMI(x,y))
来实现将共现矩阵转化为 PPMI 矩阵的函数:
def ppmi(C, verbose=False, eps=1e-8): """将共现矩阵转化为 PPMI 矩阵的函数 C:共现矩阵,verbose:决定是否输出运行情况的标志。当处理大语料库时,设置 verbose=True,可以用于确认运行情况 """ M = np.zeros_like(C, dtype=np.float32) N = np.sum(C) S = np.sum(C, axis=0) total = C.shape[0] * C.shape[1] cnt = 0 for i in range(C.shape[0]): for j in range(C.shape[1]): pmi = np.log2(C[i, j] * N / (S[j]*S[i]) + eps) M[i, j] = max(0, pmi) if verbose: cnt += 1 if cnt % (total//100+1) == 0: print('%.1f%% done' % (100*cnt/total)) return M
text = 'You say goodbye and I say hello.' corpus, word_to_id, id_to_word = preprocess(text) vocab_size = len(word_to_id) C = create_co_matrix(corpus, vocab_size) W = ppmi(C) np.set_printoptions(precision=3) # 有效位数为3位 print('covariance matrix') print(C) print('-'*50) print('PPMI') print(W) out: covariance matrix [[0 1 0 0 0 0 0] [1 0 1 0 1 1 0] [0 1 0 1 0 0 0] [0 0 1 0 1 0 0] [0 1 0 1 0 0 0] [0 1 0 0 0 0 1] [0 0 0 0 0 1 0]] -------------------------------------------------- PPMI [[ 0. 1.807 0. 0. 0. 0. 0. ] [ 1.807 0. 0.807 0. 0.807 0.807 0. ] [ 0. 0.807 0. 1.807 0. 0. 0. ] [ 0. 0. 1.807 0. 1.807 0. 0. ] [ 0. 0.807 0. 1.807 0. 0. 0. ] [ 0. 0.807 0. 0. 0. 0. 2.807] [ 0. 0. 0. 0. 0. 2.807 0. ]]
这个 PPMI 矩阵存在一个很大的问题,那就是随着语料库的词汇量增加,各个单词向量的维数也会增加。如果语料库的词汇量达到 10 万,则单词向量的维数也同样会达到 10 万。实际上,处理 10 万维向量是不现实的。
另外,这个矩阵中很多元素都是 0。这表明向量中的绝大多数元素并不重要,也就是说,每个元素拥有的“重要性”很低。另外,这样的向量也容易受到噪声影响,稳健性差。对于这些问题,一个常见的方法是向量降维。
降维(dimensionality reduction),顾名思义,就是减少向量维度(特征个数)。但是,并不是简单地减少,而是在尽量保留“重要信息”的基础上减少。
奇异值分解(Singular Value Decomposition,SVD)将任意矩阵分解为 3 个矩阵的乘积,如下式所示:
X
=
U
S
V
T
\mathbf{X}=\mathbf{U}\mathbf{S}\mathbf{V}^\Tau
X=USVT
SVD 将任意的矩阵 X 分解为 U、S、V 这 3 个矩阵的乘积,其中 U 和 V 是列向量彼此正交的正交矩阵,S 是除了对角线元素以外其余元素均为 0 的对角矩阵。
求解 U、S、V 矩阵:
- U 矩阵:解特征方程 ( X X T ) α = λ α (\mathbf{X}\mathbf{X}^\Tau)\alpha=λ\alpha (XXT)α=λα ,特征向量组成的矩阵即为 U 矩阵。
- V 矩阵:解特征方程 ( X T X ) α = λ α (\mathbf{X}^\Tau\mathbf{X})\alpha=λ\alpha (XTX)α=λα ,特征向量组成的矩阵即为 V 矩阵。
- S 矩阵:由上述结果带入原式即可解出 S 矩阵。
继续阅读:
《深度学习进阶:自然语言处理(第1章)》-读书笔记
《深度学习进阶:自然语言处理(第2章)》-读书笔记
《深度学习进阶:自然语言处理(第3章)》-读书笔记
《深度学习进阶:自然语言处理(第4章)》-读书笔记
《深度学习进阶:自然语言处理(第5章)》-读书笔记
《深度学习进阶:自然语言处理(第6章)》-读书笔记
《深度学习进阶:自然语言处理(第7章)》-读书笔记
《深度学习进阶:自然语言处理(第8章)》-读书笔记
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。