1. 程式人生 > >從0到1,瞭解NLP中的文字相似度

從0到1,瞭解NLP中的文字相似度

本文由雲+社群發表

作者:netkiddy

導語

AI在2018年應該是網際網路界最火的名詞,沒有之一。時間來到了9102年,也是專案相關,涉及到了一些AI寫作相關的功能,為客戶生成一些素材文章。但是,AI並不一定最懂你,客戶對於AI寫出來的文章,多少是會做些修改的。為了更好的衡量出AI文章的可用度,在這兒就會需要存有一個反饋的環節,來看看使用者潤色後的文章與原始AI文章之間的區別是多大,AI寫出來的文章可用性是否足夠。由於目前還沒精力細究AI寫作其中的細節,為了更好地計算每次成文與原文的區分,便花了點小時間看了看文字相似度的知識點,記錄於此。

本文將從預備知識的概念開始介紹,從距離名詞,到文字分詞,相似度演算法,並將這些概念融合、統一的介紹NLP中文字相似度的知識,期望通過本文,大家可以與我一樣,對這些知識有個基本的瞭解。

幾個距離

在介紹更多的內容之前,我們需要了解文字距離的概念,這些距離是我們在後文比較文字相似度的基礎,所以下面將首先形象的為大家介紹幾個重要且基礎的距離含義。

歐幾里德距離

Euclidean Distance,是最直白的、最容易直觀理解的距離度量方法,在二維空間來看,用一句幾乎耳熟能詳的話來解釋就是:兩點之間直線最短。這句話中說到的「直線距離」就是歐幾里德距離。我們來看下相關數學公式定義。

二維的公式:

​ p = sqrt( (x1-y1)^2+(x2-y2)^2 )

三維的公式:

​ p = sqrt( (x1-y1)^2+(x2-y2)^2+(x3-y3)^2 )

當然,畢竟不是存活於劉慈溪的三體世界之下,我們在小學或者日常所能感知到的多是,二維或者三維空間的距離,當大於3維,從數學理論上的n維空間的公式,在歐幾里德空間中,點x

=(x1,...,xn)和 y =(y1,...,yn)之間的歐氏距離為:

​ p = sqrt( (x1-y1)^2+(x2-y2)^2+(x3-y3)^2+ ... +(xn-yn)^2 )

曼哈頓距離

Manhattan Distance的命名原因,是從規劃為方型建築區塊的城市(如曼哈頓)間,最短的計程車從一個點A到另一個點B的行車路徑距離,任何往東三區塊、往北六區塊的的路徑一定最少要走九區塊(計程車當然不能穿插過街區),沒有其他捷徑。抽象到數學角度,從點A(x1, y1)到點B(x2, y2)的曼哈頓距離為兩個點上在標準座標系上的絕對軸距之總和:

​ p = |x1-x2| + |y1-y2|

那麼,曼哈頓距離和歐幾里得距離的區別是什麼呢?我們從維基百科拉過來一張圖,就可以很直白的看到這二者的區別,假設在下方棋盤一樣的圖示中,白色方塊表示為建築物,灰色線條表示為道路,那麼其中綠色線路表示為黑色兩點之間的歐幾里德距離(兩點之間直線最短),而剩下的紅藍黃三色線路表示的均為為曼哈頓距離:

img歐幾里德距離和曼哈頓距離

切比雪夫距離

Chebyshev distance得名自俄羅斯數學家切比雪夫。大家對切比雪夫應該多少是覺得有印象的,沒錯,切比雪夫更被我們熟悉的是中學時候學過的關於他的切比雪夫多項式吧。所謂切比雪夫距離,是將兩點之間的距離定義為其各座標數值差的最大值。如果我們以二維空間中兩點A(x1,y1)和B(x2,y2)二點為例,其切比雪夫距離:

​ p = max(|x2-x1|, |y2-y1|)

