内容简介:学习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
就是我们的中间词, The
、 quick
、 fox
、 jump
就是前后词。
我们知道,word2vec实际上就是一个神经网络(后面会解释),那么这样的数据,我们是以什么样的格式用来训练的呢? 看一张图,你就明白了:
可以看到,我们总是以 中间词 放在第一个位置,然后跟着我们的前后相邻词。可以看到,每一对词都是一个输入和一个输出组成的数据对(X,Y)。其中,X是feature,Y是label。
所以,我们训练模型之前,需要根据语料,整理出所有的像上面这样的输入数据用来训练。
word2vec是一个神经网络
word2vec是一个简单的神经网络,有以下几个层组成:
- 1个输入层
- 1个隐藏层
- 1个输出层
输入层输入的就是上面我们说的数据对的数字表示,输出到隐藏层。 隐藏层的神经网络单元的数量,其实就是我们所说的 embedding size ,只有为什么,我们后面简单计算一下就知道。需要注意的是,我们的隐藏层后面不需要使用激活函数。 输出层,我们使用softmax操作,得到每一个预测结果的概率。
这里有一张图,能够表示这个网络:
输入层
现在问题来了,刚刚我们说,输入层的输入是我们之前准备的数据对的数字表示,那么我们该如何用数字表示文本数据呢?
好像随便一种方式都可以用来表示我们的文本啊。
看上图,我们发现,它的输入使用的是 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),这个矩阵就是我们的所有词的向量表示啦!这个矩阵的每一行,就是一个词的矢量表示。如果两个矩阵相乘...
看到了吗?ont-hot编码的特点, 在矩阵相乘的时候,就是选取出矩阵中的某一行,而这一行就是我们输入这个词语的word2vec表示! 。
怎么样?是不是很妙?
由此,我们可以看出来,所谓的word2vec,实际上就是一个 查找表 ,是一个 二维 的浮点数矩阵!
以上是word2vec的skip-gram模型的完整分析,怎么样,是不是弄清楚了word2vec的原理和细节?
完整代码请查看 luozhouyang/word2vec
以上所述就是小编给大家介绍的《自己动手实现word2vec(Skip-gram模型)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 自己动手实现神经网络分词模型
- 拒绝网络暴力!动手开发基于深度学习的网络欺凌检测模型
- 一步步动手实现高并发的Reactor模型 —— Kafka底层如何充分利用多线程优势去处理网络I/O与业务分发...
- 《自己动手写Docker》
- 自己动手编译OpenJDK
- 自己动手写Spring
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
JavaScript语言精粹
Douglas Crockford / 赵泽欣、鄢学鹍 / 电子工业出版社 / 2009-4 / 35.00元
本书通过对JavaScript语言的分析,甄别出好的和坏的特性,从而提取出相对这门语言的整体而言具有更好的可靠性、可读性和可维护性的JavaScript的子集,以便你能用它创建真正可扩展的和高效的代码。 雅虎资深JavaScript架构师Douglas Crockford倾力之作。 向读者介绍如何运用JavaScript创建真正可扩展的和高效的代码。一起来看看 《JavaScript语言精粹》 这本书的介绍吧!