针对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。
首先贴一张词向量文件的样例图,有助于对源码的理解。其中,文件第一行为“ 单词个数 词向量维度”,后续为“词汇 词向量”。下图展示的词向量文件中共有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; }
相关文章推荐
- CYQ.IISLogViewer 一款IIS 日志分析工具 V1.0 发布[提供源码]
- sql server 2005日志文件过大问题解决后分析--针对发布订阅产生的日志问题
- Giraph源码分析(八)—— 统计每个SuperStep中参与计算的顶点数目
- STL源码分析--向量(vector)的实现
- mjpg_streamer源码的分析及针对图像处理算法的修改
- CYQ.IISLogViewer 一款IIS 日志分析工具 V1.0 发布[提供源码]
- Asp.net源码程序分析所感——印度,一个不可轻视的近邻!
- 根据源码分析python列表的插入、抛出操作时当条目索引为负数时的处理
- S3C44B0的向量和非向量中断和的详细源码分析
- Spark源码分析(1) 从WordCount示例看Spark延迟计算原理
- sql server 2005日志文件过大问题解决后分析--针对发布订阅产生的日志问题
- mapreduce源码分析作业提交、初始化、分配、计算过程之初始化篇
- opencv 仿射变换 计算旋转矩阵源码分析
- 性能最高的javascript 发布订阅系统(pub/sub)Arbiter.js 源码分析
- go-home源码分析----一款针对12306的火车票订票软件
- g723源码详细分析-8-计算冲激响应与振铃减法
- go-home(一款针对12306的火车票订票软件)源码分析
- rt-thread装载共享目标文件的过程源码分析
- rstplib源码分析---快速生成树之优先级向量
- Redis源码分析(三十)--- pubsub发布订阅模式