我们首先过一遍源码word2phrase.c。这个源码中的很多内容和word2vec.c中的源码是共用的,可以之后方便我们理解word2vec.c。这个代码的的思想很简单,就是希望找出语料中的短语。比如New York这个短语,如果我们把它当做两个单词分开处理显然不合适。这里从语料中找短语的基本思想和和互信息的思想很相似,需要考虑两方面的信息,第一个方面是两个单词,比如New和York,在这个语料中共同出现的次数。如果两个单词基本就没有在语料中以New York这样的形式出现过,那么显然New York就不是常用短语了。另外还需要考虑两个单词在语料中单独出现的次数。像of the在语料中肯定大量的出现,但是他们并不是短语。这两个单词本身单独就会在语料中大量的出现。这也导致他们碰巧一块出现的次数也会非常的多。
单词用结构体表示,记录了单词以及其在语料中出现的次数
struct vocab_word {
long long cn;
char *word;
};
一些全局变量,有的后面会说它们的含义
char train_file[MAX_STRING], output_file[MAX_STRING];//输入文件名和输出文件名 这个代码的功能就是把输入的语料过滤一遍,输出把单词整合成phrase的语料struct vocab_word *vocab;//字典,保存所有单词以及他们在语料中出现的次数
int debug_mode = 2, min_count = 5, *vocab_hash, min_reduce = 1;
long long vocab_max_size = 10000, vocab_size = 0;
long long train_words = 0;//已经处理的单词或是叫token的数量
real threshold = 100;
首先从main函数看起
vocab = (struct vocab_word *)calloc(vocab_max_size, sizeof(struct vocab_word));//为字典分配空间
vocab_hash = (int *)calloc(vocab_hash_size, sizeof(int));//由于C语言中没有python中字典(dict)这样的数据结构,所以这里是自己实现的。vocab_hash记录了hash值和vocab_word字典中的位置的对应关系
TrainModel();//开始训练
TrainModel函数中的代码:
long long pa = 0, pb = 0, pab = 0, oov, i, li = -1, cn = 0;
char word[MAX_STRING], last_word[MAX_STRING], bigram_word[MAX_STRING * 2];
real score;
FILE *fo, *fin;
printf("Starting training using file %s\n", train_file);
LearnVocabFromTrainFile();
fin = fopen(train_file, "rb");
fo = fopen(output_file, "wb");
word[0] = 0;
上面这些代码声明了一些变量,打开了输入和输出文件。最重要的代码就是LearnVocabFromTrain();它的功能是建立字典一般做自然语言问题第一件事都是要过一遍语料建立字典。这部分代码和word2vec.c中是一模一样的。下面仔细讲解一下如何建立字典。
字典的数据结构有单词结构体数组vocab以及int数组vocab_hash。C语言中没有python中字典数据结构所以这里是自己实现的。当我们要找到一个单词,想知道字典中有没有这个单词以及它的id的时候,首先计算这个单词的哈希值。比如是15,vocab_hash[15]中就记录了这个单词在vocab中的位置。比如vocab[15]=100,那么我们就直接知道了这个单词的id是100,以及vocab[100]中就存了单词的以及它的频数。如果vocab[15]=-1那么这个单词不在字典中,我们会进行插入等操作。还有可能几个单词有一样的哈希值,后面介绍代码的时候会介绍如何处理这种情况。
ReadWord函数从文件中读取一个单词。一个一个字符的读,遇到空格,\t,\n,或是文件结束就代表单词已经读完了,word[a] = 0;C语言中字符串最后一位为0,表示字符串结束
void ReadWord(char *word, FILE *fin) {
int a = 0, ch;
while (!feof(fin)) {
ch = fgetc(fin);
if (ch == 13) continue;
if ((ch == ' ') || (ch == '\t') || (ch == '\n')) {
if (a > 0) {