专注细节
努力进步

Stanford CS224d笔记之Word2Vec

前言

这篇文章的目的仅仅只是对word2vec有一个大致的了解,网络上有很多相关的文章对word2vec的原理以及数学分析讲的很清楚了,如果你想把word2vec完全搞清楚,可以去看看那些讲解数学原理的文章。本文只适用那些想了解word2vec而且可能工作当中会用到word2vec的同学,毕竟不是每个人都需要去做research的工作,其中的细节也不需要都每个人都了解清楚。知道它是个什么东西,了解基本原理,怎么使用才是一些工程师应该要知道的。

文章最后提了下怎么在gensim中使用word2vec来train中文维基语料库,这部分的实验,我老早之前有玩过,但是在换电脑之后,这部分代码没有保存下来,所以只是贴出了一个52nlp的链接,关于如何使用gensim训练一个中文语料模型,它讲的很清楚。

最后,感觉这篇文章干活不是很多。说实话,nlp这块不是我的擅长,学习CS224d也对我很难。 但是学会使用,了解这些基本的概念,还是行的。另外,文章的一些代码在https://github.com/burness/simpleML中有,也是一个很simple的word2vec的各个模块的实现,仅用于学习而已。

word2vec是什么

word2vec是一套能将词向量化的工具。额,好吧,这样的解释其实我自己也不太清楚,本人研究生期间是做图像相关的,比如人脸检测、异常检测什么的,所以,在我的意识里,比如像图片这类数据本来就应该是一些数字(rgb,3层8为二进制数就可以表示)。所以,最开始在接触NLP相关的东西时,不太习惯,必须要先把词表示为一套一套的数字,而且貌似这也是一个十分复杂的过程。到这里有人就提出了,我们是不是可以直接把word像image一样直接用计算机内部的编码来表示呢 ?那我们首先在这儿脑洞下,image和word在这块的差异。我们知道image是由一些像素点来表示,而RGB(0-255)的像素值能够构成我们日常看到颜色,这就是所谓的”三基色”,在这个规范下,1个计算机世界里的数字其实是对应着现实中的一种颜色的成分。而word则不同,不去探讨各种不同语系下的各种编码方式,但就是各种同类词下的各种不同的语义,就更加复杂。很不幸,无法一个数字来代表某个word,然后还对应着在实际当中的用途。那么,又有同学提出问题了,”常用词汇就那么多,比如中文就只有1w多字,我们做一个大小为1w的字典,给每个字标个号不就很好了吗,然后向量化只需要相应地位置1不就成了吗”,确实,常用的词汇基本就那么多,这样的方式确实可以有效地去表征一个word,但也仅仅只是一个word而已,这种方式无法表明word之间的语义关系,更别提在这种表示方法下,各种计算的限制性了。好吧,解释了那么多,应该知道了word2vec是一个什么鬼了,是的,他就是NLP中将人们之间使用的word转化为计算机能识别的一种高效地向量化表示方式的一种工具。

word2vec是Google在13年开源的,代码可以见https://github.com/burness/word2vec,google那个地址貌似不维护了,但是可以自动import到github。word2vec能够将文本内容处理简化为指定维度大小的向量空间的实数型向量表示,并且其空间上的相似度可以用来表示文本语义的相似度。因此,word2vec训练的词向量能够用来做很多NLP的任务。 

与word2vec相似的模型

统计语言模型

n-gram模型

n=1时即简单地做词频统计,当n=2时,我们也称其为Bigram模型,考虑到两个词之间共现概率,比如”早餐吃鸡蛋”,”早餐吃汽车”逻辑更正常的多,即P(鸡蛋|早餐)>>P(汽车|早餐)。

NewImage

n-pos模型

n-pos(Part-of-Speech)模型是n-gram的升级版本,n-gram只通过前面出现的词的概率。n-pos比n-gram提高的在于很多次出现的概率是依赖于前面词的语法功能的,所以这里模型比n-gram稍微复杂点的在于考虑词性,这个尤其在中文语义里面会更加明显。

决策树模型

决策树大家都很熟悉,在nlp中,决策树的每个会表明一些上下文相关的问题,比如”前一个词为名词”吗,决策树模型的好处在于是根绝训练语料预估,坏处是语料较大时,建这样一棵决策树成本很大。

最大熵模型

