如何設計一個比較兩篇文章相似性的算法


如何設計一個比較兩篇文章相似性的算法?
假如我們想得到更多的局部信息,如相似片段、相似百分比,那又該如何去做?
任何idea都可以分享

 

如果是話題是否相似,一般是關鍵詞匹配的方法

想了一種基於統計模型的算法,不知道實際效果如何:
首先收集足夠多的樣本,分詞,統計各個詞的頻度(文章中出現次數 / 總詞數),

然后計算每個詞的平均頻度(頻度和 / 文章數)和頻度方差((頻度 - 平均值) ^ 2 / (文章數 - 1))
即將每個詞的出現頻度建模為一個高斯隨機變量。

然后對要比較的文章先分詞,統計頻度,然后計算對數似然比:
ln(p(A,B獨立) / p(A,B同類))
= ln(p(A)P(B) / p(A,B同類))
= lnp(A) + lnp(B) - lnp(A,B同類)

將概率p用似然值P替代,根據高斯變量的特性:
lnP(A) = -1/2 * ∑((D_Ak - D_k)^2 / Sigma_k + ln(2 * PI * Sigma_k))
lnP(B) = -1/2 * ∑((D_Bk - D_k)^2 / Sigma_k + ln(2 * PI * Sigma_k))
lnP(A,B同類) = - 1/2 * ∑((D_Ak - D_Bk)^2 / Sigma_0k + ln(2 * PI * Sigma_0k))

其中D_Ak是A中詞k的頻度,D_Bk是B中詞k的頻度,D_k是平均頻度,Sigma_k

是方差,而Sigma_0k是某組參數,代表同類文章 中的詞頻方差(或者說接受多接

近的詞頻為同類),可以用人工標記訓練的方法得到,也可以簡單點所有的都設為

一個常數,或者設為Sigma_k的一個倍數。
則對數似然比可以寫為:
ln(p(A,B獨立) / p(A,B同類))
= -1/2 * ∑((D_Ak - D_k)^2 / Sigma_k + ln(2 * PI * Sigma_k)) -

1/2 * ∑((D_Bk - D_k)^2 / Sigma_k + ln(2 * PI * Sigma_k)) +

1/2 * ∑((D_Ak - D_Bk)^2 / Sigma_0k + ln(2 * PI * Sigma_0k))
= -1/2 * ∑((D_Ak - D_k)^2 / Sigma_k + ln(2 * PI * Sigma_k) +

(D_Bk - D_k)^2 / Sigma_k + ln(2 * PI * Sigma_k) -

(D_Ak - D_Bk)^2 / Sigma_0k - ln(2 * PI * Sigma_0k))

計算出的這個結果就可以當做兩篇文章的相似度來看。值越小,兩篇文章相

似度越高;否則相似度越低。

和直接計算∑(D_Ak - D_Bk)^2的方法比起來,充分考慮了不同詞的關鍵性

作用不同,比較關鍵的詞會有比較高的加權。

 

 

余弦定理和新聞的分類似乎是兩件八桿子打不着的事,但是它們確有緊密的聯系。

具體說,新聞的分類很大程度上依靠余弦定理。

Google 的新聞是自動分類和整理的。所謂新聞的分類無非是要把相似的新聞放

到一類中。計算機其實讀不懂新聞,它只能快速計算。這就要求我們設計一個算

法來算出任意兩篇新聞的相似性。為了做到這一點,我們需要想辦法用一組數字

來描述一篇新聞。

我們來看看怎樣找一組數字,或者說一個向量來描述一篇新聞。回憶一下我們在

“如何度量網頁相關性”一文中介紹的TF/IDF 的概念。對於一篇新聞中的所有實詞,

我們可以計算出它們的單文本詞匯頻率/逆文本頻率值(TF/IDF)。不難想象,和新

聞主題有關的那些實詞頻率 高,TF/IDF 值很大。我們按照這些實詞在詞匯表的位

置對它們的 TF/IDF 值排序。比如,詞匯表有六萬四千個詞,分別為

  • 單詞編號 漢字詞
  •     ------------------
  •     1
  •     2
  •     3 阿斗
  •     4 阿姨
  •     ...
  •     789 服裝
  •     ....
  •     64000 做作
在一篇新聞中,這 64,000 個詞的 TF/IDF 值分別為

  • 單詞編號 TF/IDF
  •     ==============
  •     1 0
  •     2 0.0034
  •     3 0
  •     4 0.00052
  •     5 0
  •     ...
  •     789 0.034
  •     ...
  •     64000 0.075
 
           

如果單詞表中的某個次在新聞中沒有出現,對應的值為零,那么這 64,000 個數,

組成一個64,000維的向量。我們就用這個向量來代表這篇新聞,並成為新聞的特

征向量。如果兩篇新聞的特征向量相近,則對應的新聞內容相似,它們應當歸在一

類,反之亦然。

學過向量代數的人都知道,向量實際上是多維空間中有方向的線段。如果兩個向量

的方向一致,即夾角接近零,那么這兩個向量就相近。而要確定兩個向量方向是否

一致,這就要用到余弦定理計算向量的夾角了。

 

余弦定理對我們每個人都不陌生,它描述了三角形中任何一個夾角和三個邊的關系,

換句話說,給定三角形的三條邊,我們可以用余弦定理求出三角形各個角的角度。

