对word2vec不了解的可以先看看这篇Word2vec入门
网上很多word2vec的前向传播和反向传播给出的推导都不是很好理解,而且对于不太同种的实现方式也没有具体说明,所以一直想把这部分的坑填上,对于skip的模型解释的也很模糊,本文以一个单词输入为引子,主要推导这部分的正向传播和反向传播,然后介绍skip-model和CBOW。
在说word2vec模型之前,先谈一谈ONE-WORD-MODEL,这个模型使用率其实很低,先说这个是为了更好的理解word2vec的推导过程。这个模型是输入一个单词,然后输出词库中和它最接近的单词。
为了方便解释,我们约定V代表着单词库中单词的数量,N代表隐藏层数量也就是一个单词embedding_size,代表用几个离散值来表示这个单词。简化为V=4,N=5,见下图:
这个模型一共有三个层,输入层,隐藏层,输出层,两个权重分别W1,W2,在这里W2其实就是W1的转置,输入为一个one-hot形式,比如(1,0,0,0)输出也是一个one-hot形式,表示和输入最接近的词。
在开始介绍模型计算之前,先要想明白一件事情,这是一个监督学习网络,也就是我们有正确label的真实值记为Y_true,我们的输出预测值Y,在计算lost(Y,Y_true)以后去更新参数W1,W2,一开始W2是W1的转置,但是经过几次训练以后他的参数值更新以后很有可能就不是W1的转置了,当然这里也可以选用不同的权重。
隐藏层:H = W1转置 * X 得到一个(N,1)的向量
输出层: W2转置 *H 得到一个(V,1)的向量,然后经过softmax函数得到向量中每项概率加和为1的向量记为Y_pre,最后转为Y,one-hot形式
Lost函数:
为了便于理解,先说一个例子,比如我得到的Y_pre=[0.2,0.3,0.1,0.4]
然后我的真实值Y_true=[0,0,0,1],显然我的预测结果是正确的,那么此时我的cost值就是
计算每一个i=(0,1,2,3)时的Ji,然后再加和,但是我的正确值只有i=3时候才为1,其他时候都为0,那么J = -log(Y_pre) = log(0.4)
下面的梯度推导过程:
参数更新比较简单就省略了,要注意的是上面的推导过程其实是向量化以后的书写形式,没有繁杂的(i,j)标签来表示各个W矩阵的更新,我觉得这样比较好理解一些,加上标签反而有点混乱,但是道理都是一样的。
下面就终于进入正题了,在单个单词作为输入输出的时候,不知道大家会不会产生这样的疑惑,label是怎么样形成的?
我们知道label是和input输入的单词最接近的一个单词,那么这个标签是怎么打上去的,如果是按照人工经验来考虑,比如中文“早”可能对应的就是“上”,但这种判断也不一定精准,比如“我”这个字该对应什么呢?似乎有很多答案。如果交给计算机来处理,可选择的一种方式就是,在给定的语料库中计算“我”这个字相邻字出现的次数,出现次数最大的单词作为label,那么我们的“我”以及输出的单词都是作为了这个语料库的关键字的,面对成千上万的关键字,这无疑是极大的计算量。
那么word2vec对此作了什么改进呢?
首先,它是无监督方式的一种模型,也就是说我们不需要label,那么怎么样判断我们模型学习的效果是否满足需要呢?word2vec有两个很重要的特点:
第一:上下文相似的两个词,它们的词向量也是相似的
第二:提出了一个滑动窗口概念
滑动窗口其实也很好理解,比如我这里有一个长句子,我要把这个长句子喂给模型,模型不可能一次吃下这么长的句子,于是我指定一个滑动窗口=5,每次我只喂给模型5个单词,那么下一次喂的单词是从紧接着上次一最后一个单词开始,还是其他靠前一点的位置,这就和滑动窗口的步长有关系了,如果步长为1,那么意味着每一次我的窗口只朝着句子末端的方向走一步。
word2vec有两个模型:
skip-gram:根据当前词预测上下文
CBOW:根据上下文去预测当前词
模型结构图如下,skip和one-word最大的区别就是输出,skip有多个输出,每个输出的形式都是one-hot,代表词库的哪个单词。
前面我们提到了word2vec模型引入了窗口的概念,还有步长,分别将这两项称为skip-window和skip-num,假设skip-window=5,skip-num=1,然后我们截取了语库中这样一个句子:“I dont like my dog”,按道理来说在处理数据的时候这里的dont应该转化为“do not”,但这不是我们今天的主题,所以先不考虑这个细节。
like就是我们的中心词,skip-window=5,就是包含中心词在内的有五个单词,skip-num作为步长就是说下一次的句子截取从dont开始。
在不考虑模型推导之前,先思考这样一个问题,为什么上面模型中输出会得到C个不同的结果?我们的W权重是一样的,输入经过隐藏层计算得到的结果也是一样的,那怎么会有C个不同的输出结果呢?
其实在我们设置窗口以后,会组成这样四个序列:(center word,output word)
(“like”,“dont”)
(“like”,“I”)
(“like”,“my”)
(“like”,“dog”)
表示以like为中心词下,他的输出词(也就是上下文)可能有这些选择,我们把序列喂给模型,就可以得到每个output word的概率分布,比如这里就会得到四个概率分布。代表着输出值是这四个次的可能性。这个可能性自然是越大越好。
这个过程是在哪一步实现的呢?输入层输入的还是单个词汇也就是center word,这是毋庸置疑的,然后计算得到隐藏层的输出,这里的输出实际上就是单词向量化的结果。然后,重点来了,这里会针对获取到序列,然后根据序列值中的output word来计算概率分布,自然不同的序列就会对应不同的概率分布了。
cost函数是所有概率分布的乘积,因为我们默认这些概率是独立分布的,也就是彼此之间没有关系。
公式推导其实和上面的是一样的,就不再复述了。
cbow要比Skip好理解,他的输入是窗口中的output word,然后输出结果是center word
今天没时间了,以后再把这部分内容补上。