如何用 word2vec 計算兩個句子之間的相似度?
看了下 word2vec,貌似只能計算詞之間的相似度,不能計算 setence 之間的相似度?有人說將 sentence 里的 word vector 直接相加然後歸一化後的向量計算 cosine 就可以得到 sentence 之間的相似度,不知道有人試過嗎,效果怎麼樣?
今年ICML-15上有個文章From Word Embeddings To Document Distances,是解決題主的問題。文章裡面定義了Word Mover』s Distance,詞-詞 的相似度用word2vec結果算歐式距離,句子-句子 的相似度通過求解一個transportation的優化問題得到。文章裡面證明了詞向量求平均算歐式距離是Word Mover』s Distance的下界,這樣在求解最近鄰集合的時候,就可以先用詞向量平均對候選集快速進行pruning。
我實現了一下,結果看著還有些道理,但感覺想計算句子-句子 的相似度,還是要做sentence modeling才可以,而不是只從word level去算。如果用word2vec結果作為一種soft matching score的話,其實可以把機器翻譯裡面的評價指標BLEU改造一下,也可以計算句子-句子 的相似度。======如何定義句子的similarity其實是比較困難的,往往和具體應用也比較相關,到底需求是topic上的相關,還是說semantic上的相關,例如:
- I like this laptop.
- I do not like this laptop.
如果用不同的similarity定義方法,得出的結果也是不同的。這個和paraphrase的研究其實也有些關係,現在大多數工作感覺都是從similarity的角度去做,但其實按照嚴格定義應該是看雙向的entailment。(扯遠了)
======只從詞去比較的話,還有個比較困難的地方是similarity往往需要結合context去看,例如:- hot girl(性感)
- hot water(溫度高)
- hot dog(吃的熱狗)
裡面都有hot,但是similarity應該是比較低的。
@董力 學長提到的Word Mover"s Distance a.k.a. WMD,我是這篇ICML論文的第二作者。就算是推銷一下自己的論文:)針對你說的句子間距離的問題,我們的方法是我已知範圍內的state of the art。我們測試的數據集里比較的大多是幾十個詞的段落,你用句子應該會更快,不需要用到董力說到的lower bound pruning。具體方法很簡單:假設我有句子A, B。A里的每個詞是A_i, B里的每個詞是B_j。
- 計算A,B句子里每兩個詞的距離 i.e. D = dist(A_i, B_j) over all i,j(這裡用Euclidean distance b/t the word embeddings, from w2v)。
- 生成optimal transport (也叫earth mover"s distance a.k.a. EMD)problem,給solver(網上有很多各種語言的EMD solver)。輸入是D, A所有詞的詞頻(A_BOW i.e. bag of words), B所有詞的詞頻(B_BOW)。EMD基本概念就是把兩個句子看成兩個probability distribution的histogram,A的是山,B的是坑,用A的山填B的坑,每兩個histogram格之間搬運一個詞頻單元需要做的功是兩詞間的距離。
- EMD返回的就是A,B的距離,1,2,3對每兩篇文章可以CPU平行。
論文鏈接:
http://jmlr.org/proceedings/papers/v37/kusnerb15.pdf——————關於一詞多用以及similarity的定義這兩個問題:這是NLP公認的難題,我們上面的論文很明顯無法解決一詞多用(任何embedding完全基於w2v的模型都不能),對similarity的定義也是單一的。歸根結底,WMD是完全不需要訓練的,而如果想不用人工設計解決上述兩個問題,必須有training data。我們新投稿到NIPS的論文改進了WMD,用Neighborhood Component Analysis來學習詞距,實驗中已經能比較好的區分詞義,由此也能影響similarity的定義。如果NIPS中了,會在這裡貼出鏈接。——————MATLAB核心代碼,抱歉複製來的沒有仔細comment。用的solver是emd_mex。% copyright 2015 Yu Sundisp("getting pairwise EMD...");dists = zeros(n_rows, n_cols);matlabpool open 8parfor i = 1 : n_rows
disp(strcat("working on i=" , num2str(i))); for j = 1: n_cols i_BOW = A_BOW{i}./sum(A_BOW{i}); j_BOW = B_BOW{j}./sum(B_BOW{j}); DE = sqrt(distance(A_vecs{i}, B_vecs{j})); % L2 norm b/t word vectors DE(DE &< 0) = 0; % numerical instability may cause negative dists [emd, flow] = emd_mex(i_BOW, j_BOW, DE); dists(i, j) = emd; endend
matlabpool closedists(dists &< 0) = 0; % numerical instability may cause negative dists之前在bat做過這個,和大家分享一下。先說一個還是從詞的角度出發考慮的,最後的效果非常好,就是怎麼樣從詞的向量得到句子的向量,首先選出一個詞庫,比如說10萬個詞,然後用w2v跑出所有詞的向量,然後對於每一個句子,構造一個10萬維的向量,向量的每一維是該維對應的詞和該句子中每一個詞的相似度的最大值。這種方法實際上是bag of words的一個擴展,比如說對於 我喜歡用蘋果手機 這麼一句話,對應的向量,會在三星,諾基亞,小米,電腦等詞上也會有比較高的得分。這種做法對於bag of words的稀疏性問題效果非常好。還做過一個直接訓練句子的相似度的一個query2vec模型,效果也不錯,就不細說了。
2016.7.30-----------------------------------------------看了知友們的答案,我再補充一個結合word2vec和RNN來構造無監督式的句子特徵學習模型。ps:本科畢業論文的一個子模塊(E-LSTM-S),根據文獻題目和文獻摘要來衡量文本相似度。
其中A=&>B表示跟A文獻最為語義相關的是B文獻,可以看到:
1. 200=&>6091表明《Learning to cluster web search results》和《Web Document Clustering: A Feasibility Demonstration》兩篇文獻具有相近的語義,從標題上也可以直觀地判斷它們都跟「web clustering」相關。
2. 6091=&>5282表明《Web Document Clustering: A Feasibility Demonstration》和《A personalized search engine based on web-snippet hierarchical clustering》兩篇文獻也具有相近的表達,同樣跟「web clustering」相關。
3. 200=&>6091、6091=&>5282這個組合表明,跟「web clustering」語義相關的文檔將構成一類(類似聚類)。
1.採用one-hot vector,定義embedding layer,word2vec作為其初始參數,訓練時進行fine-tune。
2.直接採用word2vec作為詞向量,即在訓練過程中不改變每個詞的詞向量。接下來,就涉及到了紅色虛線處的Encoder-Decoder結構了,細節如下:其中,上半部分是Embedding,下半部分則是RNN(LSTM)。具體應用時,將詞向量表示依次輸入RNN的每一時間步,綜合詞語義和詞順序的影響,將其編碼成中間表示,再用Softmax解碼。-----------------------------------------------當然,最好採用End-to-End(端到端)的方式直接生成句子的中間特徵表示(E-LSTM-S是對每一時間步的隱層輸出取平均),可以參考用於機器翻譯、郵件回復的seq2seq模型。這裡我給出幾篇文獻供大家閱讀。&<1&> Sequence to Sequence Learning with Neural Networks
&<2&> Effective Approaches to Attention-based Neural Machine Translation
&<3&> Learning Phrase Representations using RNN Encoder-Decoder for Statistical Machine Translation
此外,也可嘗試使用CNN替換以上的RNN,目前CNN在文本處理上也有不少研究了。&<4&> A Convolutional Neural Network for Modelling Sentences
&<5&> Convolutional Neural Networks for Sentence Classification
&<6&> A Sensitivity Analysis of (and Practitioners』 Guide to) Convolutional Neural Networks for Sentence Classification
-----------------------------------------------
先回答問題,最簡單的方法可以用doc2vec from gensim=============================================為啥要用doc2vec 換句話說,為什麼doc2vec是性價比最高的選擇。注意我一直強調簡單或性價比最高,沒說這是唯一解決方案。我們先看看其他的解決方法:1.用word2vec做:如果word能表示成向量,最直觀的思路,對於phrase和sentence,可以將組成它們的所有word詞向量加起來(簡單相加不做任何處理),作為短語向量,句向量。首先這個方法確實有效,但只對15個字以內的短句子比較有效,這種方案常用在搜索時做query比對。如果簡單向量相加,丟掉了很多詞與詞相關意思,也對句子與句子的模式、結構等照成負面影響,無法更精細的表達句子與句子之間的關係。2.用LSI或LSA做:LSI是處理相似度的,而基於的是SVD分解方式,SVD分解方式主要用於特徵降維,LSI求解出來的相似度跟topic相關性更強,而句子結構等信息較少。順便說下,句子中詞的順序是不會影響LSI相似度結果的,可想而知了。
而對於word2vec和doc2vec的模型異同點,以下文章提到了:
引用自:語義分析的一些方法(中篇)
Le和Mikolov在文章《Distributed Representations of Sentences and Documents》里介紹了sentence vector。
先看c-bow方法,相比於word2vec的c-bow模型,區別點有:
- 訓練過程中新增了paragraph id,即訓練語料中每個句子都有一個唯一的id。paragraph id和普通的word一樣,也是先映射成一個向量,即paragraph vector。paragraph vector與word vector的維數雖一樣,但是來自於兩個不同的向量空間。在之後的計算里,paragraph vector和word vector累加或者連接起來,作為輸出層softmax的輸入。在一個句子或者文檔的訓練過程中,paragraph id保持不變,共享著同一個paragraph vector,相當於每次在預測單詞的概率時,都利用了整個句子的語義。
- 在預測階段,給待預測的句子新分配一個paragraph id,詞向量和輸出層softmax的參數保持訓練階段得到的參數不變,重新利用梯度下降訓練待預測的句子。待收斂後,即得到待預測句子的paragraph vector。
圖14. sentence2vec cBow演算法
sentence2vec相比於word2vec的skip-gram模型,區別點為:在sentence2vec里,輸入都是paragraph vector,輸出是該paragraph中隨機抽樣的詞。
引用完畢。我們看看gensim文檔中出現了一個dbow參數,而且是這麼解釋的,dbow_words if set to 1 trains word-vectors (in skip-gram fashion) simultaneous with DBOW doc-vector training; default is 0 (faster training of doc-vectors only).
也就是要快還是要效果你是可以控制的,這就是體現性價比的地方。
===================================================================以上,所以推薦doc2vec from gensim已經有人提到了,如何定義句子相似度是這個問題的關鍵。一種理解方式是一個句子是一個word sequence,句子之間的相似度是topic層面的東西;一種理解方式是一個句子表達了一個meaning,句子之間相似度是衡量兩個句子在何種程度上可以paraphrase。
我猜題主想問的是第一種理解方式。從word2vec出發最自然的處理方式,上面有人也提到了,就是向量疊加平均;之後Mikolov等還提出了doc2vec,對詞向量進行處理(DM/DBOW),不過基本思路沒有差很遠。
我在實際工作嘗試使用了這些方法,可能在其實用效果上有一些發言權,拋磚引玉。word2vec和doc2vec我都用的gensim的實現,訓練在自己的語料(幾萬文檔)上,語料不大跑得快所以各種參數試了很多。因為訓練出來的模型沒有一個太好的evaluation benchmark(我覺得word analog不太有普遍意義),所以模型的評判更主觀一點兒。中文語境下,通過適當的調參(對結果影響比較顯著的,我覺得有window size和min count),word2vec的結果還比較能看,doc2vec的結果一直都比較差,尤其是比較長一點兒的句子/文章。在文檔級別上,我覺得doc2vec的robust程度還不足以支撐一個產品,穩健性程度不如LSI,或者簡單的tf-idf。 @董力 提到的paper我打算看一下,聽起來是一個可能可行的方法。關於句子相似度的另一種理解方式, @董力說的給我很大啟發。Textual Entailment角度來看這個問題應該會比較準確,能更深層次地model問題,打算重新溫習一下傳統方法,看看有沒有能跟word embedding結合的點。謝邀。。。 這裡面牛人太多,我就說下自己嘗試過方法一點看法。
首先,目前的模型對於長句子絕大部分不能work。Mikolov在google group裡面也回答這個模型很難訓練句子詞向量。
不過對於短句子或者片語效果還是很明顯的。
1、項目中想對評價里抽取的屬性情感詞進行聚類。我將評價裡面抽取出屬性情感片語合成一個詞放入語料中,然後用word2ve訓練,效果非常好,已經在實際項目中使用。2、對於短文本如query ,我用word2vec 的變種DM/DBOW訓練句子向量,效果也挺不錯。不過這個也只是一定的嘗試,其實編輯距離也能ko絕大部分這種問題。。。
總體上感覺word2vec 模型還是過於粗暴,一個詞的意思基本固定不變,最多也就兩三種意思。用word2vec這種基於共現的方法還是不錯的。
不過對於句子,這個就精細很多,即使絕大部分詞語相同,整句話的意思也很有可能南轅北轍。句子級別的訓練,句法分析感覺絕對少不了,個人比較看好lstm 這種模型。附上一篇牛人剛寫的文章 ,很有意思。感受就是很多時候不work其實不是模型的問題,而是語料和參數。。。
《How to Generate a Good Word Embedding?》導讀 《How to Generate a Good Word Embedding?》導讀
如何通過詞向量技術來計算2個文檔的相似度? - 知乎用戶的回答引用一下自己的答案~
Paraphrase Identification (State of the art)
原始的word2vec只對words之間的關係進行建模。題主要做句子相似度計算,首先需要定義清楚一個問題:「句子相似」是什麼,「句子不相似」是什麼?,別看這兩個定義naive,仔細考慮一下會發現,這兩個定義不同,則句子相似度的建模方法差別也會很大。
如果,我們採用最簡單的定義:「句子相似」=「句子中的詞語相似」,則用word2vec得到的詞向量相加,然後歸一化一下,是可以較好得代表句子的。然而,如果希望model到詞語順序和語法結構恐怕就不能用這種方法。
此外,對句子進行建模的paper是有的,只是大量都不是為了得到句子的embedding,而是為了處理一些傳統NLP任務,比如分詞(使用HMM, CTR)等等。
BTW. 這篇 Distributed Representations of Sentences and Documents 參考意義不大,只要還停留在word2vec的框架內,就無法model到語法結構對句子相似度的影響。有不少童鞋問到具體的實現方法,更新一下...
這篇paper的方法並沒有具體介紹太多細節,感覺他們著重在表示很簡單的pca就能得到很好的效果。另一篇相關文章會對理解這個方法的具體實現有很多幫助,來自同樣的作者:
Arora, Sanjeev, et al. "A latent variable model approach to pmi-based word embeddings." Transactions of the Association for Computational Linguistics 4 (2016): 385-399.
附上這篇paper的github repo: PrincetonML/SIF
&<如果原作者的下載量暴增他是不是該感謝我哈哈哈哈&>
===========================================
找草稿找了半天,打不開還是來重新寫吧。
上面有人提到sentence2vec,最近有篇很有用的paper叫 "A simple but tough-to-beat baseline for sentence embeddings."Arora, Sanjeev, Yingyu Liang, and Tengyu Ma.2017. 正如其名,演算法簡單但performance很好。直接上圖:
簡而言之就是weighted average word embedding = sentence embedding。 其中weights = a / (a + p(w)),然後經過pca。
真的很簡單吧?我最近在用自己的dataset來跑word2vec和這篇paper, performance真的好很多(make sense很多),當然因為沒有benchmark所以沒法提供一個evaluation metrics.
這篇paper另一個比較convincing的地方是,他們的試驗是基於sts task,
其中專門有sentence similarity的task。
個人覺得word2vec就是很虛,因為大家都在用所以拿來用。從syntax意義上來說,它或許能很好的做出representation,可是semantic representation呢? 我不覺得word embedding能準確capture。上面幾個回答幾乎都提到了怎麼定義sentence similarity。如果trivial task,或許word embedding就夠,performance肯定會比word overlap之類的要好得多,如果真的想要準確表現semantic level, 那就很難說,不覺得是能靠調調參數提高performance就能解決的問題。
另一種方法是基於LDA + RNN的lm。還沒試過,不過感覺基於lda的話,既能得到distributed representation又有topical semantics。 有人能嘗試一下就好了。
效果還行,句子一長效果就差了;
用lstm做句子encoding吧,不需要word2vec這種,根據句子上下文做訓練;
不行的話用denoising Autoencoder,這種效果最好哦;
也可以試試看seq2seq的最終向量,輸入輸出都是句子本身;完全無監督;
實在不行只能用那個啥variational Autoencoder了,用kl divergence計算句子的距離;
怎樣利用word2vec計算兩篇文檔的相似度?有沒有大神給我一些啟發? - 機器學習Mikolov後來又發了一篇:http://arxiv.org/abs/1405.4053,完全符合你的需求。但是實際應用中這篇paper的效果並不好。可以把詞向量作為輸入,用LSTM或者CNN來對句子建模。知乎大神太多,匿了。
我用gensim實驗了wmd距離,覺得效果還是不錯的,但是速度比向量平均後求餘弦要慢很多的。但是可以用求餘弦的方法來進行快速判斷,然後用wmd來精細化判斷。下面是我用100萬對句子數據畫的散點圖,用餘弦距離做快速篩選還是很靠譜的。
有多種選擇,你可以使用similarity.docsim doc2vec,也可以使用lsh,前面兩種屬於gensim,後面一種屬於scikit-learn,具體的例子可以參見 用docsim/doc2vec/LSH比較兩個文檔之間的相似度
在NLP裡面和sentence modeling、semantic matching相關的模型很多,分別是對單個語句進行建模、對兩個語句之間的語義關聯進行建模。
將一句話所有詞的詞向量加在一起求平均,將所得結果作為句向量也是一種簡單的解決方案,所以由word2vec衍生出了seq2vec和doc2vec之類。
當然模型弄得很複雜,計算成本也會高很多,原理也沒那麼好理解,最後同樣是在一些通用的測試數據集上刷成績,提高一兩個百分點。所以很多模型僅限於研究,在實際應用中還是需要結合模型的性能和複雜度進行選擇。
最近也在研究這個方面。
文檔相似度首先得看文檔長度,如果是短文本,如句子層面,傳統方法tf-idf,lsi,lda,餘弦相似度以及交並集相似度等方法基本只從詞頻出發,無法體現相近的詞義。效果一般,不過也看具體應用,如果只是詞形上的相似而基本不涉及到語義上的相似,那麼可用。反之,則需要詞向量等方式來解決。
如果是長文本,可以用doc2vec,效果一般,或者用word2vec,然後用wmd度量相似度,但gensim中的實現貌似不能含有沒有出現過的詞。。。
佔個坑
在Stack Overflow上看到一個回答
How to calculate the sentence similarity using word2vec model of gensim with python
還有人說分詞後載入word2vec模型直接用 gensim.models.word2vec.n_similarity(ws1, ws2) 計算兩個詞序列之間的相似度,不知道好不好用
stackoverflow上有一個相似的提問,How to calculate the sentence similarity using word2vec model of gensim with python按照這個帖子,對於並不是非常長的句子,簡單的向量疊加可能會起作用。但最高票回答者也承認這是一個非常有挑戰性的問題。
This is actually a pretty challenging problem that you are asking. Computing sentence similarity requires building a grammatical model of the sentence, understanding equivalent structures (e.g. "he walked to the store yesterday" and "yesterday, he walked to the store"), finding similarity not just in the pronouns and verbs but also in the proper nouns, finding statistical co-occurences / relationships in lots of real textual examples, etc.
The simplest thing you could try -- though I don"t know how well this would perform and it would certainly not give you the optimal results -- would be to first remove all "stop" words (words like "the", "an", etc. that don"t add much meaning to the sentence) and then run word2vec on the words in both sentences, sum up the vectors in the one sentence, sum up the vectors in the other sentence, and then find the difference between the sums. By summing them up instead of doing a word-wise difference, you"ll at least not be subject to word order. That being said, this will fail in lots of ways and isn"t a good solution by any means (though good solutions to this problem almost always involve some amount of NLP, machine learning, and other cleverness).
So, short answer is, no, there"s no easy way to do this (at least not to do it well).
推薦閱讀:
※如何評價Word2Vec作者提出的fastText演算法?深度學習是否在文本分類等簡單任務上沒有優勢?
※為何做DL用word2vec比glove多?
※怎樣計算兩篇文檔的相似度?