更形象的來介紹,切比雪夫距離在二維空間有著一個應用場景:國際象棋中「國王」的行走距離。由於王可以往斜前或斜後方向移動一格,因此可以較有效率的到達目的的格子,我們將國際象棋的棋盤對映到二維直角座標系中,格子的邊長定義為1,座標的x軸及y軸和棋盤方格平行,原點恰落在某一格的中心點,則「國王」從一個位置走到其他位置需要的步數恰為這兩個位置的切比雪夫距離。下圖是棋盤上所有位置距f6位置的切比雪夫距離。

img國際象棋的國王

餘弦距離

Cosine distance使用兩個向量夾角的餘弦值作為衡量兩個個體間差異的大小。相比於歐幾里德距離,餘弦距離更加註重的是兩個向量在方向上的差異。首先我們來看一下數學中餘弦定理的定義:

img餘弦定理公式

關於餘弦定理,這兒我們簡單來複習下這個初中概念。前幾年曾經有過一個地方的高考題出過餘弦定理的證明,當時也有人通過向量的方法來證明,兩行就得出了答案(其實這兒有點疑問,因為課本中對向量內積是通過餘弦定理來證明的,所以從個人來看通過向量內積來證明餘弦定理是有些邏輯問題的),那麼具體應該如何證明呢?其實很簡單,通過一張圖就可以證明:

img餘弦定理的證明

結合這張圖,花2分鐘應該就可以得到餘弦定理的結論了,其中需要了解的一點事對於b*sin(θ)的定義,是直接使用了畢氏定理。

數學家已經證明,餘弦的這種計算方法對n維向量也成立。假定A和B是兩個n維向量,A是 [A1, A2, ..., An] ,B是 [B1, B2, ..., Bn] ,則A與B的夾角θ的餘弦等於:

img餘弦定理的向量計算公式

使用這個公式,我們會可以更方便的計算餘弦距離。

回到餘弦距離上來,它與我們上面說的歐幾里得距離的區別是什麼呢?我們引用一張網上的圖片來形象的瞭解下餘弦距離的含義:

img餘弦距離和歐幾里得距離

在上圖中,歐幾里德距離dist(A, B)衡量的是空間中兩點的絕對距離,跟各個點所在的位置座標是直接相關的;而餘弦距離衡量的是空間向量的夾角,更加體現在方向上的差異,而不是位置。如果我們保持A點位置不變,B點朝原方向遠離座標軸原點,那麼這個時候餘弦距離是保持不變的(因為夾角沒有發生變化),而A、B兩點的歐幾里德距離顯然在發生改變,這就是兩者之間的不同之處。

關於餘弦距離和歐幾里得距離在現實場景中的區別,我們可以通過下面這個例子來形象的瞭解。現在有使用者A和使用者B分別對外賣騎手員工X和員工Y進行了評分。使用者A對員工X的評分為2,對員工Y的評分為3,表示到座標系中為座標點AB(2, 3);同樣使用者B對員工X、Y的評分表示為座標點B(4, 6),因此他們之間的歐幾里德距離為:

​ p = sqrt((2 - 4)^2 + (3- 6)^2) = 3.6

而他們的餘弦距離為:

​ p = (2 * 4 + 3 * 6) / ( sqrt( 2^2 + 3^2 ) * sqrt( 4^2 + 6^2 ) ) = 1

結合圖示如下,其中,點A與點B之間的之間距離為紅色線段所示,也就是上述的歐幾里得距離。同時,線段0A和線段0B由於斜度相等,也就是夾角為0度,反映出的餘弦距離就是cos(0) = 1,說明二者完全相似。

img使用者評分距離圖示

歐幾里得距離和餘弦距離各自有不同的計算方式和衡量特徵,因此它們適用於不同的資料分析模型:前者能夠體現個體數值特徵的絕對差異,所以更多的用於需要從維度的數值大小中體現差異的分析,如使用使用者行為指標分析使用者價值的相似度或差異。後者則傾向於是從方向上區分差異,而對絕對的數值不敏感,更多的用於使用使用者對內容評分來區分興趣的相似度和差異,同時修正了使用者間可能存在的度量標準不統一的問題(因為餘弦距離對絕對數值不敏感)。

