NSS的这篇文章实在是写得很经典,简要翻译学习一下。 原文链接:https://www.analyticsvidhya.com/blog/2017/06/word-embeddings-count-word2veec/
目录 0 介绍 1 什么是词嵌入? 2 不同种类的词嵌入 2.1 词频嵌入 2.1.1 计数向量 2.1.2 TF-IDF 2.1.3 共现矩阵 2.2 预测嵌入 2.2.1 CBOW 2.2.2 Skip-gram 3 词嵌入的应用案例 4 使用预训练的词向量 5 训练属于自己的词向量 6 结语
我喜欢唱、跳、打怪、篮。。。(原谅我,让我皮一下 ^_^)
开始之前先看几个例子:
这些例子有什么相同点?
答案是——“文本处理”。上面三个场景通过处理海量文本,完成了三个不同的任务:聚类、分类和机器翻译。
人类处理文本任务既不可扩展,也十分低效。
让机器代替人力,关键是创建词的表征
,该表征可以获取词义、语义关系和不同的上下文种类。
表征可以通过词嵌入
和数值表征
来完成。
下面就来看看什么是词嵌入,和词嵌入的不同类型,以及如何使用词嵌入完成返回搜索结果的任务。
简单来说,词嵌入就是将文本转换成数字,方法不同,数值表征也不同。在深入之前,先来讨论下为什么需要词嵌入?
人们经过实践得出结论,多数机器学习算法和几乎所有的深度学习框架都不能处理原始个格式的字符串和文本。机器需要数字作为输入,才能执行分类回归这样的任务。文本中蕴含着海量的数据,有必要从中提取出有用的东西,并创建应用,比如亚马逊商品评论、文档或新闻的情感分析、谷歌搜索的分类和聚类。
正式给词嵌入下个定义:词嵌入是使用词典,将单词映射到矢量上
。把这句话分解,逐一分析。
看这个例子:
# 将sentence作为一个变量
sentence = "Word Embeddings are Word converted into numbers"
所谓的字典
,是sentence
中不同单词组成的列表,也就是:
[‘Word’,’Embeddings’,’are’,’Converted’,’into’,’numbers’]
可以用独热编码
来生成矢量,在独热编码中,1
表示单词在该位置存在,0
表示不存在。根据上面的字典,单词numbers
的独热编码是[0,0,0,0,0,1]
,converted
的编码是[0,0,0,1,0,0]
。
这只是用矢量表征单词的一个非常简单的方法。接下来看看不同的词嵌入或词向量的方法,以及各自的优缺点。
可以将词嵌入大致分成两类:
基于频率,有三种向量表示法:
一个包含D
篇文档{D1,D2…..DD}
的语料库C
,包含有N
个不同的单词。这N
个单词就组成了词典。计数向量矩阵M
的形状是D x N
。矩阵M
的每一行,是单词出现在D(i)
中的频率。
这么说很难懂,举个栗子?:
D1: He is a lazy boy. She is also lazy.
D2: Neeraj is a lazy person.
D1
和D2
两个文档的词典是不同单词组成的列表,也就是
corpus =[‘He’,’She’,’lazy’,’boy’,’Neeraj’,’person’]
有文档共有两篇、词典中有六个单词,所以D=2, N=6
。
根据计数矩阵的定义,就该表示成一个2 x 6
的矩阵:
其中,每一列就是单词的词向量,例如,lazy
的词向量就是[2,1]
。
计数向量矩阵有几种变体,区别在于:
下面是一个矩阵的表征图(注:和刚才的例子相比,文档和词的位置发生了转置):
TF-IDF也是一种基于词频的方法,跟计数向量不同的地方是,他不仅考虑了某个词在一篇文档中的出现次数,也考虑了单词在整个预料库中的出现情况。
像is、the、a这样的常见词,总是在文章有更多的出现机会。我们要做的就是降低这些常见词的权重。
TF-IDF是这么做的,考虑下面的两个文档:
先来解释下TF和IDF分别是什么?
TF是词频两个单词term frequency
的缩写:
TF = (某个词在文档中出现的次数) / (文档中所有词的总频次数)
所以,This的TF值应该如下:
TF(This, Document1) = 1/8
TF(This, Document2) = 1/5
TF可以表示某个单词对文档的贡献,也就是说,和文档相关性强的词应该出现的多,例如,一篇关于梅西的文章,应该会多次出现梅西的名字“Messi”。
IDF是逆文档频率inverse document frequency
的缩写:
IDF = log(N/n)
# N是总文档数,n是文档出现过某个单词的文档数
因此,This和Messi的IDF值是:
IDF(This) = log(2/2) = 0
IDF(Messi) = log(2/1) = 0.301
IDF的意义是,如果一个单词在所有文档中都出现过,那么这个单词相对于任何一篇文档都不重要。如果一个单词只在某些文档中出现过,说明该单词和这些文档有相关性。
将TF和IDF结合起来,再比较This和Messi两个词的值:
TF-IDF(This,Document1) = (1/8) * (0) = 0
TF-IDF(This, Document2) = (1/5) * (0) = 0
TF-IDF(Messi, Document1) = (4/8)*0.301 = 0.15
对于Document1来说,This的分值低于Messi的分值,说明Messi对于Document1更重要。
根据常识,相似的词通常会出现在相似的上下文,比如这两句话:
Apple is a fruit.
Mango is a fruit.
Apple和Mango有相似的上下文,也就是fruit。
先解释下什么是共现矩阵和内容窗口:
绿色部分就是单词Fox的大小为2的内容窗口,在计算共现时,只有内容窗口之内的词才会被计算
看一个具体的例子,语料如下:
Corpus = He is not lazy. He is intelligent. He is smart.
内容窗口大小为2的共现矩阵
红格子 —— 窗口大小为2时,He和is共现了4次; 蓝格子 —— lazy从来没有和intelligent出现在窗口中;
示意图:He和is的4次共现
共现矩阵的变化
假设语料中有V个不同的词。共现矩阵可以有两种变体:
V x V
。这样的矩阵,对于任何语料V,都会特别大且难于处理,所以很少用;V x N
,N是V(例如去除了停用词the、a这样的词)的子集,矩阵仍然很大,计算还是困难。其实,共现矩阵并不是通常使用的词向量,而是经过PCA(主成分分析)、SVD(奇异值分解)之后,才构成词向量。
假如对上面大小是V x V
的矩阵做了主成分分析,可以获得V个主成分,从其中挑出k个,就可以构成一个大小是V x k
的矩阵。
对于某一个单词,就算经过了降维,语义也不会下降很多。k的大小通常是数百。
其实PCA的作用,是将贡献矩阵分解成三个三个矩阵U、S和V,U和V都是正交矩阵。重点在于,U和S的点积是词向量表征,V是词的上下文表征。
共现矩阵的优点:
共现矩阵的缺点
Mitolov推出的word2vec是一种基于预测的方法,性能比前面的方法好的多。
word2vec是两种技术的集合 —— CBOW(连续词袋)和Skip-gram模型。这两种方法都是浅层神经网络。
CBOW的原理是通过给定的上下文,预测词的概率。上下文可以是一个词,也可以是一组词。简单起见,我举的例子是用一个词来预测另一个词。
假设语料如下:
C = “Hey, this is sample corpus using only one context word.”
内容窗口的大小是1。这个语料可以转化为如下的CBOW模型的训练集。下图的左边是输入和输出,右边是独热编码矩阵,一共包含17个数据点。
将这个矩阵输入给一个只有3层的神经网络:一个输入层、一个隐藏层、一个输出层。输出层是softmax层,确保输出层的概率之和是1。下面就来看看前向传播是如何计算隐藏层的。
先来看一个图像化的CBOW:
一个数据点的向量表征如下所示:
过程如下:
1 x V
的独热编码,在这个例子中V=10;这是只有一个上下文词的情况。假如有多个上下文词,就要像下图来进行计算:
下图是矩阵表征:
在这张图中,使用3个上下文词来预测目标值。输入层有3个1 x V
的矢量,输出层是1个1 x V
矢量。不同的地方是隐藏激活矢量需要做一次取平均值。
在上下文词是1和3的两种情况下,画的图都是只到隐藏激活矢量而已,因为这部分是CBOW区别于多层感知机网络MLP
的地方。
MLP和CBOW的区别在于:
-log(p(wo/wi))
,p(wo/wi)
如下:wo : 输出词, wi : 上下文词
CBOW的优势:
CBOW的劣势:
Skip-gram的底层原理和CBOW差不多,就是把CBOW正好给反了过来。Skip-gram的目标是根据单词预测上下文。还是使用前面的语料,构建训练数据:
C=”Hey, this is sample corpus using only one context word.”
skip-gram的输入矢量跟上下文是1时的CBOW模型很像。另外,输入层到隐藏层的计算也一模一样。不同的地方在于目标值。因为定义的内容窗口大小是1,所以有两个独热编码目标值,两个对应的输出(下图中的蓝色部分)。
分别根据两个目标值得到两个独立的误差矢量,两个误差矢量合并成一个误差矢量,再反向传播更新权重。
训练完成后,输入层和隐藏层之间的权重矩阵作为词向量。
skip-gram的架构如下所示:
矩阵计算示意图如下:
在这张图中,输入层的大小是1 x V
,输入-隐藏层权重矩阵的大小是V x N
,隐藏层的神经元数量是N,隐藏-输出权重矩阵大小是N x V
,输出层的大小是C个1 x V
。
在这个例子中,窗口大小C=2, V=10,N=4:
skip-gram模型的优势:
因为词嵌入是词的上下文相似性的表示,可以用来做以下任务:
model.similarity('woman','man')
0.73723527
model.doesnt_match('breakfast cereal dinner lunch';.split())
'cereal'
woman + king - man = queen
model.most_similar(positive=['woman','king'],negative=['man'],topn=1)
queen: 0.508
model.score(['The fox jumped over the lazy dog'.split()])
0.21
一张word2vec的可视化图:
词向量在二维的t-SNE表示,可以看到Apple的两种上下文都获取到了。
这张图表示了中文和英文的双语词嵌入,可以看到语义相似的词,位置也靠近,因此可以用来做翻译。
使用谷歌的预训练模型。词典大小是300万,用大小是1000亿词的谷歌新闻数据集训练而成,大小是1.5GB,下载地址。
from gensim.models import Word2Vec
# 加载模型
model = Word2Vec.load_word2vec_format('GoogleNews-vectors-negative300.bin', binary=True, norm_only=True)
# 加载模型之后,就可以完成上面的任务了。
# 获得某个词的词向量
dog = model['dog']
# 做king queen的词语字符串计算
print(model.most_similar(positive=['woman', 'king'], negative=['man']))
# 找出奇异值
print(model.doesnt_match("breakfast cereal dinner lunch".split()))
# 打印相似指数
print(model.similarity('woman', 'man'))
使用gensim和自己的语料来训练word2vec。
训练数据的格式如下:
sentence=[[‘Neeraj’,’Boy’],[‘Sarwan’,’is’],[‘good’,’boy’]]
用这3句话来训练
model = gensim.models.Word2Vec(sentence, min_count=1,size=300,workers=4)
参数的含义如下:
sentence —— 包含列表的列表语料; min_count=1 —— 词的阈值,只有高于阈值才会计算; size=300 —— 词的表征维度,即词向量的大小 workers=4 —— 并行的worker数量
词嵌入一直就是一个活跃的研究领域,但是近年的研究不仅越来越臃肿,还越来越复杂。这篇文章的目的,是用浅显易懂的语言加少量的数学,回顾词嵌入的发展。