图计算中的GraphX分区策略与算法实现
1. GraphX分区策略
GraphX是为分布式计算而构建的,因此它必须在多台机器之间对图进行分区。一般来说,分区图有两种方法:“边切割”和“顶点切割”,各有不同的权衡。
1.1 边切割策略
边切割策略可能是最“自然”的图分区方式。通过沿边分割图,确保每个顶点恰好分配到一个分区。但这对于跨越分区的边的表示存在问题,因为沿边的任何计算都需要从一个分区发送到另一个分区,而最小化网络通信是实现高效图算法的关键。
1.2 顶点切割策略
GraphX采用“顶点切割”方法,确保边分配到分区,且顶点可在分区之间共享。这似乎只是将网络通信转移到图的不同部分(从边转移到顶点),但GraphX提供了多种策略,可确保顶点以最适合应用算法的方式进行分区。
1.3 Glittering的分区函数
Glittering提供了
partition-by
函数,接受一个关键字来表示图的分区策略,接受的值有
:edge-partition-1d
、
:edge-partition-2d
、
:canonical-random-vertex-cut
和
:random-vertex-cut
。
| 分区策略 | 描述 | 优缺点 |
|---|---|---|
| :edge-partition-1d | 确保具有相同源的所有边被分区在一起 | 优点:最小化网络流量;缺点:对于幂律图,少数分区可能接收大部分边 |
| :random-vertex-cut | 根据源和目标顶点将图拆分为边 | 优点:创建更平衡的分区;缺点:运行时性能可能受影响 |
| :canonical-random-vertex-cut | 分组边时不考虑方向 | 平衡分区且不考虑边方向 |
| :edge-partition-2d | 使用更复杂的分区策略,根据源和目标顶点对边进行分区 | 适用于聚合共享源和目标节点的边信息的算法 |
2. 运行内置三角形计数算法
2.1 算法步骤
-
选择分区策略,这里选择
:random-vertex-cut。 - 加载规范方向的边列表。
- 对图进行分区。
- 执行三角形计数。
- 可视化结果。
2.2 代码示例
(defn ex-8-24 []
(spark/with-context sc (-> (g/conf)
(conf/master "local")
(conf/app-name "ch8"))
(let [triangles (->> (load-canonical-edgelist
sc "data/twitter_combined.txt")
(g/partition-by :random-vertex-cut)
(ga/triangle-count)
(g/vertices)
(to-java-pair-rdd)
(spark/values)
(spark/collect)
(into []))
data (frequencies triangles)]
(-> (c/scatter-plot (keys data) (vals data))
(c/set-axis :x (c/log-axis :label "# Triangles"))
(c/set-axis :y (c/log-axis :label "# Vertices"))
(i/view)))))
2.3 结果分析
triangle-count
的输出是一个新图,每个顶点的属性是该顶点参与的三角形数量。我们只对三角形计数本身感兴趣,因此从顶点中提取值。
spark/collect
函数将所有值收集到一个Clojure序列中,但对于非常大的图不建议这样做。通过计算每个计数的频率并使用Incanter在对数 - 对数散点图上可视化结果,我们可以看到幂律分布的影响,少数节点连接大量三角形。
3. 使用Glittering实现三角形计数
3.1 算法步骤
- 计算每个顶点的邻居集。
- 对于每条边,计算两端顶点的交集。
- 将交集的计数发送到两个顶点。
- 计算每个顶点的计数总和。
- 由于每个三角形被计算了两次,将计数除以2。
graph LR
A[计算每个顶点的邻居集] --> B[计算每条边两端顶点的交集]
B --> C[将交集计数发送到两个顶点]
C --> D[计算每个顶点的计数总和]
D --> E[将计数除以2]
3.2 代码实现
(defn triangle-m [{:keys [src-id src-attr dst-id dst-attr]}]
(let [c (count (set/intersection src-attr dst-attr))]
{:src c :dst c}))
(defn triangle-count [graph]
(let [graph (->> (g/partition-by :random-vertex-cut graph)
(g/group-edges (fn [a b] a)))
adjacent (->> (g/collect-neighbor-ids :either graph)
(to-java-pair-rdd)
(spark/map-values set))
graph (g/outer-join-vertices
(fn [vid attr adj] adj) adjacent graph)
counters (g/aggregate-messages triangle-m + graph)]
(->> (g/outer-join-vertices (fn [vid vattr counter]
(/ counter 2))
counters graph)
(g/vertices))))
3.3 代码解释
-
确保边的唯一性
:使用
g/group-edges函数确保输入图的边是不同的,只保留共享相同起始和结束节点的边的第一条。 -
步骤一:收集邻居ID
:使用
g/collect-neighbor-ids函数收集每个顶点的邻居ID,可选择收集入边、出边或两者。返回的RDD需转换为JavaRDD,并将邻居ID序列转换为集合。 -
步骤二、三、四:聚合消息
:使用
g/aggregate-messages函数,需要消息发送函数和消息合并函数。消息发送函数负责沿边发送消息,消息合并函数负责合并特定顶点的所有消息。 -
步骤五:划分计数
:使用
outer-join-vertices函数将每个顶点的计数除以2,并更新顶点属性。
3.4 运行自定义三角形计数算法
3.4.1 在单个Twitter图上运行
(defn ex-8-25 []
(spark/with-context sc (-> (g/conf)
(conf/master "local")
(conf/app-name "triangle-count"))
(->> (load-canonical-edgelist
sc "data/twitter/396721965.edges")
(triangle-count)
(spark/collect)
(into []))))
结果是一系列元组,键为顶点ID,值为连接的三角形数量。
3.4.2 在整个Twitter数据集上运行
(defn ex-8-26 []
(spark/with-context sc (-> (g/conf)
(conf/master "local")
(conf/app-name "triangle-count"))
(let [triangles (->> (load-canonical-edgelist
sc "data/twitter_combined.txt")
(triangle-count)
(to-java-pair-rdd)
(spark/values)
(spark/reduce +))]
(/ triangles 3))))
该算法运行时间不会太长,自定义三角形计数代码足以在整个Twitter组合数据集上运行。
4. Pregel API
4.1 概述
Pregel API是GraphX用于表达自定义、迭代、图并行计算的主要抽象。它以Google内部用于大规模图处理的系统命名。Pregel模型基于图中顶点之间的消息传递,组织成一系列称为超步的步骤。
4.2 Glittering的pregel函数
Glittering实现的
pregel
函数与Pregel类似,但顶点不进行“投票停止”,计算在没有更多消息发送或超过指定迭代次数时终止。它使用三个相关函数(消息函数、消息组合器和顶点程序)迭代实现图算法。消息函数和消息组合器与
aggregate-messages
中的类似,顶点程序处理每个顶点的传入消息,其返回值作为下一个超步的顶点属性。
5. 使用Pregel API实现连通分量算法
5.1 算法步骤
- 将所有顶点属性初始化为顶点ID。
- 对于每条边,确定源或目标顶点属性是否最低。
- 沿每条边,将两个属性中较低的一个发送到对面顶点。
- 对于每个顶点,将属性更新为传入消息中的最低值。
- 重复直到节点属性不再变化。
5.2 代码实现
(defn connected-component-m [{:keys [src-attr dst-attr]}]
(cond
(< src-attr dst-attr) {:dst src-attr}
(> src-attr dst-attr) {:src dst-attr}))
(defn connected-components [graph]
(->> (glitter/map-vertices (fn [id attr] id) graph)
(p/pregel {:vertex-fn (fn [id attr msg]
(min attr msg))
:message-fn connected-component-m
:combiner min})))
5.3 代码解释
-
步骤一:映射顶点
:使用
g/map-vertices函数将所有顶点属性初始化为顶点ID。 -
步骤二和三:消息函数
:
connected-component-m函数根据源和目标顶点属性的大小,将较低的属性发送到对面顶点。 -
步骤四:更新属性
:顶点程序
vertex-fn将顶点属性更新为当前属性和传入消息中的最低值。 -
步骤五:迭代到收敛
:
pregel函数会自动迭代,直到没有更多消息发送。
5.4 运行连通分量算法
(defn ex-8-27 []
(spark/with-context sc (-> (g/conf)
(conf/master "local")
(conf/app-name "cljds.ch8"))
(->> (load-edgelist sc "data/twitter/396721965.edges")
(connected-components)
(g/vertices)
(spark/collect)
(into []))))
通过将图转换回RDD,可以以数据并行的方式进行分析,例如通过计算共享相同属性的节点数量来确定所有连通分量的大小。
6. 不同算法的性能与适用场景
6.1 分区策略性能对比
| 分区策略 | 性能特点 | 适用场景 |
|---|---|---|
| :edge-partition-1d | 最小化网络流量,但幂律图中少数分区可能接收大量边 | 适用于按源聚合边操作,如计算出边数量 |
| :random-vertex-cut | 可创建更平衡的分区,但运行时性能可能受影响 | 对分区平衡要求较高,不太在意运行时间的场景 |
| :canonical-random-vertex-cut | 平衡分区且不考虑边方向 | 不考虑边方向,需要更平衡分区的情况 |
| :edge-partition-2d | 适用于聚合共享源和目标节点的边信息的算法 | 算法需要同时按源、目标独立聚合信息,且对节点分区数量有上限要求的场景 |
6.2 三角形计数算法性能
-
内置算法
:使用方便,代码简洁,但对于大规模图,
spark/collect操作可能会带来性能问题。例如在处理整个Twitter数据集时,将所有数据收集到一个序列中可能会导致内存不足。 - 自定义算法 :通过对图进行分区和消息聚合,能够更灵活地处理大规模图。在处理整个Twitter数据集时,能够有效地进行三角形计数,且性能较好。
6.3 连通分量算法性能
使用Pregel API实现的连通分量算法,通过迭代更新顶点属性,直到收敛。由于采用了消息传递机制,能够在分布式环境下高效运行。在处理大规模图时,通过合理设置分区策略,可以进一步提高算法的性能。
7. 实际应用案例
7.1 社交网络分析
- 三角形计数 :在社交网络中,三角形计数可以用于衡量用户之间的紧密程度。例如,在Twitter网络中,一个用户与另外两个用户之间形成的三角形越多,说明这三个用户之间的关系越紧密。通过对整个Twitter数据集进行三角形计数,可以发现社交网络中的核心用户群体。
- 连通分量 :连通分量可以用于发现社交网络中的社区结构。例如,在一个Twitter用户的关注图中,每个连通分量代表一个相对独立的社区,社区内的用户之间相互关注,而不同社区之间的连接较少。通过分析连通分量的大小和分布,可以了解社交网络的社区结构和用户行为。
7.2 推荐系统
在推荐系统中,可以利用图计算来发现用户之间的相似性和物品之间的关联性。例如,将用户和物品看作图中的顶点,用户对物品的操作(如购买、浏览)看作边。通过计算图中的三角形和连通分量,可以发现用户之间的共同兴趣和物品之间的关联,从而为用户提供更精准的推荐。
8. 总结与展望
8.1 总结
本文介绍了GraphX的分区策略,包括边切割和顶点切割策略,以及Glittering提供的不同分区函数。详细阐述了三角形计数和连通分量算法的实现,包括内置算法和自定义算法,并分析了它们的性能和适用场景。通过实际应用案例,展示了图计算在社交网络分析和推荐系统中的应用。
8.2 展望
随着数据规模的不断增大,图计算在各个领域的应用将越来越广泛。未来,图计算算法将不断优化,以提高性能和处理大规模图的能力。同时,图计算与其他技术(如机器学习、深度学习)的结合也将成为研究的热点,为解决更复杂的问题提供支持。
graph LR
A[图计算] --> B[分区策略]
A --> C[三角形计数算法]
A --> D[连通分量算法]
B --> B1[:edge-partition-1d]
B --> B2[:random-vertex-cut]
B --> B3[:canonical-random-vertex-cut]
B --> B4[:edge-partition-2d]
C --> C1[内置算法]
C --> C2[自定义算法]
D --> D1[Pregel API实现]
C --> E[社交网络分析]
D --> E
E --> E1[三角形计数应用]
E --> E2[连通分量应用]
E --> F[推荐系统]
通过以上的分析和总结,我们可以看到图计算在分布式环境下的强大能力。在实际应用中,我们可以根据具体的需求选择合适的分区策略和算法,以提高计算效率和准确性。同时,不断探索图计算与其他技术的结合,将为我们解决更多复杂的问题提供新的思路和方法。
超级会员免费看
1504

被折叠的 条评论
为什么被折叠?