漢明距離

Hamming distance在資訊理論中,表示為兩個「等長」字串之間對應位置的不同字元的個數。換句話說,漢明距離就是將一個字串變換成另外一個字串所需要「替換」的字元個數。如下圖所示:

  • 0110與1110之間的漢明距離是1;
  • 0100與1001之間的漢明距離是3;

img漢明距離圖示

分詞

在瞭解了上述一系列的距離含義之後,我們已經基本瞭解了衡量相似度的一個判定方法,但是對於一段文字內容來說,我們對什麼來計算距離呢?這就涉及到了第二個基礎知識:分詞。

分詞方法

為了實現對文字相似度的比較,我們需要分析文字的內容,也就必然會涉及到對文字進行分詞處理。而說到分詞,其中涉及的內容不比任何一個其他知識點要少,考慮到不是本文重點講述,此處僅僅簡單的列舉了下當前分詞演算法的幾種方向,有興趣的同學可以就此列表再去細細琢磨

  • 基於詞表的分詞方法
    • 正向最大匹配法(forward maximum matching method, FMM)
    • 逆向最大匹配法(backward maximum matching method, BMM)
    • N-最短路徑方法
  • 基於統計模型的分詞方法
    • 基於N-gram語言模型的分詞方法
  • 基於序列標註的分詞方法
    • 基於HMM的分詞方法
    • 基於CRF的分詞方法
    • 基於詞感知機的分詞方法
    • 基於深度學習的端到端的分詞方法

工程方案

從工程角度來看,目前分詞已經有了十分成熟工程實現了,如IK,ansj等,列出一些比較常用的中文分詞方案,以供大家學習使用:

文字相似度

在介紹完距離和分詞之後,接下來,我們就需要來關注計算文字相似度的演算法了。總的來說,計算文字相似度的演算法共分為4類:

  • 基於詞向量
  • 基於具體字元
  • 基於概率統計
  • 基於詞嵌入的

結合我們上文的幾種距離,其中歐幾里德距離、曼哈頓距離和餘弦距離等適合應用於詞向量,漢明距離應屬於基於字元的文字相似度的度量方法。本文接下來將重點介紹基於餘弦複雜度的文字相似度比較演算法,和適用於海量資料的simhash文字相似度演算法,並給予一定的工程實現方案。

餘弦複雜度

對於多個不同的文字或者短文字對話訊息要來計算他們之間的相似度如何,一個好的做法就是將這些文字中詞語,對映到向量空間,形成文字中文字和向量資料的對映關係,再通過計算幾個或者多個不同的向量的差異的大小,來計算文字的相似度。下面介紹一個詳細成熟的向量空間餘弦相似度方法計算相似度演算法。

原理

枯燥的原理不如示例來的簡單明瞭,我們將以一個簡單的示例來介紹餘弦複雜度的原理。現在有下面這樣的兩句話,從我們直覺感官來看,說的是一模一樣的內容,那麼我們通過計算其餘弦距離來看看其相似度究竟為多少。

S1: "為什麼我的眼裡常含淚水,因為我對這片土地愛得深沉"
S2: "我深沉的愛著這片土地,所以我的眼裡常含淚水"

第一步,分詞:

我們對上述兩段話分詞分詞並得到下面的詞向量:

S1: [為什麼 我 的 眼裡 常含 淚水 因為 我 對 這片 土地 愛得 深沉 ,]
S2: [我 深沉 的 愛 著 這片 土地 所以 我 的 眼裡 常含 淚水 ,]

第二步,統計所有片語:

將S1和S2中出現的所有不同片語融合起來,並得到一個詞向量超集,如下:

[眼裡 這片 為什麼 我 的 常含 因為 對 所以 愛得 深沉 愛 著 , 淚水 土地]

