刘  勇  Email:lyssym@sina.com

简介

  鉴于在Web抓取服务和文本挖掘之句子向量中对权重值的计算需要,本文基于MapReduce计算模型实现了PageRank算法。为验证本文算法的有效性,本文采用177万余条源URL到目标URL链接的数据集,并迭代101次来展开测试,测试结果表明:对上述数据集进行测试,总计耗时40.29分钟。因此,在权重评定的算法设计与实现中引入该思想,具有较好的现实意义。

引言

  在Web抓取服务中,由于采用多个定向爬虫对网页进行抓取,因此其面临2个重要问题,1)爬虫的调度问题,不同的爬虫的抓取频率决定了获取该站点的信息数量;2)爬虫的深度问题,在某个站点内抓取越深,其获取的信息越陈旧,而爬虫设计者的本意是及时回头,即到达一定深度后,返回站点首页或者回退至上一步。因此,基于上述现实问题,本文作者拟将Google的PageRank算法的排名思想引入至该应用中,通过其PageRank值来确定各站点的抓取频率,但是采用这种设计的结果,则是设计一个全网爬虫。

  在文本挖掘研究中,对句子权重的研究中,拟引入PageRank思想来计算句子向量的权重中,主要是基于句子相互间存在语义关联。

  鉴于上述原因,结合Web数据规模日益扩展的需要,采用MapReduce计算模型实现PageRank算法。

PageRank设计

  1) 有向图

  互联网中的网页可以视为有向图,每一个网页可以视为一个节点,若网页A中有一个链接到B,则存在一条有向边AàB。如图1所示,为一个简单的网络链接有向图。

图1 网络链接有向图

  根据图1对PageRank排名思想解释如下:对于A节点,其存在3条链出(A-->B,A-->C,A-->D),因此B、C、D分别获得A节点PageRank(PR)值的1/3;但是,节点A又存在2条链入(B--> A,C-->A),即节点B的PR的1/2和节点C的PR对A有贡献。其它节点,与之类同,在此不做赘述。

  根据上述分析,很明显PageRank算法需要做多次迭代,以期使各节点的PR值趋于稳定(最终达到收敛)。本文作者设计时,拟采用100为最大迭代次数。

  2) 终止与陷阱

  若将Web抓取服务(网络爬虫)视为马尔科夫链过程,则上述收敛问题,必须满足有向图是强关联的。但是现实网络世界里,强关联有向图不太现实,即有些站点(网址)中的信息中根本没有链接或者链接已失效,即面临节点终止问题,如图2所示。

图2网络链接有向图

  在图2中,节点C没有链出,即Web抓取服务在C节点面临终止,其造成PR值在多次迭代之后收敛于0。按照网络爬虫设计的本意,当面临节点终止时,应该跳转至其它的节点,最常见的做法为跳转至站点首页或者返回上一级。

  此外,有些站点(网址)为了方便向用户提供服务,会在网页醒目位置多次嵌入本网页的链接,如门户网站。但是,若某一个站点(网址)只嵌入本网页的链接,则必然造成网络爬虫掉入陷阱中,进入死循环,即面临节点陷阱问题,如图3所示。

图3 网络链接有向图

  在图3中,节点C只存在自身的链出,即Web抓取服务在C节点陷入死循环,其造成PR值在多次迭代之后,C节点的PR值为1,其它节点的PR值均为0。按照网络爬虫设计的本意,当面节点陷阱时,应该跳转至其它的节点,最常见的做法为跳转至站点首页或者返回上一级。

因此,本文作者综合上述2种特殊情形,引入以下策略,每个节点,都有权力选择进入下一个节点,或者回退至上一个节点。具体数学表达式如公式1所示:

          (公式1)

