对word2vec的理解
来源:互联网 发布:金润软件下载 编辑:程序博客网 时间:2024/06/11 00:57
一、词向量
在自然语言处理中,需要把自然语言转化为计算机可以理解的形式,一般采用向量空间模型(VSM)进行表示,在这里就是词向量。
1、one-hot词向量
在中文处理中,将分词的结果经过降噪等预处理后,选择出来的特征在进行权重计算时,采用布尔权重,就能够得到one-hot词向量。
从表现形式上来看,向量的长度是词典的大小V,同时它的分量只有一个是1,其他全是0,1的位置对应词在词典中的索引。
但是这种词向量的长度是随着词典的大小增加而增加,同时数据非常稀疏。这样很容易受到维数灾难的困扰,而且由于是独立形式的词索引,无法体现出词与词之间的联系。
2、分布式词向量
分布式词向量被提出来解决one-hot向量的不足,它的基本思想是:通过某种训练将语料中的每一个词映射成一个固定长度的向量,所有的词向量组成一个向量空间,这样就可以把词向量对应成空间中的一个点。
分布式词向量的做法很好解决one-hot词向量的问题:词向量的长度可以自行确定,词与词直接的关系也可以由空间中点的距离进行衡量。机器学习中提到的word2vec或者深度学习中的word embedding都可以理解为训练分布式词向量的方法。
上面提到的训练词向量的方法,比较流行的都是使用语言模型。
二、语言模型
语言模型的作用实际上是确定字符串s作为一个句子出现的概率,词向量可以看成在训练语言模型的过程中出现的副产品。
统计语言模型的训练方式常见的有:n-gram模型、神经网络等方法,Word2vec是基于神经网络的方法来进行训练的,关于语言模型可以参考:语言模型。
三、Word2Vec
1、什么是word2vec?
word2vec的本质是一个神经网络语言模型,基于语言模型进行分布式词向量的训练。
与传统神经概率语言模型不同的是,作者提出了新的方法进行训练:CBOW模型(Continuous Bag-of-Words Model)和Skip-gram模型(Continuous Skip-gram Model)。同时为了加快训练速度,使用Hierarchical Softmax和Negative Sampling这两种tricks。
2、CBOW模型和Skip-gram模型
上面可以看到,连个模型都包含三层:输入层,投影层和输出层。
(1)CBOW模型
CBOW模型是是在已知当前词的上下文,,,的情况下,预测当前词。
(2)Skip-gram模型
Skip-gram模型则是在已知当前词的情况下,预测其上下文,,,。
(3)模型其他知识
从形式上来看,这两种模型与n-gram模型的思想很相似。在统计语言模型中,n-gram通过引入马尔科夫假设,假设当前词只与在它之前出现的n个词有关。这实际上隐式的带入了一种思想,当前词可以在某种形式上由其上下文代替。而这种思想也是符合人的思维的,通过上下文猜测当前词(填空)、通过当前词猜测上下文(造句)。
word2vec用了一种称为window的说法,实现了类似的上下文范围划定功能。
3、基于Hierarchical Softmax的CBOW模型
上述模型包括三层:输入层、投影层和输出层。简单的来说:
* 输入层包含了中2c个词的词向量,...,。
* 投影层将输入层的2c个向量做求和累加,合并成向量。
* 输出层对应一颗huffman二叉树,将V分类转换成多次二分类,得到输出。
具体流程,下面通过gensim.models.Word2vec源码,进行说明。
(1)遍历语料,构造词典
代码详见Word2vec.scan_vocab(),如下:
for sentence_no, sentence in enumerate(sentences): ... for word in sentence: vocab[word] += 1在Word2Vec模型中,默认sentences是可迭代类型。在中文自然语言处理中,常用分词降噪后的文本作为sentences输入,关于中文分词可以参考:j浅谈中文分词与jieba源码
上述代码也非常简单,就是统计词频的操作。
(2)对语料词典进行预处理
代码详见Word2vec.scale_vocab(),
1)低频词的处理
for word, v in iteritems(self.raw_vocab): if keep_vocab_item(word, v, min_count, trim_rule=trim_rule): retain_words.append(word) retain_total += v代码中引入min_count参数,若某个词再语料中出现的次数小于它,则将其从词典中删除。
2)高频词的处理
for w in retain_words: v = self.raw_vocab[w] word_probability = (sqrt(v / threshold_count) + 1) * (threshold_count / v)
在语料中,那些最常见的词如“”的“”等,出现次数太多以至于没有区分性,因此引入downsample方法,将词频高于某个阈值的词从字典中去除。
(3)构建huffman二叉树
代码详见Word2vec.create_binary_tree(),关于huffman二叉树可以参考:Huffman Tree
1)构建huffman二叉树
heap = list(itervalues(self.wv.vocab))heapq.heapify(heap)for i in xrange(len(self.wv.vocab) - 1): min1, min2 = heapq.heappop(heap), heapq.heappop(heap) heapq.heappush(heap, Vocab(count=min1.count + min2.count, index=i + len(self.wv.vocab), left=min1, right=min2))基于词频构建huffman二叉树,词频越高离根节点越近,响应的搜索路劲也更短。
2)生成叶子节点对应的huffman编码
max_depth, stack = 0, [(heap[0], [], [])]while stack: node, codes, points = stack.pop() if node.index < len(self.wv.vocab): # leaf node => store its path from the root node.code, node.point = codes, points max_depth = max(len(codes), max_depth) else: # inner node => continue recursion points = array(list(points) + [node.index - len(self.wv.vocab)], dtype=uint32) stack.append((node.left, array(list(codes) + [0], dtype=uint8), points)) stack.append((node.right, array(list(codes) + [1], dtype=uint8), points))递归寻找二叉树,为每一个叶子节点,即语料词典中的每一个词生成huffman编码。
(4)初始化模型参数
模型中需要训练的参数包括huffman树种每一个非叶子节点对应的向量,以及词典中每个词的词向量,代码详见Word2vec.reset_weights(),
1)初始化叶子节点向量
self.wv.syn0 = empty((len(self.wv.vocab), self.vector_size), dtype=REAL)# randomize weights vector by vector, rather than materializing a huge random matrix in RAM at oncefor i in xrange(len(self.wv.vocab)): # construct deterministic seed from word AND seed argument self.wv.syn0[i] = self.seeded_vector(self.wv.index2word[i] + str(self.seed))叶子节点的向量,也就是词典中每一个词的词向量,这里采用随机初始化的方法。
2)初始化非叶子节点向量
self.syn1 = zeros((len(self.wv.vocab), self.layer1_size), dtype=REAL)非叶子节点向量,是模型训练过程中的中间向量,与神经网络模型中的隐藏变量相似,这里采用零初始化的方法。
(5)输入层处理
回到前面的模型图上,输入层包含了中2c个词的词向量,...,。
这里的词向量就是上面在huffman树种的叶子节点初始化的向量,一般习惯把所有词向量构建成一张look-up table,把每一个词转化为对应table中的索引,在使用的时候直接查找,能够提高效率。
(6)投影层处理
投影层将输入层的2c个向量做求和累加,合并成向量。
之前提到,模型中隐式的带入一种思想:当前词可以在某种形式上由其上下文代替。在投影层,处理方法是直接将上下文向量相加,在gensim的模型中,还有另一种方法,是将所有上下文向量求平均。
相比传统神经概率网络模型的做法(将当前的词的前n-1个词首尾相接),这样的做法是不需要考虑缺失词的情况,在这里只是少加上词而已,向量的长度不会改变。
(7)输出层处理
假设有字符串s=(我,喜欢,观看,巴西,足球,世界杯),其中每个字符出现的频数如下:
我:15
喜欢:8
观看:6
巴西:5
足球:3
世界杯:1
以上述为语料,假设w=足球,那么可构建模型流程图如下:
上图中,白色节点即是叶子节点,也就是语料中的词,黄色节点便是中间节点了。由红色边串起来的5个节点构成路径,其长度=5。,,,,为路径上的5个结点。,,,分别为1,0,0,1,即Huffman编码为(1,0,0,1)。,,,为路径上的非叶子节点对应的中间向量。
上面说到,利用Huffman Tree的的分类其实是把N分类转换了多个二分类。这里的二分类是在路径中的每一次分支,进行了一次logistic回归。每一次的二分类的正负标签由Huffman编码指定,这里规定:编码为1的结点定义为负类,编码为0的结点定义为正类,即:
根据逻辑回归的定义,对每一次的分支可以得到以下概率:
利用极大似然,很容易推导得到:
到这里,就已经将N分类转化成了多次二分类,并最终得到给定上下文的条件下,w=足球的概率。
由上可以推导得到,条件概率更一般的公式如下:
其中,
上述条件概率的公式,如logistic回归的似然函数公式形式上并没有什么不同。logistic回归中,输入x一般是固定的向量,根据每一次分类的误差对参数θ进行修正。而在这里无论是输入x还是参数θ,都是需要在迭代过程中更新的。
(8)梯度优化
1)由上述条件概率公式,可以得到目标极大似然函数如下:
即可得,
2)对上述表达式求求偏导,
3)那么,参数θ的更新公式如下:
4)词向量的更新公式如下:
在每一个节点中,根据节点的输入x与中间向量的计算结果进行二分类,根据分类结果对θ进行修正并记录误差量。
直到到达叶子节点,再根据之前的累计误差e,对组成输入x的每一个词向量v(u)进行修正。由于输入x是由所有上下文词向量相加得到,所以这里的修正也是直接加上误差e。
详细代码,参见gensim.models.word2vec.train_batch_cbow(),
for sentence in sentences: word_vocabs = [model.wv.vocab[w] for w in sentence if w in model.wv.vocab and model.wv.vocab[w].sample_int > model.random.rand() * 2**32] # 语料预检查,去除未在词典中出现及词频不满足要求的词 for pos, word in enumerate(word_vocabs): reduced_window = model.random.randint(model.window) # `b` in the original word2vec code start = max(0, pos - model.window + reduced_window) window_pos = enumerate(word_vocabs[start:(pos + model.window + 1 - reduced_window)], start) word2_indices = [word2.index for pos2, word2 in window_pos if (word2 is not None and pos2 != pos)] l1 = np_sum(model.wv.syn0[word2_indices], axis=0) # 叶子节点向量 if word2_indices and model.cbow_mean: l1 /= len(word2_indices) train_cbow_pair(model, word, word2_indices, l1, alpha, compute_loss=compute_loss) 训练对每份语料都进行一次训练,参见gensim.models.word2vec.train_cbow_pair(),
l2a = model.syn1[word.point] # 中间节点向量 prod_term = dot(l1, l2a.T) fa = expit(prod_term) # propagate hidden -> output ga = (1. - word.code - fa) * alpha # vector of error gradients multiplied by the learning rate if learn_hidden: model.syn1[word.point] += outer(ga, l1) # learn hidden -> output neu1e += dot(ga, l2a) # save error # loss component corresponding to hierarchical softmax if compute_loss: # 损失 sgn = (-1.0)**word.code # ch function, 0-> 1, 1 -> -1 model.running_training_loss += sum(-log(expit(-sgn * prod_term)))
4、基于Hierarchical Softmax的Skip-gram模型
Skip-gram的基本计算方式与CBOW基本一致,不同的是skip-gram输入的是当前词的词向量,利用当前词向量预测上下文。
所以实际上,skip-gram的模型在进行梯度优化时,相比CBOW要多遍历几次,每一次都是用输入x预测上下文中的每一个,直到结束,具体的推导方式这里就不展开了。
5、Negative Sampling模型
回顾上面说到的内容,word2vec提出的方法是用来加速神经网络训练速度的,NEG模型与Hierarchical模型相比,去掉了复杂的Huffman树,转而利用随机负采样进行训练。
(1)什么是随机负采样?
在训练神经网络时,如上述基于Hierarchical的CBOW模型,每次进行一个(上下文,目标词)的样本的训练,都会对整个网络的权重进行调整。
基于语料词典V的大小,网络中拥有大规模的权重矩阵,这样调整起来需要耗费大量的时间。而负采样每次让一个训练样本仅仅更新一小部分的权重,这样用来降低梯度下降中的计算量。
(2)怎么进行随机负采样?
假如(上下文,w)为一组训练样本,在w为正样本的情况下,其他的词都可以当成负样本。
而在语料中出现的词,不同的词拥有不同的频数。对于那些高频词,被选负样本的概率就应该比较大;反之,对于低频词,被选中的概率就会比较小,本质上是一个带权采样问题。
具体的公式如下:
上述公式中,每个单词都具有一个权重f(w),代表单词出现的频次;另一个3/4的根号则是基于经验,论文提及这个公式效果更为出色。
代码中具体的做法是初始化一个大小为M的table,这个table由语料词典中词的索引填充。具体填充方式基于词频确定,即:
计算出的负采样概率*M = 单词在表中出现的次数
之后在进行负采样时,只需要在0-M范围内生成一个随机数,将table中对应随机数索引的词取出作为负样本即可。由上述公式可以看出,一个词的负采样概率越大,那么在表中出现的次数越多,被选中的概率也越大。
在论文中,作者提到对于小规模的数据集,选择5-20个负样本会比较好,对于大规模数据集可以仅选择2-5个负样本。
(3)其他
最后根据采样出来的(词,负样本),进行随机梯度下降优化即可,这里就不展开了。
四、参考
1、《数据结构》(c语言版) 严蔚敏等
2、《统计自然语言处理》 宗成庆
3、http://www.cnblogs.com/peghoty/p/3857839.html
4、Distributed Representations of Sentences and Documents Mikolov
5、Efficient estimation of word representations in vector space Mikolov
- 对word2vec的理解
- Word2Vec的一些理解
- word2vec的细节理解
- word2vec我的理解
- 我所理解的word2vec
- 理解word2vec的训练过程
- 理解word2vec的训练过程
- Mikolov的Word2vec学习理解
- deeplearning4j对word2vec的介绍
- word2vec理解
- word2vec理解
- word2vec 过程理解&词向量的获取
- word2vec中一种模型的理解
- word2vec个人理解
- word2vec简单理解
- 理解 TensorFlow 之 word2vec
- 对word2vec训练的model的结果操作笔记
- word2vec 模型的详细数学推导和直观理解
- Android Interpolator属性动画解析
- [23]_Linux设备驱动模型(重要)
- 算法提高 最长公共子序列 (DP VS 递归)
- Servlet相关知识点
- spring 启动 读取数据库
- 对word2vec的理解
- css 实现蒙版效果
- loj6006「网络流 24 题」试题库(最大流)
- jplayer更换播放音乐
- 网站建设:网页模板你知道多少?
- JAVA代码执行Windows控制台命令
- java将字符串进行MD5加密
- Android开发---Scrollerview中嵌套RecyclerView RecyclerView显示不全问题
- 深入理解事务的四种隔离级别