第三步,獲取詞頻:

對應上述的超級詞向量,我們分別就S1的分詞和S2的分詞計算其出現頻次,並記錄:

S1: [1 1 1 2 1 1 1 1 0 1 1 0 0 1 1 1]
S2: [1 1 0 2 2 1 0 0 1 0 1 1 1 1 1 1]

第四步,複雜度計算:

通過上述的準備工作,現在我們可以想象在空間中存在著兩條線段:SA和SB,二者均從原點([0, 0, ...])出發,指向不同的方向,並分別終結於點A [1 1 1 2 1 1 1 1 0 1 1 0 0 1 1 1]和點B[1 1 0 2 2 1 0 0 1 0 1 1 1 1 1 1],其中點A和點B的座標與我們上述的詞頻一致。到了這一步,我們可以發現,對於句子S1和S2的相似度問題,已經被我們抽象到如何計算上述兩個向量的相似問題了。

通過上文介紹的餘弦定理,我們知道當兩條線段之間形成一個夾角,如果夾角為0度,意味著方向相同、線段重合,我們就認定這是表示兩個向量代表的文字完全相等;如果夾角為90度,意味著形成直角,方向完全不相似。因此,我們可以通過夾角的大小,來判斷向量的相似程度。夾角越小,就代表越相似。

那麼對於上述給定的兩個屬性向量AB,其餘弦相似性θ由點積和向量長度給出,其餘弦相似度的計算如下所示:

img餘弦相似度計算公式

實現

下面我們將通過golang來實現一個簡單的餘弦相似度演算法。

func CosineSimilar(srcWords, dstWords []string) float64 {
   // get all words
   allWordsMap := make(map[string]int, 0)
   for _, word := range srcWords {
      if _, found := allWordsMap[word]; !found {
         allWordsMap[word] = 1
      } else {
         allWordsMap[word] += 1
      }
   }
   for _, word := range dstWords {
      if _, found := allWordsMap[word]; !found {
         allWordsMap[word] = 1
      } else {
         allWordsMap[word] += 1
      }
   }

   // stable the sort
   allWordsSlice := make([]string, 0)
   for word, _ := range allWordsMap {
      allWordsSlice = append(allWordsSlice, word)
   }

   // assemble vector
   srcVector := make([]int, len(allWordsSlice))
   dstVector := make([]int, len(allWordsSlice))
   for _, word := range srcWords {
      if index := indexOfSclie(allWordsSlice, word); index != -1 {
         srcVector[index] += 1
      }
   }
   for _, word := range dstWords {
      if index := indexOfSclie(allWordsSlice, word); index != -1 {
         dstVector[index] += 1
      }
   }

   // calc cos
   numerator := float64(0)
   srcSq := 0
   dstSq := 0
   for i, srcCount := range srcVector {
      dstCount := dstVector[i]
      numerator += float64(srcCount * dstCount)
      srcSq += srcCount * srcCount
      dstSq += dstCount * dstCount
   }
   denominator := math.Sqrt(float64(srcSq * dstSq))

   return numerator / denominator
}

結果

--- PASS: TestCosineSimilar (0.84s)
    similarity_test.go:23: CosineSimilar score: 0.7660323462854266

我們看到,對於上述兩句話,在經過餘弦計算後,得到的相似度為0.766(其夾角大概是40度),還是比較接近於1,所以,上面的句子S1和句子S2是基本相似的。由此,我們就得到了文字相似度計算的處理流程是:

  1. 找出兩篇文章的關鍵詞;
  2. 每篇文章各取出若干個關鍵詞,合併成一個集合,計算每篇文章對於這個集合中的詞的詞頻;
  3. 生成兩篇文章各自的詞頻向量;
  4. 計算兩個向量的餘弦相似度,值越接近於1就表示越相似;

simhash

