深度学习(四十二)word2vec词向量学习笔记

4374人阅读 评论(0) 收藏 举报
分类:

word2vec词向量学习笔记

原文地址http://blog.csdn.net/hjimce/article/details/51564783

个人微博黄锦池-hjimce

一、使用原版word2vec工具训练

1、英文编译测试

(1)到官网到下载:https://code.google.com/archive/p/word2vec/,然后选择export 到github,也可以直接到我的github克隆:

 git clone https://github.com/hjimce/word2vec.git

(2)编译:make

(3)下载测试数据http://mattmahoney.net/dc/text8.zip,并解压

(4)输入命令train起来:

time ./word2vec -train text8 -output vectors.bin -cbow 1 -size 200 -window 8 -negative 25 -hs 0 -sample 1e-4 -threads 20 -binary 1 -iter 15

(5)测试距离功能:

./distance vectors.bin

 2、中文训练测试

(1)中文词向量:下载数据msr_training.utf8,这个数据已经做好分词工作,如果想要直接使用自己的数据,就需要先做好分词工作

(2)输入命令train起来: 

time ./word2vec -train msr_training.utf8 -output vectors.bin -cbow 1 -size 200 -window 8 -negative 25 -hs 0 -sample 1e-4 -threads 20 -binary 1 -iter 15

(3)启动相似度距离测试:

./distance vectors.bin

(4)输入相关中文词:中国,查看结果:

hjimce@hjimcepc:~/workspace/word2vec$ ./distance vectors.bin
Enter word or sentence (EXIT to break): 中国

Word: 中国  Position in vocabulary: 35

                                              Word       Cosine distance
------------------------------------------------------------------------
                                      中国人民		0.502711
                                            美国		0.480650
                                      中国政府		0.463177
                                            我国		0.447327
                                      亚太地区		0.444878
                                         加勒比		0.418471
                                            两国		0.408678
                                            各国		0.392190
                                      独立自主		0.391517
                                            世界		0.387604
                                            国际		0.382578
                                            中美		0.382208
                                            欧美		0.379807
                                            古巴		0.378412

二、算法学习阶段

因为这个算法是半年前所学的算法,最近只是简单复习一下,所以不打算写详细的算法流程笔记,原理等也不打算啰嗦。word2vec网络结构可以分成两种:CBOW、Skip-Gram,其实网络结构都非常简单,不过是一个三层神经网络罢了。本文只讲解CBOW网络结构算法、算法流程。

CBOW又有两种方案,一种叫层次softmax,另一种叫:negative sample。这两种方法如果看不懂也没关系,你完全可以用原始的softmax替代网络的最后一层,进行训练,只是训练速度比较慢。

1、CBOW+层次softmax算法总体流程

先讲解层次softmax的算法实现过程:

(1)根据训练语料库,创建词典,并对词典每个单词进行二叉树霍夫曼编码。

如下图所示,比如经过编码后,可能汉语词典中的“自”就被编码成了:110,“我”对应的编码就是:001。这个算法与word2vec的实现过程关系不大,具体霍夫曼编码过程代码怎么写,不懂也没关系。我们只需要记住,字典中的每个单词都会被编码,每个单词对应二叉树的叶节点,每个单词的编码结果对应于:从跟节点到达当前单词,所经过的节点路径。编码中的1、0表示右节点、左节点。


这边需要知道的是每一位编码的用处,因为每位的编码节点刚好可以对应到0、1输出标签,而且这颗二叉树节点就是神经网络的输出层神经元,具体可以看下面的图。假如在训练的时候,最后一层训练数据的输出是“自”,那么其实我们只需要训练节点root、node1、node3使得这三个节点的激活值分别为:1、1、0,这样就可以了。

因此对于层次softmax来说,神经网络的隐藏层其实是连接到二叉树的每个非叶子节点上(如果是原始的sotfmax,是直接连接到叶子节点上),然后对这些非叶子节点,根据输出单词的编码路径,对路径上的每个节点,根据对应的编码进行训练。

(2)根据定义窗口大小,截取出一个窗口内的单词作为一个样本,进行训练

这一步在word2vec里面,其实我们给出的参数值是一个max window size,然后word2vec底层生成一个随机大小的窗口,只要满足其范围在max window size 即可,这个可能是为了数据扩充吧。

(3)输入层-》隐藏层:对窗口内的左右单词(不包含自己)对应的词向量,进行累加,并求取平均向量Vavg。

(4)隐藏层-》非叶子节点:每个二叉树非叶子节点,链接到Vavg都有一个可学习的参数W,然后通过sigmoid可以得到每个非叶子节点的激活值(也表示概率值):

也就是说网络的参数除了词向量之外,还有隐藏层链接到二叉树结点的参数向量(从word2vec代码上看,我没有看到偏置参数b)。

(5)反向求导:根据输出文字的节点路径,更新路径上的每个非叶子节点链接到隐藏层的参数值w;并更新窗口内各个单词的词向量。

具体网络结构图如下所示:


采用层次softmax的优点在于加快训练速度,参数个数、预测速度没啥差别。

2、CBOW+Negative Sample

这个比较简单,所谓的Negative Sample,就是除了正样本标签之外,还需要随机抽取出词典中的其它单词作为负样本(以前是把整个词典的其它单词都当成负样本),这个还是具体看源码实现吧。

