torch_geometric
包是一个进行GNN实现的非常方便的包,官网是 https://pytorch-geometric.readthedocs.io/en/latest/notes/introduction.html
需要特别注意【安装顺序,先安装torch-sparse和torch-cluster,再安装torch-geometric】。具体见官网。
确认自己的环境(cuda还是cpu)以及torch版本。进行安装。
pip install torch-scatter torch-sparse torch-cluster torch-spline-conv torch-geometric -f https://data.pyg.org/whl/torch-1.9.0+cpu.html
使用的数据集的格式如下例所示:
>>> import torch
>>> from torch_geometric.data import Data
>>> edge_index = torch.tensor([[0, 1, 1, 2],
[1, 0, 2, 1]], dtype=torch.long)
>>> x = torch.tensor([[-1], [0], [1]], dtype=torch.float)
>>> y = torch.tensor([4, 2, 1], dtype=torch.long)
>>> edge_weights = torch.tensor([1, 5, 2, 0.3], dtype=torch.float)
>>> data = Data(edge_index=edge_index, x=x, y=y, edge_attr=edge_weights)
>>> data
Data(edge_attr=[4], edge_index=[2, 4], x=[3, 1], y=[3])
将每条边的源节点和目标节点分别放在edge_index
的两行,组成一个 2 × 2\times 2×边数 的矩阵edge_index
。
节点属性组成一个 节点数 × \times ×feature大小 的矩阵x
。
label组成一个大小为节点数的数组y
。
边属性组成一个 边数 × \times ×feature大小 的矩阵edge_attr
。边权重也这样处理。
>>> from torch_geometric.nn import GCNConv
>>> layer = GCNConv(1, 5)
>>> layer(x, edge_index) # 不加边权重
tensor([[ 0.1100, 0.2275, -0.3096, 0.0185, -0.3546],
[ 0.0000, 0.0000, 0.0000, 0.0000, 0.0000],
[-0.1100, -0.2275, 0.3096, -0.0185, 0.3546]], grad_fn=<AddBackward0>)
>>> layer(x, edge_index, edge_weights)
tensor([[ 0.0367, 0.0758, -0.1032, 0.0062, -0.1182],
[ 0.0341, 0.0705, -0.0960, 0.0057, -0.1099],
[-0.0733, -0.1516, 0.2064, -0.0123, 0.2364]], grad_fn=<AddBackward0>)
>>> layer(data.x, data.edge_index, data.edge_attr)
tensor([[ 0.0367, 0.0758, -0.1032, 0.0062, -0.1182],
[ 0.0341, 0.0705, -0.0960, 0.0057, -0.1099],
[-0.0733, -0.1516, 0.2064, -0.0123, 0.2364]], grad_fn=<AddBackward0>)
layer = GCNConv(1, 5)
这一句构建了一个输入维度为1,输出维度为5的GCN层,如果需要权重,则将权重传入即可。
如果图数据很大,最好使用SparseTensor
而非edge_index
格式。
如果是自己构建数据集(比如按照 https://pytorch-geometric.readthedocs.io/en/latest/notes/create_dataset.html#creating-in-memory-datasets 的教程来做),则直接import torch_geometric.transforms as T
并设置数据集的参数transform=T.ToSparseTensor()
即可。
使用之前创建的data
,也可以转换。
>>> import torch_geometric.transforms as T
>>> transform=T.ToSparseTensor()
>>> transform(data)
Data(adj_t=[3, 3, nnz=4], x=[3, 1], y=[3])
>>> data
Data(adj_t=[3, 3, nnz=4], x=[3, 1], y=[3])
>>> data.adj_t
SparseTensor(row=tensor([0, 1, 1, 2]),
col=tensor([1, 0, 2, 1]),
val=tensor([5.0000, 1.0000, 0.3000, 2.0000]),
size=(3, 3), nnz=4, density=44.44%)
此时已经将边权重加入邻接矩阵中,因此在进行神经网络forward时,不用额外的边权重。
>>> layer(x, data.adj_t)
tensor([[ 0.0367, 0.0758, -0.1032, 0.0062, -0.1182],
[ 0.0341, 0.0705, -0.0960, 0.0057, -0.1099],
[-0.0733, -0.1516, 0.2064, -0.0123, 0.2364]], grad_fn=<AddBackward0>)
来自 https://e0hyl.github.io/BLOG-OF-E0/PyGeometric_Data/
在torch_geometric
中,有以下几种划分batch
的方法:
DataLoader
,此时会将选出的几个图拼成一个邻接矩阵,形成一个Batch
结构;或者用DataListLoader
将采样出的几个图分别组成一个列表返回。DenseDataLoader
。ClusterLoader
、ClusterData
。NeighborSampler
,GraphSAGE中的sample方法。进行采样的方法可以参考 https://towardsdatascience.com/sampling-large-graphs-in-pytorch-geometric-97a6119c41f9
edge_index
(非adj_t
),用NeighborSampler
处理:data_loader = NeighborSampler(data.edge_index, shuffle=True, sizes=[-1, -1], batch_size=batch_size)
(这一句代表:采样两层的全部邻居)data_batch[2][0]
和data_batch[2][1]
分别代表采样出的两层的edge_index
,data_batch[1]
是每个节点的原始索引。两层GCN后进行分类的示例代码为:
data_loader = NeighborSampler(data.edge_index, shuffle=True, sizes=[-1, -1], batch_size=batch_size)
for data_batch in data_loader:
nodes_index = data_batch[1]
data_batch_x = data.x[nodes_index]
preds = classifier(layer2(layer1(data_batch_x, data_batch[2][0])[0], data_batch[2][1])[0]).argmax(dim=1)