600字范文,内容丰富有趣,生活中的好帮手!
600字范文 > 【教程】PyG入门 初步尝试运行第一行GNN代码

【教程】PyG入门 初步尝试运行第一行GNN代码

时间:2019-04-23 16:55:16

相关推荐

【教程】PyG入门 初步尝试运行第一行GNN代码

转载请注明出处:小锋学长生活大爆炸[]

Colab Notebook

安装必备的库

# Install required packages.import osimport torchos.environ['TORCH'] = torch.__version__print(torch.__version__)!pip install -q torch-scatter -f /whl/torch-${TORCH}.html!pip install -q torch-sparse -f /whl/torch-${TORCH}.html!pip install -q git+/pyg-team/pytorch_geometric.git# Helper function for visualization.%matplotlib inlineimport networkx as nximport matplotlib.pyplot as pltdef visualize_graph(G, color):plt.figure(figsize=(7,7))plt.xticks([])plt.yticks([])nx.draw_networkx(G, pos=nx.spring_layout(G, seed=42), with_labels=False,node_color=color, cmap="Set2")plt.show()def visualize_embedding(h, color, epoch=None, loss=None):plt.figure(figsize=(7,7))plt.xticks([])plt.yticks([])h = h.detach().cpu().numpy()plt.scatter(h[:, 0], h[:, 1], s=140, c=color, cmap="Set2")if epoch is not None and loss is not None:plt.xlabel(f'Epoch: {epoch}, Loss: {loss.item():.4f}', fontsize=16)plt.show()

介绍:图神经网络的实践

最近,图的深度学习已经成为深度学习界最热门的研究领域之一。在这里,图神经网络(GNNs)旨在将经典的深度学习概念推广到不规则的结构化数据(与图像或文本相反),并使神经网络能够推理对象及其关系。

这是通过一个简单的神经信息传递方案来实现的,图G=(V,E)中所有节点v∈V的节点特征x(ℓ)v通过聚合来自其邻居N(v)的局部信息而反复更新。

本教程将向你介绍一些关于通过基于PyTorch Geometric(PyG)库的图神经网络进行图上深度学习的基本概念。PyTorch Geometric是流行的深度学习框架PyTorch的一个扩展库,由各种方法和工具组成,以简化图神经网络的实现。

继Kipf等人()之后,让我们通过观察一个简单的图结构的例子来深入了解GNN的世界,即著名的Zachary的空手道俱乐部网络。这个图描述了一个空手道俱乐部的34名成员的社会网络,并记录了在俱乐部外互动的成员之间的联系。在这里,我们感兴趣的是检测由成员互动产生的社区。

PyTorch Geometric通过torch_geometric.datasets子包提供了对这个数据集的简单访问。

from torch_geometric.datasets import KarateClubdataset = KarateClub()print(f'Dataset: {dataset}:')print('======================')print(f'Number of graphs: {len(dataset)}')print(f'Number of features: {dataset.num_features}')print(f'Number of classes: {dataset.num_classes}')

Dataset: KarateClub():======================Number of graphs: 1Number of features: 34Number of classes: 4

在初始化KarateClub数据集后,我们首先可以检查它的一些属性。例如,我们可以看到这个数据集正好有一个图,而且这个数据集的每个节点都被分配了一个34维的特征向量(它唯一地描述了空手道俱乐部的成员)。此外,该图正好有4个类,代表每个节点所属的社区。

现在让我们更详细地看一下底层图。

data = dataset[0] # Get the first graph object.print(data)print('==============================================================')# Gather some statistics about the graph.print(f'Number of nodes: {data.num_nodes}')print(f'Number of edges: {data.num_edges}')print(f'Average node degree: {data.num_edges / data.num_nodes:.2f}')print(f'Number of training nodes: {data.train_mask.sum()}')print(f'Training node label rate: {int(data.train_mask.sum()) / data.num_nodes:.2f}')print(f'Has isolated nodes: {data.has_isolated_nodes()}')print(f'Has self-loops: {data.has_self_loops()}')print(f'Is undirected: {data.is_undirected()}')

Data(x=[34, 34], edge_index=[2, 156], y=[34], train_mask=[34])==============================================================Number of nodes: 34Number of edges: 156Average node degree: 4.59Number of training nodes: 4Training node label rate: 0.12Has isolated nodes: FalseHas self-loops: FalseIs undirected: True

PyTorch Geometric中的每个图形都由一个数据对象表示,该对象拥有描述其图形表示的所有信息。我们可以通过print(data)随时打印数据对象,以获得关于其属性及其形状的简短摘要:

Data(edge_index=[2, 156], x=[34, 34], y=[34], train_mask=[34])

我们可以看到,这个数据对象拥有4个属性:

(1)edge_index属性拥有关于图连接性的信息,即每条边的源节点和目的节点索引的一个元组;