三、源码阅读阶段


    word = sen[sentence_position];//当前单词
    if (word == -1) continue;
    for (c = 0; c < layer1_size; c++) neu1[c] = 0;//隐藏层神经元激活值初始化为0
    for (c = 0; c < layer1_size; c++) neu1e[c] = 0;//隐藏层反向求导的误差值
    next_random = next_random * (unsigned long long)25214903917 + 11;
    b = next_random % window;//b是一个介于0~window size之间的随机数值
    //cbow算法,网络第一层:直接把左右窗口内的单词相加起来
    //sentence_position是当前单词
    if (cbow) {
      cw = 0;
      //算法第一步:除了当前单词之外,把上下文窗口单词的词向量相加起来
      for (a = b; a < window * 2 + 1 - b; a++)
        if (a != window)
        {
          c = sentence_position - window + a;//遍历sentence_position窗口内的上下文单词
          if (c < 0) continue;//边界越界处理,因为一个文档的长度是0~sentence_length
          if (c >= sentence_length) continue;
          last_word = sen[c];
          if (last_word == -1) continue;
          for (c = 0; c < layer1_size; c++)//把左右窗口内的单词的词向量全部相加,其中layer1_size表示词向量的维度
          {
            neu1[c] += syn0[c + last_word * layer1_size];//syn0是词向量表
          }

          cw++;
        }

      if (cw)
      {
      //算法第二步:平均,把上面左右单词的词向量加起来后再平均
        for (c = 0; c < layer1_size; c++)
        {
          neu1[c] /= cw;
        }

        if (hs)
          //vocab[word].codelen表示当前单词的霍夫曼编码长度,下面也就遍历当前词的路径节点
          for (d = 0; d < vocab[word].codelen; d++)
          {
      //算法第三步:启用了层次sorfmax模式,计算每个节点的概率
            //f是二叉树节点的输出值,下面是计算f,f是每个节点的逻辑回归概率值
            f = 0;
            l2 = vocab[word].point[d] * layer1_size;
            for (c = 0; c < layer1_size; c++) f += neu1[c] * syn1[c + l2];//syn1是从隐藏层到二叉树节点连接参数矩阵(可以把二叉树节点看成是神经元)
            //节点的label被定义为:1-code,而不是code,这个可能是为了方便计算吧
            // 那么节点损失函数为-[(1-code)*log(f)+code*log(1-f)]
            if (f <= -MAX_EXP) continue;
            else if (f >= MAX_EXP) continue;
            else f = expTable[(int)((f + MAX_EXP) * (EXP_TABLE_SIZE / MAX_EXP / 2))];
      //算法第四步:反向求导
            // 'g' is the gradient multiplied by the learning rate
            g = (1 - vocab[word].code[d] - f) * alpha;//梯度×学习率
            // Propagate errors output -> hidden
            for (c = 0; c < layer1_size; c++) neu1e[c] += g * syn1[c + l2];
            // Learn weights hidden -> output
            for (c = 0; c < layer1_size; c++) syn1[c + l2] += g * neu1[c];
          }
      //如果采用了negative sample
        if (negative > 0)
          for (d = 0; d < negative + 1; d++)
          {
            //正样本
            if (d == 0)
              {
                target = word;
                label = 1;
              }
            //随机采样出负样本
            else
            {
              next_random = next_random * (unsigned long long)25214903917 + 11;
              target = table[(next_random >> 16) % table_size];
              if (target == 0) target = next_random % (vocab_size - 1) + 1;
              if (target == word) continue;
              label = 0;
            }
            l2 = target * layer1_size;
            f = 0;
            for (c = 0; c < layer1_size; c++) f += neu1[c] * syn1neg[c + l2];//同样计算输出单词,逻辑回归概率值
            //似然函数为-[label*log(f)+(1-label)*log(1-f)]
            //更新链接到当前类别(单词)节点的权值向量
            if (f > MAX_EXP) g = (label - 1) * alpha;
            else if (f < -MAX_EXP) g = (label - 0) * alpha;
            else g = (label - expTable[(int)((f + MAX_EXP) * (EXP_TABLE_SIZE / MAX_EXP / 2))]) * alpha;
            for (c = 0; c < layer1_size; c++) neu1e[c] += g * syn1neg[c + l2];
            for (c = 0; c < layer1_size; c++) syn1neg[c + l2] += g * neu1[c];
        }
        //从隐藏层到词向量层反向传播,更新词向量层
        for (a = b; a < window * 2 + 1 - b; a++) if (a != window)
          {
          c = sentence_position - window + a;
          if (c < 0) continue;
          if (c >= sentence_length) continue;
          last_word = sen[c];
          if (last_word == -1) continue;
          for (c = 0; c < layer1_size; c++) syn0[c + last_word * layer1_size] += neu1e[c];
          }
      }
    }

参考文献:

1、《Efficient Estimation of Word Representations in Vector Space

2、《word2vec Explained: Deriving Mikolov et al.'s Negative-Sampling Word-Embedding Method

3、https://code.google.com/archive/p/word2vec/

 

 

0
1

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:871093次
    • 积分:8769
    • 等级:
    • 排名:第2333名
    • 原创:166篇
    • 转载:0篇
    • 译文:1篇
    • 评论:437条
    个人简介
    声明:博文的编写,主要参考网上资料,并结合个人见解,仅供学习、交流使用,如有侵权,请联系博主删除,原创文章转载请注明出处。博主qq:1393852684,知乎:https://www.zhihu.com/people/huang-jin-chi-28/activities
    博客专栏
    最新评论