鏈接分析算法之:HITS算法


轉自 http://blog.csdn.net/Androidlushangderen/article/details/43311943

參考資料:http://blog.csdn.net/hguisu/article/details/7996185
更多數據挖掘算法https://github.com/linyiqun/DataMiningAlgorithm

鏈接分析

在鏈接分析中有2個經典的算法,1個是PageRank算法,還有1個是HITS算法,說白了,都是做鏈接分析的。具體是怎么做呢,繼續往下看。

PageRank算法

要說到PageRank算法的作用,得先從搜索引擎開始講起,PageRank算法的由來正式與此相關。

搜索引擎

最早時期的搜索引擎的結構,無外乎2個核心步驟,step1:建立龐大的資料庫,step2:建立索引庫,用於指向具體的資料。然后就是用戶的查找操作了,那怎么查呢,一個很讓人會聯想到的方法就是通過關鍵字匹配的方法,例如我想輸入張三這個關鍵詞,那我就會在資源中查包含有張三這個詞語的文章,按照關鍵詞匹配方法,只要一篇文章中張三出現的次數越多,就越是要查詢的目標。(但是更公正的方法應是次數/文章總次數,一個比值的形式顯然更公平)。仔細這么想也沒錯。好繼續往下。

Term Spam攻擊

既然我已經知道了搜索的核心原理,如果我想要讓我的網頁能夠出現在搜索的結果更靠前的位置,只要在頁面中加入更多對應的關鍵詞不就OK了,比如在html的div中寫入10000個張三,讓后使其隱藏此標簽,使得前端頁面不受影響,那我的目的豈不是達到了,這就是Term Spam攻擊。

PageRank算法原理

既然關鍵詞匹配算法容易遭到攻擊,那有什么好的辦法呢,這是候就出現了著名的PageRank算法,作為新的網頁排名/重要性算法,最早是由Google的創始人所寫的算法,PageRank算法徹底摒棄了什么關鍵詞不關鍵詞的,每個網頁都有自己的PageRank值,意味一個網頁的重要程度,PR值越高,最后呈現的位置更靠前。那怎么衡量每個網頁的重要程度呢,答案是別的頁面對他的鏈接。一句話,越多的網頁在其內容上存在指向你的鏈接,說明你的網頁越有名。具體PR值的計算全是通過別的網頁的PR值做計算的,簡單計算過程如下:

 

 假設一個由只有4個頁面組成的集合:A,B,C和D。如果所有頁面都鏈向A,那么A的PR(PageRank)值將是B,C及D的和。

       

       繼續假設B也有鏈接到C,並且D也有鏈接到包括A的3個頁面。一個頁面不能投票2次。所以B給每個頁面半票。以同樣的邏輯,D投出的票只有三分之一算到了A的PageRank上。

       

      換句話說,根據鏈出總數平分一個頁面的PR值。

       

 

 所示的例子來說明PageRank的具體計算過程。  

                     

以上是網頁內部有鏈接的時候,因為還可能在1個網頁中沒有任何鏈接的情況,而這個時候,跳到任何網頁的概率都是可能的。因此最后的計算公式就變成了這個樣子:

 

      

 

q稱為阻尼系數。

PageRank的計算過程

PageRank的計算過程實際並不復雜,他的計算數學表達式如下:

就是1-q變成了1-q/n了,算法的過程其實是利用了冪法的原理,等最后計算達到收斂了,也就結束了。

按照上面的計算公式假設矩陣A = q  × P + ( 1 一 q) *  /N,e為全為1的單位向量,P是一個鏈接概率矩陣,將鏈接的關系通過概率矩陣表現,A[i][j]表示網頁i存在到網頁j的鏈接,轉化如下:

 

      
         圖2  網頁鏈接矩陣:                                      圖3  網頁鏈接概率矩陣:  
 
 

 

                         圖4  P’ 的轉置矩 陣