基於餘弦複雜度,通過兩兩比較文字向量來得到兩個文字的相似程度是一個非常簡單的演算法。然而兩兩比較也就說明了時間複雜度是O(n2),那麼在面對網際網路海量資訊時,考慮到一個文章的特徵向量詞可能特別多導致整個向量維度很高,使得計算的代價太大,就有些力不從心了。因此,為了在爬取網頁時用於快速去重,Google發明了一種快速衡量兩個文字集相似度的演算法:simhash。

簡單來說,simhash中使用了一種區域性敏感型的hash演算法。所謂區域性敏感性hash,與傳統hash演算法不同的是(如MD5,當原始文字越是相似,其hash數值差異越大),simhash中的hash對於越是相似的內容產生的簽名越相近。

原理

simhash的主要思想是降維,將文字分詞結果從一個高維向量對映成一個0和1組成的bit指紋(fingerprint),然後通過比較這個二進位制數字串的差異進而來表示原始文字內容的差異。下面我們通過圖文方式來解釋下這個降維和差異計算的過程。

imgsimhash演算法流程例項

在simhash中處理一個文字的步驟如下:

第一步,分詞:

對文字進行分詞操作,同時需要我們同時返回當前片語在文字內容中的權重(這基本上是目前所有分詞工具都支援的功能)。

第二步,計算hash:

對於每一個得到的片語做hash,將詞語表示為到01表示的bit位,需要保證每個hash結果的位數相同,如圖中所示,使用的是8bit。

第三步,加權

根據每個片語對應的權重,對hash值做加權計算(bit為1則取為1做乘積,bit為0則取為-1做乘積),如上圖中,

  • 10011111與權重2加權得到[2 -2 -2 2 2 2 2 2];
  • 01001011與權重1加權得到[-1 1 -1 -1 1 -1 1 1];
  • 01001011與權重4加權後得到[-4 4 -4 -4 4 -4 4 4];

第三步,縱向相加:

將上述得到的加權向量結果,進行縱向相加實現降維,如上述所示,得到[-3 3 -7 -3 7 -3 7 7]。

第四步,歸一化:

將最終降維向量,對於每一位大於0則取為1,否則取為0,這樣就能得到最終的simhash的指紋簽名[0 1 0 0 1 0 1 1]

第五步,相似度比較:

通過上面的步驟,我們可以利用SimHash演算法為每一個網頁生成一個向量指紋,在simhash中,判斷2篇文字的相似性使用的是海明距離。什麼是漢明距離?前文已經介紹過了。在在經驗資料上,我們多認為兩個文字的漢明距離<=3的話則認定是相似的。

實現

完整程式碼見Github

func SimHashSimilar(srcWordWeighs, dstWordWeights []WordWeight) (distance int, err error) {

   srcFingerPrint, err := simhashFingerPrint(srcWordWeighs)
   if err != nil {
      return
   }
   fmt.Println("srcFingerPrint: ", srcFingerPrint)
   dstFingerPrint, err := simhashFingerPrint(dstWordWeights)
   if err != nil {
      return
   }
   fmt.Println("dstFingerPrint: ", dstFingerPrint)

   distance = hammingDistance(srcFingerPrint, dstFingerPrint)

   return
}

func simhashFingerPrint(wordWeights []WordWeight) (fingerPrint []string, err error) {
   binaryWeights := make([]float64, 32)
   for _, ww := range wordWeights {
      bitHash := strHashBitCode(ww.Word)
      weights := calcWithWeight(bitHash, ww.Weight) //binary每個元素與weight的乘積結果陣列
      binaryWeights, err = sliceInnerPlus(binaryWeights, weights)
      //fmt.Printf("ww.Word:%v, bitHash:%v, ww.Weight:%v, binaryWeights: %v\n", ww.Word,bitHash, ww.Weight, binaryWeights)
      if err != nil {
         return
      }
   }
   fingerPrint = make([]string, 0)
   for _, b := range binaryWeights {
      if b > 0 { // bit 1
         fingerPrint = append(fingerPrint, "1")
      } else { // bit 0
         fingerPrint = append(fingerPrint, "0")
      }
   }

   return
}

