自然语言处理(2:基于计数方法的分布式表示和假设,共现矩阵,向量相似度)
系列文章目录
第一章 1:同义词词典和基于计数方法语料库预处理
第一章 2:基于计数方法的分布式表示和假设,共现矩阵,向量相似度
第一章 3:基于计数方法的改进以及总结
第二章 1:word2vec
提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮
目录
助文档
前言
本节共计5123字,阅读时间可能长,但内容丰富,坚持看下去,收获亦会非常丰富!
上一节的内容我们主要学了基于计数方法语料库预处理,如果还不懂,点击这里自然语言处理(1:同义词词典和基于计数方法语料库预处理)-CSDN博客
继续回顾,相信自己,当你把它拆分开来,其实不难!
提示:以下是本篇文章正文内容,下面案例可供参考
一、单词的分布式表示
颜色可以通过RGB(Red/Green/ Blue)三原色分别存在多少来表示。前者为不同的颜色赋予不同的名字,有 多少种颜色,就需要有多少个不同的名字;后者则将颜色表示为三维向量。
需要注意的是,使用RGB这样的向量表示可以更准确地指定颜色,并 且这种基于三原色的表示方式很紧凑,也更容易让人想象到具体是什么颜 色。比如,即便不知道“深绯”是什么样的颜色,但如果知道它的(R,G, B) =(201,23,30),就至少可以知道它是红色系的颜色。此外,颜色之间的 关联性(是否是相似的颜色)也更容易通过向量表示来判断和量化。那么,能不能将类似于颜色的向量表示方法运用到单词上呢?更准确地 说,可否在单词领域构建紧凑合理的向量表示呢?接下来,我们将关注“能准确把握单词含义的向量表示”。在自然语言处理领域,这称为“分布式表示”。
二、分布式假设
1.定义
上节我们知道,要想让计算机生成文本,认识我们的语言,就要转换为向量表示,那么分布式假设,就是“某个单词的含义由它周围的单词形成”。
分布式假设所表达的理念非常简单。单词本身没有含义,单词含义由它 所在的上下文(语境)形成。
2.例子
含义相同的单词经常出现在相同的语境 中。比如“I drink beer.”“ We drink wine.”, drink 的附近常有饮料出现。另外,从“I guzzle beer.”“ We guzzle wine.”可知,guzzle 和 drink 所在 的语境相似。进而我们可以推测出,guzzle和drink是近义词。
从现在开始,我们会经常使用“上下文”一词。本章说的上下文是指某个单词(关注词)周围的单词。
比如在以下图中,上下文是指某个居中单词的周围词汇:

窗口大小为2的上下文例子。在关注goodbye时,将其左右各2个单词用作上下文,
我们将上下文的大小(即周围的单词有多少个)称为窗口大小(window size)。窗口大小为1,上下文包含左右各1个单词;窗口大小为2,上下文包含左右各 2 个单词,以此类推。(注意:根据具体情况,也可以仅将左边的单词或者右边的单词作为上下文。 此外,也可以使用考虑了句子分隔符的上下文。),这里可以想想马克思告诉我们遇到任何事要辩证分析,不要学死了。
三、共现矩阵
1.定义
现在你已经基本了解了上述分布式假设的定义和相关信息了,那么问题来了,如何基于分布式假设使用向量表示单词?
答:最直截了当的实现方法是对周围单词的数量进行计数,在关注某个单词的情况下,对它的周围出现了多少次什么单词进行计数,然后再汇总。这里,我们将 这种做法称为“基于计数的方法”,也称为“基于统计的方法”。
2.例子:
代码如下(示例):要用到上一节的preprocess() 函数,链接在此!自然语言处理(1:同义词词典和基于计数方法语料库预处理)-CSDN博客
import numpy as np
# preprocess函数定义在common.py文件中,
# 你可以在这里在复现一下preprocess函数
from common.util import preprocess
t
ext = '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:'.'}
从上面的结果可以看出,词汇总数为7个。下面,我们计算每个单词的上下文所包含的单词的频数。在这个例子中,我们将窗口大小设为1,从单词ID为0的you开始。
3.共现矩阵的引入
从上图可以看出,单词you的上下文仅有say这个单词。用表格(如果不知道看看上一节我发的)表示的话,如下:

这个图表示的是作为单词you的上下文共现的单词的频数。同时,这也 意味着可以用向量[0, 1, 0, 0, 0, 0, 0] 表示单词you。
同理,接着对单词ID为1的say进行同样的处理,结果如下图:

从上面的结果可知,单词say可以表示为向量[1, 0, 1, 0, 1, 1, 0]。 对所有的7个单词进行上述操作,就会得到如下图表(矩阵):

