自己动手实现word2vec(Skip-gram模型)

栏目: 编程工具 · 发布时间: 6年前

内容简介:学习word2vec的skip-gram实现,除了skip-gram模型还有CBOW模型。 Skip-gram模式是根据中间词,预测前后词,CBOW模型刚好相反,根据前后的词,预测中间词。那么什么是首先,我们需要定义一个窗口大小,在窗口里面的词,我们才有中间词和前后词的定义。一般这个窗口大小在5-10之间。 举个例子,我们设置窗口大小(window size)为2:

学习word2vec的skip-gram实现,除了skip-gram模型还有CBOW模型。 Skip-gram模式是根据中间词,预测前后词,CBOW模型刚好相反,根据前后的词,预测中间词。

那么什么是 中间词 呢?什么样的词才叫做 前后词 呢?

首先,我们需要定义一个窗口大小,在窗口里面的词,我们才有中间词和前后词的定义。一般这个窗口大小在5-10之间。 举个例子,我们设置窗口大小(window size)为2:

|The|quick|brown|fox|jump|
复制代码

那么, brown 就是我们的中间词, Thequickfoxjump 就是前后词。

我们知道,word2vec实际上就是一个神经网络(后面会解释),那么这样的数据,我们是以什么样的格式用来训练的呢? 看一张图,你就明白了:

自己动手实现word2vec(Skip-gram模型)

可以看到,我们总是以 中间词 放在第一个位置,然后跟着我们的前后相邻词。可以看到,每一对词都是一个输入和一个输出组成的数据对(X,Y)。其中,X是feature,Y是label。

所以,我们训练模型之前,需要根据语料,整理出所有的像上面这样的输入数据用来训练。

word2vec是一个神经网络

word2vec是一个简单的神经网络,有以下几个层组成:

  • 1个输入层
  • 1个隐藏层
  • 1个输出层

输入层输入的就是上面我们说的数据对的数字表示,输出到隐藏层。 隐藏层的神经网络单元的数量,其实就是我们所说的 embedding size ,只有为什么,我们后面简单计算一下就知道。需要注意的是,我们的隐藏层后面不需要使用激活函数。 输出层,我们使用softmax操作,得到每一个预测结果的概率。

这里有一张图,能够表示这个网络:

自己动手实现word2vec(Skip-gram模型)

输入层

现在问题来了,刚刚我们说,输入层的输入是我们之前准备的数据对的数字表示,那么我们该如何用数字表示文本数据呢?

好像随便一种方式都可以用来表示我们的文本啊。

看上图,我们发现,它的输入使用的是 one-hot 编码。什么是ont-hot编码呢?如图所示,假设有n个词,则每一个词可以用一个n维的向量来表示,这个n维向量只有一个位置是1,其余位置都是0。

那么为什么要用这样的编码来表示呢?答案后面揭晓。

隐藏层

隐藏层的神经单元数量,代表着每一个词用向量表示的维度大小。假设我们的 hidden_size 取300,也就是我们的隐藏层有300个神经元,那么对于每一个词,我们的向量表示就是一个 的向量。 有多少个词,就有多少个这样的向量!

所以对于 输入层隐藏层 之间的权值矩阵 ,它的形状应该是 [vocab_size, hidden_size] 的矩阵,

输出层

那么我们的输出层,应该是什么样子的呢?从上面的图上可以看出来,输出层是一个 [vocab_size] 大小的向量,每一个值代表着输出一个词的概率。

为什么要这样输出?因为我们想要知道,对于一个输入词,它接下来的词最有可能的若干个词是哪些,换句话说,我们需要知道它接下来的词的概率分布。

你可以再看一看上面那张网络结构图。

你会看到一个很常见的函数 softmax ,为什么是softmax而不是其他函数呢?不妨先看一下softmax函数长啥样:

很显然,它的取值范围在(0,1),并别所有的值和为1。这不就是天然的概率表示吗?