最大熵模型无论在NLP还是在其他方面都是经常在用,它名字听起来十分高大上,但是原理很简单”保留全部的不确定性,将风险降到最小”。当我们对一个随机事件的概率分布预测时,我们的预测应该满足全部已知的条件,且对未知的情况不做假设。这里举一个别的博文的例子:wang-xiao-bo可以被转换为王小波和王晓波,根据上下文主题,王小波是作者,王晓波是台湾研究两岸关系的学者。这里我们可以建立一个最大熵模型(被数据学家证明存在且唯一)

NewImage

w1,w2表示我们预测(王晓波或者王小波之前的两个词),比如这里是”出版”和””,也就是上下文的一个大致估计,subject表示主题,这样我们就可以根据这样一个模型来预测是王小波还是王晓波,其参数是根据给定语料得到的,即训练过程。 

神经语义模型

神经语义模型的极少大家可以看看[1]中的文章的Model Architectures,因为我不是专业做nlp的,这块我怕我的翻译会造成一些问题,不过这也不影响我们学习word2vec,大家自行下去去理解下吧,最后是把那篇文章中引用的文章也读下,也蛮有意思的。 

word2vec的语义模型

 本节,我们描述下word2vec里面的两个比较有意思的模型Skip-gram model和Continuous Bag-of-word model

Skip-gram

NewImage

Skip-gram模型的cost函数如下:

NewImage

其中F一般会使用log来表示概率 ,对于整个语料:

NewImage

其中p为:

NewImage

很熟悉是吧,这里就是softmax公式,经典地把二类分类扩展到n类分类的手段。

大家也看到了 公式当中有些标量有点不对应,对的!我是在不同的文章里面截的,我也想自己书写公式,但是为知的mathjax一直有点小bug,现在只能截图来写了,不过这个不影响我们理解。

至此,Skip-gram模型就这样被建立了,如果去进行模型寻优呢 ?

其实在建模之后,这里和一般的machine learning问题没有了其他的差别,这里无非计算一个cost值,两类梯度值当前词的梯度和上下文的词的梯度

计算两个梯度:对当前词和上下文词汇:

NewImage

然后,由计算到的梯度去更新的两类词向量,这样依次下去,就可以得到各个词的词向量表示,注意:每一次参数更新不仅会更新当前词的向量表示,也会更新上下文的词的向量表示。公式推导我这里就不贴出来了,如果有兴趣的话可以去看看http://blog.csdn.net/itplus/article/details/37969979这篇博文。我这篇文章的目的只是介绍下word2vec的基本概念,知道word2vec的基本工作原理,具体的可以去看看上面文章的公式推导还有google的那版本源码,里面有很多细节是paper里面没有说过的,在这里我们不作深究。

def skip_gram(self, current_word, C, context_words, tokens, input_vecs, output_vecs, word2vec_cost_grad = softmax_cost_grad):
        '''
        :param current_word:
        :param C:
        :param context_words:
        :param tokens:
        :param input_vecs:
        :param output_vecs:
        :param word2vec_cost_grad:
        :return:
        '''
        current_index = tokens[current_word]
        current_vec = input_vecs[current_index]
        cost = 0
        grad_in = np.zeros(input_vecs.shape)
        grad_out = np.zeros(output_vecs.shape)

        for context_word in context_words:
            target = tokens[context_word]
            curr_cost, curr_grad_in, curr_grad_out = word2vec_cost_grad(current_vec, target,output_vecs)
            cost += curr_cost
            grad_in[current_index] += curr_grad_in
            grad_out += curr_grad_out

        return cost, grad_in, grad_out
def softmax_cost_grad(self, predicted, target, output_vec):
        '''
        :param predicted: predicted word vec
        :param target: the index of the target word
        :param output_vec:
        :return:
        '''
        V,D = output_vec.shape
        scores = self.softmax(output_vec.dot(predicted).reshape(1,V)).reshape(V,)
        cost = -np.log(scores[target])

        # one hot vec
        labels = np.zeros(V)
        labels[target] = 1
        diff_scores = scores - labels
        grad_pred = diff_scores.dot(output_vec)
        grad = diff_scores.reshape(V, 1).dot(predicted.reshape(D,1).T)

        return cost, grad_pred, grad

 

Skip-gram+NegativeSampling

负采样是word2vec里面一个很有意思的东西,大致原理是在目标函数中添加一个最小化与当前词不相关的部分,保证在原本模型的基础上尽量减少与不相关词之间的相似度:

NewImage