這里為什么要把矩陣做轉置操作呢,原本a[i][j]代表i到j鏈接,現在就變為了j到i的鏈接的概率了,好,關鍵記住這點就夠了。最后A就計算出來了,你可以把他理解為網頁鏈接概率矩陣,最后只需要乘上對應的網頁PR值就可以了。

此時初始化向量R[1, 1, 1];代表最初的網頁的PR值,與此A概率矩陣相乘,第一個PR值R[0]'=A[0][0]*R[0] + A[0][1]*R[1] + A[0][2]*R[2],又因為A[i][j]此時的意思正是j到i網頁的鏈接概率,這樣的表達式恰恰就是上文我們所說的核心原理。然后將計算新得的R向量值域概率矩陣迭代計算直到收斂。

PageRank小結

PageRank的計算過程巧妙的被轉移到了矩陣的計算中了,使得過程非常的精簡。

Link Spam攻擊

魔高一尺道高一丈,我也已經知道了PageRank算法的原理無非就是靠鏈接數升排名嘛,那我想讓我自己的網頁排名靠前,只要搞出很多網頁,把鏈接指向我,不就行了,學術上這叫Link Spam攻擊。但是這里有個問題,PR值是相對的,自己的網頁PR值的高低還是要取決於指向者的PR值,這些指向者 的PR值如果不高,目標頁也不會高到哪去,所以這時候,如果你想自己造成一堆的僵屍網頁,統統指向我的目標網頁,PR也不見的會高,所以我們看到的更常見的手段是在門戶網站上放鏈接,各大論壇或者類似於新浪,網頁新聞中心的評論中方鏈接,另類的實現鏈接指向了。目前針對這種作弊手法的直接的比較好的解決辦法是沒有,但是更多采用的是TrustRank,意味信任排名檢測,首先挑出一堆信任網頁做參照,然后計算你的網頁的PR值,如果你網頁本身很一般,但是PR值特別高,那么很有可能你的網頁就是有問題的。

HITS

HITS算法同樣作為一個鏈接分析算法,與PageRank算法在某些方面還是比較像的,將這2種算法放在一起做比較,再好不過的了,一個明顯的不同點是HITS處理的網頁量是小規模的集合,而且他是與查詢相關的,首先輸入一個查詢q,假設檢索系統返回n個頁面,HITS算法取其中的200個(假設值),作為分析的樣本數據,返回里面更有價值的頁面。

HITS算法原理

HITS衡量1個頁面用A[i]和H[i]值表示,A代表Authority權威值,H代表Hub樞紐值。

大意可理解為我指出的網頁的權威值越高,我的Hub值越大。指向我的網頁的Hub值越大,我的權威值越高。二者的變量相互權衡。下面一張圖直接明了:

 

           

                                                                圖3 Hub與Authority權值計算

如果理解了PageRank算法的原理,理解HITS應該很容易,最后結果的輸出是根據頁面的Authority權威值從高到低。

   1)  分別表示網頁結點 i 的Authority值(權威度)和Hub值(中心度)。

 

       2) 對於“擴展集base”來說,我們並不知道哪些頁面是好的“Hub”或者好的“Authority”頁面,每個網頁都有潛在的可能,所以對於每個頁面都設立兩個權值,分別來記載這個頁面是好的Hub或者Authority頁面的可能性。在初始情況下,在沒有更多可利用信息前,每個頁面的這兩個權值都是相同的,可以都設置為1,即:

      

         3)每次迭代計算Hub權值和Authority權值:

           網頁 a (i)在此輪迭代中的Authority權值即為所有指向網頁 a (i)頁面的Hub權值之和:

            a (i) = Σ h (i) ;

           網頁 a (i)的Hub分值即為所指向的頁面的Authority權值之和:

           h (i) = Σ a (i) 。

           對a (i)、h (i)進行規范化處理:

           將所有網頁的中心度都除以最高中心度以將其標准化:

           a (i) = a (i)/|a(i)| ;

           將所有網頁的權威度都除以最高權威度以將其標准化:

           h (i) = h (i)/ |h(i)| :

          
         5)如此不斷的重復第4):上一輪迭代計算中的權值和本輪迭代之后權值的差異,如果發現總體來說權值沒有明顯變化,說明系統已進入穩定狀態,則可以結束計算,即a ( u),h(v)收斂 。

