文章相似度算法調研

文章相似度算法大體上分爲兩類,現實中文本相似性算法分爲兩類,一類是hash算法,一類是字符串直接匹配算法

image-20210824163034574

字符串匹配算法在現實情況中由於效率太低一般沒人使用,經過調研發現,在實際使用中,幾乎所有人都會在simhashminhash算法中使用其中一種算法計算文本相似度。

一、基本概念

1.TF

TF(term frequency),就是分詞出現的頻率:該分詞在該文檔中出現的頻率,算法是:(該分詞在該文檔出現的次數)/(該文檔分詞的總數),這個值越大表示這個詞越重要,即權重就越大。

例如:一篇文檔分詞後,總共有500個分詞,而分詞”Hello”出現的次數是20次,則TF值是: tf =20/500=2/50=0.04

2.IDF

IDF(inversedocument frequency)逆向文件頻率,一個文檔庫中,一個分詞出現在的文檔數越少越能和其它文檔區別開來。算法是: log((總文檔數/出現該分詞的文檔數)+0.01) ;(注加上0.01是爲了防止log計算返回值爲0)。

例如:一個文檔庫中總共有50篇文檔,2篇文檔中出現過“Hello”分詞,則idf是:

Idf = log(50/2 + 0.01) = log(25.01)=1.39811369

3.TF-IDF

TF-IDF(termfrequency–inverse document frequency)是一種用於資訊檢索與資訊探勘的常用加權技術。TF-IDF是一種統計方法,用以評估一字詞對於一個文件集或一個語料庫中的其中一份文件的重要程度。字詞的重要性隨著它在文件中出現的次數成正比增加,但同時會隨著它在語料庫中出現的頻率成反比下降。TF-IDF加權的各種形式常被搜尋引擎應用,作爲文件與用戶查詢之間相關程度的度量或評級。除了TF-IDF以外,因特網上的搜尋引擎還會使用基於連結分析的評級方法,以確定文件在搜尋結果中出現的順序。

在文本挖掘中,要對文本庫分詞,而分詞後需要對個每個分詞計算它的權重,而這個權重可以使用TF-IDF計算。

TF-IDF結合計算就是 tf*idf,比如上面的“Hello”分詞例子中:

TF-IDF = tf* idf = (20/500)* log(50/2 + 0.01)= 0.04*1.39811369=0.0559245476

4.準確率和召回率

召回率(Recall Rate,也叫查全率)是檢索出的相關文檔數和文檔庫中所有的相關文檔數的比率,衡量的是檢索系統的查全率;

準確率是檢索出的相關文檔數與檢索出的文檔總數的比率,衡量的是檢索系統的查準率。

image-20210824164328142

5.編輯距離

編輯距離(Minimum Edit Distance,MED),由俄羅斯科學家 Vladimir Levenshtein 在1965年提出,也因此而得名 Levenshtein Distance。

在信息論、語言學和計算機科學領域,Levenshtein Distance 是用來度量兩個序列相似程度的指標。通俗地來講,編輯距離指的是在兩個單詞<w_1,w_2>之間,由其中一個單詞w_1轉換爲另一個單詞w_2所需要的最少單字符編輯操作次數。

在這裏定義的單字符編輯操作有且僅有三種:

  • 插入(Insertion)
  • 刪除(Deletion)
  • 替換(Substitution)

譬如,"kitten" 和 "sitting" 這兩個單詞,由 "kitten" 轉換爲 "sitting" 需要的最少單字符編輯操作有:

1.kitten → sitten (substitution of "s" for "k")
2.sitten → sittin (substitution of "i" for "e")
3.sittin → sitting (insertion of "g" at the end)

因此,"kitten" 和 "sitting" 這兩個單詞之間的編輯距離爲 3 。

6.漢明距離(海明距離)

簡單的說,Hamming Distance,又稱漢明距離,在信息論中,兩個等長字符串之間的漢明距離是兩個字符串對應位置的不同字符的個數。也就是說,它就是將一個字符串變換成另外一個字符串所需要替換的字符個數。例如:1011101 與 1001001 之間的漢明距離是 2。至於我們常說的字符串編輯距離則是一般形式的漢明距離。

