Spark에서 PageRank 알고리즘의 간단한 구현

"Spark Fast BigData Analysis"에는 Google의 PageRank 알고리즘을 단 몇 줄로 구현하는 불명확 한 Scala 코드가 있으므로이를 확인하기 위해 작은 실험을 수행했습니다.

1. 실험 환경
스파크 1.5.0

2. PageRank 알고리즘 소개 ( "Spark Fast Big Data Analysis"에서
        가져옴 ) PageRank는 다중 연결을 수행하는 반복 알고리즘이므로 RDD 파티션 작업을위한 좋은 사용 사례입니다. 알고리즘은 두 개의 데이터 세트를 유지합니다. 하나는 (pageID, linkList) 요소로 구성되고 각 페이지의 인접 페이지 목록을 포함하고, 다른 하나는 (pageID, rank) 요소로 구성되고 각 페이지의 현재 순위 값을 포함합니다. 다음과 같이 계산됩니다.
각 페이지의 정렬 값을 1.0으로 초기화합니다.
각 반복에서 페이지 p에 대해 rank (p) / numNeighbors (p)의 기여 값이 인접한 각 페이지 (직접 링크가있는 페이지)로 전송됩니다.
각 페이지의 순위 값을 0.15 + 0.85 * tributionsReceived로 설정합니다.
        마지막 두 단계는 여러주기를 반복하며이 과정에서 알고리즘은 점차 각 페이지의 실제 PageRank 값으로 수렴됩니다. 실제로 수렴에는 일반적으로 약 10 회 반복이 필요합니다.

3. 시뮬레이션 데이터
A, B, C, D의 4 페이지로 구성된 소그룹을 가정합니다. 인접한 페이지는 다음과 같습니다.
A : BC
B : AC
C : ABD
D : C

네, 테스트 코드

import org.apache.spark.HashPartitioner
 
val links = sc.parallelize(List(("A",List("B","C")),("B",List("A","C")),("C",List("A","B","D")),("D",List("C")))).partitionBy(new HashPartitioner(100)).persist()
 
var ranks=links.mapValues(v=>1.0)
 
for (i <- 0 until 10) {
val contributions=links.join(ranks).flatMap {
case (pageId,(links,rank)) => links.map(dest=>(dest,rank/links.size))
}
ranks=contributions.reduceByKey((x,y)=>x+y).mapValues(v=>0.15+0.85*v)
}
 
ranks.sortByKey().collect()

         실행 결과는 아래 그림과 같습니다.


초기 링크 RDD 및 ranksRDD는 다음과 같습니다.
linksRDD :
Array [(String, List [String])] = Array ((A, List (B, C)), (B, List (A, C)), (C, List (A, B, D)), (D, List (C)))
ranksRDD :
Array [(String, Double)] = Array ((A, 1.0), (B, 1.0), (C, 1.0), (D, 1.0))
첫 번째 반복 후 기여도 RDD 및 순위 RDD는 다음과 같습니다 .tributionsRDD
:
Array [(String, Double)] = Array ((A, 0.5), (A, 0.3333333333333333), (B, 0.5), ( B, 0.3333333333333333), (C, 0.5), (C, 0.5), (C, 1.0), (D, 0.3333333333333333))
ranksRDD :
Array [(String, Double)] = Array ((A, 0.8583333333333333), (B , 0.8583333333333333), (C, 1.8499999999999999), (D, 0.43333333333333335))
검증 데이터 : 첫
번째 반복 :
PR (A) = 0.15 + 0.85 * (1/2 + 1/3) = 0.858333
PR (B) = 0.15 + 0.85 * (1/2 + 1/3) = 0.858333
PR (C) = 0.15 + 0.85 * (1/2 + 1/2 + 1/1) = 1.85
PR (D) = 0.15 + 0.85 * (1/3) = 0.433333
第 2 다음 迭代 :
PR (A) = 0.15 + 0.85 * (0.858333 / 2 + 1.85 / 3) = 1.038958191100
PR (B) = 0.15 + 0.85 * (0.858333 / 2 + 1.85 / 3) = 1.038958191100
PR (C) = 0.15 + 0.85 * (0.858333 / 2 + 0.858333 / 2 + 0.433333 / 1) = 1.247916100000
PR (D) = 0.15 + 0.85 * (1.85 / 3) = 0.67416667
第 3 다음 迭代 :
PR (A) = 0.15 + 0.85 * (1.038958191100 / 2 + 1.247916100000 / 3) = 0.945133459550833333
PR (B) = 0.15 + 0.85 * (1.038958191100 / 2 + 1.247916100000 / 3) = 0.945133459550833333
PR (C) = 0.15 + 0.85 * (1.038958191100 / 2 + 1.038958191100 / 2 + 0.67416667 / 1) = 1.606156131935000000
D ( 0. ) + 0.85 * (1.247916100000 / 3) = 0.503576228333333333

5. 코드 설명 ( "Spark Fast Big Data Analysis"에서 발췌)
        그게 다입니다! 알고리즘은 ranksRDD의 각 요소 값을 1.0으로 초기화 한 다음 각 반복에서 순위 변수를 지속적으로 업데이트하여 시작합니다. Spark에서 PageRank의 본문을 작성하는 것은 매우 간단합니다. 먼저 현재 ranksRDD 및 static linkRDD에 대해 join () 작업을 수행하여 각 페이지 ID 및 현재 순위 값에 해당하는 인접 페이지 목록을 얻은 다음 flatMap을 사용하여 각 인접 페이지에 대한 각 페이지의 기여도를 기록하려면 "기여도". 그런 다음 페이지 ID (공유 페이지에 따라)에 따라 이러한 기여도 값을 더하고 페이지의 순위 값을 0.15 + 0.85 * tributionsReceived로 설정합니다.
        코드 자체는 매우 간단하지만이 샘플 프로그램은 통신 오버 헤드를 최소화하기 위해 RDD
가보다 효율적인 방식으로 분할되도록하기 위해 많은 작업을 수행합니다. (1) linksRDD는 각 반복에서 순위에 연결됩니다. 링크는 정적 데이터 세트이므로 프로그램 시작 부분에 분할 했으므로 네트워크를 통해 섞을 필요가 없습니다. 실제로 linksRDD의 바이트 수는 일반적으로 순위보다 훨씬 큽니다. 결국 Double 값이 아닌 각 페이지의 인접 페이지 목록 (페이지 ID로 구성됨)을 포함하므로이 최적화는 상대적으로 PageRank의 원래 구현 (예 : 일반 MapReduce)은 상당한 네트워크 통신 오버 헤드를 절약합니다.
(2) 같은 이유로 링크의 persist () 메소드를 호출하고 각 반복마다 메모리에 보관합니다.
(3) 처음으로 순위를 생성 할 때 부모 RDD (링크)의 분할 방법을 유지하기 위해 map () 대신 mapValues ​​()를 사용하여 첫 번째 연결 작업에 약간의 오버 헤드가 발생합니다.
(4) 루프 본문에서는 reduceByKey () 뒤에 mapValues ​​()를 사용합니다. 연결되면 더 효율적입니다.

scala의 언어는 매우 간결합니다. 다음 그림과 같이 scala에 한 줄을 작성하여 빅 데이터에 대한 일반적인 예제 프로그램 단어 수를 작성할 수 있습니다.

var input = sc.textFile("/NOTICE.txt")
input.flatMap(x=>x.split(" ")).countByValue()

추천

출처blog.csdn.net/qq_32445015/article/details/103548598