HITS算法描述

 

             

具體可以對照后面我寫的程序。

HITS小結

從鏈接反作弊的角度來思考,HITS更容易遭受到Link Spam的攻擊,因為你想啊,網頁數量少啊,出錯的幾率就顯得會大了。

PageRank算法和HITS算法實現

最后奉上本人親自實現的2個算法,輸入數據是同一個文(每條記錄代表網頁i到網頁j存在鏈接):

 
1 2  
1 3  
2 3  
3 1  

算法都不是太難:

 

package DataMining_PageRank;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Array;
import java.text.MessageFormat;
import java.util.ArrayList;

/**
 * PageRank網頁排名算法工具類
 * 
 * @author lyq
 * 
 */
public class PageRankTool {
    // 測試輸入數據
    private String filePath;
    // 網頁總數量
    private int pageNum;
    // 鏈接關系矩陣
    private double[][] linkMatrix;
    // 每個頁面pageRank值初始向量
    private double[] pageRankVecor;

    // 網頁數量分類
    ArrayList<String> pageClass;

    public PageRankTool(String filePath) {
        this.filePath = filePath;
        readDataFile();
    }

    /**
     * 從文件中讀取數據
     */
    private void readDataFile() {
        File file = new File(filePath);
        ArrayList<String[]> dataArray = new ArrayList<String[]>();

        try {
            BufferedReader in = new BufferedReader(new FileReader(file));
            String str;
            String[] tempArray;
            while ((str = in.readLine()) != null) {
                tempArray = str.split(" ");
                dataArray.add(tempArray);
            }
            in.close();
        } catch (IOException e) {
            e.getStackTrace();
        }

        pageClass = new ArrayList<>();
        // 統計網頁類型種數
        for (String[] array : dataArray) {
            for (String s : array) {
                if (!pageClass.contains(s)) {
                    pageClass.add(s);
                }
            }
        }

        int i = 0;
        int j = 0;
        pageNum = pageClass.size();
        linkMatrix = new double[pageNum][pageNum];
        pageRankVecor = new double[pageNum];
        for (int k = 0; k < pageNum; k++) {
            // 初始每個頁面的pageRank值為1
            pageRankVecor[k] = 1.0;
        }
        for (String[] array : dataArray) {

            i = Integer.parseInt(array[0]);
            j = Integer.parseInt(array[1]);

            // 設置linkMatrix[i][j]為1代表i網頁包含指向j網頁的鏈接
            linkMatrix[i - 1][j - 1] = 1;
        }
    }

    /**
     * 將矩陣轉置
     */
    private void transferMatrix() {
        int count = 0;
        for (double[] array : linkMatrix) {
            // 計算頁面鏈接個數
            count = 0;
            for (double d : array) {
                if (d == 1) {
                    count++;
                }
            }
            // 按概率均分
            for (int i = 0; i < array.length; i++) {
                if (array[i] == 1) {
                    array[i] /= count;
                }
            }
        }

        double t = 0;
        // 將矩陣轉置換,作為概率轉移矩陣
        for (int i = 0; i < linkMatrix.length; i++) {
            for (int j = i + 1; j < linkMatrix[0].length; j++) {
                t = linkMatrix[i][j];
                linkMatrix[i][j] = linkMatrix[j][i];
                linkMatrix[j][i] = t;
            }
        }
    }

