一、前言
在今年的很多论文中,出现频率最高的几种语言模型是:word2vec、glove、ELMo、BERT,所以对glove进行了一下学习,glove是斯坦福大学做出来的,其实现和word2vec一样也是c语言,而且包括四个c文件:
- vocab_count.c
- 计算词频
- cooccur.c
- 计算共现矩阵
- shuffle.c
- 对上一步计算的共现矩阵的行进行乱序处理
- glove.c
- 在共现矩阵基础上计算词向量
我在重写的过程中删掉了所有工程性的代码(包括多线程训练等)。本文不探讨glove的原理,网上已经有很多文章了,我只说我个人对glove的感受。
github地址:https://github.com/wangyaomail/word2vec-demo/blob/master/src/main/java/cn/dendarii/GloveDemo.java
二、与word2vec相比
glove全文都在和word2vec进行比较,glove的作者认为他们的模型比word2vec计算更快且效果更好。实际体验下,glove的计算速度和word2vec各有千秋,word2vec的训练过程每一轮都要遍历语料,因此语料越长计算越慢。glove的话最终的向量迭代过程和语料无关,只和共现矩阵有关,但共现矩阵的增长是和词的数量呈平方关系的,所以体验中词稍微多一些计算效率就会大幅下降,因此glove比较适合语料很长,但实际需要训练的词很少的场景。实际中发现glove的计算收敛速度并不快,而且损失函数的阈值和词的数量高度相关。
但这都不影响glove是个原理很简单的模型,从美观上说glove比word2vec要漂亮,设计很粗放,但管用。word2vec考虑的是如何训练语言模型的简化版P(Wi|Wj),word2vec整体思路是以分类参数和词向量之间的反馈式训练来得到精确的词向量。而glove则删掉了所有和分类相关的一切,只保留word2vec中的训练窗口机制。在word2vec中huffman树发挥了巨大的作用,但是在glove中统计词频在算法层面只起到一个截断长尾词的作用,换句话说没有对模型的训练结果起到直接的影响。在glove中有一个因素起到了重要的作用,就是这个词在窗口中距离被训练词的距离,这个值应该越大越好,一个思路是,如果我们把训练语料连城一个环,窗口朝两边取直到碰到一起为止是否效果会更好?
三、实验
参考glove的原始训练语料,模拟四种模式:
1 | { "a", "x", "b" }, |
这个是根据king-queen=man-woman这一特点进行构建的,在这样的模式中间随机插入不等的混淆字符xyz,组成一个只有7个单词的语言序列。按照“混淆字符串+训练字符串1+混淆字符串+训练字符串2+…”这样的模式生成范例如下:
1 | y y x z y a x b y y y y c x b y x x c x d z x x z a x d z y z z z y a x b x z y c x b x y z x c x d x x z y z y x x x a x d a x b y z y x z x x y c x b y z z x y y x y c x d z y z z y y a x d y x x x y a x b z c x b x z x x z z z y c x d a x d x z y z z y y a x b x x y x y x c x b x x x y c x d y z z x y z z a x d |
进行词频统计:
1 | x:53 |
按照词频统计的结果编号:
1 | x:0 |
计算共现矩阵:
使用glove训练,训练过程:
1 | 第[0]次训练,损失值为=8.05820615861041,损失率变动=-0.8759029019217295 |
得到词向量:
这里我只用2维的向量,因为可以直接画到图里:
具有很明显的ac聚集、db聚集的现象。
如果以欧式距离计算abcd四个点两两向量之间的距离,则有:
看这个矩阵主要看白色块,越白说明两个向量相似度越高。
四、实验过程中发现的问题
1、原模型加不加乱序对于训练速度具有非常大的影响,如果不乱序训练时间能慢到天上去。
2、abcd在原始语料中出现的频率对于结果也有非常大的影响,我们默认是1:1:1:1的比例,如果改动这个比例的话,比如改为3:3:2:2,则图像会变成:
可以看出c和d走的更远了。
3、严格来说,无论是word2vec还是glove都没有携带语义信息,都是频率派的