Hadoop系列(5):MapReduce实现PageRank

1、PageRank 原理

(1) 算法定义:

PageRank 算法

输入:所有节点的初始权重值;节点邻接矩阵;迭代次数n

输出:节点权重值

while i <  n:

    for x in nodelist:

         PR(x) = a + (1-a)*sum(PR(t)/C(t))

    end for

end i

其中a为常数,一般取0.15。t为与指向x的所有节点(t->x), C(t)表示t节点的出度。

Hadoop系列(5):MapReduce实现PageRank_第1张图片

(2)算法示例(注:为简单起见,示例中省去了常量a和系数1-a)

Hadoop系列(5):MapReduce实现PageRank_第2张图片

PR(n2)=PR(n5)/C(n5)+PR(n1)/C(n1)=0.2/3+0.2/0.1=0.166 

在计算节点n3时,仍使用PR(n2)=0.2。在图中所有节点更新一遍后,节点的PR值才会更新。

2、MapReduce 实现 PageRank

思路简介:

1、预处理输入文件

由于PageRank为迭代算法,map的输入与reduce的输出格式需要一致。第一次迭代结果需要传递给第二次迭代,所以在Reduce输出时需要保留第一轮迭代后的各用户的PR值。因此,需要对input文件进行预处理,为各节点初始化PR值为1。

2、Map

在计算过程中,不仅需要向reduce端传送当前节点对其link的其他节点的贡献值,还需要传递link的情况。“link:”开始的value值保存的是节点间的关系,“pr:”开始的value值保存的是节点的贡献值。在数据集中,有些节点的出度0,仅需要记录其pr值即可。

3、Reduce

如果value以pr开始,则将对应的value值累加;Reduce的输出格式为>。

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.input.KeyValueTextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

import java.io.IOException;

public class Weibo2 {

    public static enum ValueEnum{
        CLOSURE_VALUE;
    }

    public static void main(String[] args)
    {

        //access hdfs's user
//        System.setProperty("HADOOP_USER_NAME","root");
//
        Configuration conf = new Configuration();
//        conf.set("fs.defaultFS", "hdfs://node1:8020");

        try {
            int i = 0;
            while(true) {
                i++;
                conf.setInt("count", i);

                FileSystem fs = FileSystem.get(conf);
                Job job = Job.getInstance(conf);
                job.setJarByClass(Weibo2.class);
                job.setMapperClass(MyMapper.class);
                job.setReducerClass(MyReducer.class);

                //需要指定 map out 的 key 和 value
                job.setOutputKeyClass(Text.class);
                job.setOutputValueClass(Text.class);

                //设置输入类的
                job.setInputFormatClass(KeyValueTextInputFormat.class);
                if(i==1)
                    FileInputFormat.addInputPath(job, new Path("page_rank_data_small.txt"));
                else
                    FileInputFormat.addInputPath(job, new Path("output/"+(i-1)));

                Path path = new Path("/Users/jinggu/Documents/git/HadoopApps/output/"+i);
                if (fs.exists(path))//如果目录存在,则删除目录
                {
                    fs.delete(path, true);
                }
                FileOutputFormat.setOutputPath(job, path);

                boolean b = job.waitForCompletion(true);
                if (b) {
                    long closure =job.getCounters().findCounter(ValueEnum.CLOSURE_VALUE).getValue();
                    double avg= closure/4000.0;//计算收敛的平均值,浮动小于0.001则认为收敛
                    System.out.println("执行第"+i+"次, closure="+closure+",avg="+avg);
                    if(avg < 0.001){
                        System.out.println("总共执行了"+i+"次,之后收敛");
                        break;
                    }
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    public static class MyMapper extends Mapper {
        @Override
        protected void map(Text key, Text value, Context context) throws IOException, InterruptedException {
            AdjacentNodes adjacentNodes  = new AdjacentNodes();
            int count = context.getConfiguration().getInt("count", 1);

            if(count == 1)
            {
                AdjacentNodes firstAdj = new AdjacentNodes();
                firstAdj.setValue(1.0);
                firstAdj.setNodes(value.toString().split("\t"));
                adjacentNodes = firstAdj;
            }
            else
            {
                //格式化 value: 1.0 B C D
                adjacentNodes.formatInfo(value.toString());
            }
            //A 1.0 B C D
            context.write(key, new Text(adjacentNodes.toString()));

            double pagerank = adjacentNodes.getValue()/adjacentNodes.getNum();
            for(int i=0; i {
        @Override
        protected void reduce(Text key, Iterable values, Context context) throws IOException, InterruptedException {

            AdjacentNodes adjacentNodes = new AdjacentNodes();

            Double sum = 0.0;
            for(Text adj : values)
            {
                String str = adj.toString();
                if(str.split("\t").length>1)
                {
                    adjacentNodes.formatInfo(str);
                }
                else{
                    sum += Double.parseDouble(str);   //对节点的 pagerank 求和,
                }
            }

            //计算pagerank

            double pagerank = sum * 0.85 + 0.15;// 计算pagerank 公式 sum * n + (1-n)/N

            //计算收敛的差值
            int closure =(int)(Math.abs(pagerank - adjacentNodes.getValue()) * 1000);
            //通过context.getCounter(ENUM)方法,每次执行reduce将closure的值进行累加,结果传递给主函数,

            context.getCounter(ValueEnum.CLOSURE_VALUE).increment(closure);

            adjacentNodes.setValue(pagerank);
            context.write(key, new Text(adjacentNodes.toString()));
        }
    }
}

你可能感兴趣的:(分布式系统)