链接挖掘算法之PageRank算法和HITS算法

来源:互联网 发布:坦桑尼亚军事实力知乎 编辑:程序博客网 时间:2024/04/26 05:33

参考资料: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权威值从高到低。

HITS算法描述

             

具体可以对照后面我写的程序。

HITS小结

从链接反作弊的角度来思考,HITS更容易遭受到Link Spam的攻击,因为你想啊,网页数量少啊,出错的几率就显得会大了。

PageRank算法和HITS算法实现

最后奉上本人亲自实现的2个算法,输入数据是同一个文(每条记录代表网页i到网页j存在链接):

[java] view plaincopyprint?
  1. 1 2  
  2. 1 3  
  3. 2 3  
  4. 3 1  
算法都不是太难:

[java] view plaincopyprint?
  1. package DataMining_PageRank;  
  2.   
  3. import java.io.BufferedReader;  
  4. import java.io.File;  
  5. import java.io.FileReader;  
  6. import java.io.IOException;  
  7. import java.lang.reflect.Array;  
  8. import java.text.MessageFormat;  
  9. import java.util.ArrayList;  
  10.   
  11. /** 
  12.  * PageRank网页排名算法工具类 
  13.  *  
  14.  * @author lyq 
  15.  *  
  16.  */  
  17. public class PageRankTool {  
  18.     // 测试输入数据  
  19.     private String filePath;  
  20.     // 网页总数量  
  21.     private int pageNum;  
  22.     // 链接关系矩阵  
  23.     private double[][] linkMatrix;  
  24.     // 每个页面pageRank值初始向量  
  25.     private double[] pageRankVecor;  
  26.   
  27.     // 网页数量分类  
  28.     ArrayList<String> pageClass;  
  29.   
  30.     public PageRankTool(String filePath) {  
  31.         this.filePath = filePath;  
  32.         readDataFile();  
  33.     }  
  34.   
  35.     /** 
  36.      * 从文件中读取数据 
  37.      */  
  38.     private void readDataFile() {  
  39.         File file = new File(filePath);  
  40.         ArrayList<String[]> dataArray = new ArrayList<String[]>();  
  41.   
  42.         try {  
  43.             BufferedReader in = new BufferedReader(new FileReader(file));  
  44.             String str;  
  45.             String[] tempArray;  
  46.             while ((str = in.readLine()) != null) {  
  47.                 tempArray = str.split(" ");  
  48.                 dataArray.add(tempArray);  
  49.             }  
  50.             in.close();  
  51.         } catch (IOException e) {  
  52.             e.getStackTrace();  
  53.         }  
  54.   
  55.         pageClass = new ArrayList<>();  
  56.         // 统计网页类型种数  
  57.         for (String[] array : dataArray) {  
  58.             for (String s : array) {  
  59.                 if (!pageClass.contains(s)) {  
  60.                     pageClass.add(s);  
  61.                 }  
  62.             }  
  63.         }  
  64.   
  65.         int i = 0;  
  66.         int j = 0;  
  67.         pageNum = pageClass.size();  
  68.         linkMatrix = new double[pageNum][pageNum];  
  69.         pageRankVecor = new double[pageNum];  
  70.         for (int k = 0; k < pageNum; k++) {  
  71.             // 初始每个页面的pageRank值为1  
  72.             pageRankVecor[k] = 1.0;  
  73.         }  
  74.         for (String[] array : dataArray) {  
  75.   
  76.             i = Integer.parseInt(array[0]);  
  77.             j = Integer.parseInt(array[1]);  
  78.   
  79.             // 设置linkMatrix[i][j]为1代表i网页包含指向j网页的链接  
  80.             linkMatrix[i - 1][j - 1] = 1;  
  81.         }  
  82.     }  
  83.   
  84.     /** 
  85.      * 将矩阵转置 
  86.      */  
  87.     private void transferMatrix() {  
  88.         int count = 0;  
  89.         for (double[] array : linkMatrix) {  
  90.             // 计算页面链接个数  
  91.             count = 0;  
  92.             for (double d : array) {  
  93.                 if (d == 1) {  
  94.                     count++;  
  95.                 }  
  96.             }  
  97.             // 按概率均分  
  98.             for (int i = 0; i < array.length; i++) {  
  99.                 if (array[i] == 1) {  
  100.                     array[i] /= count;  
  101.                 }  
  102.             }  
  103.         }  
  104.   
  105.         double t = 0;  
  106.         // 将矩阵转置换,作为概率转移矩阵  
  107.         for (int i = 0; i < linkMatrix.length; i++) {  
  108.             for (int j = i + 1; j < linkMatrix[0].length; j++) {  
  109.                 t = linkMatrix[i][j];  
  110.                 linkMatrix[i][j] = linkMatrix[j][i];  
  111.                 linkMatrix[j][i] = t;  
  112.             }  
  113.         }  
  114.     }  
  115.   
  116.     /** 
  117.      * 利用幂法计算pageRank值 
  118.      */  
  119.     public void printPageRankValue() {  
  120.         transferMatrix();  
  121.         // 阻尼系数  
  122.         double damp = 0.5;  
  123.         // 链接概率矩阵  
  124.         double[][] A = new double[pageNum][pageNum];  
  125.         double[][] e = new double[pageNum][pageNum];  
  126.   
  127.         // 调用公式A=d*q+(1-d)*e/m,m为网页总个数,d就是damp  
  128.         double temp = (1 - damp) / pageNum;  
  129.         for (int i = 0; i < e.length; i++) {  
  130.             for (int j = 0; j < e[0].length; j++) {  
  131.                 e[i][j] = temp;  
  132.             }  
  133.         }  
  134.   
  135.         for (int i = 0; i < pageNum; i++) {  
  136.             for (int j = 0; j < pageNum; j++) {  
  137.                 temp = damp * linkMatrix[i][j] + e[i][j];  
  138.                 A[i][j] = temp;  
  139.   
  140.             }  
  141.         }  
  142.   
  143.         // 误差值,作为判断收敛标准  
  144.         double errorValue = Integer.MAX_VALUE;  
  145.         double[] newPRVector = new double[pageNum];  
  146.         // 当平均每个PR值误差小于0.001时就算达到收敛  
  147.         while (errorValue > 0.001 * pageNum) {  
  148.             System.out.println("**********");  
  149.             for (int i = 0; i < pageNum; i++) {  
  150.                 temp = 0;  
  151.                 // 将A*pageRankVector,利用幂法求解,直到pageRankVector值收敛  
  152.                 for (int j = 0; j < pageNum; j++) {  
  153.                     // temp就是每个网页到i页面的pageRank值  
  154.                     temp += A[i][j] * pageRankVecor[j];  
  155.                 }  
  156.   
  157.                 // 最后的temp就是i网页的总PageRank值  
  158.                 newPRVector[i] = temp;  
  159.                 System.out.println(temp);  
  160.             }  
  161.   
  162.             errorValue = 0;  
  163.             for (int i = 0; i < pageNum; i++) {  
  164.                 errorValue += Math.abs(pageRankVecor[i] - newPRVector[i]);  
  165.                 // 新的向量代替旧的向量  
  166.                 pageRankVecor[i] = newPRVector[i];  
  167.             }  
  168.         }  
  169.   
  170.         String name = null;  
  171.         temp = 0;  
  172.         System.out.println("--------------------");  
  173.         for (int i = 0; i < pageNum; i++) {  
  174.             System.out.println(MessageFormat.format("网页{0}的pageRank值:{1}",  
  175.                     pageClass.get(i), pageRankVecor[i]));  
  176.             if (pageRankVecor[i] > temp) {  
  177.                 temp = pageRankVecor[i];  
  178.                 name = pageClass.get(i);  
  179.             }  
  180.         }  
  181.         System.out.println(MessageFormat.format("等级最高的网页为:{0}", name));  
  182.     }  
  183.   
  184. }  