如公式1所示,α为影响因子, 和分别为当前迭代中某节点的PR值和上一次迭代中某节点的PR。鉴于每个节点均有权利回退至上一个节点,由于表征上一次迭代中所有链入之和,故此引入该参数来表征回退至上一次的可能性。

  3) 算法设计

  本文在设计中,采用两级MapReduce计算模型来实现,第一级MapReduce生成网络链接的有向图;第二级MapReduce用于迭代PR,以确定PR是否收敛。需要指出,本文在设计过程中,Reducer均设定为5个。

  针对第一级MapReduce,对其Mapper和Reducer简要描述如下:

  Mapper:完成源URL和目标URL的标识,如A-->B;

  Reducer: 根据源URL(有向图),实现某一节点至其所有链出的标识,如A-->B-->C-->D,需要指出,上述标识表示,A-->B,A-->C,A-->D。采用该设计主要是为了节省节点存储空间。

  针对第二级MapReduce,对其Mapper和Reducer简要描述如下:

  Mapper: 完成链出节点及其PR获取,如B节点存在B-->A,B-->D,则A和D均分B的PR值;

  Reducer:针对Mapper中每个节点及其PR,对该节点PR值求和,并采用公式1进行量化。

  其中第二级MapReduce采用多次迭代,若迭代过程中,节点的PR已收敛,则退出迭代。

  4)  测试结果

  本文测试环境描述如下,采用10台物理机组成Hadoop集群,CPU:Intel(R) Core(TM) i5-4440 CPU @ 3.10GHz,内存:4G,Hadoop:2.7.1,以上描述为集群的大概配置,其中某个节点的配置可能不一致,本文作者也并未对每个节点进行详细确认。本文测试数据集采用177万余条链接,迭代101次(迭代次数当时控制失误,本意为100次,作者比较懒就没有重新再次迭代了),总计耗时为40.29分钟。从整体而言,其处理速率在1小时内,还是能够接受的。

