Word2Vec解释
一、Word2Vec梗概
字面意思:即Word to Vector,由词到向量的方法。
专业解释:Word2Vec使用一层神经网络将one-hot(独热编码)形式的词向量映射到分布式形式的词向量。使用了Hierarchical softmax, negative sampling等技巧进行训练速度上的优化1。
作用:我们日常生活中使用的自然语言不能够直接被计算机所理解,当我们需要对这些自然语言进行处理时,就需要使用特定的手段对其进行分析或预处理。使用one-hot编码形式对文字进行处理可以得到词向量,但是,由于对文字进行唯一编号进行分析的方式存在数据稀疏的问题,Word2Vec能够解决这一问题,实现word embedding(个人理解为:某文本中词汇的关联关系 例如:北京-中国,伦敦-英国)。
最主要的用途:一是作为其他复杂的神经网络模型的初始化(预处理);二是把词与词之间的相似度用作某个模型的特征(分析)。
二、Word2Vec的具体算法阐述
2.1 算法流程
第一步:将one-hot形式的词向量输入到单层神经网络中,其中输入层的神经元结点个数应该和one-hot形式的词向量维数相对应。比如,输入词是“夏天”,它对应的one-hot词向量
第二步:通过神经网络中的映射层中的激活函数,计算目标单词与其他词汇的关联概率,其中在计算时,使用了负采样(negative sampling)的方式来提高其训练速度和正确率
第三步:通过使用随机梯度下降(SGD)的优化算法计算损失
第四步:通过反向传播算法将神经元的各个权重和偏置进行更新
所以,word2vec实质上是一种降维操作,将one-hot形式的词向量转化为word2vec形式。
2.2 两种训练方式
Word2Vec有两种训练词向量的方式。通过这两种方式训练出来的结果,都是相同的即一个可进行词汇关联预测的单层神经网络。
2.2.1 Continous Bag of Words Model(CBOW)
CBOW是通过目标单词的语境推测出可能出现的单词。CBOW的训练流程图如下所示:
从上图中,我们也可以更加清晰地理解从语境中推测目标单词的概率,这句话的意义。从Tensorflow的教程中来看,Tensorflow更倾向于使用Skip-Gram model来进行embedding,所以这里不再展开叙述。
补充:CBOW的更一般情况2。
我们将上下文单词输入时,可能会遇到多种上下文的形式,所以,情况可能会为下图所示:
即多种情况对应同一个单词的预测输出。
2.2.2 Skip-Gram model
Skip-Gram model是通过目标单词推测语境,在大规模的数据集中Skip-Gram model训练速度快。不过,本人并未有过测试,以上结论有待考证。Skip-Gram 的训练流程如下图所示:
和CBOW进行对比,相比区别还是很明显的。
Skip-Gram 的损失函数是通过将目标词汇的正例和负例使用二元对数回归(Binary Logistic Regeression)计算概率并相加得到的。损失函数公式3为:
其中
注:因为现阶段没有对这一知识点进行了解,所以具体的推导先搁置。重在初步理解概念。
补充:Skip-Gram的更一般情况5
同样的,Skip-Gram也有更一般的情形,也就是再输入一个目标单词时,可能会得到多个上下文单词结果,情况如下图所示:
这一形式是与CBOW相反的。
2.3 算法的权重的更新
我们都知道,一般而言,机器学习和深度学习中,常常使用的梯度下降(GD)的算法来计算和更新权重与偏置。在数据量极大的情况下,虽然批量梯度下降能够向全局最优的方向迭代,但是每次迭代都需要使用数据集中的所有数据且迭代的速度将十分缓慢,所以,在数据量极大的情况下,并不推荐使用。所以,在word2vec的权重更新时,我使用的是随机梯度下降算法(Stochastic Gradient Descent)。随机梯度下降的算法是通过对样本中随机一个数据进行权重的更新,从而,减少冗余数据对整个迭代过程的影响,提高收敛效率。不过,由于是随机取值,所以,可能会落入局部最优中去,而且更新时会出现较大的波动,更新值方差较大。下图也能直观的看出其较大波动的图像6:
但是,通过降低学习率能够,使波动减小,呈现出与批量梯度下降相似的效果,同时,随机性能够跳出局部最优的情况,从而得到更好的收敛效果。同时,随机梯度下降在前期的的迭代中效果显著,曲线如下图7所示:
说了那么多,从公式上看看梯度下降和随机梯度下降的区别吧。假设待优化函数为:
梯度下降是每次计算n个梯度进行以下迭代:
而随机梯度下降是每次选择一个
迭代量的缩减在公式中也显而易见。
三、代码实现
代码实现参照的是Tensorflow官网Tutorial中的Vector Representations of Words实现的。具体的实现如下:
3.1 构建图(Build the Graph)
1.使用Variable来构建一个够我们嵌入词向量的矩阵,且初始值为uniform
embeddings = tf.Variable(tf.random_uniform([vocabulary_size, embedding_size], -1.0, 1.0))
其中,vocabulary_size是输入文本中的词汇个数,embedding_size是词向量嵌入矩阵的维度,是超参数之一,一般取128,也是词向量的大小,其具体数值可以由我们自己定夺,当然他的值也会影响整个实验的效果。 tf.random_uniform函数的含义是了一个vocabulary_size*embedding_size大小的矩阵,使用
2.初始化损失(loss)函数的权重和偏置,同样是以Variable的形式来进行初始化
nce_weights = tf.Variable(tf.truncated_normal([vocabulary_size, embedding_size],stddev=1.0 / math.sqrt(embedding_size)))
nce_biases = tf.Variable(tf.zeros([vocabulary_size]))
其中,nce_weights是权重矩阵,nce_biases是偏置矩阵。truncated_normal函数的含义是输出在截断正态分布中的随机值填入vocabulary_size*embedding_size大小的矩阵中,且保证每个随机生成的值在合理的标准差范围之内。也就是说,truncated_normal函数能够保证权重随机生成为一个合理的数值。stddev参数的含义是定义该正态分布的标准差。tf.zeros函数是生成了一个vocabulary_size * 1大小的零矩阵。
The generated values follow a normal distribution with specified mean and standard deviation, except that values whose magnitude is more than 2 standard deviations from the mean are dropped and re-picked.
摘自:tf.truncated_normal in Tensorflow
这句话是truncated_normal函数的解释,想阐述的便是生成一个在两个标准差范围内的数据,当生成数据大于两个标准差时,便会被遗弃。
3.初始化输入数据容器和标签容器
因为word2vec本身算是一个有监督学习算法,所以使用数据时需要将输入数据和标签数据分开。
train_inputs = tf.placeholder(tf.int32, shape=[batch_size])
train_labels = tf.placeholder(tf.int32, shape=[batch_size, 1])
其中,tf.placeholder函数的含义是生成一个batch_size大小的numpy数组来处理后期将要放入的数据。batch_size是超参数,他对深度学习迭代求解的收敛速度和内存的消耗都存在影响。知乎上的大佬对batch_size大小对训练的影响有一个较为直观的理解和总结。相关链接:深度机器学习中的batch的大小对学习效果有何影响?
4.将训练数据的词向量嵌入到embedding矩阵中
embed = tf.nn.embedding_lookup(embeddings, train_inputs)
tf.nn.embedding_lookup函数的含义是查找train_inputs在embeddings中的位置,并且最后返回一个tensor形式的结果,也就是相当于将对应的输入词语或句子对应的词向量检索出来。
5.定义loss函数
loss = tf.reduce_mean(tf.nn.nce_loss(weights=nce_weights,biases=nce_biases,labels=train_labels,inputs=embed,num_sampled=num_sampled,num_classes=vocabulary_size))
其中,tf.reduce_mean函数的含义是计算nce_loss函数得到的损失值的平均值。而nce_loss函数是tensorflow中常用的损失函数,可以将其理解为其将多元分类分类问题强制转化为了二元分类问题,也就能和上文提到的二元对数回归形成对应了。num_sampled参数代表将选取负例的个数。num_classes参数代表可能出现的类型个数这里我们就选择vocabulary_size个即可。
6.定义优化器(Optimizer)
optimizer = tf.train.GradientDescentOptimizer(learning_rate=1.0).minimize(loss)
此处,使用的是SGD随机梯度下降来进行参数权重的迭代更新。其中,learning_rate参数代表学习率。学习率也是模型中超参数之一,它代表的是每次梯度下降找到最优方向后,我们要向前的值的大小。如果把梯度下降理解为找最快的方式从山顶走到山底,梯度下降的求导计算,便是计算往哪个方向下山最快,而学习率就是每一步应该迈多远。过大的学习率会导致下降过程中错过最低点,过小的学习率则会在迭代下降的速度及其缓慢。
3.2 训练模型
使用for循环将符合train_inputs和train_labels的numpy数组输入到feed_dict中,即可开始训练。
for inputs, labels in generate_batch(...):feed_dict = {train_inputs: inputs, train_labels: labels}_, cur_loss = session.run([optimizer, loss], feed_dict=feed_dict)
完整代码可参考Tensorflow官方教程示例:tensorflow/examples/tutorials/word2vec/word2vec_basic.py
四、总结
word2vec是自然语言或者与自然语言有相似特性的数据的泛化处理方式,他能够将相似词汇进行联系。同时,它也是数据进入RNN或LSTM模型前的前置处理手段。在文中还有很多数学和理论方面的知识由于本人的掌握有限未能提及。若本文中出现理解错误或者笔误,烦请指正!
- 专业解释摘自:无痛理解word2vec ↩
- 秒懂词向量Word2vec的本质 ↩
- 在使用Latex书写公式时,想要将下标放置在操作符正下方使用即
\limits_{}
即可,若设置下标的字符并不是操作符,则需要使用\mathop{}
命令进行操作,将一般字符转化为操作符之后,再使用\limits_{}
↩ - Monte Carlo average ↩
- 秒懂词向量Word2vec的本质 ↩
- [Machine Learning] 梯度下降法的三种形式BGD、SGD以及MBGD ↩
- 为什么我们更宠爱“随机”梯度下降?(SGD) ↩