    /**
     * 利用冪法計算pageRank值
     */
    public void printPageRankValue() {
        transferMatrix();
        // 阻尼系數
        double damp = 0.5;
        // 鏈接概率矩陣
        double[][] A = new double[pageNum][pageNum];
        double[][] e = new double[pageNum][pageNum];

        // 調用公式A=d*q+(1-d)*e/m,m為網頁總個數,d就是damp
        double temp = (1 - damp) / pageNum;
        for (int i = 0; i < e.length; i++) {
            for (int j = 0; j < e[0].length; j++) {
                e[i][j] = temp;
            }
        }

        for (int i = 0; i < pageNum; i++) {
            for (int j = 0; j < pageNum; j++) {
                temp = damp * linkMatrix[i][j] + e[i][j];
                A[i][j] = temp;

            }
        }

        // 誤差值,作為判斷收斂標准
        double errorValue = Integer.MAX_VALUE;
        double[] newPRVector = new double[pageNum];
        // 當平均每個PR值誤差小於0.001時就算達到收斂
        while (errorValue > 0.001 * pageNum) {
            System.out.println("**********");
            for (int i = 0; i < pageNum; i++) {
                temp = 0;
                // 將A*pageRankVector,利用冪法求解,直到pageRankVector值收斂
                for (int j = 0; j < pageNum; j++) {
                    // temp就是每個網頁到i頁面的pageRank值
                    temp += A[i][j] * pageRankVecor[j];
                }

                // 最后的temp就是i網頁的總PageRank值
                newPRVector[i] = temp;
                System.out.println(temp);
            }

            errorValue = 0;
            for (int i = 0; i < pageNum; i++) {
                errorValue += Math.abs(pageRankVecor[i] - newPRVector[i]);
                // 新的向量代替舊的向量
                pageRankVecor[i] = newPRVector[i];
            }
        }

        String name = null;
        temp = 0;
        System.out.println("--------------------");
        for (int i = 0; i < pageNum; i++) {
            System.out.println(MessageFormat.format("網頁{0}的pageRank值:{1}",
                    pageClass.get(i), pageRankVecor[i]));
            if (pageRankVecor[i] > temp) {
                temp = pageRankVecor[i];
                name = pageClass.get(i);
            }
        }
        System.out.println(MessageFormat.format("等級最高的網頁為:{0}", name));
    }

}
 

下面是HITS算法的實現: 

package DataMining_HITS;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;

/**
 * HITS鏈接分析算法工具類
 * @author lyq
 *
 */
public class HITSTool {
    //輸入數據文件地址
    private String filePath;
    //網頁個數
    private int pageNum;
    //網頁Authority權威值
    private double[] authority;
    //網頁hub中心值
    private double[] hub;
    //鏈接矩陣關系
    private int[][] linkMatrix;
    //網頁種類
    private ArrayList<String> pageClass;
    
    public HITSTool(String filePath){
        this.filePath = filePath;
        readDataFile();
    }
    
    /**
     * 從文件中讀取數據
     */
    private void readDataFile() {
        File file = new File(filePath);
        ArrayList<String[]> dataArray = new ArrayList<String[]>();

        try {
            BufferedReader in = new BufferedReader(new FileReader(file));
            String str;
            String[] tempArray;
            while ((str = in.readLine()) != null) {
                tempArray = str.split(" ");
                dataArray.add(tempArray);
            }
            in.close();
        } catch (IOException e) {
            e.getStackTrace();
        }

        pageClass = new ArrayList<>();
        // 統計網頁類型種數
        for (String[] array : dataArray) {
            for (String s : array) {
                if (!pageClass.contains(s)) {
                    pageClass.add(s);
                }
            }
        }

        int i = 0;
        int j = 0;
        pageNum = pageClass.size();
        linkMatrix = new int[pageNum][pageNum];
        authority = new double[pageNum];
        hub = new double[pageNum];
        for(int k=0; k<pageNum; k++){
            //初始時默認權威值和中心值都為1
            authority[k] = 1;
            hub[k] = 1;
        }
        
        for (String[] array : dataArray) {

            i = Integer.parseInt(array[0]);
            j = Integer.parseInt(array[1]);

            // 設置linkMatrix[i][j]為1代表i網頁包含指向j網頁的鏈接
            linkMatrix[i - 1][j - 1] = 1;
        }
    }
    