海明距離的求法:異或時,只有在兩個比較的位不同時其結果是1 ,否則結果爲0,兩個二進制“異或”後得到1的個數即爲海明距離的大小。

7.鴿巢原理

桌上有十個蘋果,要把這十個蘋果放到九個抽屜裏,無論怎樣放,我們會發現至少會有一個抽屜裏面放不少於兩個蘋果。這一現象就是我們所說的“抽屜原理”。 抽屜原理的一般含義爲:“如果每個抽屜代表一個集合,每一個蘋果就可以代表一個元素,假如有n+1個元素放到n個集合中去,其中必定有一個集合裏至少有兩個元素。”

抽屜原理有時也被稱爲鴿巢原理,它是組合數學中一個重要的原理 。

8.倒排索引

搜索引擎中每個文件都對應一個文件ID,文件內容被表示爲一系列關鍵詞的集合(實際上在搜索引擎索引庫中,關鍵詞也已經轉換爲關鍵詞ID)。例如“文檔1”經過分詞,提取了20個關鍵詞,每個關鍵詞都會記錄它在文檔中的出現次數和出現位置。

得到正向索引的結構如下:

​ “文檔1”的ID > 單詞1:出現次數,出現位置列表;單詞2:出現次數,出現位置列表;…………。

​ “文檔2”的ID > 此文檔出現的關鍵詞列表。

正向索引

根據正向索引,查找的時候是挨個文檔查找對應的關鍵詞,在數據量大的情況下效率會很低,典型的是mysql的like查詢方式。

倒排索引則反之,是根據關鍵詞查詢文檔的技術。

倒排索引的結構如下:

倒排索引

倒排索引的典型應用是ElasticSearch。

二、simhash算法及原理簡介

1. 什麼是SimHash

SimHash算法是Google在2007年發表的論文《Detecting Near-Duplicates for Web Crawling》中提到的一種指紋生成算法,被應用在Google搜索引擎網頁去重的工作之中。

簡單的說,SimHash算法主要的工作就是將文本進行降維,生成一個SimHash值,也就是論文中所提及的“指紋”,通過對不同文本的SimHash值進而比較海明距離,從而判斷兩個文本的相似度。

對於文本去重這個問題,常見的解決辦法有餘弦算法、歐式距離、Jaccard相似度、最長公共子串等方法。但是這些方法並不能對海量數據高效的處理。

比如說,在搜索引擎中,會有很多相似的關鍵詞,用戶所需要獲取的內容是相似的,但是搜索的關鍵詞卻是不同的,如“北京好喫的火鍋“和”哪家北京的火鍋好喫“,是兩個可以等價的關鍵詞,然而通過普通的hash計算,會產生兩個相差甚遠的hash串。而通過SimHash計算得到的Hash串會非常的相近,從而可以判斷兩個文本的相似程度。

2. SimHash的計算原理

SimHash算法主要有五個過程:分詞、Hash、加權、合併、降維。

20180704175340391

3.步驟

3.1 分詞

給定一段語句,進行分詞,得到有效的特徵向量,然後爲每一個特徵向量設置1-5等5個級別的權重(如果是給定一個文本,那麼特徵向量可以是文本中的詞,其權重可以是這個詞出現的次數)。例如給定一段語句:“CSDN博客結構之法算法之道的作者July”,分詞後爲:“CSDN 博客 結構 之 法 算法 之 道 的 作者 July”,然後爲每個特徵向量賦予權值:CSDN(4) 博客(5) 結構(3) 之(1) 法(2) 算法(3) 之(1) 道(2) 的(1) 作者(5) July(5),其中括號裏的數字代表這個單詞在整條語句中的重要程度,數字越大代表越重要。

計算權重的值可以使用TF-IDF計算。

其中,數字越大,代表特徵詞在句子中的重要性就越高。這樣,我們就得到了一個文本的分詞的詞向量和每個詞向量對應的權重。

3.2 Hash

通過hash函數計算各個特徵向量的hash值,hash值爲二進制數01組成的n-bit簽名。比如“CSDN”的hash值Hash(CSDN)爲100101,“博客”的hash值Hash(博客)爲“101011”。就這樣,字符串就變成了一系列數字。

3.3 加權

