DGL学习笔记——第二章 消息传递范式

提示:DGL用户指南学习中


消息传递是实现GNN的一种通用框架和编程范式。它从聚合与更新的角度归纳总结了多种GNN模型的实现。

假设节点 𝑣 上的的特征为 𝑥𝑣∈ℝ𝑑1,边 (𝑢,𝑣) 上的特征为 𝑤𝑒∈ℝ𝑑2。 消息传递范式 定义了以下逐节点和边上的计算:

请添加图片描述

在上面的等式中, 𝜙 是定义在每条边上的消息函数,它通过将边上特征与其两端节点的特征相结合来生成消息。 聚合函数 𝜌 会聚合节点接受到的消息。 更新函数 𝜓 会结合聚合后的消息和节点本身的特征来更新节点的特征。

一、内置函数和消息传递API

在DGL中,消息函数接受一个参数 edges,这是一个 EdgeBatch 的实例, 在消息传递时,它被DGL在内部生成以表示一批边。 edges 有 src、 dst 和 data 共3个成员属性, 分别用于访问源节点、目标节点和边的特征。

聚合函数 接受一个参数 nodes,这是一个 NodeBatch 的实例, 在消息传递时,它被DGL在内部生成以表示一批节点。 nodes 的成员属性 mailbox 可以用来访问节点收到的消息。 一些最常见的聚合操作包括 sum、max、min 等。

更新函数 接受一个如上所述的参数 nodes。此函数对 聚合函数 的聚合结果进行操作, 通常在消息传递的最后一步将其与节点的特征相结合,并将输出作为节点的新特征。

DGL在命名空间 dgl.function 中实现了常用的消息函数和聚合函数作为 内置函数。 一般来说,DGL建议 尽可能 使用内置函数,因为它们经过了大量优化,并且可以自动处理维度广播。

如果用户的消息传递函数无法用内置函数实现,则可以实现自己的消息或聚合函数(也称为 用户定义函数 )。

内置消息函数可以是一元函数或二元函数。对于一元函数,DGL支持 copy 函数。对于二元函数, DGL现在支持 add、 sub、 mul、 div、 dot 函数。消息的内置函数的命名约定是 u 表示源节点, v 表示目标节点,e 表示边。这些函数的参数是字符串,指示相应节点和边的输入和输出特征字段名。 关于内置函数的列表,请参见 DGL Built-in Function。例如,要对源节点的 hu 特征和目标节点的 hv 特征求和, 然后将结果保存在边的 he 特征上,用户可以使用内置函数 dgl.function.u_add_v(‘hu’, ‘hv’, ‘he’)。 而以下用户定义消息函数与此内置函数等价。

请添加图片描述

DGL支持内置的聚合函数 sum、 max、 min 和 mean 操作。 聚合函数通常有两个参数,它们的类型都是字符串。一个用于指定 mailbox 中的字段名,一个用于指示目标节点特征的字段名, 例如,dgl.function.sum(‘m’, ‘h’) 等价于如下所示的对接收到消息求和的用户定义函数:

请添加图片描述

在DGL中,也可以在不涉及消息传递的情况下,通过 apply_edges() 单独调用逐边计算。 apply_edges() 的参数是一个消息函数。并且在默认情况下,这个接口将更新所有的边。例如:
请添加图片描述
对于消息传递, update_all() 是一个高级API。它在单个API调用里合并了消息生成、 消息聚合和节点特征更新,这为从整体上进行系统优化提供了空间。

update_all() 的参数是一个消息函数、一个聚合函数和一个更新函数。 更新函数是一个可选择的参数,用户也可以不使用它,而是在 update_all 执行完后直接对节点特征进行操作。 由于更新函数通常可以用纯张量操作实现,所以DGL不推荐在 update_all 中指定更新函数。例如:

此调用通过将源节点特征 ft 与边特征 a 相乘生成消息 m, 然后对所有消息求和来更新节点特征 ft,再将 ft 乘以2得到最终结果 final_ft。

调用后,中间消息 m 将被清除。上述函数的数学公式为:

请添加图片描述

二、编写高效的消息传递代码

DGL优化了消息传递的内存消耗和计算速度。利用这些优化的一个常见实践是通过基于内置函数的 update_all() 来开发消息传递功能。

除此之外,考虑到某些图边的数量远远大于节点的数量,DGL建议避免不必要的从点到边的内存拷贝。对于某些情况,比如 GATConv,计算必须在边上保存消息, 那么用户就需要调用基于内置函数的 apply_edges()。有时边上的消息可能是高维的,这会非常消耗内存。 DGL建议用户尽量减少边的特征维数。

下面是一个如何通过对节点特征降维来减少消息维度的示例。该做法执行以下操作:拼接 源 节点和 目标 节点特征, 然后应用一个线性层,即 𝑊×(𝑢||𝑣)。 源 节点和 目标 节点特征维数较高,而线性层输出维数较低。 一个直截了当的实现方式如下:


总结

提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。

### 消息传递范式的实现方式 消息传递范式是一种广泛应用于分布式计算、并行处理以及图神经网络等领域的重要编程模型。它通过节点之间的消息交换来完成状态更新和数据传播的任务。 在图神经网络框架 DGL 中,消息传递的核心机制可以通过 `update_all()` 方法实现[^5]。此方法允许开发者定义三个主要的功能模块:`message_func`(用于生成消息)、`reduce_func`(用于聚合消息)以及 `apply_node_func`(用于更新节点的状态)。以下是其实现的具体细节: #### 1. 创建子图 当只需要更新部分节点时,可以先提取目标节点的子图。这一步骤有助于减少不必要的计算开销。例如,在给定的索引 `(u, v)` 上构建图之后,可以选择特定节点集合 `[0, 1, 2]` 来创建子图 `sg`。 ```python import dgl import torch # 定义连接关系 u, v = torch.tensor([1, 1, 3]), torch.tensor([2, 3, 1]) g = dgl.graph((u, v)) # 提取子图 nid = [0, 1, 2] sg = g.subgraph(nid) ``` #### 2. 定义消息函数 (`message_func`) 消息函数负责指定如何基于源节点特征生成要发送的消息。通常情况下,这是通过对输入特征执行某种操作(如乘法或加法)来完成的。假设每个节点都有一个名为 `'h'` 的特征向量,则可以这样定义消息函数: ```python def message_func(edges): return {'m': edges.src['h']} ``` 这里,`edges.src['h']` 表示从源节点获取特征 `'h'` 并将其命名为 `'m'` 作为消息内容。 #### 3. 聚合消息 (`reduce_func`) 一旦消息被生成,下一步就是将接收到的所有消息汇总到目标节点处。常用的汇聚策略包括求和、平均值或者最大值等。下面展示了一个简单的求和例子: ```python def reduce_func(nodes): return {'h_sum': nodes.mailbox['m'].sum(dim=1)} ``` 在此代码片段中,`nodes.mailbox['m']` 是当前接收节点所累积的所有传入消息列表;对其沿维度 1 进行累加运算得到最终的结果存储于新字段 `'h_sum'` 中。 #### 4. 更新节点状态 (`apply_node_func`, 可选) 最后一步可能涉及利用已有的信息去修改本地属性值。虽然并非总是必需,但在某些场景下确实很有必要。比如我们可以把之前算出来的总和重新赋回原位置: ```python def apply_node_func(nodes): return {'h': nodes.data['h_sum']} ``` 至此为止,完整的调用链路如下所示: ```python sg.update_all(message_func, reduce_func, apply_node_func) ``` 以上即为一种典型的消息传递流程描述及其对应的 Python 实现方案[^5]。 ### 总结 消息传递范式提供了一种灵活而强大的工具集用来建模复杂的交互模式。通过精心设计的消息生成规则高效的聚集手段相结合,可以在多种实际应用场景里取得良好的效果。值得注意的是,不同问题域往往对应着各自独特的最佳实践路径——因此理解具体业务需求至关重要。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值