0. word2vec地址
1. word2vec算法原理
本模块主要介绍word2vec的算法原理。
word2vec用到了两个重要的模型:CBOW模型和Skip-gram模型,两个模型的结构都包含输入层、投影层和输出层。CBOW模型是根据上下文来预测中心词,Skip-gram模型则是根据中心词来预测上下文。
这里暂时不再过多的介绍word2vec原理细节了,网上的博客已经写的非常全面了,这里列出我在学习过程中,阅读的比较深入的几篇博客。
参考文献:
- word2vec 中的数学原理详解(作者:peghoty,网上有pdf版)
- word2vec是如何得到词向量的?(知乎的一篇回答)
- 全面理解word2vec(主要看CBOW/skip-gram的两种结构)
- Word2vec数学原理全家桶(主要看权重矩阵更新部分)
- 推荐系统的中 EMBEDDING 的应用实践(不只是word2vec,还包含了推荐系统的一些其他知识)
- 另外一个pdf文件:《Deep Learning实战之word2vec》
- 机器学习算法实现解析——word2vec源码解析
- word2vec原理(二) 基于Hierarchical Softmax的模型(刘建平)
- word2vec原理(三) 基于Negative Sampling的模型(刘建平)
2. 源码解析(一)
2.1 main函数流程
- 输入参数与程序变量匹配
- 预计算sigmod,保存到expTable[]中
- 模型训练
2.2 模型训练流程
图1【转】:
图2【转】:
2.3 预处理
预处理部分,首先完成的是参数的初始化,包括一些训练传入参数和静态参数等。同时也对训练中使用的一些指针进行了声明。
预处理部分的另外一个重要操作的提前将sigmoid的值计算出来并存储到一个数组expTable中,使用时直接在数组中查找即可。word2vec计算了[-6,6]区间的sigmoid的值,将MAX_EXP定义为6是因为sigmoid函数在区间[-6,6]以外的函数值基本不变。
// 申请EXP_TABLE_SIZE+1个空间,数组expTable存储预处理阶段计算好的sigmoid值
expTable = (real *) malloc((EXP_TABLE_SIZE + 1) * sizeof(real));
for (i = 0; i < EXP_TABLE_SIZE; i++) {
expTable[i] = exp((i / (real) EXP_TABLE_SIZE * 2 - 1) * MAX_EXP); // Precompute the exp() table
expTable[i] = expTable[i] / (expTable[i] + 1); // Precompute f(x) = x / (x + 1)
}
2.4 构建词库
word2vec给出了两种构建词库的方法,从词汇表文件中加载ReadVocab()或者从训练文件中加载LearnVocabFromTrainFile()。
ReadVocab()函数是从已有词汇表文件中直接读词并构建词表和hash表,由于词汇表中的词语不存在重复,因此与LearnVocabFromTrainFile相比没有做重复词汇的检测。LearnVocabFromTrainFile()函数是从训练文件中获取所有词并构建词表和hash表。
在处理词的过程中,保留了两个数组:存储词的vocab和存储词的hash的vocab_hash。在vocab_hash中存储的是词在词汇表中的Index。在处理词的hash的过程中使用到了线性探测的开放定址法处理冲突。
ReadVocab()流程:
- ReadWord(word, fin):从文件中逐个字符读取单个单词到word,输入文件以空格' ',tab'\t',EOL'\n'为词的分界符;
- AddWordToVocab(word):strcpy(vocab[vocab_size].word, word) -> GetWordHash(word);
- SortVocab():根据词频排序,按照词频对词表中的项从大到小排序,把出现数量少的word排在vocab数组的后面,并删除一些词频小于阈值的词。
LearnVocabFromTrainFile()流程:
- AddWordToVocab((char *)"</s>"):将</s>添加到vocab的第一个位置;
- ReadWord(word, fin):从文件中逐个字符读取单个单词到word,输入文件以空格' ',tab'\t',EOL'\n'为词的分界符;
- SearchVocab(word):GetWordHash(word) -> 根据hash返回一个词在词表中的位置,若不存在则返回-1;
- 词汇表中不存在这个词,进行AddWordToVocab(word),词汇表中存在这个词,进行词频累加;
- ReduceVocab():从词表中删除出现次数小于min_reduce的词;
- SortVocab():根据词频排序,按照词频对词表中的项从大到小排序,把出现数量少的word排在vocab数组的后面,并删除一些词频小于阈值的词。
// 词的结构体
struct vocab_word {
long long cn; // 词频,从训练集中计数得到或直接提供词频文件
int *point; // huffman树中从根节点到该词的路径,存放的是路径上每个节点的索引
char *word, *code, codelen; // word