func calcWithWeight(bitHash string, weight float64) []float64 {
   bitHashs := strings.Split(bitHash, "")
   binarys := make([]float64, 0)

   for _, bit := range bitHashs {
      if bit == "0" {
         binarys = append(binarys, float64(-1)*weight)
      } else {
         binarys = append(binarys, float64(weight))
      }
   }

   return binarys
}

結果

我們使用了調換一段長本文的語序來測試simhash的效果:

文字1:
"沉默螺旋模式中呈現出民意動力的來源在於人類有害怕孤立的弱點,但光害怕孤立不至於影響民意的形成," +
"主要是當個人覺察到自己對某論題的意見與環境中的強勢意見一致(或不一致時),害怕孤立這個變項才會產生作用。	" +
"從心理學的範疇來看,社會中的強勢意見越來越強,甚至比實際情形還強,弱勢意見越來越弱,甚至比實際情形還弱,這種動力運作的過程成–螺旋狀"

文字2:
"從心理學的範疇來看,害怕孤立這個變項才會產生作用。社會中的強勢意見越來越強,甚至比實際情形還強,弱勢意見越來越弱," +
"主要是當個人覺察到自己對某論題的意見與環境中的強勢意見一致(或不一致時),甚至比實際情形還弱,這種動力運作的過程成–螺旋狀	" +
"但光害怕孤立不至於影響民意的形成,沉默螺旋模式中呈現出民意動力的來源在於人類有害怕孤立的弱點"

通過計算,結果得到二者的指紋是一模一樣,其漢明距離為0.

srcFingerPrint:  [1 0 0 0 0 0 1 0 1 0 1 0 1 0 0 0 1 0 0 1 0 0 0 1 1 1 0 0 0 0 0 0]
dstFingerPrint:  [1 0 0 0 0 0 1 0 1 0 1 0 1 0 0 0 1 0 0 1 0 0 0 1 1 1 0 0 0 0 0 0]
--- PASS: TestSimHashSimilar (0.85s)
    similarity_test.go:57: SimHashSimilar distance: 0
PASS

適用性

注意一:

我們再來看一個文章主旨類似,但是內容相關性較低的文字比較示例:

文字1:
"關於區塊鏈和數字貨幣的關係,很多人或多或少都存在疑惑。簡單來說,區塊鏈是比特幣的底層運用,而比特幣只是區塊鏈的一個小應用而已。" +
"數字貨幣即虛擬貨幣,最早的數字貨幣誕生於2009年,其發明者中本聰為了應對經濟危機對於實體貨幣經濟的衝擊。比特幣是最早的數字貨幣,後來出現了以太幣、火幣以及萊特幣等虛擬貨幣,這些虛擬貨幣是不能用來交易的。" +
"狹義來講,區塊鏈是一種按照時間順序將資料區塊以順序相連的方式組合成的一種鏈式資料結構, 並以密碼學方式保證的不可篡改和不可偽造的分散式賬本。" +
"廣義來講,區塊鏈技術是利用塊鏈式資料結構來驗證與儲存資料、利用分散式節點共識演算法來生成和更新資料、利用密碼學的方式保證資料傳輸和訪問的安全、利用由自動化指令碼程式碼組成的智慧合約來程式設計和操作資料的一種全新的分散式基礎架構與計算方式。"