前面的計算我們已經得到了每個詞向量的Hash串和該詞向量對應的權重,這一步我們計算權重向量W=hash*weight,即遇到1則hash值和權值正相乘,遇到0則hash值和權值負相乘。

例如給“CSDN”的hash值“100101”加權得到:W(CSDN) = 100101 * 4 = 4 -4 -4 4 -4 4,給“博客”的hash值“101011”加權得到:W(博客)=101011 * 5 = 5 -5 5 -5 5 5,其餘特徵向量類似此般操作。

3.4 合併

對於一個文本,我們計算出了文本分詞之後每一個特徵詞的權重向量,在合併這個階段,我們把文本所有詞向量的權重向量相累加,得到一個新的權重向量,拿前兩個特徵向量舉例,例如“CSDN”的“4 -4 -4 4 -4 4”和“博客”的“5 -5 5 -5 5 5”進行累加,得到“4+5 -4+-5 -4+5 4+-5 -4+5 4+5”,得到“9 -9 1 -1 1”。

3.5 降維

對於前面合併後得到的文本的權重向量,如果大於0則置1,否則置0,就可以得到該文本的SimHash值。最後我們便可以根據不同語句simhash的海明距離來判斷它們的相似度。例如把上面計算出來的“9 -9 1 -1 1 9”降維(某位大於0記爲1,小於0記爲0),得到的01串爲:“1 0 1 0 1 1”,從而形成它們的simhash簽名。

到此爲止,我們已經計算出了一個文本的SimHash值。那麼,如何判斷兩個文本是否相似呢?我們要用到海明距離。

3.6 相似度判斷

一般來說SimHash值是一個64位的長整數,作爲對應文本的簽名,從經驗上來看,只要找到海明距離在3以內的所有簽名,即可找出所有相似的文本。

4. 海量數據下的漢明距離計算

如何擴展到海量數據呢?譬如如何在海量的樣本庫中查詢與其海明距離在3以內的記錄呢?

一種方案是,查找待查詢文本的64位simhash code的所有3位以內變化的組合:大約需要四萬多次的查詢。
另一種方案是,預生成庫中所有樣本simhash code的3位變化以內的組合:大約需要佔據4萬多倍的原始空間。

這兩種方案,要麼時間複雜度高,要麼空間複雜度複雜,能否有一種方案可以達到時空複雜度的絕佳平衡呢?答案是肯定的,這時候有一種較好的辦法來均衡計算海明距離的時間複雜度和空間複雜度,具體的計算思想是這樣的:

我們可以把 64 位的二進制simhash簽名均分成4塊,每塊16位。根據鴿巢原理(也稱抽屜原理),如果兩個SimHash相似,即兩個簽名的海明距離在 3 以內,它們必有一塊完全相同。

20200406211055101

把分成的4 塊中的每一個塊分別作爲前16位來進行查找,建倒排索引。

三、Minhash算法及原理介紹

給出N個集合,找到相似的集合對,如何實現呢?直觀的方法是比較任意兩個集合。那麼可以十分精確的找到每一對相似的集合,但是時間複雜度是\(O(n^2)\)。當N比較小時,比如K級,此算法可以在接受的時間範圍內完成,但是如果N變大時,比B級,甚至P級,那麼需要的時間是不能夠被接受的。比如N= 1B = 1,000,000,000。一臺計算機每秒可以比較1,000,000,000對集合是否相等。那麼大概需要15年的時間才能找到所有相似的集合!

上面的算法雖然效率很低,但是結果會很精確,因爲檢查了每一對集合。假如,N個集合中只有少數幾對集合相似,絕大多數集合都不等呢?那麼根據上述算法,絕大多數檢測的結果是兩個結合不相似,可以說這些檢測“浪費了計算時間”。所以,如果能找到一種算法,將大體上相似的集合聚到一起,縮小比對的範圍,這樣只用檢測較少的集合對,就可以找到絕大多數相似的集合對,大幅度減少時間開銷。雖然犧牲了一部分精度,但是如果能夠將時間大幅度減少,這種算法還是可以接受的。接下來的內容講解如何使用Minhash和LSH(Locality-sensitive Hashing)來實現上述目的,在相似的集合較少的情況下,可以在\(O(n)\)時間找到大部分相似的集合對。

1.Jaccard相似度