其中函数为sigmoid函数,也就是说这里的优化目标函数与原先得Skip-gram模型不一致,然后也就是计算梯度的问题。这部分,就不详述了,去看看上文提到的那篇文章吧,我这里贴出一个网上的推导:
NewImage

拉出我们需要的:

NewImage

 那么,这里的参数更新就很明显了,上代码吧:

    def negsampling_cost_grad(self, predicted, target, output_vec, K=10):
        '''
        :param predicted:
        :param target:
        :param output_vec:
        :param K:
        :return:
        '''
        sample_indexs = []
        for i in xrange(10):
            index = self.dataset.sampleTokenIdx()
            sample_indexs.append(index)
        sample_vecs = output_vec[sample_indexs,:]
        w_r_out = self.sigmoid(output_vec[target].dot(predicted))
        w_r_k = self.sigmoid(-sample_vecs.dot(predicted))

        cost = -np.log(w_r_out) - np.sum(np.log(w_r_k))
        grad_pred = output_vec[target]*(w_r_out - 1) + (1-w_r_k).dot(sample_vecs)
        grad = np.zeros(output_vec.shape)
        for i in xrange(K):
            grad[sample_indexs[i]] += predicted*(1-w_r_k)[i]
        return cost, grad_pred, grad 

CBOW

NewImage

CBOW和Skip-gram比较类似,只是CBOW是更具context去预测,是的!和Skip-gram相反,这块很多都和Skip-gram相似,都存在Softmax和Negative Sampling两个版本,只是在参数寻优这部分不太一样而已,这里就不详细写了,上文提到的文章把这些都讲的很清楚了。

 

GloVe

Glove是另外一种向量化的方法,是stanford的大牛做出来的一种方法,在一些方面能打败word2vec,详见http://nlp.stanford.edu/projects/glove/

word2vec试用

2016-03-13

gensim中包含了word2vec的部分,1年前有试用gensim做过中文部分的实验,但是后来换电脑后,那部分代码现在找不到了,如果之后有这块的工作的话,我再添加,这里我就先不特意花时间再重弄一遍了,如果有兴趣的话可以去看看这篇博文http://www.52nlp.cn/中英文维基百科语料上的word2vec实验,这里说明下,我这里使用jieba这个库来进行的中文分词,文章的mecab我没有尝试。对了,大家一定安装gensim之前安装c的编译器,这样gensim会在矩阵计算时使用cython,大大提高训练模型的速度。

2016-04-01

今天是愚人节,在朋友圈看到有人用word2vec看金庸小说里面的一些人物的关系,想起来这篇文章里面也应该有个这样的东西,就做了下实验,代码很简单,先把小说收集起来,做些基本的处理,然后用jieba做分词,最后塞到gensim的word2vec里面训练得到词向量,废话不说,直接撸码util.py:

import jieba
from gensim.models import Word2Vec
import logging
def segment_novel(novel_path, dict_path):
    text_list = []
    jieba.load_userdict(dict_path)
    with open(novel_path, 'r') as fread:
        for line_num, line in enumerate(fread.readlines()):
            # if line_num> 100:
            #     break
            seg_list = jieba.cut(str(line),cut_all=False)
            word_list = [item for item in seg_list if len(item) > 1]
            text_list.append(word_list)
        logging.info(text_list)
    return text_list

def train_word2vec(corpora,out_model):
    w2v_model = Word2Vec(corpora)
    w2v_model.save(out_model)

 

调用 gulong.py:

from util import *
# text_list = segment_novel('./data/gulong.txt','gulong_dict2.txt')
out_model_file = './model/gulong2.model'
# train_word2vec(text_list,out_model_file)
model = Word2Vec.load(out_model_file)
for e in model.most_similar(positive=[u'沈浪'],topn=30):
    print e[0],e[1]

这里,我收集了古龙的小说,并且用到小说里面的人物名来做一个自定义词典,然后我们来看看结果:

NewImageNewImageNewImageNewImage

红楼梦可能由于整体风格与白话还是有点差别,效果不是特别明显:

NewImageNewImage

还有金庸的小说:

NewImageNewImageNewImage

文章引用

1,Mikolov T, Chen K, Corrado G, et al. Efficient estimation of word representations in vector space[J]. arXiv preprint arXiv:1301.3781, 2013.

2,Word2vec中的数学原理详解:http://blog.csdn.net/itplus/article/details/37969519

 

分享到:更多 ()