(2)节点特征称为 x(34个节点中的每一个都被分配了一个34维的特征向量);

(3)节点标签称为 y(每个节点都被精确地分配到一个类别);

(4) 还存在一个额外的属性,叫做train_mask,它描述了我们已经知道哪些节点的社区分配。

总的来说,我们只知道4个节点的真实标签(每个社区一个),我们的任务是推断其余节点的社区分配

数据对象还提供了一些实用函数来推断基础图的一些基本属性。例如,我们可以很容易地推断出图中是否存在孤立的节点(即不存在通往任何节点的边),图中是否包含自循环(即(v,v)∈E),或者图是否是无向的(即对于每条边(v,w)∈E,也存在边(w,v)∈E)。

现在让我们更详细地检查edge_index属性。

from IPython.display import Javascript # Restrict height of output cell.display(Javascript('''google.colab.output.setIframeHeight(0, true, {maxHeight: 300})'''))edge_index = data.edge_indexprint(edge_index.t())

[0 1], [0 2], [0 3], [0 4], [0 5], [0 6], [0 7], [0 8], [0 10], [0 11], [0 12], [0 13], [0 17], [0 19], [0 21], [0 31], [1 0], [1 2], [1 3], [1 7], [1 13], [1 17], [1 19], [1 21], [1 30], [2 0], [2 1], [2 3], [2 7], [2 8], [2 9], [2 13], [2 27], [2 28], [2 32], [3 0], [3 1], [3 2], [3 7], [3 12], [3 13], [4 0], [4 6], [4 10], [5 0], [5 6], [5 10], [5 16], [6 0], [6 4], [6 5], [6 16], [7 0], [7 1], [7 2], [7 3], [8 0], [8 2], [8 30], [8 32], [8 33], [9 2], [9 33], [10 0], [10 4], [10 5], [11 0], [12 0], [12 3], [13 0], [13 1], [13 2], [13 3], [13 33], [14 32], [14 33], [15 32], [15 33], [16 5], [16 6], [17 0], [17 1], [18 32], [18 33], [19 0], [19 1], [19 33], [20 32], [20 33], [21 0], [21 1], [22 32], [22 33], [23 25], [23 27], [23 29], [23 32], [23 33], [24 25], [24 27], [24 31], [25 23], [25 24], [25 31], [26 29], [26 33], [27 2], [27 23], [27 24], [27 33], [28 2], [28 31], [28 33], [29 23], [29 26], [29 32], [29 33], [30 1], [30 8], [30 32], [30 33], [31 0], [31 24], [31 25], [31 28], [31 32], [31 33], [32 2], [32 8], [32 14], [32 15], [32 18], [32 20], [32 22], [32 23], [32 29], [32 30], [32 31], [32 33], [33 8], [33 9], [33 13], [33 14], [33 15], [33 18], [33 19], [33 20], [33 22], [33 23], [33 26], [33 27], [33 28], [33 29], [33 30], [33 31], [33 32],

通过打印edge_index,我们可以了解PyG在内部如何表示图的连接性。我们可以看到,对于每一条边,edge_index持有两个节点索引的元组,其中第一个值描述了源节点的节点索引,第二个值描述了一条边的目的节点的节点索引。

这种表示法被称为COO格式(坐标格式),通常用于表示稀疏矩阵。PyG不是用密集表示法A∈{0,1}|V|×|V|来保存邻接信息,而是稀疏地表示图,这指的是只保存A中条目为非零的坐标/值。

重要的是,PyG不区分有向图和无向图,并将无向图视为有向图的一个特例,其中edge_index中的每个条目都存在反向的边

我们可以通过将其转换为networkx库的格式来进一步实现图形的可视化,该库除了实现图形操作的功能外,还实现了强大的可视化工具。

from torch_geometric.utils import to_networkxG = to_networkx(data, to_undirected=True)visualize_graph(G, color=data.y)

实现图神经网络

在学习了PyG的数据处理之后,是时候实现我们的第一个图谱神经网络了!为此,我们将使用最简单的GNN运算符,即GCN层(Kipf等人)。为此,我们将使用最简单的GNN操作,GCN层,其定义为:

其中W(ℓ+1)表示形状为[num_output_features, num_input_features]可训练权重矩阵cw,v是指每条边的固定归一化系数

PyG通过GCNConv实现这一层,可以通过传入节点特征表示xCOO图连接表示edge_index来执行。

有了这些,我们就可以通过在torch.nn.Module类中定义我们的网络架构来创建我们的第一个图形神经网络。

import torchfrom torch.nn import Linearfrom torch_geometric.nn import GCNConvclass GCN(torch.nn.Module):def __init__(self):super().__init__()torch.manual_seed(1234)self.conv1 = GCNConv(dataset.num_features, 4)self.conv2 = GCNConv(4, 4)self.conv3 = GCNConv(4, 2)self.classifier = Linear(2, dataset.num_classes)def forward(self, x, edge_index):h = self.conv1(x, edge_index)h = h.tanh()h = self.conv2(h, edge_index)h = h.tanh()h = self.conv3(h, edge_index)h = h.tanh() # Final GNN embedding space.# Apply a final (linear) classifier.out = self.classifier(h)return out, hmodel = GCN()print(model)

