参考内容:https://blog.csdn.net/Gamer_gyt/article/details/51533186
参考内容:https://blog.csdn.net/hguisu/article/details/7996185
一:PageRank与PeopleRank
PageRank,网页排名,又称网页级别、Google左侧排名或佩奇排名,是一种由 根据网页之间相互的超链接计算的技术,而作为网页排名的要素之一,以Google公司创办人拉里·佩奇(Larry Page)之姓来命名。Google用它来体现网页的相关性和重要性,在搜索引擎优化操作中是经常被用来评估网页优化的成效因素之一。Google的创始人拉里·佩奇和谢尔盖·布林于1998年在斯坦福大学发明了这项技术。
PageRank通过网络浩瀚的超链接关系来确定一个页面的等级。Google把从A页面到B页面的链接解释为A页面给B页面投票,Google根据投票来源(甚至来源的来源,即链接到A页面的页面)和投票目标的等级来决定新的等级。简单的说,一个高等级的页面可以使其他低等级页面的等级提升。
PR算法主要用于网页评分计算,它利用互联网的网页之间的连接关系,给网页进行打分,最终PR值越高的网页价值也就越高。
自2012以来,中国开始进入社交网络的时代,开心网,人人网,新浪微博,腾讯微博,微信等社交网络应用,开始进入大家的生活。最早是由“抢车位”,“偷菜”等社交游戏带动的社交网络的兴起,如今人们会更多的利用社交网络,获取信息和分享信息。我们的互联网,正在从以网页信息为核心的网络,向着以人为核心的网络转变着。
于是有人就提出了,把PageRank模型应用于社交网络,定义以人为核心的个体价值。这样PageRank模型就有了新的应用领域,同时也有了一个新的名字PeopleRank。
二:PageRank算法的思路
这个例子中只有四个网页,如果当前在A网页,那么悠闲的上网者将会各以1/3的概率跳转到B、C、D,这里的3表示A有3条出链,如果一个网页有k条出链,那么跳转任意一个出链上的概率是1/k,同理D到B、C的概率各为1/2,而B到C的概率为0。
一般用转移矩阵表示上网者的跳转概率,如果用n表示网页的数目,则转移矩阵M是一个n*n的方阵;如果网页j有k个出链,那么对每一个出链指向的网页i,有M[i][j]=1/k,而其他网页的M[i][j]=0;上面示例图对应的转移矩阵如下:
初试时,假设上网者在每一个网页的概率都是相等的,即1/n,于是初试的概率分布就是一个所有值都为1/n的n维列向量V0,用V0去右乘转移矩阵M,就得到了第一步之后上网者的概率分布向量MV0,(nXn)*(nX1)依然得到一个nX1的矩阵。下面是V1的计算过程:
注意矩阵M中M[i][j]不为0表示用一个链接从j指向i,M的第一行乘以V0,表示累加所有网页到网页A的概率即得到9/24。得到了V1后,再用V1去右乘M得到V2,一直下去,最终V会收敛,即Vn=MV(n-1),上面的图示例,不断的迭代,最终V=[3/9,2/9,2/9,2/9]’:
以上为Pagerank的算法原理.具体的你也可以参考https://blog.csdn.net/hguisu/article/details/7996185.
三:PsgeRank技术的部分知识
由于存在一些出链为0,也就是那些不链接任何其他网页的网, 也称为孤立网页,使得很多网页能被访问到。因此需要对 PageRank公式进行修正,即在简单公式的基础上增加了***阻尼系数***(damping factor)q, q一般取值q=0.85。
其意义是,在任意时刻,用户到达某页面后并继续向后浏览的概率。 1- q= 0.15就是用户停止点击,随机跳到新URL的概率)的算法被用到了所有页面上,估算页面可能被上网者放入书签的概率。
最后,即所有这些被换算为一个百分比再乘上一个系数q。由于下面的算法,没有页面的PageRank会是0。所以,Google通过数学系统给了每个页面一个最小值。
这个公式就是.S Brin 和 L. Page 在《The Anatomy of a Large- scale Hypertextual Web Search Engine Computer Networks and ISDN Systems 》定义的公式。
所以一个页面的PageRank是由其他页面的PageRank计算得到。Google不断的重复计算每个页面的PageRank。如果给每个页面一个随机PageRank值(非0),那么经过不断的重复计算,这些页面的PR值会趋向于正常和稳定。这就是搜索引擎使用它的原因。
四:pagepank的mapreduce化
github地址:https://github.com/chubbyjiang/MapReduce/tree/master/src/main/java/info/xiaohei/www/mr/peoplerank
(提示代码中用的25个节点,原始数据:链接: https://pan.baidu.com/s/1QIPCfahpSTs8vcq8fCer3w 提取码: yaxg )
以4个节点为例
people.csv数据为
1,2
1,3
1,4
2,3
2,4
3,4
4,2
peoplerank.csv(默认各页面起始pr值为1)
1,1
2,1
3,1
4,1
第一步: 构建概率矩阵
map:
public static class AdjacencyMapper extends Mapper<LongWritable, Text, Text, Text> {
Text k = new Text();
Text v = new Text();
@Override
protected void setup(Context context) throws IOException, InterruptedException {
super.setup(context);
System.out.println("AdjacencyMapper input:");
}
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
//打印当前读入的数据
System.out.println(value.toString());
String[] strArr = HadoopUtil.SPARATOR.split(value.toString());
//原始用户id为key,目标用户id为value
k.set(strArr[0]);
v.set(strArr[1]);
context.write(k, v);
}
}
输入people.csv
输出到reduce内容为
1 2
1 3
1 4
2 3
2 4
3 4
4 2
reduce:
public static class AdjacencyReducer extends Reducer<Text, Text, Text, Text> {
Text v = new Text();
@Override
protected void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException {
//初始化概率矩阵,概率矩阵只有一列,函数和总用户数相同
//用户数
int nums = 4;
float[] G = new float[nums];
//概率矩阵的值为pr公式的(1-d)/n的部分
//阻尼系数
float d = 0.85f;
Arrays.fill(G, (1 - d) / nums);
//构建用户邻接矩阵
float[] U = new float[nums];
//该用户的链出数
int out = 0;
StringBuilder printSb = new StringBuilder();
for (Text value : values) {
//从value中拿到目标用户的id
int targetUserIndex = Integer.parseInt(value.toString());
//邻接矩阵中每个目标用户对应的值为1,其余为0
U[targetUserIndex - 1] = 1;
out++;
printSb.append(",").append(value.toString());
}
//打印reducer的输入
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < nums; i++) {
stringBuilder.append(",").append(G[i] + U[i] * d / out);
}
v.set(stringBuilder.toString().replaceFirst(",", ""));
context.write(key, v);
}
}
注意:float d = 0.85f;
Arrays.fill(G, (1 - d) / nums);
这里定义了阻尼系数,阻尼系数的作用在上文提起过.
G[i] + U[i] * d / out这里
U[i]/out得到的为概率矩阵,上文也提起过.
邻接矩阵*阻尼系数/该用户链出数+概率矩阵=邻接概率矩阵
正常输出的邻接概率矩阵为
第二步:
public class CalcPeopleRank {
/**
* 输入邻接概率矩阵和pr矩阵
* 按照矩阵相乘的公式,将对应的数据输出到reduce进行计算
*/
public static class CalcPeopleRankMapper extends Mapper<LongWritable, Text, Text, Text> {
Text k = new Text();
Text v = new Text();
String flag = "";
@Override
protected void setup(Context context) throws IOException, InterruptedException {
super.setup(context);
FileSplit fileSplit = (FileSplit) context.getInputSplit();
flag = fileSplit.getPath().getName();
System.out.println("CalcPeopleRankMapper input type:");
System.out.println(flag);
}
/**
* k的作用是将pr矩阵的列和邻接矩阵的行对应起来
* 如:pr矩阵的第一列要和邻接矩阵的第一行相乘,所以需要同时输入到reduce中
*/
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
System.out.println(value.toString());
int nums = 4;
//处理pr矩阵
if (flag.startsWith("peoplerank")) {
String[] strArr = HadoopUtil.SPARATOR.split(value.toString());
//第一位为用户id,输入的每行内容都为pr矩阵中的一列,所以也可以看成是列数
k.set(strArr[0]);
for (int i = 1; i <= nums; i++) {
//pr为标识符,i为该列中第i行,strArr[1]为值
v.set("pr:" + i + "," + strArr[1]);
context.write(k, v);
}
}
//处理邻接概率矩阵
else {
String[] strArr = HadoopUtil.SPARATOR.split(value.toString());
//k为用户id,输入的每行就是邻接概率矩阵中的一行,所以也可以看成行号
k.set(strArr[0]);
for (int i = 1; i < strArr.length; i++) {
//matrix为标识符,i为该行中第i列,strArr[i]为值
v.set("matrix:" + i + "," + strArr[i]);
context.write(k, v);
}
}
}
}
/**
* 每行输入都是两个矩阵相乘中对应的值
* 如:邻接矩阵的第一行的值和pr矩阵第一列的值
*/
public static class CalcPeopleRankReducer extends Reducer<Text, Text, Text, Text> {
Text v = new Text();
@Override
protected void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException {
System.out.println("CalcPeopleRankReducer input:");
StringBuilder printStr = new StringBuilder();
//pr统计
float pr = 0f;
StringBuffer sb = new StringBuffer();
//存储pr矩阵列的值
Map<Integer, Float> prMap = new HashMap<Integer, Float>();
//存储邻接矩阵行的值
Map<Integer, Float> matrixMap = new HashMap<Integer, Float>();
//将两个矩阵对应的值存入对应的map中
for (Text value : values) {
String valueStr = value.toString();
String[] kv = HadoopUtil.SPARATOR.split(valueStr.split(":")[1]);
if (valueStr.startsWith("pr")) {
prMap.put(Integer.parseInt(kv[0]), Float.valueOf(kv[1]));
} else {
matrixMap.put(Integer.parseInt(kv[0]), Float.valueOf(kv[1]));
}
printStr.append(",").append(valueStr);
}
System.out.println(printStr.toString().replaceFirst(",", ""));
//根据map中的数据进行计算
for (Map.Entry<Integer, Float> entry : matrixMap.entrySet()) {
pr += entry.getValue() * prMap.get(entry.getKey());
sb.append(entry.getKey()+"mm"+entry.getValue()+"pp"+ prMap.get(entry.getKey())+"ee"+pr+"pr");
}
//v.set(String.valueOf(pr));
v.set(sb.toString()+"sl"+pr);
System.out.println("CalcPeopleRankReducer output:");
System.out.println(key.toString() + ":" + v.toString());
System.out.println();
context.write(key, v);
}
}
public static void run() throws InterruptedException, IOException, ClassNotFoundException {
Configuration conf = new Configuration();
String inPath1 = HadoopUtil.HDFS + "/out/5-peoplerank/probility-matrix";
String inPath2 = HadoopUtil.HDFS + "/data/5-peoplerank/peoplerank.csv";
String outPath = HadoopUtil.HDFS + "/out/5-peoplerank/pr";
JobInitModel job = new JobInitModel(new String[]{inPath1, inPath2}, outPath, conf, null, "CalcPeopleRank", CalcPeopleRank.class
, null, CalcPeopleRankMapper.class, Text.class, Text.class, null, null
, CalcPeopleRankReducer.class, Text.class, Text.class);
BaseDriver.initJob(new JobInitModel[]{job});
// HdfsUtil.rmr(inPath2);
// HdfsUtil.rename(outPath + "/part-r-00000", inPath2);
}
}
以上为全部内容,如有错误,恳请斧正.