再次强调一下,如果还是有不理解这个矩阵如何得到的大佬们,一定要看看上一节,链接再次奉上!自然语言处理(1:同义词词典和基于计数方法语料库预处理)-CSDN博客
上图是汇总了所有单词的共现单词的表格。这个表格的各行对应相应单词的向量。因为图的表格呈矩阵状,所以称为”共现矩阵“。
4.共现矩阵的实现
那么问题来了,如何得到这个共现矩阵?绝大部分聪明的孩子们已经想到用那个方法,对,就是那个(嘻嘻,不卖关子了!咳咳)
1.我(TM)直接创建:
C = np.array([[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]],
dtype=np.int32)
这个就是我们共现矩阵。那么使用这个共现矩阵,可以获得各个单词的向量,如下所示。
print(C[0]) # 单词ID为0的向量
# [0 1 0 0 0 0 0]
print(C[4]) # 单词ID为4的向量
# [0 1 0 1 0 0 0]
print(C[word_to_id['goodbye']]) # goodbye的向量
# [0 1 0 1 0 0 0]
至此,我们通过共现矩阵成功地用向量表示了单词。
2.那么上面我们是手动输入共现矩阵(手动狗头)的,但这一操作显然可以自动化。下面,来实现一个能直接从语料库生成共现矩阵的函数。我们把这个函数称为create_co_matrix(corpus, vocab_size, window_size=1),其中参数 corpus 是单词ID 列表,参数vocab_ size 是词汇个数,window_size 是窗口大小,共现矩阵具体实现如下:
def create_to_matrix(corpus, vocab_size, window_size=1):
"""
corpus:单词ID列表
vocab_size: 单词个数
window_size:上下文的窗口大小
"""
corpus_size = len(corpus) # corpus = [0, 1, 2, 3, 4 ,1, 5, 6]
co_matrix = np.zeros((vocab_size, vocab_size), dtype=np.int32)
# 共现矩阵一定是个方阵(行数等于列数)
for idx, word_id in enumerate(corpus):
# idx是索引值,word_id是索引对应的值
for i in range(1, window_size + 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
首先,用元素为0的二维数组对co_matrix进行初始化。然后,针对语 料库中的每一个单词,计算它的窗口中包含的单词。同时,检查窗口内的单 词是否超出了语料库的左端和右端。 这样一来,无论语料库多大,都可以自动生成共现矩阵。之后,我们都将使用这个函数生成共现矩阵。
四.向量间的相似度
1.定义
前面我们通过共现矩阵将单词表示为了向量。下面,我们看一下如何测 量向量间的相似度。测量向量间的相似度有很多方法,其中具有代表性的方法有向量内 积或欧式距离等。虽然除此之外还有很多方法,但是在测量单词的向量 表示的相似度方面,余弦相似度(cosine similarity)是很常用的设有 x =(x1,x2,x3,···,xn) 和 y =(y1,y2,y3,···,yn) 两个向量,它们之间的余弦相似度的定义如下式所示。

熟悉吧,高中的知识,余弦计算公式。但不免有部分大佬把知识还给了老师,所以再解释一下:分子是向量内积,分母是各个向量的范数。范数表示向量的大小,这里计算的是L2范数(即向量各个元素的平方和的平方根)。 它的要点是先对向量进行正规化,再求它们的内积。(L1范数你可想类似)。
2.代码实现
那现在实现一下它,对各位大佬来说,应该并不困难把?!
现在,我们来实现余弦相似度
def cos_similarity(x, y):
nx = x / np.sqrt(np.sum(x ** 2))
ny = y / np.sqrt(np.sum(y ** 2))
return np.dot(nx, ny)
这里,我们假定参数x和y是NumPy数组。首先对向量进行正规化, 然后求两个向量的内积。这里余弦相似度的实现虽然完成了,但是还有一个问题-----------------那就是当零向量(元素全部为0的向量)被赋值给参数时,会出现 “除数为0”( zero division)的错误。
如何解决?
解决此类问题的一个常用方法是,在执行除法时加上一个微小值。这里,通过参数指定一个微小值eps(eps是epsilon的缩写),并默认eps=1e-8 (=0.000 000 01)。这样修改后的余弦相似度的实现如下:
def cos_similarity(x, y, eps=1e-8):
nx = x / (np.sqrt(np.sum(x ** 2)) + eps)
ny = y / (np.sqrt(np.sum(y ** 2)) + eps)
我知道肯定又有疑问了,(1):为什么要指定一个微小值?(2):1e-8是什么鬼?
答:(1):用了1e-8作为微小值,在这么小的值的情况下,根据浮点 数的舍入误差,这个微小值会被其他值“吸收”掉。在上面的实现中, 因为这个微小值会被向量的范数“吸收”掉,所以在绝大多数情况下, 加上eps不会对最终的计算结果造成影响。而当向量的范数为0时, 这个微小值可以防止“除数为0”的错误。
(2): 1e-8在深度学习中就代表10的-8次方,这样表示更方便(10的8次方就用1e8表示)
利用这个函数,可以如下求得单词向量间的相似度。
具体如下:
from common.util import preprocess, create_co_matrix, cos_similarity
# 上面是将定义的函数封装在了common文件夹下
# 各位可以再到此处实现一遍,加深记忆
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)
c0 = C[word_to_id['you']]
c1 = C[word_to_id['i']]
print(cos_similarity(c0, c1))
# 0.7071067691154799
从上面的结果可知,you和i的余弦相似度是0.70...。由于余弦相似度 的取值范围是−1到1,所以可以说这个值是相对比较高的(存在相似性)
总结
以上就是今天要讲的内容,本文内容确实还算多一点,而给看完的大佬们点个赞,也请多多支持我,谢谢!
更多推荐

所有评论(0)