图神经网络22-DGL实战:针对边分类任务的邻居采样训练方法

简介: 图神经网络22-DGL实战:针对边分类任务的邻居采样训练方法

边分类/回归的训练与节点分类/回归的训练类似,但还是有一些明显的区别。


定义邻居采样器和数据加载器


用户可以使用和节点分类一样的邻居采样器 。

sampler = dgl.dataloading.MultiLayerFullNeighborSampler(2)


想要用DGL提供的邻居采样器做边分类,需要将其与

:class:~dgl.dataloading.pytorch.EdgeDataLoader 结合使用。

:class:~dgl.dataloading.pytorch.EdgeDataLoader 以小批次的形式对一组边进行迭代,

从而产生包含边小批次的子图以及供下文中模块使用的

例如,以下代码创建了一个PyTorch数据加载器,该PyTorch数据加载器以批的形式迭代训练边ID的数组

train_eids,并将生成的块列表放到GPU上。

dataloader = dgl.dataloading.EdgeDataLoader(
        g, train_eid_dict, sampler,
        batch_size=1024,
        shuffle=True,
        drop_last=False,
        num_workers=4)


有关DGL的内置采样器的完整列表,用户可以参考neighborhood sampler API reference <api-dataloading-neighbor-sampling>

如果用户希望开发自己的邻居采样器,或者想要对块的概念有更详细的了解,请参考guide_cn-minibatch-customizing-neighborhood-sampler


小批次邻居采样训练时删边


用户在训练边分类模型时,有时希望从计算依赖中删除出现在训练数据中的边,就好像这些边根本不存在一样。

否则,模型将 "知道" 两个节点之间存在边的联系,并有可能利用这点 "作弊" 。

因此,在基于邻居采样的边分类中,用户有时会希望从采样得到的小批次图中删去部分边及其对应的反向边。

用户可以在实例化

:class:~dgl.dataloading.pytorch.EdgeDataLoader

时设置 exclude='reverse_id',同时将边ID映射到其反向边ID。

通常这样做会导致采样过程变慢很多,这是因为DGL要定位并删除包含在小批次中的反向边。