当然,softmax还有一个性质,因为它函数指数操作,如果 损失函数使用对数函数 ,那么可以抵消掉指数计算。

关于更多的softmax,请看斯坦福Softmax Regression

整个过程的数学表示

至此,我们已经知道了整个神经网络的结构,那么我们应该怎么用数学表示出来呢?

回顾一下我们的结构图,很显然,三个层之间会有两个权值矩阵 ,同时,两个偏置项 。所以我们的整个网络的构建,可以用下面的伪代码:

import tensorflow as tf

# 假设vocab_size = 1000
VOCAB_SIZE = 1000
# 假设embedding_size = 300
EMBEDDINGS_SIZE = 300

# 输入单词x是一个[1,vocab_size]大小的矩阵。当然实际上我们一般会用一批单词作为输入,那么就是[N, vocab_size]的矩阵了
x = tf.placeholder(tf.float32, shape=(1,VOCAB_SIZE))
# W1是一个[vocab_size, embedding_size]大小的矩阵
W1 = tf.Variable(tf.random_normal([VOCAB_SIZE, EMBEDDING_SIZE]))
# b1是一个[1,embedding_size]大小的矩阵
b1 = tf.Variable(tf.random_normal([EMBEDDING_SIZE]))
# 简单的矩阵乘法和加法
hidden = tf.add(tf.mutmul(x,W1),b1)

W2 = tf.Variable(tf.random_normal([EMBEDDING_SIZE,VOCAB_SIZE]))
b2 = tf.Variable(tf.random_normal([VOCAB_SIZE]))
# 输出是一个vocab_size大小的矩阵,每个值都是一个词的概率值
prediction = tf.nn.softmax(tf.add(tf.mutmul(hidden,w2),b2))
复制代码

损失函数

网络定义好了,我们需要选一个损失函数来使用梯度下降算法优化模型。

我们的输出层,实际上就是一个softmax分类器。所以按照常规套路,损失函数就选择 交叉熵损失函数

哈哈,还记得交叉熵是啥吗?

p,q是真是概率分布和估计概率分布。

# 损失函数 
cross_entropy_loss = tf.reduce_mean(-tf.reduce_sum(y_label * tf.log(prediction), reduction_indices=[1]))
# 训练操作
train_op = tf.train.GradientDescentOptimizer(0.1).minimize(cross_entropy_loss)
复制代码

接下来,就可以准备号数据,开始训练啦!

为啥输入使用one-hot编码?

我们知道word2vec训练后会得到一个权值矩阵W1(暂时忽略b1),这个矩阵就是我们的所有词的向量表示啦!这个矩阵的每一行,就是一个词的矢量表示。如果两个矩阵相乘...

自己动手实现word2vec(Skip-gram模型)

看到了吗?ont-hot编码的特点, 在矩阵相乘的时候,就是选取出矩阵中的某一行,而这一行就是我们输入这个词语的word2vec表示!

怎么样?是不是很妙?

由此,我们可以看出来,所谓的word2vec,实际上就是一个 查找表 ,是一个 二维 的浮点数矩阵!

以上是word2vec的skip-gram模型的完整分析,怎么样,是不是弄清楚了word2vec的原理和细节?

完整代码请查看 luozhouyang/word2vec


以上所述就是小编给大家介绍的《自己动手实现word2vec(Skip-gram模型)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

JavaScript语言精粹

JavaScript语言精粹

Douglas Crockford / 赵泽欣、鄢学鹍 / 电子工业出版社 / 2009-4 / 35.00元

本书通过对JavaScript语言的分析,甄别出好的和坏的特性,从而提取出相对这门语言的整体而言具有更好的可靠性、可读性和可维护性的JavaScript的子集,以便你能用它创建真正可扩展的和高效的代码。 雅虎资深JavaScript架构师Douglas Crockford倾力之作。 向读者介绍如何运用JavaScript创建真正可扩展的和高效的代码。一起来看看 《JavaScript语言精粹》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

URL 编码/解码
URL 编码/解码

URL 编码/解码

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试