    /**
     * 輸出結果頁面,也就是authority權威值最高的頁面
     */
    public void printResultPage(){
        //最大Hub和Authority值,用於后面的歸一化計算
        double maxHub = 0;
        double maxAuthority = 0;
        int maxAuthorityIndex =0;
        //誤差值,用於收斂判斷
        double error = Integer.MAX_VALUE;
        double[] newHub = new double[pageNum];
        double[] newAuthority = new double[pageNum];
        
        
        while(error > 0.01 * pageNum){
            for(int k=0; k<pageNum; k++){
                newHub[k] = 0;
                newAuthority[k] = 0;
            }
            
            //hub和authority值的更新計算
            for(int i=0; i<pageNum; i++){
                for(int j=0; j<pageNum; j++){
                    if(linkMatrix[i][j] == 1){
                        newHub[i] += authority[j];
                        newAuthority[j] += hub[i];
                    }
                }
            }
            
            maxHub = 0;
            maxAuthority = 0;
            for(int k=0; k<pageNum; k++){
                if(newHub[k] > maxHub){
                    maxHub = newHub[k];
                }
                
                if(newAuthority[k] > maxAuthority){
                    maxAuthority = newAuthority[k];
                    maxAuthorityIndex = k;
                }
            }
            
            error = 0;
            //歸一化處理
            for(int k=0; k<pageNum; k++){
                newHub[k] /= maxHub;
                newAuthority[k] /= maxAuthority;
                
                error += Math.abs(newHub[k] - hub[k]);
                System.out.println(newAuthority[k] + ":" + newHub[k]);
                
                hub[k] = newHub[k];
                authority[k] = newAuthority[k];
            }
            System.out.println("---------");
        }
        
        System.out.println("****最終收斂的網頁的權威值和中心值****");
        for(int k=0; k<pageNum; k++){
            System.out.println("網頁" + pageClass.get(k) + ":"+ authority[k] + ":" + hub[k]);
        }
        System.out.println("權威值最高的網頁為:網頁" + pageClass.get(maxAuthorityIndex));
    }

}

2個結果的輸出如下: 

PageRank算法;

**********
1.0
0.7499999999999999
1.25
**********
1.125
0.75
1.1249999999999998
**********
1.0624999999999998
0.78125
1.15625
**********
1.078125
0.7656249999999998
1.1562499999999998
**********
1.0781249999999998
0.7695312499999998
1.1523437499999998
**********
1.0761718749999998
0.7695312499999998
1.1542968749999996
**********
1.0771484374999996
0.7690429687499997
1.1538085937499996
--------------------
網頁1的pageRank值:1.077
網頁2的pageRank值:0.769
網頁3的pageRank值:1.154
等級最高的網頁為:3

HITS算法:

 

0.5:1.0
0.5:0.5
1.0:0.5
---------
0.3333333333333333:1.0
0.6666666666666666:0.6666666666666666
1.0:0.3333333333333333
---------
0.2:1.0
0.6000000000000001:0.6000000000000001
1.0:0.2
---------
0.125:1.0
0.625:0.625
1.0:0.125
---------
0.07692307692307693:1.0
0.6153846153846154:0.6153846153846154
1.0:0.07692307692307693
---------
0.04761904761904762:1.0
0.6190476190476191:0.6190476190476191
1.0:0.04761904761904762
---------
0.029411764705882356:1.0
0.6176470588235294:0.6176470588235294
1.0:0.029411764705882356
---------
****最終收斂的網頁的權威值和中心值****
網頁1:0.029411764705882356:1.0
網頁2:0.6176470588235294:0.6176470588235294
網頁3:1.0:0.029411764705882356
權威值最高的網頁為:網頁3

結果都是網頁3排名最高。


免責聲明!

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



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