判斷兩個集合是否相等,一般使用稱之爲Jaccard相似度的算法(後面用Jac(S1,S2)來表示集合S1和S2的Jaccard相似度)。舉個列子,集合X = {a,b,c},Y = {b,c,d}。那麼Jac(X,Y) = 2 / 3 = 0.67。也就是說,結合X和Y有67%的元素相同。下面是形式的表述Jaccard相似度公式:

\[J(A,B)=\frac{|A∩B|}{|AUB|} \]

也就是兩個集合交集的個數比上兩個集合並集的個數。範圍在[0,1]之間。

2.降維技術Minhash

原始問題的關鍵在於計算時間太長。所以,如果能夠找到一種很好的方法將原始集合壓縮成更小的集合,而且又不失去相似性,那麼可以縮短計算時間。Minhash可以幫助我們解決這個問題。舉個例子,\(S1 = \{a,d,e\}\)\(S2 = \{c, e\}\),設全集\(U = \{a,b,c,d,e\}\)。集合可以如下表示:

行號 元素 S1 S2 類別
1 a 1 0 Y
2 b 0 0 Z
3 c 0 1 Y
4 d 1 0 Y
5 e 1 1 X

表1中,列表示集合,行表示元素,值1表示某個集合具有某個值,0則相反(X,Y,Z的意義後面討論)。Minhash算法大體思路是:採用一種hash函數,將元素的位置均勻打亂,然後將新順序下每個集合第一個值爲1的元素作爲該集合的特徵值。比如哈希函數\(h_{1}(i) = (i + 1)\% 5\),其中i爲行號。作用於集合S1和S2,得到如下結果:

行號 元素 S1 S2 類別
1 e 1 1 X
2 a 1 0 Y
3 b 0 0 Z
4 c 0 1 Y
5 d 1 0 Y
Minhash e e

這時,Minhash(S1) = e,Minhash(S2) = e。也就是說用元素e表示S1,用元素e表示集合S2。那麼這樣做是否科學呢?也就是說,如果Minhash(S1) 等於Minhash(S2),那麼S1是否和S2類似呢?

3.一個神奇的結論

\[P(Minhash(S­1) = Minhash(S2)) = Jac(S1,S2) \]

在哈希函數\(h_{1}\)均勻分佈的情況下,集合\(S1\)的Minhash值和集合\(S2\)的Minhash值相等的概率等於集合\(S1\)與集合\(S2\)的Jaccard相似度,下面簡單分析一下這個結論。

S1和S2的每一行元素可以分爲三類:

  • X類 均爲1。比如表2中的第1行,兩個集合都有元素e。

  • Y類 一個爲1,另一個爲0。比如表2中的第2行,表明S1有元素a,而S2沒有。

  • Z類 均爲0。比如表2中的第3行,兩個集合都沒有元素b。

這裏忽略所有Z類的行,因爲此類行對兩個集合是否相似沒有任何貢獻。由於哈希函數將原始行號均勻分佈到新的行號,這樣可以認爲在新的行號排列下,任意一行出現X類的情況的概率爲

\[\frac{|X|}{|X|+|Y|} \]

這裏爲了方便,將任意位置設爲第一個出現X類行的行號。所以

\[P(第一個出現X類) = \frac{|X|}{|X|+|Y|} = Jac(S1,S2) \]

這裏很重要的一點就是要保證哈希函數可以將數值均勻分佈,儘量減少衝撞。

一般而言,會找出一系列的哈希函數,比如h個(h << |U|),爲每一個集合計算h次Minhash值,然後用h個Minhash值組成一個摘要來表示當前集合(注意Minhash的值的位置需要保持一致)。舉個列子,還是基於上面的例子,現在又有一個哈希函數h2(i) = (i -1)% 5。那麼得到如下集合:

行號 元素 S1 S2 類別
1 b 0 0 Z
2 c 0 1 Y
3 d 1 0 Y
4 e 1 1 X
5 a 1 0 Y
Minhash d c

所以,現在用摘要表示的原始集合如下:

哈希函數 S1 S2
h1(i) = (i + 1) % 5 e e
h2(i) = (i - 1) % 5 d c

從上表還可以得到一個結論,令X表示Minhash摘要後的集合對應行相等的次數(比如上表,X=1,因爲哈希函數h1情況下,兩個集合的minhash相等,h2不等):