文字2:
"區塊鏈技術為我們的資訊防偽與資料追蹤提供了革新手段。區塊鏈中的資料區塊順序相連構成了一個不可篡改的資料鏈條,時間戳為所有的交易行為貼上了一套不講課偽造的真是標籤,這對於人們在現實生活中打擊假冒偽劣產品大有裨益; " +
"市場分析指出,整體而言,區塊鏈技術目前在十大金融領域顯示出應用前景,分別是資產證券化、保險、供應鏈金融、場外市場、資產託管、大宗商品交易、風險資訊共享機制、貿易融資、銀團貸款、股權交易交割。" +
"這些金融場景有三大共性:參與節點多、驗真成本高、交易流程長,而區塊鏈的分散式記賬、不可篡改、內建合約等特性可以為這些金融業務中的痛點提供解決方案。" +
"傳統的工業網際網路模式是由一箇中心化的機構收集和管理所有的資料資訊,容易產生因裝置生命週期和安全等方面的缺陷引起的資料丟失、篡改等問題。區塊鏈技術可以在無需任何信任單個節點的同時構建整個網路的信任共識,從而很好的解決目前工業網際網路技術領域的一些缺陷,讓物與物之間能夠實現更好的連線。"

通過計算,當我們選擇前top10高頻詞作為衡量時,結果得到二者的指紋是如下,其漢明距離為4:

srcFingerPrint:  [1 0 1 1 0 1 0 0 0 1 1 1 1 1 1 0 1 0 0 1 0 1 1 1 0 0 0 1 0 0 0 1]
dstFingerPrint:  [1 0 1 1 0 1 0 0 0 1 1 1 1 1 1 0 1 1 0 1 1 1 1 0 0 0 0 1 0 1 0 1]
--- PASS: TestSimHashSimilar (0.84s)
    similarity_test.go:58: SimHashSimilar distance: 4
PASS

當我們選擇前top50高頻詞作為衡量時,結果得到二者的指紋是如下,其漢明距離為9:

srcFingerPrint:  [1 0 1 1 0 1 0 0 0 1 1 1 1 0 0 0 1 0 0 1 0 1 1 0 1 0 0 0 0 1 0 1]
dstFingerPrint:  [1 0 1 1 0 1 0 1 0 0 1 0 1 1 0 0 1 1 0 1 1 1 1 0 0 0 0 1 0 0 0 1]
--- PASS: TestSimHashSimilar (0.83s)
    similarity_test.go:58: SimHashSimilar distance: 9
PASS

所以我們發現了一個對結果判定很重要的引數:分詞數量。在上面的示例中,當我們選擇10個分詞時,其漢明距離僅為4,幾乎符合了我們對文字相似(漢明距離3)的判斷。而隨著topN數量的增加,引入了更多的片語,其漢明距離越來越大,這也說明了,當大文字內容出現時,選擇合適的topN分詞數量進行比較對結果的影響是十分大的。

注意二:

另外一點需要需要注意的是,simhash的優點是適用於高維度的海量資料處理,當維度降低,如短文字的相似度比較,simhash並不合適,以我們計算餘弦相似度的文字為例,

S1: "為什麼我的眼裡常含淚水,因為我對這片土地愛得深沉"
S2: "我深沉的愛著這片土地,所以我的眼裡常含淚水"

得到的結果如下:

srcFingerPrint:  [1 1 1 0 1 0 0 1 1 1 0 1 0 0 0 0 1 0 1 1 1 0 1 0 1 0 1 1 0 0 1 0]
dstFingerPrint:  [1 0 1 0 0 0 1 1 0 1 1 0 1 0 0 1 0 0 0 1 1 0 1 1 1 1 1 1 0 0 1 0]
--- PASS: TestSimHashSimilar (0.86s)
    similarity_test.go:53: SimHashSimilar distance: 12
PASS

也就是結果的漢明距離為12,遠遠大於我們預定的漢明距離3,這樣的結果跟我們通過預先相似度計算出來的0.76分(相比於1分)相差很遠,可見simhash對於短文字的相似度比較還是存在一些偏差的。

參考文獻

http://static.googleusercontent.com/media/research.google.com/en//pubs/archive/33026.pdf

https://lujiaying.github.io/posts/2018/01/Chinese-word-segmentation/

https://www.zhihu.com/question/19578687

此文已由騰訊雲+社群在各渠道釋出

獲取更多新鮮技術乾貨,可以關注我們騰訊雲技術社群-雲加社群官方號及知乎