总结

  本文对基于MapReduce的PageRank算法进行研究与实现,经过实际数据集进行测试,测试结果表明,该测试结果处理速率还是能够接受的。但是,本文作者的意图并不是为了实现该算法,而是将该算法的设计思想引入后续Web抓取服务的优化与改进之中,以及后续文本挖掘中对权重值计算的需要之中。

  程序源代码:

 import java.io.IOException;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; public class GraphSet { public static class GraphMapper extends Mapper<Object, Text, Text, Text> {
public static Logger logger = LogManager.getLogger(GraphMapper.class); public void map(Object key, Text value, Context context)
{
String[] link = value.toString().split("\t");
try {
context.write(new Text(link[0]), new Text(link[1]));
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public static class GraphReducer extends Reducer<Text, Text, NullWritable, Text> {
public static Logger logger = LogManager.getLogger(GraphReducer.class);
public void reduce(Text key, Iterable<Text> values, Context context)
{
StringBuilder sb = new StringBuilder();
sb.append(key.toString());
for (Text e : values) {
sb.append("\t");
sb.append(e.toString());
} try {
context.write(NullWritable.get(), new Text(sb.toString()));
} catch (IOException e1) {
e1.printStackTrace();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
} }

Class GraphSet

 import java.io.IOException;
import java.util.Map;
import java.util.HashMap; import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.DoubleWritable;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; public class PageRank {
public static double alpha = 0.8; public static class PageRankMapper extends Mapper<Object, Text, Text, DoubleWritable> {
public static Map<String, Double> mapPR = new HashMap<String, Double>();
public static Logger logger = LogManager.getLogger(PageRankMapper.class); public void map(Object key, Text value, Context context) {
String[] str = value.toString().split("\t");
int size = str.length;
double linkOut = mapPR.get(str[0]);
try {
for (int i = 0; i < size-1; i++) {
DoubleWritable dw = new DoubleWritable();
dw.set(linkOut / (size-1)); // count the output of links
context.write(new Text(str[i+1]), dw);
}
} catch (Exception e) {
e.printStackTrace();
}
}
} public static class PageRankReducer extends Reducer<Text, DoubleWritable, NullWritable, Text> {
public static Logger logger = LogManager.getLogger(PageRankReducer.class);
private int numberOfUrl = 0; public void setup(Context context) {
numberOfUrl = context.getConfiguration().getInt("numberOfUrl", Integer.MAX_VALUE);
} public void reduce(Text key, Iterable<DoubleWritable> values, Context context) {
double factor = 0;
double sum = 0;
for (DoubleWritable d : values)
sum += d.get(); if (PageRankMapper.mapPR.containsKey(key.toString()))
factor = PageRankMapper.mapPR.get((key.toString()));
else
factor = (double)1/(2*numberOfUrl); sum = alpha*sum + (1-alpha)*factor;
String ret = key.toString() + "\t" + String.valueOf(sum);
try {
context.write(NullWritable.get(), new Text(ret));
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} }

Class PageRank

 import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
import java.util.TreeSet; import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.DoubleWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import com.gta.graph.GraphSet;
import com.gta.graph.GraphSet.GraphMapper;
import com.gta.graph.GraphSet.GraphReducer;
import com.gta.pagerank.PageRank.PageRankMapper;
import com.gta.pagerank.PageRank.PageRankReducer; public class PR {
public static final int MAX = 100;
public static final int TASK = 5;
public static final double THRESHOLD = 1e-10;
public static final String INPUT_PATH = "hdfs://10.1.130.10:9000/user/hadoop/pagerank/input/";
public static final String OUTPUT_PATH = "hdfs://10.1.130.10:9000/user/hadoop/pagerank/output/";
public static final String TMP_PATH = "hdfs://10.1.130.10:9000/user/hadoop/pagerank/tmp/";
public static Logger logger = LogManager.getLogger(PR.class);
private Configuration conf = null;
private int numberOfUrl = 0; public PR()
{
conf = new Configuration();
} public void initGraph() throws IOException, InterruptedException, ClassNotFoundException
{
Job job = Job.getInstance(conf, "Init Graph");
job.setJarByClass(GraphSet.class);
job.setMapperClass(GraphMapper.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(Text.class);
job.setNumReduceTasks(TASK);
job.setReducerClass(GraphReducer.class);
job.setOutputKeyClass(NullWritable.class);
job.setOutputValueClass(Text.class);
FileInputFormat.addInputPath(job, new Path(INPUT_PATH));
FileOutputFormat.setOutputPath(job, new Path(TMP_PATH));
job.waitForCompletion(true);
initPRMap(TMP_PATH);
conf.setInt("numberOfUrl", numberOfUrl);
} public void pageRank() throws IOException, InterruptedException, ClassNotFoundException
{
int iter = 0;
while (iter <= MAX) {
Job job = Job.getInstance(conf, "PageRank");
job.setJarByClass(PageRank.class);
job.setMapperClass(PageRankMapper.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(DoubleWritable.class);
job.setNumReduceTasks(TASK);
job.setReducerClass(PageRankReducer.class);
job.setOutputKeyClass(NullWritable.class);
job.setOutputValueClass(Text.class);
FileInputFormat.addInputPath(job, new Path(TMP_PATH));
FileOutputFormat.setOutputPath(job, new Path(OUTPUT_PATH + iter));
job.waitForCompletion(true);
Map<String, Double> newPR = getNewPageRank(OUTPUT_PATH + iter);
if (compare(PageRankMapper.mapPR, newPR))
break;
else {
for (String key : newPR.keySet())
PageRankMapper.mapPR.put(key, newPR.get(key));
} iter++;
}
} public void initPRMap(String filePath)
{
String fileName = filePath + "/part-r-0000";
Set<String> set = new TreeSet<String>();
try {
for (int i = 0; i < TASK; i++) {
FileSystem fs = FileSystem.get(URI.create((fileName+i)), conf);
FSDataInputStream is = fs.open(new Path((fileName+i).toString()));
BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
String s = null;
while ((s = br.readLine()) != null) {
String[] str = s.split("\t");
if (str.length == 0)
break;
set.add(str[0]);
}
br.close();
} numberOfUrl = set.size();
for (String s : set)
PageRankMapper.mapPR.put(s, (double)1/numberOfUrl); } catch (IOException e) {
e.printStackTrace();
}
} public Map<String, Double> getNewPageRank(String filePath)
{
Map<String, Double> newPR = new HashMap<String, Double>();
String fileName = filePath + "/part-r-0000";
try {
for (int i = 0; i < TASK; i++) {
FileSystem fs = FileSystem.get(URI.create((fileName+i)), conf);
FSDataInputStream is = fs.open(new Path((fileName+i).toString()));
BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
String s = null;
while ((s = br.readLine()) != null) {
String[] elements = s.split("\t");
newPR.put(elements[0], Double.parseDouble(elements[1]));
}
br.close();
}
} catch (IOException e) {
e.printStackTrace();
}
return newPR;
} public boolean compare(Map<String, Double> oldPR, Map<String, Double> newPR) {
boolean ret = false;
int newPRSize = newPR.size();
int oldPRSize = oldPR.size();
if (oldPRSize == newPRSize) {
int count = 0;
for (String key : oldPR.keySet()) {
if (newPR.containsKey(key)) {
if (Math.abs(newPR.get(key) - oldPR.get(key)) <= THRESHOLD)
count++;
}
} if (count == newPRSize)
ret = true;
}
return ret;
} public static void main(String[] args) {
PR pr = new PR();
try {
long start = System.currentTimeMillis();
pr.initGraph();
pr.pageRank();
long end = System.currentTimeMillis();
PR.logger.info("共耗时: " + (end-start));
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
} }

Class PR

  本文插图参考:http://www.cnblogs.com/fengfenggirl/p/pagerank-introduction.html


  作者:志青云集
  出处:http://www.cnblogs.com/lyssym
  如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下角的【推荐】。
  如果,您希望更容易地发现我的新博客,不妨点击一下左下角的【关注我】。
  如果,您对我的博客所讲述的内容有兴趣,请继续关注我的后续博客,我是【志青云集】。
  本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。


数据挖掘之权重计算(PageRank)的更多相关文章

  1. 前端极易被误导的css选择器权重计算及css内联样式的妙用技巧

    记得大学时候,专业课的网页设计书籍里面讲过css选择器权重的计算:id是100,class是10,html标签是5等等,然后全部加起来的和进行比较... 我只想说:真是误人子弟,害人不浅! 最近,在前 ...

  2. CSS-选择器权重计算

    权重计算规则 内联样式,如: style=" ",权值为1000. ID选择器,如:#content,权值为0100. 类,伪类和属性选择器,如.content,权值为0010. ...

  3. CSS 选择器权重计算规则

    其实,CSS有自己的优先级计算公式,而不仅仅是行间>内部>外部样式:ID>class>元素. 一.样式类型 1.行间 <h1 style="font-size: ...

  4. CSS中选择器优先级的权重计算

    CSS中选择器优先级的权重计算 先看一段代码,如下: a{ color: red; } #box a{ color: green; } [class="box"] a{ color ...

  5. HTML+CSS基础 权重的计算 权重计算规则

    权重的计算 将选择器上面的选择器进行叠加,叠加后的总和就是该选择器的权重. 权重计算规则

  6. tf-idf 词条权重计算

    在文本分类问题中,某些高频词一直出现,这样的词对区分文档的作用不大,例如: D1:  'Job was the chairman of Apple Inc.' D2:  'I like to use ...

  7. JS简单实现:根据奖品权重计算中奖概率实现抽奖的方法

    本文主要介绍:使用 JS 根据奖品权重计算中奖概率实现抽奖的方法. 一.示例场景 1.1.设置抽奖活动的奖项名称 奖项名称:["一等奖", "二等奖", &qu ...

  8. CSS 选择器权重计算规则(转)

    其实,CSS有自己的优先级计算公式,而不仅仅是行间>内部>外部样式:ID>class>元素. 一.样式类型 1.行间 <h1 style="font-size: ...

  9. css系列,选择器权重计算方式

    CSS选择器分基本选择器(元素选择器,类选择器,通配符选择器,ID选择器,关系选择器), 属性选择器,伪类选择器,伪元素选择器,以及一些特殊选择器,如has,not等. 在CSS中,权重决定了哪些CS ...

随机推荐

  1. 系统字体的Regular、Light等几种名称的区别

    以苹果系统中的PingFang SC系列字体为例,其中常见的有下面几种类型可以细分如下. PingFang SC ExtraLight         苹方 特细 PingFang SC Light  ...

  2. Oracle 快速插入1000万条数据的实现方式

    1.使用dual配合connect by level create table BigTable as select rownum as id from dual connect by level & ...

  3. Mac 安装 brew

    安装方法:命令行输入 /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/ma ...

  4. 嵌入式linux GUI--DirectFB + GTK至尊秘笈

    前言 数年前,曾经开发过一个嵌入式的产品,如今市场依然存在,但由于电子产品的升级换代很快,许多元器件都采购不到了,为了延续产品的生命周期,计划在linux平台上开发新的版本.而在linux上的GUI上 ...

  5. Objective-C:浅复制(拷贝)

    浅复制:复制对象时,如果对象中包含对象类型的实例变量,只是复制指针.新对象中的对象类型实例变量和旧对象中的对象类型实例变量指的是同一个对象.任何一方实例变量对对象做修改,另一方实例变量指向的该对象也就 ...

  6. Golang 中错误与异常需要重新认识

    如何进行错误处理,这是一个Go程序员之间,特别是一些新的Go程序员,会经常讨论的问题.讨论到最后往往由于以下代码的多次出现而变成了抱怨. if err != nil { return err } 我们 ...

  7. Gh0st整理资料1

    题首 Gh0st是一款开源的远程控制软件.界面友好,性能高效.网上流传很多版本,比如红狼,饭客,败笔,大灰狼版本以及多如牛毛的个人修改的如外星人,Drat等个人修改版本.但内核都是基于Gh0st3.6 ...

  8. PREEMPT_RT的未来

    因为开发资金的问题,Thomas Gleixner宣布他已经不想干了. 商业公司往往用了PREEMPT_RT的功能去不愿意去回报社区,那就自己弄吧. http://lwn.net/Articles/6 ...

  9. SharePoint 2013网站突然不能登录了。

    SharePoint 2013网站突然不能登录了,访问的时候,总是报错: The list has not shared with you.   原因: 原来我不知道什么时候把web applicat ...

  10. Discuz常见小问题-如何修改自己发布的帖子

    在发布的帖子的下方就有编辑的按钮,可以直接点击进去编辑