基於熵的方法計算query與docs類似度

一.簡單總結git

  其實類似度計算方法也是老生常談,好比經常使用的有:github

  1.常規方法web

    a.編輯距離ide

    b.Jaccardthis

    c.餘弦距離spa

    d.曼哈頓距離3d

    e.歐氏距離code

    f.皮爾遜相關係數blog

  2.語義方法排序

    a.LSA

    b.Doc2Vec

    c.DSSM

  ......

二.利用熵計算類似度

  關於什麼是熵、相對熵、交叉熵的概念,網上有不少,這裏就不總結了。本篇主要關注工程方面,即怎麼用代碼實現,參考的論文來自《Content-based relevance estimation on the web using inter-document similarities》(2012-CIKM)。

  利用熵計算query與文檔類似度並排序的步驟分爲召回和重排序,好比先從大規模文檔中召回小部分子集再進行重排序。召回部分能夠用一些簡單的效率高的方法快速肯定候選子集,再將這些子集進行重排序。本篇關注如何利用熵重排序相關文檔。

  召回後的排序公式以下:

    

  說明:

  (1).H(d)表示文檔d的熵

    

    其中=|w|/|d|,分子是詞w個數,分母爲文檔d中的總詞數

  (2).文檔間的類似度

    

    

    其中表示query的top-k個相關文檔;利用交叉熵計算文檔間的類似度,這裏面的文檔去除了query中的詞。

    表示語言模型Dirichlet-smoothed,常見的平滑方法以下:

    

    其中Dirichlet 方法:
    a.首先計算最基本的最大的似然估計w|d 單詞在單個文檔出現的頻率(有可能爲0,因此就須要平滑,將全部f(w|d1), f(w|d2)....f(w|dn) 的全部頻率加總
    b.設定u值,根據實證研究: Dirichlet 方法的u值在100-200之間是最理想 ,但論文中給出的是1000,0爲不使用平滑
    c. 計算P(w|C)的機率

    (3).sim(q,d)表示query與doc的類似度,能夠使用其它方法計算,也能夠使用如(2)中的方法計算

 三.程序

  完整程序https://github.com/jiangnanboy/entropy_sim

  核心程序:

 1 /**
 2      * 結合交叉熵和狄裏克雷平滑語言方法計算相關度
 3      * @param queryTerms
 4      * @return
 5      */
 6     private Map<String, Double> queryDocScore(List<String> queryTerms) {
 7         //統計查詢中的詞頻
 8         Map<String, Long> queryTermsCount = queryTerms
 9                 .stream()
10                 .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
11         //查詢中的總詞頻
12         long queryTermsSize = queryTermsCount
13                 .values()
14                 .stream()
15                 .mapToLong(word -> word)
16                 .sum();
17 
18         //文檔集中的詞頻
19         Map<String, Long> collectionTermsCount = corpusTerms
20                 .stream()
21                 .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
22         //文檔集中的總詞頻
23         long collectionTermsSize = collectionTermsCount
24                 .values()
25                 .stream()
26                 .mapToLong(word -> word)
27                 .sum();
28 
29         Map<String, Double> scoredDocument = new HashMap<>();
30         documentList.forEach(docTerms -> {
31             //文檔中的詞頻
32             Map<String, Long> docTermsCount = docTerms
33                     .stream()
34                     .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
35             //文檔中的總詞頻
36             long docTermsSize = docTermsCount
37                     .values()
38                     .stream()
39                     .mapToLong(word -> word)
40                     .sum();
41 
42             //計算交叉熵(或者相對熵)
43             OptionalDouble score = queryTerms
44                     .stream()
45                     .mapToDouble(queryTerm -> {
46                         //queryTerm的似然
47                         double queryCE = (double)queryTermsCount.get(queryTerm) / queryTermsSize;
48                         //通過Dirichlet smooth的term weight
49                         double docCE = (1.0 + docTermsCount.getOrDefault(queryTerm, 0L) +
50                                 this.lambda * (collectionTermsCount.getOrDefault(queryTerm, 0L) / collectionTermsSize)) /
51                                 (docTermsSize + this.lambda);
52                         return queryCE * Math.log(1 / docCE);//交叉熵
53                         //return queryCE * Math.log(queryCE / docCE);//相對熵
54                     })
55                     .reduce(Double::sum);
56             String docID = corpusHashMap.get(docTerms);
57             scoredDocument.put(docID, Math.exp(-score.getAsDouble()));
58         });
59         return scoredDocument;
60     }
相關文章
相關標籤/搜索