当当当,欢迎来学习word2vec skipgram,关于word2vec,网上介绍的例子一大堆,这里就简单说明下。
最开始进行tokenizer的时候,是使用onehot编码,缺点就是矩阵太大,另外太稀疏,而且词和词之前是不具备语义信息的。
你说什么叫语义?语义没有官方定义,可以简单理解成更符合人类认知的,我觉得就可以理解成语义。
而word2vec带来了稠密向量,并且词和词之间有了语义关联,可以用于计算词和词之间的空间距离,也可以叫做相似度。
实现word2vec的方式有两种,一个是Hierarchical Softmax(也是softmax),另外一种是negative sampling。
下面将分别介绍这两种方式的实现思路。
偷懒群众可以直接看:
啥叫Hierarchical Softmax,嘿嘿,这个我没看。总而言之也是softmax,不过应用场景主要在对大规模语料进行softmax的时候加速计算结果,比如softmax(100w个),那么他的优势就体现出来了。
如果感兴趣其实现原理的可以自行百度,我就葛优躺了。
|
|
其中corpus
长下面这个样子:
|
|
构建训练数据集的步骤如下:
|
|
看这段代码,其核心在于,取当前词的index,然后以当前词取左右大小为context_size
的词出来来作为他的中心词,然后在collate_fn那里就转换成当前词的id,和周围词的id作为其预测目标。
是不是瞬间明白他想干什么事儿了吧!!
没明白是吧,继续往下看。
|
|
这个结构是不是相当简单,从今天来看,这个结构很简单,另外pytorch已经帮忙做了很多,但是放到实现那年,不得不佩服这些研究者。
训练部分就很直白了,每次取batch_size=1024个样本,然后输入模型,获得log_probs=(1024, 31081)结果,其中31081是指vocab_size,然后交叉熵算loss。
到这是不是明白了Hierarchical Softmax而不是softmax的意义了吧。
当面对百万级的文本,就算是隐藏层是检索功能,其计算量也是相当大,而且还会造成冗余计算,这时候对高频词抽样以及负采样就应运而生了。
他的做法是对文档中出现的每个词计算其出现频率,然后以当前词周围context_size大小的作为postive sampling,和选中的词无关的并且以词出现频率高为negative sampling。分别计算当前词和positive以及negative的相关度,就是直接计算其点积,将其loss最低即完成训练。
|
|
其中get_unigram_distribution
是获取每个词的出现概率。每次记不住这个unigram,bigram,还有trigram。。。
|
|
这地方有两个地方需要注意的:
第一是获取positive sampling,也就是以当前词周围context_size大小的作为postive sampling,这个在__init__
函数中完成。
第二是获取negative sampling,这个是在collate_fn中完成,其中涉及到一个采样函数multinomial
,这个大家可以参考这篇文章。
|
|
这部分可以直接看源代码了,其中在计算负样本的分类(对数)似然地方,我改了下,更方便理解,其实本质没啥~
|
|
这部分属于个人猜测部分,不保证其准确性。
抛开所有的理论,单纯看代码思考一个问题,源码这里使用了两个embedding,分别是词嵌入
和上下文嵌入
,那我们能不能只用一个embedding呢?
那既然如此,我们需要修改下源码,有两个地方需要修改:
关于第一点,为什么要在负样本中去掉当前词?
不可能让当前词和当前词在计算相似度时出现悖论吧。
关于第二点,这个没啥好解释的了。
修改后的完整代码如下:
|
|
在训练的过程中,发现其loss是往下不断的降低,那么我们可以认为是有用的,但是具体效果要再自行实验下。