從0到1,瞭解NLP中的文本類似度

本文由雲+社區發表

做者:netkiddyjavascript

導語

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

本文將從預備知識的概念開始介紹,從距離名詞,到文本分詞,類似度算法,並將這些概念融合、統一的介紹NLP中文本類似度的知識,指望經過本文,你們能夠與我同樣,對這些知識有個基本的瞭解。java

幾個距離

在介紹更多的內容以前,咱們須要瞭解文本距離的概念,這些距離是咱們在後文比較文本類似度的基礎,因此下面將首先形象的爲你們介紹幾個重要且基礎的距離含義。git

歐幾里德距離

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

二維的公式:golang

​ 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分鐘應該就能夠獲得餘弦定理的結論了,其中須要瞭解的一點事對於bsin(θ*)的定義,是直接使用了畢氏定理。

數學家已經證實,餘弦的這種計算方法對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.googleuserconte...

https://lujiaying.github.io/p...

https://www.zhihu.com/questio...

此文已由騰訊雲+社區在各渠道發佈

獲取更多新鮮技術乾貨,能夠關注咱們騰訊雲技術社區-雲加社區官方號及知乎機構號

相關文章
相關標籤/搜索