n_edges = g.number_of_edges()
    dataloader = dgl.dataloading.EdgeDataLoader(
        g, train_eid_dict, sampler,
        # 下面的两个参数专门用于在邻居采样时删除小批次的一些边和它们的反向边
        exclude='reverse_id',
        reverse_eids=torch.cat([
            torch.arange(n_edges // 2, n_edges), torch.arange(0, n_edges // 2)]),
        batch_size=1024,
        shuffle=True,
        drop_last=False,
        num_workers=4)


调整模型以适用小批次训练


边分类模型通常由两部分组成:

  • 获取边两端节点的表示。
  • 用边两端节点表示为每个类别打分。

第一部分与

:ref:随机批次训练节点分类 <guide_cn-minibatch-node-classification-model>

完全相同,用户可以简单地复用它。输入仍然是DGL的数据加载器生成的块列表和输入特征。

class StochasticTwoLayerGCN(nn.Module):
        def __init__(self, in_features, hidden_features, out_features):
            super().__init__()
            self.conv1 = dglnn.GraphConv(in_features, hidden_features)
            self.conv2 = dglnn.GraphConv(hidden_features, out_features)
        def forward(self, blocks, x):
            x = F.relu(self.conv1(blocks[0], x))
            x = F.relu(self.conv2(blocks[1], x))
            return x


第二部分的输入通常是前一部分的输出,以及由小批次边导出的原始图的子图。

子图是从相同的数据加载器产生的。用户可以调用 :meth:dgl.DGLHeteroGraph.apply_edges 计算边子图中边的得分。

以下代码片段实现了通过合并边两端节点的特征并将其映射到全连接层来预测边的得分。

class ScorePredictor(nn.Module):
        def __init__(self, num_classes, in_features):
            super().__init__()
            self.W = nn.Linear(2 * in_features, num_classes)
        def apply_edges(self, edges):
            data = torch.cat([edges.src['x'], edges.dst['x']])
            return {'score': self.W(data)}
        def forward(self, edge_subgraph, x):
            with edge_subgraph.local_scope():
                edge_subgraph.ndata['x'] = x
                edge_subgraph.apply_edges(self.apply_edges)
                return edge_subgraph.edata['score']


模型接受数据加载器生成的块列表、边子图以及输入节点特征进行前向传播,如下所示:

class Model(nn.Module):
        def __init__(self, in_features, hidden_features, out_features, num_classes):
            super().__init__()
            self.gcn = StochasticTwoLayerGCN(
                in_features, hidden_features, out_features)
            self.predictor = ScorePredictor(num_classes, out_features)
        def forward(self, edge_subgraph, blocks, x):
            x = self.gcn(blocks, x)
            return self.predictor(edge_subgraph, x)


DGL保证边子图中的节点与生成的块列表中最后一个块的输出节点相同。


模型的训练


模型的训练与节点分类的随机批次训练的情况非常相似。用户可以遍历数据加载器以获得由小批次边组成的子图,


以及计算其两端节点表示所需的块列表。

model = Model(in_features, hidden_features, out_features, num_classes)
    model = model.cuda()
    opt = torch.optim.Adam(model.parameters())
    for input_nodes, edge_subgraph, blocks in dataloader:
        blocks = [b.to(torch.device('cuda')) for b in blocks]
        edge_subgraph = edge_subgraph.to(torch.device('cuda'))
        input_features = blocks[0].srcdata['features']
        edge_labels = edge_subgraph.edata['labels']
        edge_predictions = model(edge_subgraph, blocks, input_features)
        loss = compute_loss(edge_labels, edge_predictions)
        opt.zero_grad()
        loss.backward()
        opt.step()


异构图上的模型训练


在异构图上,计算节点表示的模型也可以用于计算边分类/回归所需的两端节点的表示。

class StochasticTwoLayerRGCN(nn.Module):
        def __init__(self, in_feat, hidden_feat, out_feat, rel_names):
            super().__init__()
            self.conv1 = dglnn.HeteroGraphConv({
                    rel : dglnn.GraphConv(in_feat, hidden_feat, norm='right')
                    for rel in rel_names
                })
            self.conv2 = dglnn.HeteroGraphConv({
                    rel : dglnn.GraphConv(hidden_feat, out_feat, norm='right')
                    for rel in rel_names
                })
        def forward(self, blocks, x):
            x = self.conv1(blocks[0], x)
            x = self.conv2(blocks[1], x)
            return x


在同构图和异构图上做评分预测时,代码实现的唯一不同在于调用

:meth:~dgl.DGLHeteroGraph.apply_edges

时需要在特定类型的边上进行迭代。

class ScorePredictor(nn.Module):
        def __init__(self, num_classes, in_features):
            super().__init__()
            self.W = nn.Linear(2 * in_features, num_classes)
        def apply_edges(self, edges):
            data = torch.cat([edges.src['x'], edges.dst['x']])
            return {'score': self.W(data)}
        def forward(self, edge_subgraph, x):
            with edge_subgraph.local_scope():
                edge_subgraph.ndata['x'] = x
                for etype in edge_subgraph.canonical_etypes:
                    edge_subgraph.apply_edges(self.apply_edges, etype=etype)
                return edge_subgraph.edata['score']
    class Model(nn.Module):
        def __init__(self, in_features, hidden_features, out_features, num_classes,
                     etypes):
            super().__init__()
            self.rgcn = StochasticTwoLayerRGCN(
                in_features, hidden_features, out_features, etypes)
            self.pred = ScorePredictor(num_classes, out_features)
        def forward(self, edge_subgraph, blocks, x):
            x = self.rgcn(blocks, x)
            return self.pred(edge_subgraph, x)


数据加载器的定义也与节点分类的非常相似。唯一的区别是用户需要使用

~dgl.dataloading.pytorch.EdgeDataLoader而不是~dgl.dataloading.pytorch.NodeDataLoader

并且提供边类型和边ID张量的字典,而不是节点类型和节点ID张量的字典。

sampler = dgl.dataloading.MultiLayerFullNeighborSampler(2)
    dataloader = dgl.dataloading.EdgeDataLoader(
        g, train_eid_dict, sampler,
        batch_size=1024,
        shuffle=True,
        drop_last=False,
        num_workers=4)


如果用户希望删除异构图中的反向边,情况会有所不同。在异构图上,

反向边通常具有与正向边本身不同的边类型,以便区分 向前向后 关系。

例如,关注被关注 是一对相反的关系, 购买被买下 也是一对相反的关系。

如果一个类型中的每个边都有一个与之对应的ID相同、属于另一类型的反向边,

则用户可以指定边类型及其反向边类型之间的映射。删除小批次中的边及其反向边的方法如下。

dataloader = dgl.dataloading.EdgeDataLoader(
        g, train_eid_dict, sampler,
        # 下面的两个参数专门用于在邻居采样时删除小批次的一些边和它们的反向边
        exclude='reverse_types',
        reverse_etypes={'follow': 'followed by', 'followed by': 'follow',
                        'purchase': 'purchased by', 'purchased by': 'purchase'}
        batch_size=1024,
        shuffle=True,
        drop_last=False,
        num_workers=4)


除了 compute_loss 的代码实现有所不同,异构图的训练循环与同构图中的训练循环几乎相同,

计算损失函数接受节点类型和预测的两个字典。

model = Model(in_features, hidden_features, out_features, num_classes, etypes)
    model = model.cuda()
    opt = torch.optim.Adam(model.parameters())
    for input_nodes, edge_subgraph, blocks in dataloader:
        blocks = [b.to(torch.device('cuda')) for b in blocks]
        edge_subgraph = edge_subgraph.to(torch.device('cuda'))
        input_features = blocks[0].srcdata['features']
        edge_labels = edge_subgraph.edata['labels']
        edge_predictions = model(edge_subgraph, blocks, input_features)
        loss = compute_loss(edge_labels, edge_predictions)
        opt.zero_grad()
        loss.backward()
        opt.step()


完整代码可以查看:GCMC <https://github.com/dmlc/dgl/tree/master/examples/pytorch/gcmc>


相关文章
|
7天前
|
数据采集 网络协议 API
HTTP协议大揭秘!Python requests库实战,让网络请求变得简单高效
【9月更文挑战第13天】在数字化时代,互联网成为信息传输的核心平台,HTTP协议作为基石,定义了客户端与服务器间的数据传输规则。直接处理HTTP请求复杂繁琐,但Python的`requests`库提供了一个简洁强大的接口,简化了这一过程。HTTP协议采用请求与响应模式,无状态且结构化设计,使其能灵活处理各种数据交换。
36 8
|
2天前
|
数据采集 API 开发者
🚀告别网络爬虫小白!urllib与requests联手,Python网络请求实战全攻略
在网络的广阔世界里,Python凭借其简洁的语法和强大的库支持,成为开发网络爬虫的首选语言。本文将通过实战案例,带你探索urllib和requests两大神器的魅力。urllib作为Python内置库,虽API稍显繁琐,但有助于理解HTTP请求本质;requests则简化了请求流程,使开发者更专注于业务逻辑。从基本的网页内容抓取到处理Cookies与Session,我们将逐一剖析,助你从爬虫新手成长为高手。
15 1
|
11天前
|
机器学习/深度学习 数据采集 数据可视化
深度学习实践:构建并训练卷积神经网络(CNN)对CIFAR-10数据集进行分类
本文详细介绍如何使用PyTorch构建并训练卷积神经网络(CNN)对CIFAR-10数据集进行图像分类。从数据预处理、模型定义到训练过程及结果可视化,文章全面展示了深度学习项目的全流程。通过实际操作,读者可以深入了解CNN在图像分类任务中的应用,并掌握PyTorch的基本使用方法。希望本文为您的深度学习项目提供有价值的参考与启示。
|
1天前
|
Python
HTTP协议不再是迷!Python网络请求实战,带你走进网络世界的奥秘
本文介绍了HTTP协议,它是互联网信息传递的核心。作为客户端与服务器通信的基础,HTTP请求包括请求行、头和体三部分。通过Python的`requests`库,我们可以轻松实现HTTP请求。本文将指导你安装`requests`库,并通过实战示例演示如何发送GET和POST请求。无论你是想获取网页内容还是提交表单数据,都能通过简单的代码实现。希望本文能帮助你在Python网络请求的道路上迈出坚实的一步。
9 0
|
17天前
|
SQL 安全 网络安全
网络安全之盾:漏洞防御与加密技术的实战应用
【9月更文挑战第2天】在数字时代的浪潮中,网络安全成为保护个人隐私和企业资产的坚固盾牌。本文深入探讨了网络安全的两个核心要素:防御漏洞和加密技术。我们将从基础概念入手,逐步剖析常见的网络攻击手段,并分享如何通过实践加强安全意识。同时,提供代码示例以增强理解,旨在为读者构建一道坚不可摧的网络安全防线。
|
20天前
|
开发者 图形学 API
从零起步,深度揭秘:运用Unity引擎及网络编程技术,一步步搭建属于你的实时多人在线对战游戏平台——详尽指南与实战代码解析,带你轻松掌握网络化游戏开发的核心要领与最佳实践路径
【8月更文挑战第31天】构建实时多人对战平台是技术与创意的结合。本文使用成熟的Unity游戏开发引擎,从零开始指导读者搭建简单的实时对战平台。内容涵盖网络架构设计、Unity网络API应用及客户端与服务器通信。首先,创建新项目并选择适合多人游戏的模板,使用推荐的网络传输层。接着,定义基本玩法,如2D多人射击游戏,创建角色预制件并添加Rigidbody2D组件。然后,引入网络身份组件以同步对象状态。通过示例代码展示玩家控制逻辑,包括移动和发射子弹功能。最后,设置服务器端逻辑,处理客户端连接和断开。本文帮助读者掌握构建Unity多人对战平台的核心知识,为进一步开发打下基础。
42 0
|
20天前
|
安全 开发者 数据安全/隐私保护
Xamarin 的安全性考虑与最佳实践:从数据加密到网络防护,全面解析构建安全移动应用的六大核心技术要点与实战代码示例
【8月更文挑战第31天】Xamarin 的安全性考虑与最佳实践对于构建安全可靠的跨平台移动应用至关重要。本文探讨了 Xamarin 开发中的关键安全因素,如数据加密、网络通信安全、权限管理等,并提供了 AES 加密算法的代码示例。
30 0
|
3月前
|
机器学习/深度学习 PyTorch 算法框架/工具
【从零开始学习深度学习】26.卷积神经网络之AlexNet模型介绍及其Pytorch实现【含完整代码】
【从零开始学习深度学习】26.卷积神经网络之AlexNet模型介绍及其Pytorch实现【含完整代码】
|
3月前
|
机器学习/深度学习 PyTorch 算法框架/工具
【从零开始学习深度学习】28.卷积神经网络之NiN模型介绍及其Pytorch实现【含完整代码】
【从零开始学习深度学习】28.卷积神经网络之NiN模型介绍及其Pytorch实现【含完整代码】
|
1月前
|
机器学习/深度学习 PyTorch 算法框架/工具
PyTorch代码实现神经网络
这段代码示例展示了如何在PyTorch中构建一个基础的卷积神经网络(CNN)。该网络包括两个卷积层,分别用于提取图像特征,每个卷积层后跟一个池化层以降低空间维度;之后是三个全连接层,用于分类输出。此结构适用于图像识别任务,并可根据具体应用调整参数与层数。