one_hot
one_hot模型,将每个单词映射到一个V维的向量中,此种V代表词汇的数量,V[i]=1表示当前单词,0表示不是当前单词。如:
我
爱
中
国
[
1
0
0
0
1
0
0
0
1
]
\begin{matrix} 我\\ 爱\\ 中国 \end{matrix} \begin{bmatrix} 1 & 0 & 0\\ 0 & 1& 0\\ 0 & 0 & 1 \end{bmatrix}
我爱中国⎣⎡100010001⎦⎤
one_hot编码的问题是:
1)每个向量组之间的内积为0,既正交,意味着词与词之间没有任何关系。
2)每个词向量的维度与词库大小相关,当词库变得特别大的时候会造成维度灾难。
word2vec
word2vec来源于两篇论文cbow和skim-gram,随后又有论文对这两个模型进行讲解,最后Google在2013年开源了word2vec工具包。
word2vec主要包含两个模型,CBOW和skim-gram。CBOW主要是根据当前词的上下文词,既context来推断当前词.而skim-gram主要是根据context上下文的词
单个词到单个词的训练
单个词到单个词的训练是CBOW和skim-gram模型的基础。
假定我们有这样一句话"我喜欢观看巴西足球世界杯",经过分词,我们得到,[‘我’,‘喜欢’,‘观看’,‘巴西’,‘足球’,‘世界杯’],由于我们这个模型是训练一个词到一个词的模型,所以,我们对单词进行两两分组,得到[[‘我’,‘喜欢’],[‘喜欢’,‘观看’],[‘观看’,‘巴西’],[‘巴西’,‘足球’],[‘足球’,‘世界杯’]]。在最原始的word2vec模型中,我们将分组好的词汇分别输入到下面的模型中,进行训练。
- 输入层
在第一轮训练中,我们将[‘我’,‘喜欢’]这两个词输入到这个模型中,在这个模型中,输入为一个one-hot向量,当我们输入’我’这个词的时候,'我’这个词所对应的节点为1,其余的为0,输入层的节点个数即为词汇数量V,我们这里的V即为6。 - 输入层->隐藏层
输入层到隐藏层是一个VH维的向量,其中H为隐藏层节点个数,一般情况下,我们会把VH的向量作为最终的词向量,我们把这个V*H的权值向量成为W,其实隐藏层大的节点即为输入节点所对应的词向量,为啥呢,因为其他词的输入都为0,只有’我’这个词的输入为1,所以只有’我’所对应的权值会参与计算,而其他的词都不会参与计算。 - 隐藏层->输出层
隐藏层到输出层是一个H*V的权值向量W’,其中输出层节点个数也是V,即我们根据计算,得到每一个输出节点的权值。 - 更新参数W和W’
接下来我们在每一轮输入的时候都需要更新权值W和W’,我们用到的方法就是构建损失函数,用梯度下降方法进行更新。在这里,先用大白话解释下损失函数,在第一轮训练,即训练集[‘我’,‘喜欢’]输出的时候,寄希望于’喜欢’那个输出节点值最大,而其他的值都最小,有了解softmax的小伙伴就知道了,可以在输出层做一次softmax,构造相应的损失函数,以最大化’喜欢’的那个词所对应的输出节点。
CBOW模型
CBOW模型就是在输入的时候没有输入一个单词,而是输入多个单词,而这多个单词就是当前单词的附近的单词,假设我们定义一个距离为1,我们要把当前单词前后距离为1的单词集合作为其context单词集合。还拿"我喜欢观看巴西足球世界杯"举例,我们最终构造的训练数据集如下:
将词所在的上下文作为输入,那个词本身作为输出
skim-gram模型
再来看Skip-Gram,它的做法和CBOW相反,将一个词所在的上下文中的词作为输出,而词本身作为输入。
其他
window_size:窗口大小。举一个例子,还是上面那句话:" the dog bark at the mailman"。假设window_size取1时,利用CBOW模型可以得到:
([the,bark],dog)
([dog,at],bark)
([bark,the],at)
([at,mailman],the)
一共4组数据。同样的,假设window_size还是1,利用Skip-Gram模型可以得到:
(dog,[the,bark])
(bark,[dog,at])
(at,[bark,the])
(the,[at,mailman])
对于每组数据会稍微做一下处理。比如对于第一组 (dog,[the,bark])一般处理成
(dog,the),(dog,bark)。
类似有:
(bark,dog),(bark,at),(at,bark),(at,the),(the,at),(the,mailman)共8组数据。
过程
下面就可以直接介绍如何进行w2v,上面提到w2v可以看作是一个将高维空间映射到低维空间的过程,用一个单层神经网络就可以实现这种功能(和自编码器有点类似其实)。对于CBOW,假如还是上面的句子" the dog bark at the mailman",训练数据是([the,brak],dog)那么网络的输入输出如下图所示:
这里有几个细节:
(1).上面介绍CBOW模型的时有一个模型的结构图,其中的SUM意思其实就是把各个上下文的词one-hot后的向量相加。比如对于the的向量是[1,0,0,0,0]。bark向量是[0,0,1,0,0],SUM之后就是[1,0,1,0,0]这就是网络的输入。输出就是[0,1,0,0,0],对应-dog
(2).我们所谓的embedding vetcor其实就第二个红框里的线,每一根线其实就是一个权值。
(3).第二个框里的红线其实就是dog这个单词的embedding结果(由5维变成3维)
(4).这个单层NN训练完毕之后有用的部分就是embedding martrix这部分,其大小为 输入个数(词汇表长度)×embedding后的维度。
类似对于Skip-Gram模型有:
当取训练数据(dog,bark)时,网络结构如上图,对比上面的CBOW最大的不同其实就是在于输入数据。
另外的举例说明
1.构造训练数据
确认输入
X
,
Y
X,Y
X,Y,本质就是单词对,(the,quick)就是一个单词对,the就是样本数据,quick就是该条样本的标签。
那么,如何从上面那句话中生成单词对数据呢?答案就是n-gram方法。多说不如看图:
我们以词为单位扫描这句话,每扫描到一个词,都把该词左右各两个词共4个词拿出来,分别与被扫描的单词组成单词对,作为我们的训练数据。
这里有两个细节,一个就是取被扫描单词左右各2个词,这里的2被称为窗口尺寸,是可以调整的,用多大的窗口生成的单词对来训练最好,需要具体问题具体分析。一般来说,取5是很好的经验值。也就是左右各取5个单词,共10个单词。这里我们用2只是为了方便说明问题。
这里我们需要停下来细细琢磨下,我们这样取单词对作为训练数据的目的何在?以(fox,jumps)为例,jumps可以理解为fox的上下文,我们将fox输入神经网络时,希望网络能够告诉我们,在语料库的8个单词中,jumps是更可能出现在fox周围的。
你可能会想,(fox,brown)也是一个单词对,它输入神经网络后,岂不是希望神经网络告诉我们,在8个单词中,brown是更可能出现在fox周围?如果是这样,那么训练完成后的神经网络,输入fox,它的输出会是brown和jumps的哪一个呢?
答案是取决于(fox,brown)和(fox,jumps)两个单词对谁在训练集中出现的次数比较多,神经网络就会针对哪个单词对按照梯度下降进行更多的调整,从而就会倾向于预测谁将出现在fox周围。
2.数字化单词对:one-hot编码
3.定义网络结构
通过之前的叙述,我们已经基本知道神经网络应该是什么样子了,总结一下,可以确定如下情况:
神经网络的输入应该是8维的向量
神经网络只有一个隐藏层
神经网络的输出应该是一个8维向量,且各维的值相加为1
3.2 隐藏层
有多少个隐藏神经元词向量就是多少维。每一个隐藏的神经元接收的输入都是一个8维向量,假设我们的隐藏神经元有3个(仅仅是为了举例说明使用,实际中,google推荐的是300个,但具体多少合适,需要你自己进行试验,怎么效果好怎么来),如此以来,隐藏层的权重就可以用一个8行3列的矩阵来表示,这个8行3列的矩阵的第一行,就是三个隐藏神经元对应于输入向量第一维的权重,如下图所示:
络训练完成后,这个8行3列的矩阵的每一行就是一个单词的词向量!如下图所示:
so,训练完成后,我们只需要保存好隐藏层的权重矩阵即可,输出层此时已经完成历史使命,可以丢掉了。
那么怎么使用去掉了输出层的网络呢?
我们知道,网络的输入是one-hot编码的单词,它与隐藏层权重矩阵相乘实际上是取权重矩阵特定的行,如下图所示:
3.输出层
当我们从隐藏层获得一个单词的词向量后,就要经过输出层了。
输出层的神经元数量和语料库中的单词数量一样。每一个神经元可以认为对应一个单词的输出权重,词向量乘以该输出权重就得到一个数,该数字代表了输出神经元对应的单词出现在输入单词周围的可能性大小,通过对所有的输出层神经元的输出进行softmax操作,我们就把输出层的输出规整为一个概率分布了。如下图所示:
这里有一点需要注意,我们说输出的是该单词出现在输入单词周围的概率大小,这个“周围”包含单词的前面,也包含单词的后面。
参考:
https://www.cnblogs.com/stephen-goodboy/articles/12574738.html
https://zhuanlan.zhihu.com/p/55127218
https://www.jianshu.com/p/1405932293ea
https://www.jianshu.com/p/09b70253c840