下面是HITS算法的实现:

[java] view plaincopyprint?
  1. package DataMining_HITS;  
  2.   
  3. import java.io.BufferedReader;  
  4. import java.io.File;  
  5. import java.io.FileReader;  
  6. import java.io.IOException;  
  7. import java.util.ArrayList;  
  8.   
  9. /** 
  10.  * HITS链接分析算法工具类 
  11.  * @author lyq 
  12.  * 
  13.  */  
  14. public class HITSTool {  
  15.     //输入数据文件地址  
  16.     private String filePath;  
  17.     //网页个数  
  18.     private int pageNum;  
  19.     //网页Authority权威值  
  20.     private double[] authority;  
  21.     //网页hub中心值  
  22.     private double[] hub;  
  23.     //链接矩阵关系  
  24.     private int[][] linkMatrix;  
  25.     //网页种类  
  26.     private ArrayList<String> pageClass;  
  27.       
  28.     public HITSTool(String filePath){  
  29.         this.filePath = filePath;  
  30.         readDataFile();  
  31.     }  
  32.       
  33.     /** 
  34.      * 从文件中读取数据 
  35.      */  
  36.     private void readDataFile() {  
  37.         File file = new File(filePath);  
  38.         ArrayList<String[]> dataArray = new ArrayList<String[]>();  
  39.   
  40.         try {  
  41.             BufferedReader in = new BufferedReader(new FileReader(file));  
  42.             String str;  
  43.             String[] tempArray;  
  44.             while ((str = in.readLine()) != null) {  
  45.                 tempArray = str.split(" ");  
  46.                 dataArray.add(tempArray);  
  47.             }  
  48.             in.close();  
  49.         } catch (IOException e) {  
  50.             e.getStackTrace();  
  51.         }  
  52.   
  53.         pageClass = new ArrayList<>();  
  54.         // 统计网页类型种数  
  55.         for (String[] array : dataArray) {  
  56.             for (String s : array) {  
  57.                 if (!pageClass.contains(s)) {  
  58.                     pageClass.add(s);  
  59.                 }  
  60.             }  
  61.         }  
  62.   
  63.         int i = 0;  
  64.         int j = 0;  
  65.         pageNum = pageClass.size();  
  66.         linkMatrix = new int[pageNum][pageNum];  
  67.         authority = new double[pageNum];  
  68.         hub = new double[pageNum];  
  69.         for(int k=0; k<pageNum; k++){  
  70.             //初始时默认权威值和中心值都为1  
  71.             authority[k] = 1;  
  72.             hub[k] = 1;  
  73.         }  
  74.           
  75.         for (String[] array : dataArray) {  
  76.   
  77.             i = Integer.parseInt(array[0]);  
  78.             j = Integer.parseInt(array[1]);  
  79.   
  80.             // 设置linkMatrix[i][j]为1代表i网页包含指向j网页的链接  
  81.             linkMatrix[i - 1][j - 1] = 1;  
  82.         }  
  83.     }  
  84.       
  85.     /** 
  86.      * 输出结果页面,也就是authority权威值最高的页面 
  87.      */  
  88.     public void printResultPage(){  
  89.         //最大Hub和Authority值,用于后面的归一化计算  
  90.         double maxHub = 0;  
  91.         double maxAuthority = 0;  
  92.         int maxAuthorityIndex =0;  
  93.         //误差值,用于收敛判断  
  94.         double error = Integer.MAX_VALUE;  
  95.         double[] newHub = new double[pageNum];  
  96.         double[] newAuthority = new double[pageNum];  
  97.           
  98.           
  99.         while(error > 0.01 * pageNum){  
  100.             for(int k=0; k<pageNum; k++){  
  101.                 newHub[k] = 0;  
  102.                 newAuthority[k] = 0;  
  103.             }  
  104.               
  105.             //hub和authority值的更新计算  
  106.             for(int i=0; i<pageNum; i++){  
  107.                 for(int j=0; j<pageNum; j++){  
  108.                     if(linkMatrix[i][j] == 1){  
  109.                         newHub[i] += authority[j];  
  110.                         newAuthority[j] += hub[i];  
  111.                     }  
  112.                 }  
  113.             }  
  114.               
  115.             maxHub = 0;  
  116.             maxAuthority = 0;  
  117.             for(int k=0; k<pageNum; k++){  
  118.                 if(newHub[k] > maxHub){  
  119.                     maxHub = newHub[k];  
  120.                 }  
  121.                   
  122.                 if(newAuthority[k] > maxAuthority){  
  123.                     maxAuthority = newAuthority[k];  
  124.                     maxAuthorityIndex = k;  
  125.                 }  
  126.             }  
  127.               
  128.             error = 0;  
  129.             //归一化处理  
  130.             for(int k=0; k<pageNum; k++){  
  131.                 newHub[k] /= maxHub;  
  132.                 newAuthority[k] /= maxAuthority;  
  133.                   
  134.                 error += Math.abs(newHub[k] - hub[k]);  
  135.                 System.out.println(newAuthority[k] + ":" + newHub[k]);  
  136.                   
  137.                 hub[k] = newHub[k];  
  138.                 authority[k] = newAuthority[k];  
  139.             }  
  140.             System.out.println("---------");  
  141.         }  
  142.           
  143.         System.out.println("****最终收敛的网页的权威值和中心值****");  
  144.         for(int k=0; k<pageNum; k++){  
  145.             System.out.println("网页" + pageClass.get(k) + ":"+ authority[k] + ":" + hub[k]);  
  146.         }  
  147.         System.out.println("权威值最高的网页为:网页" + pageClass.get(maxAuthorityIndex));  
  148.     }  
  149.   
  150. }  
