本文共 5468 字,大约阅读时间需要 18 分钟。
当用Word2Vec得到词向量后,一般我们会用余弦相似度来比较两个词的相似程度,定义为
等等!我们很快给出了相似度的计算公式,可是我们居然还没有“定义”相似!连相似都没有定义,怎么就得到了评估相似度的数学公式了呢?
要注意,这不是一个可以随意忽略的问题。很多时候我们都不知道我们干的是什么,就直接去干了。好比上一篇文章说到提取关键词,相信很多人都未曾想过,什么是关键词,难道就仅仅说关键词就是很“关键”的词?而如果想到,关键词就是用来估计文章大概讲什么的,这样我们就得到一种很自然的关键词定义
回到本文的主题来,相似度怎么定义呢?答案是:看场景定义所需要的相似。
那么,通过余弦相似度给出的相似,又是什么情况呢?事实上,Word2Vec本质上来说,还是使用上下文的平均分布描述当前词(因为Word2Vec是不考虑词序的),而余弦值与向量模长没关系,因此它描述的是“相对一致”。那么,余弦相似度大,事实上意味着这两个词经常跟同一批词搭配,或者更粗糙讲,那就是在同一句话中,两个词具有可替换性。比如,“广州”最相近的词语是“东莞”、“深圳”,那是因为很多场景下,直接将矩阵中的“广州”直接换成“东莞”、“深圳”,这个句子还是合理的(是句子本身的合理,但这个句子不一定是事实,比如“广州是广东的省会”,变成“东莞是广东的省会”,这个句子是合理的,但是这个句子并非是事实)。
>>> s = u'广州'
>>> pd.Series(model.most_similar(s))
0 (东莞, 0.840889930725) 1 (深圳, 0.799216389656) 2 (佛山, 0.786817014217) 3 (惠州, 0.779960155487) 4 (珠海, 0.735232532024) 5 (厦门, 0.725090026855) 6 (武汉, 0.724122405052) 7 (汕头, 0.719602525234) 8 (增城, 0.713532149792) 9 (上海, 0.710560560226)
前面已经说了,相似度的定义事实上要看场景的,余弦相似度只是其中之一。有时候我们会觉得“东莞”和“广州”压根就没联系,对于“老广州”来说,“白云山”、“白云机场”、“广州塔”这些词才是跟“广州”最相似的,这种场景也是很常见的,比如做旅游的推荐,旅游来到广州后,自然是希望输入“广州”后,自动输出来“白云山”、“白云机场”、“广州塔”这些广州相关的词语,而不是输出“东莞”、“深圳”这些词语。
这种“相似”,准确来说是“相关”,应该怎么描述呢?答案是互信息,定义为
这样,在给定词xx的情况下,我们就可以找出经常跟词xx一起出现的词,这个也完全可以由Word2Vec中的Skip-Gram+Huffman Softmax模型来完成。代码如下
import numpy as npimport gensimmodel = gensim.models.word2vec.Word2Vec.load('word2vec_wx')def predict_proba(oword, iword): iword_vec = model[iword] oword = model.wv.vocab[oword] oword_l = model.syn1[oword.point].T dot = np.dot(iword_vec, oword_l) lprob = -sum(np.logaddexp(0, -dot) + oword.code*dot) return lprobfrom collections import Counterdef relative_words(word): r = { i:predict_proba(i, word)-np.log(j.count) for i,j in model.wv.vocab.iteritems()} return Counter(r).most_common()
这时候,“广州”的相关词为
>>> s = u'广州'
>>> w = relative_words(s)
>>> pd.Series(w)
0 (福中路, -17.390365773) 1 (OHG, -17.4582544641) 2 (林寨镇, -17.6119545612) 3 (坪山街道, -17.6462214199) 4 (东圃镇, -17.6648893759) 5 (西翼, -17.6796614955) 6 (北京西, -17.6898282385) 7 (⇋, -17.6950761384) 8 (K1019, -17.7259853233) 9 (景泰街道, -17.7292421556) 10 (PSW3, -17.7296432222) 11 (广州铁路职业技术学院, -17.732288911) 12 (13A06, -17.7382891287) 13 (5872, -17.7404719442) 14 (13816217517, -17.7650583156) 15 (未遂案, -17.7713452536) 16 (增城市, -17.7713832873) 17 (第十甫路, -17.7727940473) 18 (广州白云机场, -17.7897457043) 19 (Faust, -17.7956389314) 20 (国家档案馆, -17.7971039916) 21 (w0766fc, -17.8051687721) 22 (K1020, -17.8106548248) 23 (陈宝琛, -17.8427718407) 24 (jinriGD, -17.8647825023) 25 (3602114109100031646, -17.8729896156)
可以发现,得到的结果基本上都是跟广州紧密相关的。当然,有时候我们稍微强调一下高频词,因此,可以考虑将互信息公式修改为
from collections import Counterdef relative_words(word): r = { i:predict_proba(i, word)-0.9*np.log(j.count) for i,j in model.wv.vocab.iteritems()} return Counter(r).most_common()
结果重新排列如下:
>>> s = u'广州'
>>> w = relative_words(s)
>>> pd.Series(w)
0 (福中路, -16.8342976099) 1 (北京西, -16.9316053191) 2 (OHG, -16.9532688634) 3 (西翼, -17.0521852934) 4 (增城市, -17.0523156839) 5 (广州白云机场, -17.0557270208) 6 (林寨镇, -17.0867272184) 7 (⇋, -17.1061883426) 8 (坪山街道, -17.1485480457) 9 (5872, -17.1627067119) 10 (东圃镇, -17.192150594) 11 (PSW3, -17.2013228493) 12 (Faust, -17.2178736991) 13 (红粉, -17.2191157626) 14 (国家档案馆, -17.2218467278) 15 (未遂案, -17.2220391092) 16 (景泰街道, -17.2336594498) 17 (光孝寺, -17.2781121397) 18 (国际货运代理, -17.2810157155) 19 (第十甫路, -17.2837591345) 20 (广州铁路职业技术学院, -17.2953441257) 21 (芳村, -17.301106775) 22 (检测院, -17.3041253252) 23 (K1019, -17.3085465963) 24 (陈宝琛, -17.3134413583) 25 (林和西, -17.3150577006)
相对来说,后面这个结果更加可读一点。另外的一些结果,展示如下:
>>> s = u'飞机'
>>> w = relative_words(s) >>> pd.Series(w) 0 (澳门国际机场, -16.5502216186) 1 (HawkT1, -16.6055740672) 2 (架飞机, -16.6105400944) 3 (地勤人员, -16.6764712234) 4 (美陆军, -16.6781627384) 5 (SU200, -16.6842796275) 6 (起降, -16.6910345896) 7 (上海浦东国际机场, -16.7040362134) 8 (备降, -16.7232609719) 9 (第一架, -16.7304077856)>>> pd.Series(model.most_similar(s))
0 (起飞, 0.771412968636) 1 (客机, 0.758365988731) 2 (直升机, 0.755871891975) 3 (一架, 0.749522089958) 4 (起降, 0.726713418961) 5 (降落, 0.723304390907) 6 (架飞机, 0.722024559975) 7 (飞行, 0.700125515461) 8 (波音, 0.697083711624) 9 (喷气式飞机, 0.696866035461)>>> s = u'自行车'
>>> w = relative_words(s) >>> pd.Series(w) 0 (骑, -16.4410312554) 1 (放风筝, -16.6607225423) 2 (助力车, -16.8390451582) 3 (自行车, -16.900188791) 4 (三轮车, -17.1053629907) 5 (租赁点, -17.1599389605) 6 (电动车, -17.2038996636) 7 (助动车, -17.2523149342) 8 (多辆, -17.2629832083) 9 (CRV, -17.2856425014)>>> pd.Series(model.most_similar(s))
0 (摩托车, 0.737690329552) 1 (骑, 0.721182465553) 2 (滑板车, 0.7102201581) 3 (电动车, 0.700758457184) 4 (山地车, 0.687280654907) 5 (骑行, 0.666575074196) 6 (单车, 0.651858925819) 7 (骑单车, 0.650207400322) 8 (助力车, 0.635745406151) 9 (三轮车, 0.630989730358)
大家可以自己尝试。要说明的是:很遗憾,Huffman Softmax虽然在训练阶段加速计算,但在预测阶段,当需要遍历一遍词典时,事实上它比原生的Softmax还要慢,所以这并不是一个高效率的方案。
根据前面两部分,我们可以看到,“相似”一般有两种情景:1、经常跟同一批词语搭配出现;2、经常一起出现。这两种情景,我们都可以认为是词语之间的相似,适用于不同的需求。
比如,在做多义词的词义推断时,比如star是“恒星”还是“明星”,就可以利用互信息。我们可以事先找到star意思为“恒星”的时候的语料,找出与star互信息比较大的的词语,这些词语可能有sun、planet、earth,类似地,可以找到star为“明星”的时候的语料,找出与star互信息比较大的词语,这些词语可能有entertainment、movie等。到了新的语境,我们就可以根据上下文,来推断究竟是哪个词义。
总而言之,需要明确自己的需求,然后再考虑对应的方法。
转载地址:http://oxuoo.baihongyu.com/