基于keras实现word2vec

译:https://adventuresinmachinelearning.com/word2vec-keras-tutorial/

很多博客都阐述了word2vec这个模型的原理,例如空间映射、稠密向量表示,cbow/skip-gram,softmax求解,霍夫曼树,blabla...

但数学公式理解起来比较抽象,结合工程实现可以更好的帮助理解。

word2vec

理解Word2Vec单词嵌入是机器学习历程中的关键组成部分。词嵌入是在机器学习模型中执行高效自然语言处理的必要步骤。本文将展示如何在Keras深度学习框架中执行Word2Vec单词嵌入。同时,本文还将展示当我们的词汇表非常大时,使用基于softmax的词嵌入训练方案会导致训练过程极其缓慢。一种称为“负采样”的技术已经解决这个问题,并且已经在TensorFlow中实现了(nce_loss)。

不幸的是,这个损失函数在Keras中不存在,因此在本文中,我们将自己实现它。这也是因祸得福了,因为自己实现它将有助于我们理解负采样如何工作,从而更好地理解Word2Vec Keras过程。


词嵌入

如果我们用一批文档去训练某种自然语言机器学习系统(比如聊天机器人),我们需要创建所有文档中最常用单词的词汇表。在某些情况下,该词汇表的长度可能大于10,000个单词。为了向我们的机器学习模型表示一个单词,一种很淳朴的方式是使用one-hot表示,即一个全0的矢量,除了表示我们那个单词的位置设置为1。但是这是一种低效的方式 ,一个10,000字的向量是很难训练的。另一个问题是,这些one-hot向量不包含单词语义信息,如何让它在文档的上下文中连贯使用?(比如一词多义?)

词嵌入的目的是将大的one-hot向量“压缩”成更小的向量(几百个元素),这些向量保留了单词的一些含义和上下文,而Word2Vec是最常用的词嵌入方法。

上下文,word2Vec以及skip-gram模型

表达词的上下文是word2Vec最关键的作用。“the cat sat on the mat”这句话中“sat”一词的上下文是(“the”,“cat”,“on”,“the”,“mat”)。换句话说,这些词是通常出现在目标词“sat”周围的词。具有相似上下文的单词在Word2Vec下具有相似的词义,并且压缩它们的向量维度后,所表示的意义也是相似的。在word2Vec的skip-gram模型版本中(稍后将详细介绍),目标是获取目标词,即“sat”,并预测其周围的上下文词。

这种学习的最终产物是网络中的嵌入层,这个嵌入层其实是一种查找表:每行表示我们词汇表中相应单词的向量。举个栗子:我们设词汇表有6个词,one-hot表示就是一个6维的向量,通过词嵌入将维度压缩到3(假设数据哦😝):

原词汇表 one-hot 压缩后的向量
[1,0,0,0,0,0] [0.2,-0.5,-0.7]
[0,1,0,0,0,0] [0.8,0.5,0.7]
[0,0,1,0,0,0] [0.4,-0.3,0.6]
[0,0,0,1,0,0] [0.1,-0.1,-0.9]
[0,0,0,0,1,0] [0.2,-0.3,0.6]
[0,0,0,0,0,1] [0.9,-0.4,-0.8]

可以看到,每个单词(行)由大小为3的向量表示。这种嵌入层/查找表可以使用简单的神经网络和softmax输出训练得出,如下图所示:


Word2Vec-softmax网络结构

上面的神经网络的输入是以one-hot表示的目标词,接着通过隐藏层的训练,让有效上下文单词的概率增加,同时让无效上下文单词的概率降低(也就是在目标单词的周围上下文中从不出现的单词)。softmax函数负责输出最后的概率。训练完成后,输出层将被丢弃,我们的嵌入向量就是隐藏层的权重。

Word2Vec有两种形式:skip-gram和CBOW。skip-gram输入目标词预测周围的上下文词,而CBOW正好相反,输入一组上下文词预测其中的目标词。本文我们只考虑skip-gram(效果好一些)。

softmax VS negative sampling

使用softmax输出的问题在于它的计算量非常大。考虑softmax函数的定义:

P(y = j \mid x)= \frac{e ^ {x ^ T w_j}} {\sum_{k = 1} ^ K e ^ {x ^ T w_k}}

这里的P为输出为类j的概率。分子的算法:将隐藏层的输出与连接到类j输出层的权重相乘;分母的算法:对所有输出类采用与分子相同的算法,并求和。如果输出采用一个10000维的one-hot向量表示,那采用梯度下降算法求解模型时,需要计算数以百万计的权重,这会非常耗时且效率低下。