2个结果的输出如下:

PageRank算法;

[java] view plaincopyprint?
  1. **********  
  2. 1.0  
  3. 0.7499999999999999  
  4. 1.25  
  5. **********  
  6. 1.125  
  7. 0.75  
  8. 1.1249999999999998  
  9. **********  
  10. 1.0624999999999998  
  11. 0.78125  
  12. 1.15625  
  13. **********  
  14. 1.078125  
  15. 0.7656249999999998  
  16. 1.1562499999999998  
  17. **********  
  18. 1.0781249999999998  
  19. 0.7695312499999998  
  20. 1.1523437499999998  
  21. **********  
  22. 1.0761718749999998  
  23. 0.7695312499999998  
  24. 1.1542968749999996  
  25. **********  
  26. 1.0771484374999996  
  27. 0.7690429687499997  
  28. 1.1538085937499996  
  29. --------------------  
  30. 网页1的pageRank值:1.077  
  31. 网页2的pageRank值:0.769  
  32. 网页3的pageRank值:1.154  
  33. 等级最高的网页为:3  
HITS算法:

[java] view plaincopyprint?
  1. 0.5:1.0  
  2. 0.5:0.5  
  3. 1.0:0.5  
  4. ---------  
  5. 0.3333333333333333:1.0  
  6. 0.6666666666666666:0.6666666666666666  
  7. 1.0:0.3333333333333333  
  8. ---------  
  9. 0.2:1.0  
  10. 0.6000000000000001:0.6000000000000001  
  11. 1.0:0.2  
  12. ---------  
  13. 0.125:1.0  
  14. 0.625:0.625  
  15. 1.0:0.125  
  16. ---------  
  17. 0.07692307692307693:1.0  
  18. 0.6153846153846154:0.6153846153846154  
  19. 1.0:0.07692307692307693  
  20. ---------  
  21. 0.04761904761904762:1.0  
  22. 0.6190476190476191:0.6190476190476191  
  23. 1.0:0.04761904761904762  
  24. ---------  
  25. 0.029411764705882356:1.0  
  26. 0.6176470588235294:0.6176470588235294  
  27. 1.0:0.029411764705882356  
  28. ---------  
  29. ****最终收敛的网页的权威值和中心值****  
  30. 网页1:0.029411764705882356:1.0  
  31. 网页2:0.6176470588235294:0.6176470588235294  
  32. 网页3:1.0:0.029411764705882356  
  33. 权威值最高的网页为:网页3  
结果都是网页3排名最高。
1 0