DataWhale组队学习——GNN(2)

DataWhale组队学习——GNN(2)


简单图论知识与PyG库初探

一、简单图论知识

1.1图的基本定义
  • 一个图可被记为一个三元组 G = { V , E , Φ } \mathcal{G}=\{\mathcal{V}, \mathcal{E}, \mathit{\Phi}\} G={V,E,Φ},其中 V = { v 1 , … , v N } \mathcal{V}=\left\{v_{1}, \ldots, v_{N}\right\} V={v1,,vN}是基数为 N = ∣ V ∣ N=|\mathcal{V}| N=V 的节点的集合, E = { e 1 , … , e M } \mathcal{E}=\left\{e_{1}, \ldots, e_{M}\right\} E={e1,,eM} 是基数为 M M M 的边的集合, Φ \mathit{\Phi} Φ 为边集到节点偶对的关系集,或为邻接矩阵。我们也可以将图简记为: G = { V , E } \mathcal{G}=\{\mathcal{V}, \mathcal{E}\} G={V,E}。下图我们给出了一有5个结点和6条边的图,其中的边 e 3 e_{3} e3 可记为 e 3 , 5 e_{3,5} e3,5,或 ( v 3 , v 5 ) \left(v_3,v_5\right) (v3,v5)
图 1 图的示例
  • 图深度学习中,用节点表示实体(entities ),用边表示实体间的关系(relations)。
  • 节点和边的信息可以是类别型的(categorical),类别型数据的取值只能是哪一类别。一般称类别型的信息为标签(label)
  • 节点和边的信息可以是数值型的(numeric),数值型数据的取值范围为实数。一般称数值型的信息为属性(attribute)
  • 大部分情况中,节点含有信息,边可能含有信息。
1.2图的基本类型
  • 有向图与无向图
        如果图中边存在方向性,则称这样的边为有向边,用尖括号表示, e i , j = < v i , v j > \mathcal{e_{i,j}}=\left<v_i,v_j\right> ei,j=vi,vj;反之则为无向边,用圆括号表示, e i , j = ( v i , v j ) \mathcal{e_{i,j}}=\left(v_i,v_j\right) ei,j=(vi,vj)。 在有向边 < v i , v j > \left<v_i,v_j\right> vi,vj中, v i v_{i} vi是边的起点, v j v_{j} vj是边的终点;而无向边 ( v i , v j ) \left(v_i,v_j\right) (vi,vj)可认为是对称的, v i v_{i} vi v j v_{j} vj称为端点。
  • 非加权图与加权图(赋权图)
        有时候图中每条边都有一实数与之对应,我们称之为加权图;非加权图与之相反,可以认为其各边的权重是相同的。
  • 同质图与异质图
        同质图(Homogeneous Graph):只有一种类型的节点和一种类型的边的图。
        异质图(Heterogeneous Graph):存在多种类型的节点和多种类型的边的图。
图 2 异质图的示例
  • 二部图(二分图)
        二分图(Bipartite Graphs):节点分为两类,只有不同类的节点之间存在边。
图 3 二分图的示例
  • 多重图与线图
        多重图:给定一图 G \mathcal{G} G,对 G \mathcal{G} G中所有边,若存在有两条或两条以上的边同起点、终点,则称 G \mathcal{G} G为多重图。
        线图:给定一图 G \mathcal{G} G,对 G \mathcal{G} G中所有边,若不存在有两条或两条以上的边同起点、终点,则称 G \mathcal{G} G为线图。特别地,不含自回路的线图称为简单图
1.3图的连通性
  • 邻接节点
        若存在一条边连接节点 v i v_{i} vi v j v_{j} vj,则称二者互为邻接节点。

  • 节点的度
        1.无向图
        考虑无向图中的节点 v i v_{i} vi,定义 v i v_{i} vi的度(degree)为以 v i v_{i} vi为端点的边的数目,记为 d e g ( v i ) deg\left(v_i\right) deg(vi)。显然地,有:
    d e g ( v i ) = v i 的 邻 接 节 点 数 deg\left(v_i\right)=v_{i}的邻接节点数 deg(vi)=vi
        2.有向图
        对于有向图中的节点 v i v_{i} vi,度相关定义如下:
         v i v_{i} vi的出度 d e g + ( v i ) deg^{+}\left(v_i\right) deg+(vi):以 v i v_{i} vi为起点的有向边数目;
         v i v_{i} vi的入度 d e g − ( v i ) deg^{-}\left(v_i\right) deg(vi):以 v i v_{i} vi为终点的有向边数目;
        则 v i v_{i} vi的度 d e g ( v i ) deg\left(v_i\right) deg(vi)= d e g + ( v i ) deg^{+}\left(v_i\right) deg+(vi)+ d e g − ( v i ) deg^{-}\left(v_i\right) deg(vi)
       
        3.握手定理
        对于任意图,有如下定理:
    ∑ v i ∈ V d e g ( v i ) = 2 M \sum_{v_i\in\mathcal{V}}deg\left(v_i\right)=2M viVdeg(vi)=2M
        其中 M M M为图的边数。
        该定理也可简记为:节点度数之和等于边数的两倍。

  • 节点的可达
        一图 G = { V , E } \mathcal{G}=\{\mathcal{V}, \mathcal{E}\} G={V,E},节点 v i 、 v j ∈ V v_{i}、v_{j}\in\mathcal{V} vivjV,若从 v i v_{i} vi v j v_{j} vj存在路径,则称从 v i v_{i} vi可达 v j v_{j} vj。规定 v i v_{i} vi到自身可达。

  • 无向图的连通性
        在无向图 G \mathcal{G} G中,若任意两节点可达,则称 G \mathcal{G} G是可达的。

  • 有向图的连通性
        在有向图 G \mathcal{G} G中:
        1. 对于任意节点对,若其互相可达,则称 G \mathcal{G} G是强连通的;
        2. 对于任意节点对,若至少从一个节点到另一节点可达,则称 G \mathcal{G} G是单向连通的;
        3. G \mathcal{G} G的底图(将 G \mathcal{G} G转换为无向图)是连通的,则称 G \mathcal{G} G是弱连通的。

  • 子图
        图 G ′ = { V ′ , E ′ } \mathcal{G^{'}}=\{\mathcal{V^{'}}, \mathcal{E^{'}}\} G={V,E}与图 G = { V , E } \mathcal{G}=\{\mathcal{V}, \mathcal{E}\} G={V,E}有如下关系: V ′ ⊆ V \mathcal{V^{'}}\subseteq\mathcal{V} VV E ′ ⊆ E \mathcal{E^{'}}\subseteq\mathcal{E} EE,则称 G \mathcal{G} G为图 G ′ \mathcal{G^{'}} G的子图。

  • 连通分量(极大连通子图)
        给定一无向图 G = { V , E } \mathcal{G}=\{\mathcal{V}, \mathcal{E}\} G={V,E}与其子图 G ′ = { V ′ , E ′ } \mathcal{G^{'}}=\{\mathcal{V^{'}}, \mathcal{E^{'}}\} G={V,E}。若满足:
        1. G ′ \mathcal{G^{'}} G是连通的;
        2. G \mathcal{G} G中不存在包含 G ′ \mathcal{G^{'}} G的更大子图 G ′ ′ \mathcal{G^{''}} G是连通的。
        那么我们称 G ′ \mathcal{G^{'}} G G \mathcal{G} G的连通分量,或极大连通子图。
        类似地,在有向图中,我们也可以定义强连通分量、单向连通分量与弱连通分量。

1.4距离
  • 距离
         v i , v j ∈ V v_{i}, v_{j} \in \mathcal{V} vi,vjV 是图 G = { V , E } \mathcal{G}=\{\mathcal{V}, \mathcal{E}\} G={V,E}上的一对结点, v i , v j v_{i}, v_{j} vi,vj之间所有路径的集合记为 P i j \mathcal{P}_{ij} Pij
        称结点对 v i , v j v_{i}, v_{j} vi,vj之间的最短路径称为 v i v_{i} vi v j v_{j} vj的距离,记为 d ( v i , v j ) d\left(v_i,v_j\right) d(vi,vj)
        即:
    d ( v i , v j ) = arg ⁡ min ⁡ p ∈ P i j ∣ p ∣ d\left(v_i,v_j\right)=\arg \min _{p \in \mathcal{P}_{ij}}|p| d(vi,vj)=argpPijminp

    其中, p p p表示 P i j \mathcal{P}_{ij} Pij中的一条路径, ∣ p ∣ |p| p是路径 p p p的长度。

  • 直径
        给定一个连通图 G = { V , E } \mathcal{G}=\{\mathcal{V}, \mathcal{E}\} G={V,E},其直径为其所有结点对之间的最短路径的最大值,形式化定义为:
    diameter ⁡ ( G ) = max ⁡ v i , v j ∈ V min ⁡ p ∈ P i j ∣ p ∣ \operatorname{diameter}(\mathcal{G})=\max _{v_{i}, v_{j} \in \mathcal{V}} \min _{p \in \mathcal{P}_{ij}}|p| diameter(G)=vi,vjVmaxpPijminp
1.5图的矩阵表示
  • 邻接矩阵
        给定一图 G = { V , E } \mathcal{G}=\{\mathcal{V}, \mathcal{E}\} G={V,E},其对应的邻接矩阵被记为 A ( G ) = ( a i j ) N × N A\left(\mathcal{G}\right)=\left(a_{ij}\right)_{N\times N} A(G)=(aij)N×N
        其中:
    a i j = { 1 [ v i , v j ] ∈ E 0 [ v i , v j ] ∉ E , i , j = 1 , 2 , … , N a_{ij}=\begin{cases} 1 & \left[v_i,v_j\right]\in\mathcal{E} \\ 0 & \left[v_i,v_j\right]\notin\mathcal{E} \end{cases},i,j=1,2,…,N aij={10[vi,vj]E[vi,vj]/E,i,j=1,2,,N

    相应地,在赋权图中也有类似定义:
a i j = { W ( v i , v j ) [ v i , v j ] ∈ E 0 [ v i , v j ] ∉ E , i , j = 1 , 2 , … , N a_{ij}=\begin{cases} W\left(v_i,v_j\right) & \left[v_i,v_j\right]\in\mathcal{E} \\ 0 & \left[v_i,v_j\right]\notin\mathcal{E} \end{cases},i,j=1,2,…,N aij={W(vi,vj)0[vi,vj]E[vi,vj]/E,i,j=1,2,,N

    其中 W ( v i , v j ) W\left(v_i,v_j\right) W(vi,vj)为边 [ v i , v j ] \left[v_i,v_j\right] [vi,vj]的权重。
    例如上文中图1则为一个无向无权图:

图 4 无向无权图示例
    其邻接矩阵为:

A = ( 0 1 0 1 1 1 0 1 0 0 0 1 0 0 1 1 0 0 0 1 1 0 1 1 0 ) \mathbf{A}=\left(\begin{array}{lllll} 0 & 1 & 0 & 1 & 1 \\ 1 & 0 & 1 & 0 & 0 \\ 0 & 1 & 0 & 0 & 1 \\ 1 & 0 & 0 & 0 & 1 \\ 1 & 0 & 1 & 1 & 0 \end{array}\right) A=0101110100010011000110110

  • 邻接矩阵相关运算的含义
        给定一有向线图 G \mathcal{G} G,其邻接矩阵为 A A A
        1. A T A^T AT:图 G \mathcal{G} G中所有边反向。
        2. A A T = ( b i j ) N × N AA^T=\left(b_{ij}\right)_{N\times N} AAT=(bij)N×N,其中:
    b i j = ∑ k = 1 N a i k ⋅ a j k b_{ij}=\sum_{k=1}^{N}a_{ik}\cdot a_{jk} bij=k=1Naikajk

    当 a i k ⋅ a j k = 1 a_{ik}\cdot a_{jk}=1 aikajk=1时,有 a i k = 1 a_{ik}=1 aik=1 a j k = 1 a_{jk}=1 ajk=1,亦即 [ v i , v k ] ∈ E \left[v_i,v_k\right]\in\mathcal{E} [vi,vk]E [ v j , v k ] ∈ E \left[v_j,v_k\right]\in\mathcal{E} [vj,vk]E
    所以, b i j b_{ij} bij表示从 v i , v j v_{i}, v_{j} vi,vj引出的边同时终止于一点的数目; b i i b_{ii} bii表示节点 v i v_{i} vi的出度。
    3. A m = ( b i j ) N × N A^m=\left(b_{ij}\right)_{N\times N} Am=(bij)N×N
     b i j b_{ij} bij表示从 v i v_{i} vi v j v_{j} vj,有 b i j b_{ij} bij条长为 m m m的路径。

    图论的基本知识就先介绍到这里,稍微复杂的知识如图卷积等到遇到时再介绍。
    图论的相关知识推荐阅读:
    1.《Deep Learning on Graphs》
    2.《深入浅出图神经网络》刘忠雨等,机械工业出版社
    图论教材:
    1.《图论导引》Douglas B.West,机械工业出版社
    2.《图论与网络流理论》高随祥,高等教出版社
 

二、PyG库初探

    本文环境:python3.8+CUDA11.1+Pytorch1.8.0,IDE:Pycharm
    PyG(PyTorch Geometric Library)库是一个基于PyTorch的用于处理不规则数据(比如图)的库,或者说是一个用于在图等数据上快速实现表征学习的框架。它有出色的运行速度,还集成了很多论文中提出的方法(GCN,SGC,GAT等)和常用数据集。

2.1PyG内置数据集

    我们通过PyG内置的一数据集Karate Club Dataset来初步探索图数据。
    Karate Club数据集官方文档
    原论文:《An Information Flow Model for Conflict and Fission in Small Groups》

图 5 空手道俱乐部社交关系图

    该数据集描述了一个空手道俱乐部会员的社交关系,以34名会员作为节点,如果两位会员在俱乐部之外仍保持社交关系,则在节点间增加一条边。在社会学家Zachary收集数据的过程中,管理人员 John A 和 教练 Mr. Hi之间产生了冲突,会员们选择了站队,一半会员跟随 Mr. Hi 成立了新俱乐部,剩下一半会员找了新教练或退出了俱乐部。数据集提出了一个图节点分类的任务,每个节点具有一个34维的特征向量。节点类型class共有4类,分别代表会员所属的社区community。
    数据集加载:

from torch_geometric.datasets import KarateClub
dataset = 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: 1
Number of features: 34
Number of classes: 4
2.2Data类

    Data类包含于torch_geometric.data模块中,为图形化数据的存储类。我们还是通过一个简单的图数据来说明,在这个图里有4个节点, v 1 v_1 v1 v 2 v_2 v2 v 3 v_3 v3 v 4 v_4 v4,每一个都带有一个2维的特征向量,和一个标签y,代表这个节点属于哪一类。

图 6 简单图数据实例

     图源:图神经网络之神器——PyTorch Geometric 上手 & 实战

  • attributes

    1. x (Tensor, optional) — 节点特征矩阵,大小为: [num_nodes, num_node_features]。对于我们的数据,则x应为:

x = torch.tensor([[2,1],[5,6],[3,7],[12,0]],dtype=torch.float)

    2. edge_index (LongTensor, optional) — 边索引矩阵,以COO方式存储的图节点连接信息。大小为:[2, num_edges]
    COO(coordinate format)是一种存储稀疏矩阵的格式。仍以上述数据为例,原始邻接矩阵应为:
A = [ 0 1 1 0 1 0 0 0 0 0 0 1 0 1 0 0 ] A=\begin{matrix} \left[\begin{array}{r} 0 & 1 & 1 & 0 \\ 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ 0 & 1 & 0 & 0 \end{array}\right] \end{matrix} A=0100100110000010
    考虑到矩阵过于稀疏(尤其是节点较多时),采用COO存储,即只关注矩阵中非零元素的所在位置。于是我们得到了如下的表格:

rowcolval
011
021
101
231
321

    上述图数据的边索引矩阵为:

edge_index = torch.tensor([[0, 0, 1, 2, 3],
                           [1, 2, 0, 3, 2]], dtype=torch.long)

    小结:边索引矩阵的数据是一个由两个list组成的列表,第一个是由边起点组成的list,第二个元素是由边终点组成的list(如果是无向图,两种方向都要写!!
    3. edge_attr (Tensor, optional) — 边属性矩阵,大小为[num_edges, num_edge_features]
    4. y(Tensor) — 边或节点的目标矩阵(尺寸可以是 [num_nodes, *][1, *] )。例如上述图数据的 y y y为:[0,1,0,1]。
    以上给出了几个重要的属性,Data实例还包含其他属性,需要使用时用限定关键字参数指定即可。

  • 方法
        1.keys 返回图属性名的list
        2.num_nodes 图中的节点数
        3.num_edges 图中的边数(无向图会返回两个方向的边数,即边数的两倍)
        4.num_node_features或 num_features  图中节点特征维度
        5.contains_isolated_nodes() 图中是否含有孤立点
        6.contains_self_loops() 图中是否含有自环
        7.is_undirected() 图是不是无向的
        8.is_directed() 图是不是有向的
        仍以Karate Club为例:
# 获取图的一些信息
print(f'Number of nodes: {data.num_nodes}') # 节点数量
print(f'Number of edges: {data.num_edges}') # 边数量
print(f'Number of node features: {data.num_node_features}') # 节点属性的维度
print(f'Number of node features: {data.num_features}') # 同样是节点属性的维度
print(f'Number of edge features: {data.num_edge_features}') # 边属性的维度
print(f'Average node degree: {data.num_edges / data.num_nodes:.2f}') # 平均节点度
print(f'if edge indices are ordered and do not contain duplicate entries.: {data.is_coalesced()}') # 是否边是有序的同时不含有重复的边
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.contains_isolated_nodes()}') # 此图是否包含孤立的节点
print(f'Contains self-loops: {data.contains_self_loops()}')  # 此图是否包含自环的边
print(f'Is undirected: {data.is_undirected()}')  # 此图是否是无向图

    结果:

Number of nodes: 34
Number of edges: 156
Number of node features: 34
Number of node features: 34
Number of edge features: 0
Average node degree: 4.59
if edge indices are ordered and do not contain duplicate entries.: True
Number of training nodes: 4
Training node label rate: 0.12
Contains isolated nodes: False
Contains self-loops: False
Is undirected: True
2.3练习

    通过继承Data类实现一个类,专门用于表示“机构-作者-论文”的网络。
该网络包含“机构”、“作者”和“论文”三类节点,以及“作者-机构”和“作者-论文”两类边。
    要求:
1)用不同的类属性存储不同类别的节点;
2)用不同的类属性存储不同类别的边;
3)逐一实现获取不同类别节点数量的方法。

    类定义:

class IPAGNN(Data):
    """
    I:Institute;
    P:Papers;
    A:Authors;
    """
    def __init__(self, x_institute=None, x_article=None, x_author=None,
                 edge_index_author_to_institute=None, edge_index_author_to_article=None,
                 y=None):
        super(IPAGNN, self).__init__(
            x=torch.cat((node_attr_institute, node_attr_article, node_attr_author)),
            edge_index=torch.cat((edge_index_author_to_institute, edge_index_author_to_article), 1))
        # node attritude
        self.x_institute = x_institute
        self.x_article = x_article
        self.x_author = x_author
        # edge index
        self.edge_index_author_to_institute = edge_index_author_to_institute
        self.edge_index_author_to_article = edge_index_author_to_article
        # label
        self.y = y

    @property
    def num_nodes_of_institute(self):
        return self.x_institute.size(0)

    @property
    def num_nodes_of_article(self):
        return self.x_article.size(0)

    @property
    def num_nodes_of_author(self):
        return self.x_author.size(0)

    获得新建类实例:

# 两个机构、两篇文章、三个作者
node_attr_institute = torch.tensor(
    [[-1, 1, 2],
     [1, 1, 1]], dtype=torch.float
)
node_attr_article = torch.tensor(
    [[1, 0, 1],
     [0, 1, 2]], dtype=torch.float
)
node_attr_author = torch.tensor(
    [[3, 1, 2],
     [2, 1, 1],
     [-1, 1, 0]], dtype=torch.float
)
edge_index_author_to_institute = torch.tensor(
    [[0, 3, 6, 6],
     [1, 4, 1, 4]], dtype=torch.long
)
edge_index_author_to_article = torch.tensor(
    [[0, 3, 2, 5],
     [2, 5, 3, 0]], dtype=torch.long
)
y = torch.tensor(
    [1, 1, 2, 2, 0, 0, 0], dtype=torch.float
)

IPAGNNtest = IPAGNN(node_attr_institute,node_attr_article,node_attr_author,edge_index_author_to_institute,edge_index_author_to_article,y)

    测试:

print(IPAGNNtest.num_nodes) # 7
print(IPAGNNtest.num_nodes_of_institute) # 2
print(IPAGNNtest.num_nodes_of_article) # 2
print(IPAGNNtest.num_nodes_of_author) # 3


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值