\[X\backsim B(h,Jac(S1,S2)) \]

X符合次數爲h,概率爲Jac(S1,S2)的二項分佈。那麼期望E(X) = h * Jac(S1,S2) = 2 * 2 / 3 = 1.33。也就是每2個hash計算Minhash摘要,可以期望有1.33元素對應相等。

所以,Minhash在壓縮原始集合的情況下,保證了集合的相似度沒有被破壞。

4.LSH – 局部敏感哈希

現在有了原始集合的摘要,但是還是沒有解決最初的問題,仍然需要遍歷所有的集合對,,才能所有相似的集合對,複雜度仍然是\(O(n^2)\)。所以,接下來描述解決這個問題的核心思想LSH。其基本思路是將相似的集合聚集到一起,減小查找範圍,避免比較不相似的集合。仍然是從例子開始,現在有5個集合,計算出對應的Minhash摘要,如下:

S1 S2 S3 S4 S5
區間1 b b a b a
c c a c b
d b a d c
區間2 a e b e d
b d c f e
e a d g a
區間3 d c a h b
a a b b a
d e a b e
區間4 d a a c b
b a c b a
d e a b e

上面的集合摘要採用了12個不同的hash函數計算出來,然後分成了B = 4個區間。前面已經分析過,任意兩個集合(S1,S2)對應的Minhash值相等的概率\(r = Jac(S1,S2)\)。先分析區間1,在這個區間內,\(P(集合S1等於集合S2) = r3\)。所以只要S­1和S2的Jaccard相似度越高,在區間1內越有可能完成全一致,反過來也一樣。那麼\(P(集合S1不等於集合S2) = 1 - r3\)。現在有4個區間,其他區間與第一個相同,所以

\[P(4個區間上,集合S1都不等於集合S2) = (1 – r^3)^4 \]

\[P(4個區間上,至少有一個區間,集合S1等於集合S2) = 1 - (1 – r^3)^4 \]

這裏的概率是一個r的函數,形狀猶如一個S型,如下:

04201419-04566a4d0aa346eea6488441ed6ef551

如果令區間個數爲B,每個區間內的行數爲C,那麼上面的公式可以形式的表示爲:

\[P(B個區間中至少有一個區間中兩個結合相等) = 1 - (1 – r^C)^B \]

令r = 0.4,C=3,B = 100。上述公式計算的概率爲0.9986585。這表明兩個Jaccard相似度爲0.4的集合在至少一個區間內衝撞的概率達到了99.9%。根據這一事實,我們只需要選取合適的B和C,和一個衝撞率很低的hash函數,就可以將相似的集合至少在一個區間內衝撞,這樣也就達成了本節最開始的目的:將相似的集合放到一起。具體的方法是爲B個區間,準備B個hash表,和區間編號一一對應,然後用hash函數將每個區間的部分集合映射到對應hash表裏。最後遍歷所有的hash表,將衝撞的集合作爲候選對象進行比較,找出相識的集合對。整個過程是採用\(O(n)\)的時間複雜度,因爲B和C均是常量。由於聚到一起的集合相比於整體比較少,所以在這小範圍內互相比較的時間開銷也可以計算爲常量,那麼總體的計算時間也是\(O(n)\)

四、實踐

文本查重

參考文檔

《計算分詞的Tf-idf值》

《文本查重:知識點總結》

《利用Minhash和LSH尋找相似的集合》

《NLP點滴——文本相似度》

《java論文查重程序》

《相似文檔查找算法之 simHash及其 java 實現》

《文本查重-SimHash和MinHash算法》

simhash算法

Choosing between SimHash and MinHash for a production system

docs-termvectors

Elasticsearch: getting the tf-idf of every term in a given document

Document Deduplication with Locality Sensitive Hashing

Near-Duplicate Detection using MinHash: Background

[Document Deduplication][1] MinHash

[Document Deduplication][2] Locality Sensitive Hashing

how-scoring-works-in-elasticsearch

相關論文

SuperMinHash – A New Minwise Hashing Algorithm for Jaccard Similarity Estimation

Detecting Near-Duplicates for Web Crawling

Deduplication in a massive clinical note dataset

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章