还有一种称为负抽样的解决方案。它在Mikolov等人的原始Word2Vec论文中有所描述。简单理解为:所有将目标词与目标词上下文连在一起的网络,则增强它们的权重;而对于非目标词上下文的网络,不是去降低它们的权重,而是简单的对它们进行下采样。这就是negative sampling。

为了在Keras中使用负样本训练嵌入层,我们可以重新设想我们训练网络的方式。我们可以将原来的softmax层替换为简单的二分类器。对于在目标词的上下文中的单词,我们希望我们的网络输出1,对于我们的负样本,我们希望我们的网络输出0。因此,我们基于keras实现的Word2Vec网络的输出层只是一个sigmoid。

我们还需要一种方法来确保进过网络训练后,相似的单词最终具有类似的嵌入向量。因此,我们要确保在相同上下文中出现的词,网络总是输出1,而从未在相同上下文中出现的词,网络总是输出0。因此,我们需要提供sigmoid输出层一个矢量相似性得分:相似矢量输出高分和不相似矢量输出低分。两个向量之间使用的最典型的相似性度量是余弦相似度得分:

similarity = cos(\theta)= \frac{\textbf {A} \cdot \textbf {B}} {\parallel \textbf {A} \parallel_2 \parallel \textbf {B} \parallel_2}

这种度量方法的分母用于归一化结果,真正的相似性操作在分子上:向量AB之间的点积

综上所述,我们基于Keras的Word2Vec实现新的负抽样网络具有以下特点:

  • 目标词的(整数)输入,上下文词的正负例
  • 嵌入层查找(即在嵌入矩阵中查找单词的整数索引以获取单词向量)
  • 点积运算
  • 输出sigmoid层

实现的体系结构如下所示:


Negative-sampling结构图

让我们更仔细地考虑这个架构。首先,我们词汇表中的每个单词都被赋予一个介于0和我们词汇表大小之间的整数索引(在本例中为10,000)。我们将两个单词传递到网络中,一个是目标词,另一个是来自周围上下文的单词或负样本。我们利用输入词的索引来“查找”嵌入层(10,000 x 300权重张量)的行,以检索这个词对应的维度为300单词向量。然后,我们在这些向量之间执行点积运算以获得相似性。最后,我们将相似性输出到sigmoid层,匹配上了上下文的词输出1,未匹配的输出0。


实现

本节将展示如何创建自己的基于Keras的Word2Vec实现 - 代码在此

数据提取

我们首先需要一些数据。在Word2Vec TensorFlow教程中,我们将使用此处的文档数据集。为了提取信息,我将使用上述Word2Vec教程中的一些相同的文本提取函数,特别是collect_data函数。这个函数实现了下载数据,将文本数据转成了由字符串表示的整数(词汇表中的每个单词由唯一的整数表示)。要调用此函数,我们运行:

vocab_size = 10000
data, count, dictionary, reverse_dictionary = collect_data(vocabulary_size=vocab_size)

数据集前7个词是:

[‘anarchism’, ‘originated’, ‘as’, ‘a’, ‘term’, ‘of’, ‘abuse’]

运行collect_data函数后,这7个词词表示为:

[5239, 3082, 12, 6, 195, 2, 3134]

从collect_data返回还有两个词典:第一个可以查找单词并获得其整数表示,第二个正好相反。

接下来,我们需要为训练定义一些常量,并创建一组验证集以便我们检查词向量的学习情况。

常量和验证集

window_size = 3
vector_dim = 300
epochs = 1000000

valid_size = 16     # Random set of words to evaluate similarity on.
valid_window = 100  # Only pick dev samples in the head of the distribution.
valid_examples = np.random.choice(valid_window, valid_size, replace=False)
  • 第一个常量 window_size是目标单词周围的单词窗口,用于从中绘制上下文单词。
  • 第二个常量vector_dim是每个字嵌入向量的大小:我们的嵌入层的大小为10,000 x 300。
  • 最后,我们有一个大的epochs变量:指定了的训练迭代次数。即使采用负抽样,词嵌入也可能是一个耗时的过程。

下一组参数与我们要检查的单词相关,以查看其他单词与此验证集的相似之处。在训练过程中,我们将通过词嵌入向量来检查哪些单词开始就被认为是相似的,并确保这些单词与我们对这些单词的含义的理解相符。我们将从数据集前100个最常见的单词中随机选择16个单词进行检查(collect_data 按升序分配数据集整数中最常用的单词,即最常用的单词分配1,下一个最常见的2)。

接下来,我们来看看keras为我们准备的skip-gram函数。

未完待续。。。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,012评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,628评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,653评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,485评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,574评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,590评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,596评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,340评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,794评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,102评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,276评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,940评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,583评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,201评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,441评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,173评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,136评论 2 352

推荐阅读更多精彩内容