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

word2vec词向量学习笔记

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

个人微博黄锦池-hjimce

一、使用原版word2vec工具训练

1、英文编译测试

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

[python]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. git clone https://github.com/hjimce/word2vec.git  

(2)编译:make

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

(4)输入命令train起来:

[python]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. 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)测试距离功能:

[python]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. ./distance vectors.bin  

 2、中文训练测试

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

(2)输入命令train起来: 

[python]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. 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)启动相似度距离测试:

[python]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. ./distance vectors.bin  

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

[python]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. hjimce@hjimcepc:~/workspace/word2vec$ ./distance vectors.bin  
  2. Enter word or sentence (EXIT to break): 中国  
  3.   
  4. Word: 中国  Position in vocabulary: 35  
  5.   
  6.                                               Word       Cosine distance  
  7. ------------------------------------------------------------------------  
  8.                                       中国人民      0.502711  
  9.                                             美国      0.480650  
  10.                                       中国政府      0.463177  
  11.                                             我国      0.447327  
  12.                                       亚太地区      0.444878  
  13.                                          加勒比        0.418471  
  14.                                             两国      0.408678  
  15.                                             各国      0.392190  
  16.                                       独立自主      0.391517  
  17.                                             世界      0.387604  
  18.                                             国际      0.382578  
  19.                                             中美      0.382208  
  20.                                             欧美      0.379807  
  21.                                             古巴      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,就是除了正样本标签之外,还需要随机抽取出词典中的其它单词作为负样本(以前是把整个词典的其它单词都当成负样本),这个还是具体看源码实现吧。

三、源码阅读阶段


[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. word = sen[sentence_position];//当前单词  
  2. if (word == -1) continue;  
  3. for (c = 0; c < layer1_size; c++) neu1[c] = 0;//隐藏层神经元激活值初始化为0  
  4. for (c = 0; c < layer1_size; c++) neu1e[c] = 0;//隐藏层反向求导的误差值  
  5. next_random = next_random * (unsigned long long)25214903917 + 11;  
  6. b = next_random % window;//b是一个介于0~window size之间的随机数值  
  7. //cbow算法,网络第一层:直接把左右窗口内的单词相加起来  
  8. //sentence_position是当前单词  
  9. if (cbow) {  
  10.   cw = 0;  
  11.   //算法第一步:除了当前单词之外,把上下文窗口单词的词向量相加起来  
  12.   for (a = b; a < window * 2 + 1 - b; a++)  
  13.     if (a != window)  
  14.     {  
  15.       c = sentence_position - window + a;//遍历sentence_position窗口内的上下文单词  
  16.       if (c < 0) continue;//边界越界处理,因为一个文档的长度是0~sentence_length  
  17.       if (c >= sentence_length) continue;  
  18.       last_word = sen[c];  
  19.       if (last_word == -1) continue;  
  20.       for (c = 0; c < layer1_size; c++)//把左右窗口内的单词的词向量全部相加,其中layer1_size表示词向量的维度  
  21.       {  
  22.         neu1[c] += syn0[c + last_word * layer1_size];//syn0是词向量表  
  23.       }  
  24.   
  25.       cw++;  
  26.     }  
  27.   
  28.   if (cw)  
  29.   {  
  30.   //算法第二步:平均,把上面左右单词的词向量加起来后再平均  
  31.     for (c = 0; c < layer1_size; c++)  
  32.     {  
  33.       neu1[c] /= cw;  
  34.     }  
  35.   
  36.     if (hs)  
  37.       //vocab[word].codelen表示当前单词的霍夫曼编码长度,下面也就遍历当前词的路径节点  
  38.       for (d = 0; d < vocab[word].codelen; d++)  
  39.       {  
  40.   //算法第三步:启用了层次sorfmax模式,计算每个节点的概率  
  41.         //f是二叉树节点的输出值,下面是计算f,f是每个节点的逻辑回归概率值  
  42.         f = 0;  
  43.         l2 = vocab[word].point[d] * layer1_size;  
  44.         for (c = 0; c < layer1_size; c++) f += neu1[c] * syn1[c + l2];//syn1是从隐藏层到二叉树节点连接参数矩阵(可以把二叉树节点看成是神经元)  
  45.         //节点的label被定义为:1-code,而不是code,这个可能是为了方便计算吧  
  46.         // 那么节点损失函数为-[(1-code)*log(f)+code*log(1-f)]  
  47.         if (f <= -MAX_EXP) continue;  
  48.         else if (f >= MAX_EXP) continue;  
  49.         else f = expTable[(int)((f + MAX_EXP) * (EXP_TABLE_SIZE / MAX_EXP / 2))];  
  50.   //算法第四步:反向求导  
  51.         // 'g' is the gradient multiplied by the learning rate  
  52.         g = (1 - vocab[word].code[d] - f) * alpha;//梯度×学习率  
  53.         // Propagate errors output -> hidden  
  54.         for (c = 0; c < layer1_size; c++) neu1e[c] += g * syn1[c + l2];  
  55.         // Learn weights hidden -> output  
  56.         for (c = 0; c < layer1_size; c++) syn1[c + l2] += g * neu1[c];  
  57.       }  
  58.   //如果采用了negative sample  
  59.     if (negative > 0)  
  60.       for (d = 0; d < negative + 1; d++)  
  61.       {  
  62.         //正样本  
  63.         if (d == 0)  
  64.           {  
  65.             target = word;  
  66.             label = 1;  
  67.           }  
  68.         //随机采样出负样本  
  69.         else  
  70.         {  
  71.           next_random = next_random * (unsigned long long)25214903917 + 11;  
  72.           target = table[(next_random >> 16) % table_size];  
  73.           if (target == 0) target = next_random % (vocab_size - 1) + 1;  
  74.           if (target == word) continue;  
  75.           label = 0;  
  76.         }  
  77.         l2 = target * layer1_size;  
  78.         f = 0;  
  79.         for (c = 0; c < layer1_size; c++) f += neu1[c] * syn1neg[c + l2];//同样计算输出单词,逻辑回归概率值  
  80.         //似然函数为-[label*log(f)+(1-label)*log(1-f)]  
  81.         //更新链接到当前类别(单词)节点的权值向量  
  82.         if (f > MAX_EXP) g = (label - 1) * alpha;  
  83.         else if (f < -MAX_EXP) g = (label - 0) * alpha;  
  84.         else g = (label - expTable[(int)((f + MAX_EXP) * (EXP_TABLE_SIZE / MAX_EXP / 2))]) * alpha;  
  85.         for (c = 0; c < layer1_size; c++) neu1e[c] += g * syn1neg[c + l2];  
  86.         for (c = 0; c < layer1_size; c++) syn1neg[c + l2] += g * neu1[c];  
  87.     }  
  88.     //从隐藏层到词向量层反向传播,更新词向量层  
  89.     for (a = b; a < window * 2 + 1 - b; a++) if (a != window)  
  90.       {  
  91.       c = sentence_position - window + a;  
  92.       if (c < 0) continue;  
  93.       if (c >= sentence_length) continue;  
  94.       last_word = sen[c];  
  95.       if (last_word == -1) continue;  
  96.       for (c = 0; c < layer1_size; c++) syn0[c + last_word * layer1_size] += neu1e[c];  
  97.       }  
  98.   }  
  99. }  

参考文献:

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/

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值