本身动手实现word2vec(Skip-gram模型)

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

那么什么是中间词呢?什么样的词才叫作先后词呢?git

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

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

那么,brown就是咱们的中间词,Thequickfoxjump就是先后词。算法

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

word2vec window

能够看到,咱们老是以中间词放在第一个位置,而后跟着咱们的先后相邻词。能够看到,每一对词都是一个输入和一个输出组成的数据对(X,Y)。其中,X是feature,Y是label。网络

因此,咱们训练模型以前,须要根据语料,整理出全部的像上面这样的输入数据用来训练。dom

word2vec是一个神经网络

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

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

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

这里有一张图,可以表示这个网络: 优化

skip-gram-net-arhc

输入层

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

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

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

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

隐藏层

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

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

输出层

那么咱们的输出层,应该是什么样子的呢?从上面的图上能够看出来,输出层是一个[vocab_size]大小的向量,每个值表明着输出一个词的几率。

为何要这样输出?由于咱们想要知道,对于一个输入词,它接下来的词最有可能的若干个词是哪些,换句话说,咱们须要知道它接下来的词的几率分布。

你能够再看一看上面那张网络结构图。

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

softmax(x) = \frac{e^x}{\sum_{i}^{N}e^{x_i}}

很显然,它的取值范围在(0,1),并别全部的值和为1。这不就是自然的几率表示吗?

固然,softmax还有一个性质,由于它函数指数操做,若是损失函数使用对数函数,那么能够抵消掉指数计算。

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

整个过程的数学表示

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

回顾一下咱们的结构图,很显然,三个层之间会有两个权值矩阵W,同时,两个偏置项b。因此咱们的整个网络的构建,能够用下面的伪代码:

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分类器。因此按照常规套路,损失函数就选择交叉熵损失函数

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

H(p,q)=-\sum_{x}p(x)logq(x)

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),这个矩阵就是咱们的全部词的向量表示啦!这个矩阵的每一行,就是一个词的矢量表示。若是两个矩阵相乘...

matrix_mult_w_one_hot

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

怎么样?是否是很妙?

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

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

完整代码请查看luozhouyang/word2vec

联系我

相关文章
相关标签/搜索