刘  勇  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. MVC使用Entity Framework Code First,用漂亮表格显示1对多关系

    部门和职员是1对多关系.用一个表格列出所有部门,并且在每行显示该部门下的所有职员名称.如下: 部门和职员的Model: using System.Collections.Generic; namesp ...

  2. arcgis runtime 100 Create geometries

    1 /* Copyright 2016 EsriEsri 2 * 3 * Licensed under the Apache License, Version 2.0 (the "Licen ...

  3. Lombok的安装及入门

    lombok 的官方网址:http://projectlombok.org/ lombok 其实到这里我就介绍完了,开个玩笑,其实官网上有 lombok 三分四十九秒的视频讲解,里面讲的也很清楚了,而 ...

  4. [Android Pro] AtomicInteger的用法

    J2SE 5.0提供了一组atomic class来帮助我们简化同步处理.基本工作原理是使用了同步synchronized的方法实现了对一个long, integer, 对象的增.减.赋值(更新)操作 ...

  5. [转]linux sort 命令详解

    原文网址:http://www.cnblogs.com/51linux/archive/2012/05/23/2515299.html 1 sort的工作原理 sort将文件的每一行作为一个单位,相互 ...

  6. UVA 10194 (13.08.05)

    :W Problem A: Football (aka Soccer)  The Problem Football the most popular sport in the world (ameri ...

  7. 系列文章 -- OpenCV入门教程

     <OpenCV3编程入门>内容简介&勘误&配套源代码下载 [OpenCV入门教程之十八]OpenCV仿射变换 & SURF特征点描述合辑 [OpenCV入门教程之 ...

  8. Android下拉刷新-SwipeRefreshLayout

    现在市面上新闻类的App基本上都有下拉刷新,算是一个标配吧,网上关于下拉刷新的博客也有很多,实现方式可以使用开源的PullToRefresh,自定义ListView,或者可以直接使用LineLayOu ...

  9. vue-router各个属性的作用及用法

    vue-router是vue单页面开发的路由,就是决定页面跳转的! <router-link> 组件支持用户在具体有路由功能的应用中(点击)导航.通过to属性指定目标地址. Props 属 ...

  10. 理解闭包的微观世界和JS垃圾回收机制

    function a() { ; function b() { alert(++i); } return b; } var c = a(); c(); 一.闭包的微观世界 如果要更加深入的了解闭包以及 ...