假定三角形的三條邊為 a, b 和 c,對應的三個角為 A, B 和 C,那么角 A 的余弦 --

cosA

如果我們將三角形的兩邊 b 和 c 看成是兩個向量,那么上述公式等價

 

cosa

其中分母表示兩個向量 b 和 c 的長度,分子表示兩個向量的內積。舉一個具體的

例子,假如新聞 X 和新聞 Y 對應向量分別是
x1,x2,...,x64000 和 y1,y2,...,y64000,
那么它們夾角的余弦等

clip

當兩條新聞向量夾角的余弦等於一時,這兩條新聞完全重復(用這個辦法可以刪除

重復的網頁);當夾角的余弦接近於一時,兩條新聞相似,從而可以歸成一類;夾

角的余弦越小,兩條新聞越不相關。

clip04

 

 

如果我們並不關注具體的相似片段的話,可以用hash的辦法來解決,

simhash算法貌似是一個簡明有效的算法。算法的原理是這樣的:

simhash算法的輸入是一個向量,輸出是一個f位的簽名值。為了陳述

方便,假設輸入的是一個文檔的特征集合,每個特征有一定的權重。

比如特征可以是文檔中的詞,其權重可以是這個詞出現的次數。

simhash算法如下:
1. 將一個f維的向量V初始化為0;f位的二進制數S初始化為0;
2. 對每一個特征:用傳統的hash算法對該特征產生一個f位的簽名b。
對i=1到f:
如果b的第i位為1,則V的第i個元素加上特征的權重;
否則,V的第i個元素減去該特征的權重。
3. 如果V的第i個元素大於0,則S的第i位為1,否則為0;

4. 輸出S作為簽名。

通過計算兩篇文章的簽名的海明距離得出相似度。
如圖:

點擊查看原始尺寸

以上的所有算法我們都只關注文章的全局信息,忽略了文章的局部信息,

如果我們現在需要知道文章的具體相似部分、相似片段、相似百分比,

就像論文查重所做的那樣,那么應該使用什么樣的算法?

 

我覺得可以采用神經網絡中hebb rule的方法。
對於任意一片文章我們都可以抽象成一個列數為Q的特征向量P=[p1,p2,p2,.....pq]
存在權重矩陣W使得Wp=a,a是一個輸出向量因此可以發現一組關系為{p1,t1}{p2,t2}....{p_q,t_q}
換言之,當網絡的輸入參數為p=p_q時,輸出應當為a=t_q
hebb rule:
W_new = W_old + αf_i(a_iq)g_j(p_jq)
推算可得W_new = W_old+t_qp_q_tranpose
(抱歉下標什么的寫不出來……)
當初始W矩陣為零時可得
W=p cross t_transpose
其中P=[p1,p2,p3,p4,p5...pq] t=[t1,t2,t3,t4,t5....tq]
a=Wp_k=t_k

以下是一個例子

public static Vector hardlims(Vector v){
        double[] a = new double[v.size()];
        
        
        for(int i =0;i<v.size();i++){
            
            a[i]=v.get(i)>0?1:-1;
            
        }
        Vector res = new DenseVector(a);
        return res;
        
    }
    public static void main(String[] args){
        
        
        String v1= "你好嗎,我好";
        String v2= "你好嗎,我不舒服";
        String v3 = "我好";
        String v4 = "我不舒服";
        Matrix W = null;
        double[] d1= {1,1,1,1,-1};
        double[] d2 = {1,1,1,-1,1};
        double[] d3 = {0,0,1,1,-1};
        double[] d4 = {0,0,1,-1,1};
        
        Vector p1 = new DenseVector(d1);
        Vector p2 = new DenseVector(d2);
        //W=ΣPT=p1p1+p2p2,在這里由於預測輸出值就是我們所要判定的文章特征向量所以t=p
        W = p1.cross(p1).plus((p2.cross(p2)));
        Vector p3 = new DenseVector(d3);
        Vector p4 = new DenseVector(d4);
        
        //a=hardlims(Wp)
        Vector a1 = W.times(p1);
        Vector a2 = W.times(p2);
        Vector a3 = W.times(p3);
        Vector a4 = W.times(p4);
        
        System.out.println(hardlims(a1));
        System.out.println(hardlims(a2));
        System.out.println(hardlims(a3));
        System.out.println(hardlims(a4));
    }

 

輸出結果:
{0:1.0,1:1.0,2:1.0,3:1.0,4:-1.0}
{0:1.0,1:1.0,2:1.0,3:-1.0,4:1.0}
{0:1.0,1:1.0,2:1.0,3:1.0,4:-1.0}
{0:1.0,1:1.0,2:1.0,3:-1.0,4:1.0}

可以看出輸入"我好"會自動對應找出"你好嗎,我好",輸入"我不舒服"會自動找出"你好嗎,我不舒服"

http://www.dewen.io/q/6668/%E5%A6%82%E4%BD%95%E8%AE%BE%E8%AE%A1%E4%B8%80%E4%B8%AA%E6%AF%94%E8%BE%83%E4%B8%A4%E7%AF%87%E6%96%87%E7%AB%A0%E7%9B%B8%E4%BC%BC%E6%80%A7%E7%9A%84%E7%AE%97%E6%B3%95%EF%BC%9F

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM