我们来做一个节点分类的任务,选择的数据集是Karate Club,Karate是空手道的意思,所以这就是一个空手道俱乐部的数据。
简而言之,这个数据集,包含34个节点,156条无向无权边,结点总共分为4类,此外,每个节点还有34个特征,也就是说还有34个指标来描述空手道俱乐部的每个成员。欸?特征数怎么和节点数一样,没错,就是one-hot编码。
下图是论文原图,颜色表示了类别。
数据集的详细说明
这个数据集是由社会学家Wayne W. Zachary在1977年的论文An Information Flow Model for Conflict and Fission in Small Groups中提出的,基于他对一个美国大学空手道俱乐部的观察和记录。这个数据集包含34个节点和78条边,每个节点代表一个空手道俱乐部的成员,每条边代表两个成员之间的社交关系。
在收集数据的过程中,俱乐部的管理员和教练之间发生了冲突,导致俱乐部分裂为两个社区,一半成员跟随教练,另一半成员跟随管理员或离开俱乐部。Zachary利用图的结构信息,成功地预测了除了一个成员之外的所有成员的类别。
Zaharu’s karateclub数据集还根据2017年的论文Semi-supervised Classification with Graph Convolutional Networks,给每个节点赋予了一个四分类的标签,表示它们属于哪个社区,这些标签是通过基于模块度的聚类方法得到的。
再说一遍数据集的本质:这个数据集,包含34个节点,156条无向无权边,结点总共分为4类,此外,每个节点还有34个特征,也就是说还有34个指标来描述空手道俱乐部的每个成员。欸?特征数怎么和节点数一样,没错,就是one-hot编码。
加载并探索数据集
torch_geometric.datasets
里已经有了这个数据集,我们可以查看一下这个数据的一些信息。包括有几个图(有的数据集有很多张图,比如有好几个俱乐部,每个俱乐部是一张图,这里是只有一张图),结点/边特征个数,结点类别个数。
from torch_geometric.datasets import KarateClub
dataset = KarateClub()
print(f'Dataset: {
dataset}:')
print('======================')
print(f'Number of graphs: {
len(dataset)}')
print(f'Number of node features: {
dataset.num_features}')
print(f'Number of classes: {
dataset.num_classes}')
print(f'Number of edge features: {
dataset.num_edge_features}')
Dataset: KarateClub():
======================
Number of graphs: 1
Number of node features: 34
Number of classes: 4
Number of edge features: 0
可以看到只有一张图,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'Contains isolated nodes: {
data.has_isolated_nodes()}')
print(f'Contains 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: 34
Number of edges: 156
Average node degree: 4.59
Number of training nodes: 4
Training node label rate: 0.12
Contains isolated nodes: False
Contains self-loops: False
Is undirected: True
Data这个数据类型
我们来详细看看Data这个数据类型。
PyTorch Geometric是一个用于图神经网络的PyTorch库,它可以处理各种类型的图数据。在PyTorch Geometric中,每个图都由一个Data对象表示,它包含了描述图结构的所有信息。Data对象可以像一个普通的Python字典一样使用,也可以提供一些分析图结构的有用功能。
Data对象可以有多个属性,每个属性都是一个PyTorch张量。例如,上面的代码中,Data对象有四个属性:
- edge_index:一个2 x 156的张量,表示图中的边的连接关系。每一列是一个边的起点和终点的索引,例如第一列[0, 1]表示从节点0到节点1的一条边。
- x:一个34 x 34的张量,表示图中的节点的特征。每一行是一个节点的34维特征向量,例如第一行[-1]表示节点0的特征。
- y:一个34维的张量,表示图中的节点的标签。每个元素是一个节点的类别,例如第一个元素0表示节点0属于类别0。
- train_mask:一个34维的布尔张量,表示哪些节点的标签是已知的。每个元素是True或False,例如第一个元素True表示节点0的标签是已知的。
注意了,Data(x=[34, 34], edge_index=[2, 156], y=[34], train_mask=[34])
这里面的数字,都是表示维度。
这个Data对象表示了一个有34个节点,156条边的图,其中只有4个节点的标签是已知的,其余的节点的标签需要通过图神经网络来推断。
Data对象还提供了一些实用函数,用于推断图的一些基本属性。例如,我们可以通过以下方式来判断图是否有孤立的节点(没有任何边相连的节点),是否有自环(从一个节点到自己的边),或者是否是无向图(每条边都是双向的):
- data.isolated_nodes():返回一个布尔张量,表示哪些节点是孤立的。
- data.contains_self_loops():返回一个布尔值,表示图是否有自环。
- data.is_undirected():返回一个布尔值,表示图是否是无向的。
coo format
在这里我还想补充要给知识点,也就是coo format,这是用来表示图的一种方式,本质上是表示稀疏矩阵的一种方式,也就是用三个向量分别表示,行、列、值,请看下面详细介绍:
coo format是一种稀疏矩阵的存储格式,它只记录矩阵中非零元素的位置和值。coo format的全称是coordinate format,意思是坐标格式,因为它用两个坐标来表示每个非零元素的行和列索引。例如,一个4 x 4的矩阵:
[ 0 3 0 1 1 0 2 0 0 1 0 0 1 0 0 0 ] \begin{bmatrix} 0 & 3 & 0 & 1 \\\\ 1 & 0 & 2 & 0 \\\\ 0 & 1 & 0 & 0 \\\\ 1 & 0 & 0 & 0 \end{bmatrix} 0101