您的位置:首页 > 其它

针对Minkolov发布的“根据词向量计算目标单词的N近邻词汇”源码的分析

2017-03-16 16:48 363 查看
最近在研究Minkolov开发的RNNLM toolkit,其主页(http://www.fit.vutbr.cz/~imikolov/rnnlm/)上挂出来一个“根据生成好的词向量文件来计算与用户输入的目标单词最近邻的top-N个单词”的小程序(http://www.fit.vutbr.cz/~imikolov/rnnlm/distance.c),读了一下并对代码做了些注释,虽然比较简单,但还是分享出来,供大家交流~~ 水平有限,难免错误和疏漏,还请各位老师多多批评指教,谢谢!

首先贴一张词向量文件的样例图,有助于对源码的理解。其中,文件第一行为“ 单词个数 词向量维度”,后续为“词汇 词向量”。下图展示的词向量文件中共有82390个单词,每个单词的词向量维度为80。



#include <stdio.h>
#include <string.h>
#include <math.h>
#include <sys/malloc.h> //原始版本是#include<malloc.h>,由于博主本人OS X系统,修改后gcc才能编译通过

const int max_size=2000;  //最大字符串长度
const int N=40; //选取N个距离最接近的单词
const int max_w=50;  //最大单词长度

int main(int argc, char **argv)
{
FILE *f;
char st1[max_size], bestw
[max_size], file_name[max_size];
float dist, len, bestd
;
int words, size, a, b, c, d;
float *M;
char *vocab;

if (argc<2) {
printf("Usage: ./dist <FILE>\nwhere FILE contains word projections\n"); //打印输出:“用法:./dist <FILE> \n FILE为存储词向量的文件的名称"
return 0;
}

strcpy(file_name, argv[1]); //将词向量文件名赋值给filename变量

f=fopen(file_name, "rb"); //只读打开一个二进制文件,只允许读数据
if (f==NULL) {printf("Input file not found\n"); return -1;} //w文件读取失败,退出程序
fscanf(f, "%d", &words); //读入词向量文件中包含的单词个数
fscanf(f, "%d", &size); //读入词向量文件中每个词向量的维度

vocab=(char *)malloc(words*max_w*sizeof(char)); //申请用于存储词汇表的动态内存空间
M=(float *)malloc(words*size*sizeof(float)); //申请用于存储词向量的动态内存空间(M先用于存储词向量,而后更新为每个词向量的单位向量)
if (M==NULL) { //空间申请失败,退出程序
printf("Cannot allocate memory: %d MB\n", words*size*sizeof(float)/1048576);
return -1;
}

for (b=0; b<words; b++) { //
fscanf(f, "%s", &vocab[b*max_w]); //从词向量文件中读入一个单词,写到当前词汇的末尾
for (a=0; a<size; a++) fscanf(f, "%f", &M[a+b*size]); //从词向量文件中读入当前单词所对应的词向量,写到当前M的末尾
len=0;
for (a=0; a<size; a++) len+=M[a+b*size]*M[a+b*size]; //计算当前单词的词向量的模的平方(向量中每个元素的平方和)
len=sqrt(len); //计算当前单词的词向量的模
for (a=0; a<size; a++) M[a+b*size]/=len;  //计算每个词向量的单位向量,存于M
}

for (a=0; a<words*max_w; a++) vocab[a]=toupper(vocab[a]); //将词汇表中的每个字符都转换为大写英文字母
fclose(f);  //关闭词向量文件

while (1) {
for (a=0; a<N; a++) bestd[a]=0; //初始化存储最近距离的数组bestd
for (a=0; a<N; a++) bestw[a][0]=0; //初始化存储距离最近单词的数组bestw

printf("Enter word (EXIT to terminate): "); //输出提示:输入待查询的目标单词,输入EXIT退出
scanf("%s", st1); //读取用户的输入
for (a=0; a<strlen(st1); a++) st1[a]=toupper(st1[a]); //将用户输入的单词转化为大写字母的形式
if (!strcmp(st1, "EXIT")) break;  //如果用户输入为EXIT,则退出while循环

for (b=0; b<words; b++) if (!strcmp(&vocab[b*max_w], st1)) break; //遍历词汇表,找到与输入词汇相匹配的单词时跳出for循环;或者直到循环结束

if (b==words) //如果前一个循环退出时,计数器数值大于词汇表中的单词数目,则表示没有找到用户输入的单词
printf("Word was not found in dictionary\n");
else { //在词汇表中找到了用户输入的单词,且位于词汇表的第b位
for (c=0; c<words; c++) { //针对词汇表中的每个单词进行查找
dist=0; //初始化距离为0
for (a=0; a<size; a++) dist+=M[a+b*size]*M[a+c*size]; //计算位于词汇表第b位的目标词与当前遍历到的词汇表中的第c个单词的单位词向量之间的点积,存于dist

//针对现有的最近邻单词表进行考量(bestd:存储top-N个最近距离,按距离从小到大进行维护;bestw:存储top-N个最近邻单词)
for (a=0; a<N; a++) {
if (dist>bestd[a]) { //找到当前dist合适的位置a,并将最近邻表中的后续元素依次后移一位(从后往前处理)
for (d=N-1; d>a; d--) {
bestd[d]=bestd[d-1];
strcpy(bestw[d], bestw[d-1]);
}
bestd[a]=dist; //下两句将找到的近邻词(词汇表中的第c个)相关信息赋给最近距离表及最近邻单词表
strcpy(bestw[a], &vocab[c*max_w]);
break;
}
}
}

printf("Closest words:\n"); //输出“最接近的单词为:”
for (a=0; a<N; a++) printf("%20s\t\t%f\n", bestw[a], bestd[a]); //依次将最接近的40个单词按照:“单词  距离”的格式输出
}
}

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息