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

最近在研究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[N][max_size], file_name[max_size];
    float dist, len, bestd[N];
    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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值