GCN((conv1): GCNConv(34, 4)(conv2): GCNConv(4, 4)(conv3): GCNConv(4, 2)(classifier): Linear(in_features=2, out_features=4, bias=True))

在这里,我们首先在__init__中初始化我们所有的构建模块,并在forward中定义我们网络的计算流程。我们首先定义并堆叠了三个图卷积层,这相当于聚集了每个节点周围的3跳邻域信息(3 "跳 "以内的所有节点)。此外,GCNConv层将节点特征维度降低到2,即34→4→4→2。每个GCNConv层都被一个tanh非线性增强。

之后,我们应用一个单一的线性变换(torch.nn.Linear),作为一个分类器,将我们的节点映射到4个类别/社区中的一个。

我们同时返回最终分类器的输出以及由GNN产生的最终节点嵌入。我们继续通过GCN()初始化我们的最终模型,并打印我们的模型,产生其所有使用的子模块的摘要。

嵌入Karate Club Network

让我们来看看由我们的GNN产生的节点嵌入。在这里,我们将初始节点特征x图的连接信息edge_index传递给模型,并将其二维嵌入可视化

model = GCN()_, h = model(data.x, data.edge_index)print(f'Embedding shape: {list(h.shape)}')visualize_embedding(h, color=data.y)

Embedding shape: [34, 2]

值得注意的是,即使在训练我们模型的权重之前,该模型产生的节点嵌入与图的社区结构非常相似。尽管我们模型的权重是完全随机初始化的,而且到目前为止我们还没有进行任何训练,但相同颜色(社区)的节点已经在嵌入空间中紧密地聚集在一起了!这导致的结论是,GNNs引入了强烈的归纳偏置,导致输入图中相互接近的节点的嵌入相似

在Karate Club Network上训练

让我们看一个例子,如何根据图中4种节点(每个社区一种)的社区分配知识来训练我们的网络参数。

由于我们模型中的所有东西都是可分的和参数化的,我们可以添加一些标签,训练模型并观察嵌入的反应。在这里,我们使用了一个半监督或过渡性的学习程序。我们只是针对每一类的一个节点进行训练,但允许我们使用完整的输入图数据。

训练我们的模型与任何其他PyTorch模型非常相似。除了定义我们的网络结构,我们还定义了一个损失准则(这里是CrossEntropyLoss)并初始化了一个随机梯度优化器(这里是Adam)。之后,我们进行多轮优化,每轮包括一个前向和后向传播,以计算我们模型参数的梯度,即前向传播得出的损失。如果你不是PyTorch的新手,这个方案对你来说应该很熟悉。否则,PyTorch文档提供了一个关于如何在PyTorch中训练神经网络的良好介绍。

请注意,我们的半监督学习方案是由以下一行实现的。

loss = criterion(out[data.train_mask], data.y[data.train_mask])

虽然我们为所有的节点计算节点嵌入,但我们只利用训练节点来计算损失。在这里,这是通过过滤分类器的输出真实标签data.y来实现的,只包含train_mask中的节点。

现在让我们开始训练,看看我们的节点嵌入是如何随时间演变的(最好是通过明确地运行代码来体验)。

import timefrom IPython.display import Javascript # Restrict height of output cell.display(Javascript('''google.colab.output.setIframeHeight(0, true, {maxHeight: 430})'''))model = GCN()criterion = torch.nn.CrossEntropyLoss() # Define loss criterion.optimizer = torch.optim.Adam(model.parameters(), lr=0.01) # Define optimizer.def train(data):optimizer.zero_grad() # Clear gradients.out, h = model(data.x, data.edge_index) # Perform a single forward pass.loss = criterion(out[data.train_mask], data.y[data.train_mask]) # Compute the loss solely based on the training nodes.loss.backward() # Derive gradients.optimizer.step() # Update parameters based on gradients.return loss, hfor epoch in range(401):loss, h = train(data)if epoch % 10 == 0:visualize_embedding(h, color=data.y, epoch=epoch, loss=loss)time.sleep(0.3)

正如人们所看到的,我们的3层GCN模型成功地线性分离了社区,并对大多数节点进行了正确分类。

此外,我们只用了几行代码就完成了这一切,这要感谢PyTorch的PyG库,它在数据处理和GNN的实现方面帮助了我们。

总结

对GNN和PyTorch Geometric世界的第一次介绍到此结束。在后续课程中,你将学习如何在一些真实世界的图数据集上实现最先进的分类结果。

下一篇:用图谱神经网络进行节点分类

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。