手撕图机器学习,图神经网络
神经网络是一个非常强大的工具,他可以处理不同模态不同类型的数据。在NLP系列文章中,介绍了循环网络在序列数据(文章主要是文本信息,由于音频信息处理由于暂未过多涉及,因此未做归纳总结)的使用,包括Transformer和注意力机制的相关解释和应用。在Gephi教程中,介绍了图的绘制和一些基于Gephi软件自带的图分析和可视化,gephi只是为我们打开了图分析的冰山一角。实际上我们并没有足够的工具去挖掘和解释图背后的信息。图分析也是一种序列信息,但这个序列信息更多在于关联性、因果推论上。因此,图的分析可以独立于NLP方向,作为一个全新的方向去探索、学习。本篇将主要汇总有关图机器学习和深度学习的相关理论内容,用作补充。此外,由于代码实现的工作量巨大,因此会另起文档整理代码,并且附在配套笔记超链接,可以按需阅读。由于图的知识会涉及到大量图论和矩阵的相关运算,因此在本文完结后,会继续开启一篇新的文档用以整理相关的数学内容,相关经典算法的论文精读作为图系列文章补充内容。
手动防爬虫,作者优快云:总是重复名字我很烦啊,联系邮箱daledeng123@163.com
1、Why?
传统的机器学习领域,数据样本A和数据样本B,C,D之间的关联并没有纳入考虑,我们把这些参数变量默认为独立同分布彼此无关变量。当然也可以用相似矩阵来描述这些变量之间的相似性和关联度,但是这并不是机器学习的初衷。而图刚好是描述这样数据关系的通用语言,利用图信息可以挖掘出更多关联信息。
2、How?
充分利用节点Node和边Edge的关系,进行数据挖掘。若想用网络处理图数据,首先需要明确,图的输入数据尺寸是不固定的,一个Node可能和N个点构成关系,这个N可以是及其庞大的无上限的数据。对于图像数据(卷积网络)处理,卷积核的卷积过程是一个从左到右,从上到下的一个扫描过程;对于文本数据(循环网络)处理,也是从左到右的一个计算过程;对于图数据,并没有一个初始锚点,并且具备多模态特性,例如某音乐的受欢迎程度可能和作曲家和歌手有关(文本数据),可能和听众画像有关(文本数据),可能和MV拍摄的好不好看节奏是否动感有关(音频数据),可能和专辑封面是否好看有关(图像数据)。因此,图数据处理,本质上也是各种卷积操作、正则化操作、监督学习操作等等把节点信息变成一个d维向量的端到端学习的结合体,这个过程叫做图嵌入(gragh embedding),搞明白如何做好gragh embedding做好,就和NLP搞好word embedding一样重要。
3、应用
(1)最短路径(图论问题)。
(2)节点重要度分析(度中心性评价)。
(3)社群检测(找到在广泛群体中有着相似性的重要群体)。
(4)相似度和关联预测(隔很远的节点之间可能存在联系)。
(5)联合作用检测(若有极大的组合可能性,超越临床医学检测的能力,则图可以发现这些节点的潜在关联,如a+k并没有得到临床验证的前提下,图分析a+k是否有利于临床表现)。
(6)图嵌入问题(和深度学习建立联系的核心)。
写在前面 & 配套链接(访者必读)
文本大篇幅用通俗易懂的语言讲解图网络和图学习的原理,会涉及到一些简单的数学推论,关键结论的完整数学推论后期有时间会单独写一个文档用作补充,本文涉及的所有图特征描述方法都有相关代码可以实现,目前主流的图挖掘工具包有Gephi、NetworkX、PyG、Gephi是一个安装在电脑本地的集成软件,内置图绘制、图可视化和一些简单的聚类分析功能,非常适合入门使用。NetworkX是基于统计学和拓扑学的一套图分析工具,安装简单,文档完整,并且具备一定程度的数据挖掘和分析能力,适合作为一款图机器学习入门的工具使用。PyG是基于pytorch的一款图神经网络工具,集成了主流的图挖掘神经网络框架,具有多模态、多维度对图信息的挖掘能力,是一个功能非常强大的图分析工具,但是需要一定GPU资源,适合进阶选择。由于相关内容还在马不停蹄的更新,所以配套链接可能不全,后续会慢慢补上。工作量较大,请勿私信催更
个人学习下来的感受:其实图挖掘的核心仍然是NLP的那套方式,只不过弱化了一定序列感知,强化了拓扑结构认知,是统筹了时间和空间维度的一套模型方法,从学习的角度上,仍然是NLP优先级高于GNN。
NLP从0开始(主要看编码章节Word2Vec思想)
Gephi图可视化工具使用
NetworkX基础操作和图分析相关计算的代码实现
【论文精读系列,图机器学习走向图深度学习的开山之作】DeepWalk,随机游走图嵌入的艺术,自然语言处理与图处理的首次融合
【动手写代码复现】DeepWalk实战
【论文精读系列,有偏随机游走和连续数实现离散采样】node2vec,站在DeepWalk巨人肩膀上再进一步
【动手写代码复现】node2vec实战
图深度学习框架PyG(Pytorch-Geometric)代码实战
【论文精读系列,GCN发展的产物】SimGNN,图网络的标准处理流程+NTN/PNC实现快速图相似度匹配
图的基本表示
图的基本参数
节点:nodes/vertices
N
N
N
边:links/egdes
E
E
E
系统:network/graph
G
(
N
,
E
)
G(N,E)
G(N,E)
例如,这两个信息图本质上可以绘制成一张图(节点数4,连接数4)。实际上,图的设计需要根据你想解决问题的实际关系来进行确定。
图的类别
对于有无指向关系可以分为,无向图和有向图。
对于关系的种类,又可分n维异质图。
G
=
(
V
,
E
,
R
,
T
)
v
i
∈
V
,环境的影响因素如血压,是否患病,年龄,性别等
(
v
i
,
r
,
v
j
)
∈
E
,这些环境因素的联合作用效果
边的关系
r
∈
R
节点类别
T
节点类别
T
G=(V,E,R,T) \\ v_i \in V ,环境的影响因素如血压,是否患病,年龄,性别等\\ (v_i, r, v_j) \in E,这些环境因素的联合作用效果\\ 边的关系r \in R\\ 节点类别T节点类别T
G=(V,E,R,T)vi∈V,环境的影响因素如血压,是否患病,年龄,性别等(vi,r,vj)∈E,这些环境因素的联合作用效果边的关系r∈R节点类别T节点类别T
最常见的异质图为二分图(2种节点,bipartite graph)。如工作和人名的关系,作者和发表论文题目等。这种二分图可以直接展开。
节点连接数(Node degree)
对上图paper2而言,节点的连接数为3,记作
k
p
a
p
e
r
2
=
3
k_{paper2}=3
kpaper2=3
平局度
k
a
v
g
=
<
k
>
=
2
E
N
k_{avg}=<k>=\frac {2E}{N}
kavg=<k>=N2E
若是有向图,则不存在节点共享,此时平局度
k
a
v
g
=
<
k
>
=
E
N
k_{avg}=<k>=\frac {E}{N}
kavg=<k>=NE
我们可以用node degree描述节点的重要性
图的矩阵表示(邻接矩阵)
对于无向图,他的邻接矩阵是一个对称阵,无向图是一个非对称阵。用数学语言表示:
A
i
j
=
1
,
i
f
t
h
e
r
e
i
s
a
l
i
n
k
f
r
o
m
n
o
d
e
i
t
o
n
o
d
e
j
,
e
l
s
e
0
A_{ij}=1,if\ there\ is\ a\ link\ from\ node\ i\ to\ node\ j, else\ 0
Aij=1,if there is a link from node i to node j,else 0
由于无向图的邻接矩阵是一个对称阵,所以连接总数
L
=
1
2
∑
A
i
j
L=\frac{1}{2} \sum A_{ij}
L=21∑Aij。有向图的连接总数
L
=
∑
A
i
j
L=\sum A_{ij}
L=∑Aij
思考1:为什么要把图表示成矩阵形式?
思考2:看似完美的邻接矩阵在实际情况中大多是稀疏矩阵。
连接列表和邻接列表
连接列表:只记录存在连接节点对的列表。上图有向图则可记录为:(1,3)(1,4)(2,1)(3,2)(3,5)(3,6)(5,6)
邻接列表:只记录邻接的节点。上图有向图则可记录为:(只用了4行就可以表示图信息)
1:3,4
2:1
3:2,5,6
4:
5:6
6:
其他图
特征工程:传统图机器学习
图有三层信息可以写成向量的形式:(1)节点信息;(2)连接信息;(3)区域信息。但是节点信息包含的属性往往是多模态的形式,例如这个人的头像(图像信息)、个性签名(文本信息)等。但是机器学习对多模态的处理稍显困难,所以传统机器学习主要挖掘节点之间的连接特征信息,例如这个节点在整个社群中扮演什么作用。
如何做图的特征工程(节点层面)
简单来说,这一部分要做的事,就是通过所有的节点信息去猜测未知节点的信息,比如用成都地铁网络所有节点的类别去猜测磨子桥站是否可能是一个地铁站。做法就是把节点信息编辑成一个D维向量,然后导入算法进行计算。
G
=
(
V
,
E
)
f
:
V
→
R
G=(V,E)\\ f:V→ \mathbb{R}
G=(V,E)f:V→R
输入某个节点的D维向量,输出该节点是某类的概率。这个的关键在于D维向量如何构造。
一般来说,在传统机器学习中可以包含以下四个信息:(1)node degree,度 (2) node centrality,重要性 (3) clustering coefficient,聚集系数 (4) graphlets,子图拆分
连接度重要性(node degree centrality)——单一相似
单独用连接度来描述,只能说明单一维度的相似性,并不能涵盖更多信息,例如1和7,明显他们的质量存在差异,但是node degree都是1。所以需要其他重要度指标来进一步描述图信息。(这里的度向量实际上就是node degree centrality)
特征向量重要度(eigenvector centrality)——物以类聚
这是一个很常规的思想:如果一个节点和他相邻的节点都很重要,那这个节点也很重要。
我们用
c
v
c_v
cv表示v节点的重要度,此时这个节点的重要度为:
c
v
=
1
λ
∑
c
u
c_v=\frac{1}{\lambda} \sum c_u
cv=λ1∑cu其中
λ
\lambda
λ为邻接矩阵最大的特征值,
c
u
c_u
cu为v相邻的节点重要度。也就是对v相邻节点的重要度求和再标准化,就可以得到v节点的重要度。
那么这个问题就变成了一个经典的递归问题。在这种情况下就可以用特征向量来解决。
c
v
=
1
λ
∑
c
u
≡
λ
c
=
A
c
c_v=\frac{1}{\lambda} \sum c_u \equiv \lambda \bm{c} = \bm{A} \bm{c}
cv=λ1∑cu≡λc=Ac
这里A是邻接矩阵。我们可以用矩阵的秩来理解这样一个过程。假设v节点和4个节点相邻,对于左侧
λ
c
\lambda \bm{c}
λc是一个4x1的矩阵,对于右侧,邻接矩阵A是一个4x4的矩阵,c是一个4x1的矩阵,他们的结果仍然是一个4x1的矩阵。
λ
[
a
b
c
d
]
4
∗
1
=
[
0
a
12
a
13
a
14
b
11
0
b
13
b
14
c
11
c
12
0
c
14
d
11
d
12
d
13
0
]
4
∗
4
[
a
b
c
d
]
4
∗
1
\lambda \left[\begin{array}{c} a \\ b \\ c \\ d \end{array}\right]_{4*1} = \left[\begin{array}{c} 0\ a_{12}\ a_{13}\ a_{14}\\ b_{11}\ 0\ b_{13}\ b_{14}\\ c_{11}\ c_{12}\ 0\ c_{14}\\ d_{11}\ d_{12}\ d_{13}\ 0 \end{array}\right]_{4*4} \left[\begin{array}{c} a \\ b \\ c \\ d \end{array}\right]_{4*1}
λ
abcd
4∗1=
0 a12 a13 a14b11 0 b13 b14c11 c12 0 c14d11 d12 d13 0
4∗4
abcd
4∗1
在这种情况下,
c
\bm{c}
c就是A的特征向量。并且,这个
c
\bm{c}
c就是我们需要求得的eigenvector centrality。
间重要度(betweenness centrality)——中心地带
这个重要度指标主要用来衡量这个节点是否处于中心地带。例如五月天北京演唱会,那么五月天肯定处于所有关联的中心。间重要度的计算方式如下:
c
v
=
∑
共有多少对节点的最短路径经过
v
除
v
节点之外,两两节点对的数量
c_v = \sum \frac{共有多少对节点的最短路径经过v}{除v节点之外,两两节点对的数量}
cv=∑除v节点之外,两两节点对的数量共有多少对节点的最短路径经过v
用一个例子来更好说明:
通过间重要度可以发现,C和D在整个图处在交通要道的位置,而B虽然和CD相邻,但是B的重要性显然没有CD高。
最短路径重要度(closeness centrality)——到哪都近
可以用以下公式计算该重要度。
c
v
=
∑
1
节点
v
到其他节点
u
的最短路径
c_v = \sum \frac{1}{节点v到其他节点u的最短路径}
cv=∑节点v到其他节点u的最短路径1
同样用上图作为例子。节点A的最短路径重要度为:
c
a
=
1
2
+
1
+
2
+
3
=
1
8
c_a=\frac{1}{2+1+2+3}=\frac{1}{8}
ca=2+1+2+31=81
节点路径长度分为是A-C-B(2),A-B(1),A-C-D(2),A-C-D-E(3)
节点C的最短路径重要度为:
c
c
=
1
1
+
1
+
1
+
2
=
1
5
c_c=\frac{1}{1+1+1+2}=\frac{1}{5}
cc=1+1+1+21=51
节点路径长度分为是C-A(1),C-B(1),C-D(1),C-D-E(2)
由于
c
c
>
c
a
c_c>c_a
cc>ca,因此c点的重要度会高于a点,这意味着c点离哪都近,a就有点郊区之感。
聚集系数(clustering coefficient)——抱团取暖
e
v
=
v
节点相邻节点构成三角形的个数
v
节点相邻节点两两对数
e_v=\frac{v节点相邻节点构成三角形的个数}{v节点相邻节点两两对数}
ev=v节点相邻节点两两对数v节点相邻节点构成三角形的个数
会发现第三个的
e
v
e_v
ev大于第一个
e
v
e_v
ev,说明对于v节点而言,第三个的抱团会更严重一点。这种以一个中心链接出去,连接出去的点又可以构成新的网络,这种网络又可称为自我中心网络。
子图向量(graphlet degree vector)
我们可以通过自己定义的一些拓扑结构,来表示节点v的局部邻域拓扑结构。相当于构建了一个直方图,然后对自己定义的节点角色进行一个各自的描述。进一步的,可以用GDV来计算节点的相似度等。若我们定义的节点数量是5个,那么会有73个拓扑结构,这种情况下,GDV是一个73维向量,在这种情况下,节点的拓扑信息描述会更加全面。
一些思考
无论如何描述节点之间的重要度,始终只能描述节点的图信息,却不能描述节点的属性信息。在这种情况下,即使特征工程做到了最好,也很难实现社群分类,例如存在3个一样的图,他们隶属于朝阳区、海淀区、西城区,通过这样的方式,是很难把他们区分为三类,特征工程做到极致,则他们三个表示的信息越一致,那么划分为一类的可能性就越大。这就是机器学习的弊端。因此,在算力不受限的情况下,尽可能考虑更多维度的信息来描述图,才是最可行的办法。
如何做图的特征工程(连接层面)
这一部分,我们需要把节点之间的连接表示成D维向量,通过节点之间的连接信息,实现节点之间未知连接信息的预测和分析。所以关键在于如何抽取这个D维向量。
思路一、直接抽取link信息变成D维向量
思路二、把link两端的节点信息拼接成一个D维向量
这样的做法其实和之前节点信息挖掘是一样的,会丢失连接结构信息,例如老板A和员工A他们的节点信息无法表示老板和员工这样一个从属关系的连接信息。因此这种方法不推荐。
最短路径距离
在这里A到B,B到E和C到E的最短路径长度都是2,但是可以发现,假若C D点是一个权重很高的点,那么A-C-B和A-C-D这两条连接的质量是不一样的。因此只关注距离长度会丢失质量信息。
邻域关系
(1)Common neighbors(共同好友个数)。
∣
N
(
v
1
)
∩
N
(
v
2
)
∣
|N(v_1) \cap N(v_2)|
∣N(v1)∩N(v2)∣
(2)Jaccard’s Coefficent(交并比)
∣
N
(
v
1
)
∩
N
(
v
2
)
∣
∣
N
(
v
1
)
∪
N
(
v
2
)
∣
\frac{|N(v_1) \cap N(v_2)|}{|N(v_1) \cup N(v_2)|}
∣N(v1)∪N(v2)∣∣N(v1)∩N(v2)∣
以上图为例,AB节点的交集为C,AB节点的并集为CD,那么AB节点的交并比就是
1
2
\frac{1}{2}
21。
(3)Adamic-Adar index
∑
u
∈
∣
N
(
v
1
)
∩
N
(
v
2
)
∣
1
l
o
g
(
k
u
)
\sum_{u\in{|N(v_1) \cap N(v_2)|}} \frac{1}{log(k_u)}
u∈∣N(v1)∩N(v2)∣∑log(ku)1
以上图为例,AB节点的共同好友只有C,此时Adamic-Adar index为
1
l
o
g
3
\frac{1}{log3}
log31。一般来说,如果两个节点的共同好友是一个公共点(类似社牛或者海王),那么这两个节点的连接就并没有那么牢固,比如小明和小红都在四川大学,那么小明和小红不一定产生太大的交集。反过来,如果两个节点的共同好友是一个社恐,例如小明和小红都认识课题组的小王。那么小明和小红之间产生关系的连接就会强一些。此时如果小王是一个深居简出,常年住在实验室的人,那么小明和小红大概率情比金坚了。
思考(Katz Index)
假如两个节点没有共同好友,那么这个时候共同好友个数、交并比都是0,但实际上这两个节点可能存在一定的联系。因此在这种情况,需要看全图信息。往往用卡姿系数(Katz index)来表示,他表示节点u和节点v之间长度为k的路径个数。可以用邻接矩阵的幂来计算katz index。
proof:
P
u
v
(
2
)
=
∑
i
A
u
i
∗
P
i
v
(
1
)
=
∑
i
A
u
i
∗
A
i
v
=
A
u
v
2
P_{uv}^{(2)}=\sum_i A_{ui}*P_{iv}^{(1)}=\sum_i A_{ui}*A_{iv}=A_{uv}^{2}
Puv(2)=i∑Aui∗Piv(1)=i∑Aui∗Aiv=Auv2
理解:
A
u
i
A_{ui}
Aui表示与u节点隔1步邻居i,
P
i
v
P_{iv}
Piv表示i是否与u隔1步。还是用12号节点举例,此时u=1,v=2,
A
1
i
A_{1i}
A1i表示的是与1号节点隔一步的节点,也就是2和4号节点,邻接矩阵里就是[0 1 0 1]第一行行向量,
P
i
2
P_{i2}
Pi2在1阶是等价于
A
i
2
A_{i2}
Ai2的,邻接矩阵里就是[1 0 0 1]第二列列向量。在这里,i和u隔1步,i和v隔1步,实际上就是u和v隔了2步。
继续证明:
P
u
v
(
2
)
≡
A
u
v
2
P
u
v
(
3
)
=
∑
i
A
u
i
∗
P
i
v
(
2
)
=
∑
i
A
u
i
∗
A
i
v
2
=
A
u
v
3
…
…
P
u
v
(
l
)
=
A
u
v
l
P_{uv}^{(2)} \equiv A_{uv}^{2}\\ P_{uv}^{(3)}=\sum_i A_{ui}*P_{iv}^{(2)}=\sum_i A_{ui}*A_{iv}^{2}=A_{uv}^{3}\\……\\P_{uv}^{(l)}=A_{uv}^{l}
Puv(2)≡Auv2Puv(3)=i∑Aui∗Piv(2)=i∑Aui∗Aiv2=Auv3……Puv(l)=Auvl
进一步,可以把这个长度加到无穷大(1,2,3,……,∞)。那么节点v1和v2的所有路径长度之和个数为:
S
v
1
v
2
=
∑
i
=
1
β
i
A
v
1
v
2
i
S_{v_1v_2}= \sum_{i=1} \beta ^i A_{v_1v_2}^i
Sv1v2=i=1∑βiAv1v2i
这里,
β
\beta
β是一个折减系数,通常
0
<
β
<
1
0<\beta<1
0<β<1。长度越大,折减的就越厉害。
实际上,
S
=
∑
i
=
1
(
β
A
)
i
S= \sum_{i=1} {(\beta A)}^i
S=∑i=1(βA)i就是一个等比数列求和,可以用几何级数展开。
S
=
∑
i
=
1
(
β
A
)
i
=
(
I
−
β
A
)
−
1
−
I
S= \sum_{i=1} {(\beta A)}^i=(I-\beta A)^{-1}-I
S=i=1∑(βA)i=(I−βA)−1−I
对这个公式进行一个证明:
(
I
+
S
)
(
I
−
β
A
)
=
(
I
+
β
A
+
β
2
A
2
+
⋯
+
β
n
A
n
)
(
I
−
β
A
)
=
I
+
β
A
+
β
2
A
2
+
⋯
+
β
n
A
n
−
(
β
A
+
β
2
A
2
+
⋯
+
β
n
+
1
A
n
+
1
)
=
I
(I+S)(I-\beta A)\\ =(I+ \beta A + \beta ^2A^2+\dots+ \beta ^nA^n)(I- \beta A)\\ =I+ \beta A + \beta ^2A^2+\dots+ \beta ^nA^n-( \beta A + \beta ^2A^2+\dots+ \beta ^{n+1}A^{n+1})\\ =I
(I+S)(I−βA)=(I+βA+β2A2+⋯+βnAn)(I−βA)=I+βA+β2A2+⋯+βnAn−(βA+β2A2+⋯+βn+1An+1)=I
因此:
S
=
(
I
−
β
A
)
−
1
−
I
S=(I-\beta A)^{-1}-I
S=(I−βA)−1−I
如何做图的特征工程(全图层面)
从BoW到BoN
这部分需要提取一个D维向量来描述整张图的特点。在NLP没有进入transformer时期,还在机器学习的阶段(N-Gram),面临同样的问题,就是如何把文字文本信息转化为向量信息输入到计算机中。这时有一个非常伟大的思想提出(bag-of-words,BoW)。这个思想的核心就是,统计一段文本中某一个词或者字出现的次数,然后编码成向量形式,例如,“我爱你你爱他”,这句话我和他出现了1次,爱你出现了2次,那么这句话就可以被编码成[1,2,2,2,2,1]。当然,这种编码方式粗糙且问题很多,并且也没有考虑分词,存在很多问题,但是这个思想实现了文本信息向张量信息转换的关键一步。顺便推销一下之前的文章,如果想进步一了解NLP相关内容,可以翻阅我blog里NLP系列文章NLP从零开始。
基于BoW的思想,我们可以把图的节点看做单词,提出一个bag of nodes(BoN)的思路。
ϕ
(
G
1
)
=
[
n
o
d
e
s
1
n
u
m
b
e
r
,
n
o
d
e
s
2
n
u
m
b
e
r
,
…
]
\phi (G_1) = [nodes1 \ number, nodes2 \ number, \dots ]
ϕ(G1)=[nodes1 number,nodes2 number,…]
当然,这种思路也有很多弊端,最致命的一点,他只有节点信息,没有连接信息,所以需要在这个思想上进一步升级。
从BoN到BoND
只看node degree个数,不看节点不看连接个数。
如果用BoN的思想,则两张图的向量表示是一样的,每个点都只出现了一次(4个节点均不相同),若用BoND的思想,则可以区分出这两张图。
BoG(bag of graphlet)
这个时候,如果把两个图的
f
G
f_G
fG做数量积,即
K
(
G
,
G
′
)
=
f
G
T
f
G
′
K(G,G')=f_G^T f_{G'}
K(G,G′)=fGTfG′,即可反应出这两张图的相似度。当然,如果两张图的数量差异非常大,例如一个是北京地铁网络,一个是阿里地区车流网络,那么即使相似,这个数量积也非常大。因此,一般需要对
f
G
f_G
fG做一个归一化处理。
h
G
=
f
G
∑
f
G
K
(
G
,
G
′
)
=
h
G
T
h
G
′
h_G=\frac{f_G}{\sum f_G}\\ K(G,G')=h_G^T h_{G'}
hG=∑fGfGK(G,G′)=hGThG′
但是如果真有一张巨大的节点数为n的网络图,做这一个k-size的子图匹配计算是非常不划算的,因为需要对全图的节点做子图匹配,计算复杂度是 n k n^k nk
BoC(Weisfeiler-Lehman Kernel)
这是一个基于颜色微调(color refinement)的方法。
假设初始节点是1,那么经过color refinement和hash map过程,可以变成最下方的形式。在这种情况下,1,1对应2,1,11对应3,1,111对应4,1,1111对应5。当然,这幅图可以重复这个过程,再重复一次,2,4对应6,2,5对应7,3,44对应8以此类推。然后就可以把图变成一个13维向量。
对于左侧的图:[6,2,1,2,1,0,2,1,0,0,2,1,0]
对于右侧的图:[6,2,1,2,1,1,1,0,1,1,1,0,1]
PageRank,线性代数在图信息挖掘中的经典实践
配套代码:NetworkX入门及实战教程-计算节点特征-PageRank
迭代求解线性方程组
PageRank中有一个很重的概念,叫做出入度。用一个通俗的例子解释,A写论文引用了B的文章,那么A->B就构成了一条指向关系。对于B,A就是一个入方向;对于A,B就是一个出方向。在这种概念前提下,可以把节点的重要度用一个传递的方式进行表示。假设A和B同时引用了C的文章,那么C的重要度就应该是A和B共同贡献的。用下图做一个具体计算示例:
计算j节点的PageRank值,需要通过指向j节点的i和k节点。由于i一共有3条指向别人的边,k一共有4条,因此j的重要度计算为:
r
j
=
r
i
3
+
r
k
4
r_j=\frac{r_i}{3}+\frac{r_k}{4}
rj=3ri+4rk
在论文中有这么一个例子:
用同样的方法可以得到ry,ra,rm的计算表达,但是为了计算,需要规定ra+ry+rm=1这样一个前提条件(高斯求解)。这样,节点的重要度就变成了线性方程组的求解问题(n元一次方程组)。但是如果这个图很大,有数千万上亿的节点,每一个节点都要这么计算,那么计算量大不说,同样不具备可拓展性,即每次都需要重新统计,这也是PageRank的一个弊端。在知晓这样一个前提后,我们可以进行多轮迭代计算:
4个节点,我们把他们的重要度都初始化为1/4,满足ra+rb+rc+rd=1。以第一轮为例,由于只有C节点指向A,因此A的PageRank值被更新为
1
4
3
\frac{\frac{1}{4}}{3}
341,B节点同时具有A和C两个节点的指向,并且A一共有2个指向关系,C一共有3个指向关系,因此B的PageRank值被更新为
1
4
2
+
1
4
3
=
2.5
12
\frac{\frac{1}{4}}{2}+\frac{\frac{1}{4}}{3}=\frac{2.5}{12}
241+341=122.5,C节点被AD节点指向,因此C的PageRank可以计算
1
4
2
+
1
4
1
=
4.5
12
\frac{\frac{1}{4}}{2}+\frac{\frac{1}{4}}{1}=\frac{4.5}{12}
241+141=124.5,D节点为
1
4
3
+
1
4
1
=
4
12
\frac{\frac{1}{4}}{3}+\frac{\frac{1}{4}}{1}=\frac{4}{12}
341+141=124。
用这样的方式多轮迭代,实际上迭代到第二轮,也可以发现一些结论,节点C是最重要的,节点A是最不重要的。可能节点A只是一个研究生,而C是一个知名教授。
迭代左乘M矩阵
上述的步骤是从出入度的角度进行分析,实际上,个线性方程组的求解过程完全可以用矩阵幂计算来简化。这样可以更好解释和计算迭代递归的过程。
我们用u表示迭代的结果,那么
u
i
=
M
u
i
−
1
\bold u_i= \bold M \bold u_{i-1}
ui=Mui−1
对于无穷次迭代结果,可以表示为:
u
=
M
(
M
(
.
.
.
M
(
M
u
)
)
)
\bold u= \bold M(\bold M(...\bold M(\bold M \bold u)))
u=M(M(...M(Mu)))
在线性代数中,如果一个矩阵可以写成:
A
α
=
λ
α
A \alpha = \lambda \alpha
Aα=λα
那么
α
\alpha
α被称作A的特征向量,
λ
\lambda
λ被称作A的特征值。在这里
M
u
=
1
u
\bold M \bold u=1 \bold u
Mu=1u
此时,u就变成了M的主特征向量,特征值为1。
对于Column Stochastic矩阵,由Perreon-Frobenius定理,可以知道最大特征值为1,存在唯一的主特征向量,向量所有元素和为1。
随机游走 & 马尔可夫链
假设有一个小人按照这个有向图随机游走,那么只要足够有耐心,每次访问到一个新节点,他的计数就加1,那么最终这个访问量的概率分布,就是PageRank值。
迭代左乘M矩阵求解PageRank
迭代运算的数学表达。
1、初始化u矩阵。
u
(
0
)
=
[
1
/
N
,
…
,
1
/
N
]
T
\bold u^{(0)}=[1/N,\dots,1/N]^T
u(0)=[1/N,…,1/N]T
2、迭代运算。
u
(
t
+
1
)
=
M
⋅
u
(
t
)
\bold u^{(t+1)}=\bold M \cdot \bold u^{(t)}
u(t+1)=M⋅u(t)
3、停止迭代判断。
∑
i
∣
u
i
t
+
1
−
u
i
t
∣
<
ϵ
\sum_i |u_{i}^{t+1}-u_i^t|<\epsilon
∑i∣uit+1−uit∣<ϵ
迭代计算收敛的判断中:
∑
i
∣
u
i
t
+
1
−
u
i
t
∣
<
ϵ
\sum_i |u_{i}^{t+1}-u_i^t|<\epsilon
i∑∣uit+1−uit∣<ϵ
可以对当前结果和上一步结果做差值计算,如果小于设定的阈值则代表收敛。这里的差值可以逐元素做差平方求和(L2范数)也可以逐元素做差绝对值求和(L1范数)。
关于收敛性这部分,存在如下问题:
Q:能否稳定收敛而不发散?不同的初始值是否收敛至同一结果?
A:存在定理,对于一个马尔可夫链,若要稳定收敛,则需要保证两个条件。其一是不可约(irreducible),其二是非周期(aperiodic)。
首先来看是否约束。节点的约束指的是不同节点之间是不是各玩各的,例如A课题组内部互相引用论文,B课题组内部也互相引用论文,A和B两个课题组各玩各的,互不影响,这种就是reducible。很显然,我们在这里讨论的网络都是互相可以查询到关系的,即使不是相邻节点,也可以通过网络图产生远距离的联系。因此满足第一个条件。
再来看周期性。这里的周期性可以理解成节点之间的关系是同时且周期变化的,例如A今天看10本书,B今天看5本书,明天A看B看的5本,B看A看的10本,第三天再交换。即同步、周期变化。很显然我们讨论的节点关系并不是这种互相串门的链。因此存在唯一稳定解且一定可以收敛到稳定解。
至于是否是需要的结果,这里可以用两个极端的例子做分析。第一个例子是“爬虫”节点。例如我从微信上看到了别人分享给我的抖音的视频,然后我就去抖音一直看啊看,然后我分析给我的朋友,他们也来抖音一直看啊看,渐渐的所有的人都住在抖音了。那么初始化重要度可能是[1,0],最终节点的重要度[0,1],抖音是1,其他全是0,并且无穷此迭代后依然是[0,1]。第二个例子是“死胡同”节点。例如有一个比特币的销毁黑洞地址,人们只要把比特币打入这个地址,就会立马消失,在这种情况下,初始化[1,0],一轮迭代后(发送到黑洞地址),变成[0,1],再次迭代后变成[0,0]。
对于这两种情况,都无法收敛。主要原因在于,他不符合Column Stochastic矩阵,即每一列求和都是1。针对这种情况,需要设置一个跳转的功能,即陷在一个节点(抖音)里,可能会随机跳转到其他节点。实现起来也非常简单,只需要改写M矩阵即可。
r
j
=
∑
i
−
>
j
β
r
i
d
i
+
(
1
−
β
)
1
N
r_j = \sum_{i->j} \beta \frac{r_i}{d_i}+(1-\beta)\frac{1}{N}
rj=i−>j∑βdiri+(1−β)N1
这里
β
\beta
β为1则不存在跳转选项。
当然也可以用M矩阵表示:
G
=
β
M
+
(
1
−
β
)
[
1
N
]
N
∗
N
u
=
G
⋅
u
G=\beta M + (1-\beta)[\frac{1}{N}]_{N*N} \\ u = G\cdot u
G=βM+(1−β)[N1]N∗Nu=G⋅u
具体计算示例如下:
图嵌入:随机游走 vs 矩阵分解 vs 人工特征
这一部分接上文,应该也是属于传统图机器学习部分,但是由于其思想非常重要,因为无论是图机器学习还是图神经网络,都需要把图数据映射成一个低维连续稠密向量,也就是node embedding过程,可以说模型的好坏一切皆基于它。因此单独作为一个章节重点梳理。
Node embedding的出现,表示人工的特征工程逐渐退出历史舞台,一种图表示,随机游走的方法逐渐进入人们视野。在同一个随机游走序列中共同出现的节点,视为相似节点,从而构建类似Word2Vec的自监督学习场景。衍生出DeepWalk、Node2Vec等基于随机游走的图嵌入方法。从数学上,随机游走方法和矩阵分解是等价的。进而讨论嵌入整张图的方法,可以通过所有节点嵌入向量聚合、引入虚拟节点、匿名随机游走等方法实现。
随机游走的方法是DeepWalk一文率先提出,后node2vec继续改进,建议先精读DeepWalk、Node2Vec的论文。而Node2Vec在一定程度上和Word2Vec有相似之处,如果只想学习图网络,可以直接看我NLP从0开始一文,自然语言转化为机器语言-编码章节部分浅做了解。
具体逐字逐句的论文精读和代码实战可以在前面配套连接跳转阅读。
直推式学习:标签传播,半监督节点分类
这部分是图学习向图深度学习过度的部分。在图深度学习的相关论文中,经常可以看到半监督学习(semi-supervised node classification)出现在里面(当然,出现的原因就是因为被新的算法吊打)。所以作为一个论文出现的常客,在这里依然做一个梳理。
首先构建这样一个场景。假设知道了ABC和LMN六个节点的属性,例如ABC承担功能x,LMN承担功能y,那么EFGHIJK节点他们的功能是什么呢?如何利用ABC和LMM这样有标签的节点去推理得到未知节点的标签,就是半监督节点分类想要完成的东西。
至于为什么GNN论文经常出现它的身影,我想主要是因为他们的工作目的是类似的,但是工作方式完全不同。具体体现在,半监督学习依然属于机器学习范畴,它是一种根据已知推理未知的预测方式;深度学习则是直接把预测节点的信息丢进网络跑一次前向传播,即可自动分类,这是一种归纳式的学习。
作为机器学习,绕不开的就是特征向量。通过之前的内容,我们知道可以通过人工特征工程和图嵌入来实现。在这里,它使用的是第三种思路,标签传播(信息传递),它甚至根本不需要图嵌入,不需要表示学习,只利用节点的属性特征就可以进行。第四种方式则是深度学习。
在正式开始之前,有必要做一个直观的对比。
(1)人工特征工程:通过数学计算得到节点重要度、集群系数、拓扑结构等,并且揉成一个向量作为特征表示;
(2)随机游走方法:代表算法有DeepWalk,Node2Vec,LINE,SDNE等。构造自监督表示学习模型(skip-gram)实现图信息的融合。无法泛化到新节点。
(3)标签传播:代表算法有Label Propagation,Iteratinve Classification,Belief Propagation,Correct & Smooth等。假设物以类聚、人以群分。利用邻域节点类别猜测当前节点类别。无法泛化到新节点。
(4)图神经网络:代表算法有GCN,GraphSAGE,GAT,GIN等。利用神经网络构造邻域节点信息聚合计算图,实现节点嵌入,可泛化到新节点。
因此,基于标签传播的半监督学习其实是独立于其他存在的一个“异类”。回到所谓标签传播,其实就是类似的节点会聚集出现,例如我们俩都喜欢玩王者荣耀,那我们俩作为游戏群体的节点会挨的比较近。这个时候有个人在我们俩附近,那他大概率也喜欢玩王者荣耀。
标签传播算法
配套代码:NetworkX入门及实战教程-社群检测
我们先假设一个节点二分类问题。即节点存在0和1两种类别。对于未知节点,我们标记为0.5。
此时,若要更新3的类别:
P
3
=
(
P
4
+
P
1
+
P
2
)
/
3
=
0.17
P_3=(P_4+P_1+P_2)/3=0.17
P3=(P4+P1+P2)/3=0.17
更新了3之后,4也可以更新为:
P
4
=
(
P
1
+
P
3
+
P
5
+
P
6
)
/
4
=
0.42
P_4=(P_1+P_3+P_5+P_6)/4=0.42
P4=(P1+P3+P5+P6)/4=0.42
一次类推,可以把整张图都更新一次:
接下去可以更新多轮,当数值波动小于0.01则认为收敛,最终结果:
我们可以认为大于0.5为1类,小于0.5为0类。这样就完成了消息传递的分类。可以发现,4号节点介于0和1之间,是最摸棱两可的,其他节点的结果都非常偏向于聚类结果。当然,如果这里的连接有权重,可以加权计算,过程一样。
但是这个算法存在如下问题:
1、不保证收敛。实际上,我们在进行这样(加权)计算的时候,就是不断的左乘邻接矩阵,如果邻接矩阵的特征值不在-1到1之间,那么结果就会发散。线代知识:对于对称矩阵和非对称矩阵中的可对角化对阵,最大奇异值(特征值)在-1到1之间,连续幂乘是收敛的,反之是发散的。
2、只用到了连接信息,没有用到节点的属性特征。
Iterative Classification(ICA算法)
由于标签传播特征没有用到节点的属性特征,因此可以进一步优化。ICA算法的核心思路是,把节点的属性特征用
f
v
f_v
fv向量表示,连接属性用
z
v
z_v
zv向量表示。至于这里的
z
v
z_v
zv如何表示,就是完全人工设定了,可以用邻接矩阵,可以用邻接节点最多类别的数量,可以用邻接节点的类别等等。接着用两个学习器
ϕ
1
ϕ
2
\phi_1 \phi_2
ϕ1ϕ2分别学习。其中
ϕ
1
\phi_1
ϕ1只使用节点的属性特征,
ϕ
2
\phi_2
ϕ2除了使用节点属性特征,还使用连接特征。
第一步:对于已标注好的节点训练
ϕ
1
\phi_1
ϕ1和
ϕ
2
\phi_2
ϕ2
第二步:用
ϕ
1
\phi_1
ϕ1训练未知节点得到
Y
v
Y_v
Yv,由于得到
Y
v
Y_v
Yv,因此可以计算
z
v
z_v
zv,再把
f
v
f_v
fv和
z
v
z_v
zv放到
ϕ
2
\phi_2
ϕ2学习器进行学习得到
ϕ
2
\phi_2
ϕ2预测的
z
v
z_v
zv。
第三步:把更新得到的
z
v
z_v
zv放到
ϕ
2
\phi_2
ϕ2学习器学习预测得到新的
Y
v
Y_v
Yv,再用更新好的
Y
v
Y_v
Yv放到
ϕ
2
\phi_2
ϕ2再次更新
z
v
z_v
zv周而复始直到收敛。
举个例子说明:
这里
ϕ
1
\phi_1
ϕ1训练出来的决策边界可能如由上图所示,此时中间节点会划分到0类,但如果中间节点是1类,那就错了。而且只靠
ϕ
1
\phi_1
ϕ1这个基学习器很难修正。因此需要引入
z
v
z_v
zv。
首先,我们用
ϕ
1
\phi_1
ϕ1预测的0类,计算得到
z
v
z_v
zv。
然后,基于
f
v
f_v
fv和
z
v
z_v
zv,我们用
ϕ
2
\phi_2
ϕ2预测,并且更新
f
v
f_v
fv和
z
v
z_v
zv
这个时候就能预测到1这个分类结果。
当然,这个算法也不能保证收敛,依然使用人工特征工程,相较于消息传递,只是考虑了更多的信息。也算是一个进步。
Correct & Smooth(C&S,一种后处理方法)
在节点预测时,通常遵循以下步骤:
第一步、训练一个基分类器。在已经有类别标注的节点上训练一个基础分类器,分类器不指定用什么,只要是一个分类的机器学习模型都可以。
第二步、用基分类器预测所有节点的预测结果。
至此,输出的结果,假如是一个二分类,那么就是(0.66,0.34)这样的一个二维向量,分别表示不同类别的概率。然后选择概率高的作为预测结果。但是C&S在这里进行了一些trick。
Correct
C&S把分类器输出的结果称作soft labels,我们举个例子:
在这里,6和8号节点,就属于比较模棱两可,分类器出错概率较大。5 7 9则看上去稳妥很多。之前提到过物以类聚人以群分的思想,在图网络中,这些摸棱两可的节点往往也是呈现一定聚集分布。以8号节点为例,如果0.6的概率是第二类,那么错误的概率则是0.4,C&S想要做就是让错误更加均匀的分布在各个节点,即分散不确定性和困惑度,把错误率大的节点分一些给错误率小的,或者把错误小的匀一些给错误大的容错。具体做法 如下图:
假设12已知2类,67已知是1类,那么可以得到他们的初始错误矩阵E0(Initial Training Error Matrix)。接下来,有点类似PageRank,就要把这个矩阵削峰填谷,分配到其他节点上,t+1时刻的错误是t时刻和t时刻传播过来的错误共同决定的:
E
(
t
+
1
)
⬅
(
1
−
α
)
⋅
E
(
t
)
+
α
⋅
A
~
E
(
t
)
E^{(t+1)}⬅(1-\alpha)\cdot E^{(t)}+\alpha \cdot \widetilde{A} E^{(t)}
E(t+1)⬅(1−α)⋅E(t)+α⋅A
E(t)
当
α
\alpha
α为1的时候,就是PageRank不考虑死胡同和黑洞情况下,连续迭代左乘M矩阵的算法,考虑
α
\alpha
α时,则类似Label Propagation为了满足Column Stochastic矩阵,改写M矩阵的形式,可以翻到上面PageRank部分对照学习。
因此,首先需要计算一个标准化的混淆矩阵
A
~
\widetilde{A}
A
(Normalized diffusion matrix):
A
~
≡
D
−
1
/
2
A
D
−
1
/
2
\widetilde{A} \equiv D^{-1/2}AD^{-1/2}
A
≡D−1/2AD−1/2
这里的D是degree matrix,除了主对角线外所有元素都是0,主对角线元素表示节点的连接度。这里的A是邻接矩阵,如果要考虑自己对自己的影响,主对角线为1即可。
这样的标准化混淆矩阵是有诸多好处的,与PageRank一样,最直观的好处在于,标准化后,特征值范围为[-1,1],连续迭代左乘
A
~
\widetilde{A}
A
矩阵后不会发散。
并且
A
~
\widetilde{A}
A
的最大特征值始终为1,证明过程如下:
A
~
D
1
/
2
1
=
D
−
1
/
2
A
D
−
1
/
2
D
1
/
2
=
D
−
1
/
2
A
1
=
D
−
1
/
2
D
1
=
1
⋅
D
1
/
2
1
\widetilde{A}D^{1/2}1=D^{-1/2}AD^{-1/2}D^{1/2}=D^{-1/2}A1=D^{-1/2}D1=1\cdot D^{1/2}1
A
D1/21=D−1/2AD−1/2D1/2=D−1/2A1=D−1/2D1=1⋅D1/21
经过三轮迭代后,可以发现错误已经被传播开。接下来只需要把这个错误矩阵加权,和之前的预测结果做一个加和即可,这里取加权2:
# 计算代码
import numpy as np
A = np.array([
[0,1,1,1,0,0,0,0,0],
[1,0,1,0,0,0,0,0,0],
[1,1,0,1,0,0,0,0,0],
[1,0,1,0,1,1,0,0,0],
[0,0,0,1,0,1,1,0,0],
[0,0,0,1,1,0,0,1,0],
[0,0,0,0,1,0,0,1,1],
[0,0,0,0,0,1,1,0,0],
[0,0,0,0,0,0,1,0,0]
])
D = np.array([
[1/3,0,0,0,0,0,0,0,0],
[0,1/2,0,0,0,0,0,0,0],
[0,0,1/3,0,0,0,0,0,0],
[0,0,0,1/4,0,0,0,0,0],
[0,0,0,0,1/3,0,0,0,0],
[0,0,0,0,0,1/3,0,0,0],
[0,0,0,0,0,0,1/3,0,0],
[0,0,0,0,0,0,0,1/2,0],
[0,0,0,0,0,0,0,0,1/1]
])
E = np.array([
[-0.05, 0.05],
[-0.3,0.3],
[0,0],
[0,0],
[0,0],
[0.4,-0.4],
[0.05,-0.05],
[0,0],
[0,0]
])
# 计算normalized diffusion matrix
dm = np.dot(np.dot(D**(0.5),A),D**(0.5))
# 迭代三次计算:
E0 = E
E1 = 0.2 * E0 + 0.8 * np.dot(dm, E0)
E2 = 0.2 * E1 + 0.8 * np.dot(dm, E1)
E3 = 0.2 * E2 + 0.8 * np.dot(dm, E2)
Smooth
Smooth过程和Correct唯一的区别在于把E矩阵改成了Correct之后的Z矩阵,即上图。但是把已知节点的类别用真实标签代替。
图卷积神经网络(GCN)
配套代码:PyG-GCN
最基础的图神经网络
在这部分,需要默认你对深度学习有最基础的认知,包括全连接线性网络(MLP基础)、常见的卷积网络(CNN基础)、常见的序列网络(RNN基础)等,不需要深入了解,但是需要明白常见的概念。从发展的角度看,最开始人们希望通过MLP,CNN和RNN的方式去实现图信息挖掘,但是直接搬过来的“拿来主义”并不能起到特别好的效果,这是因为图信息在某种程度上并不是一个一维数据,例如A->B->C,反过来C->B->A也有一定道理。在RNN中有双向RNN网络,可是图的信息又不完全呈线性。所以图神经网络核心思想,是在深度学习发展长河的基础上,加入前文提到的节点嵌入和信息传递实现。
具体的,假如这里有一张网络:
首先,根据词嵌入,我们可以整合节点的特征信息成一个固定维度的稠密向量;根据消息传递,A节点可以由B C D节点推断得到。同理,B节点可以由AC节点推断得到。如果要预测A节点,则只需要把BCD节点的信息融合放到一个MLP中,而BCD节点的信息可以通过他们的相邻节点通过一个MLP完成消息传递:
这是一个两层的图卷积网络示意。在进行信息融合的时候,通常采用平均,这样的好处是不限制输入顺序(这就一定程度上解决了A-B-C和C-B-A的尴尬),并且计算也比较简单,当然也可以换其他的方法,只要不限制顺序并且实现融合就是可行的。在每一层中,MLP是共享的。这就是最基础的图卷积网络。
它的数学表达如下:
1、最开始输入的是它的属性特征:
h
v
(
0
)
=
x
v
h_v^{(0)}=x_v
hv(0)=xv
2、进行平均过程:
h
v
(
k
+
1
)
=
σ
(
W
k
∑
u
∈
N
(
v
)
h
u
k
∣
N
(
v
)
∣
)
h_v^{(k+1)}=\sigma (W_k \sum_{u \in N(v)} \frac{h_u^{k}}{|N(v)|})
hv(k+1)=σ(Wku∈N(v)∑∣N(v)∣huk)
这里表示k+1层v节点的嵌入是由k层v节点的邻域节点u算出来的。首先找到v节点所有的邻居节点,例如C节点,就是ABEF四个节点,然后把ABEF四个节点上一层的嵌入求和再除以连接数4个,本质上就是求平均过程(
∑
u
∈
N
(
v
)
h
u
k
∣
N
(
v
)
∣
)
\sum_{u \in N(v)} \frac{h_u^{k}}{|N(v)|})
∑u∈N(v)∣N(v)∣huk))。然后新得到的向量输入到MLP网络中(
W
k
∑
u
∈
N
(
v
)
h
u
k
∣
N
(
v
)
∣
W_k \sum_{u \in N(v)} \frac{h_u^{k}}{|N(v)|}
Wk∑u∈N(v)∣N(v)∣huk),因为MLP网络本质上就是一个矩阵乘法,是一个权重再计算的过程,因此Wk表示的是MLP的权重矩阵。然后再加一个激活函数ReLU、tanh、Sigmoid等(
σ
(
W
k
∑
u
∈
N
(
v
)
h
u
k
∣
N
(
v
)
∣
)
\sigma (W_k \sum_{u \in N(v)} \frac{h_u^{k}}{|N(v)|})
σ(Wk∑u∈N(v)∣N(v)∣huk))。
3、最终得到输出结果:
z
v
=
h
v
K
z_v=h_v^K
zv=hvK
这里的K就是计算网络的层数,例如上图,K就等于2。那么输出的结果就是第二层的输出结果。
为了更方便计算,用矩阵表示如下:
再节点嵌入后,可以得到一个
Φ
\Phi
Φ表用来储存嵌入的向量。我们用H矩阵来表示这样一张表,H矩阵里每一个元素表示某一行的嵌入向量。
H
(
k
)
=
[
h
1
(
k
)
…
h
∣
V
∣
(
k
)
]
T
H^{(k)}=[h_1^{(k)} \dots h_{|V|}^{(k)}]^T
H(k)=[h1(k)…h∣V∣(k)]T
我们知道邻接矩阵储存了节点邻接节点的信息。因此我们可以左乘v节点的邻接矩阵,选出v节点邻接节点的嵌入向量并完成求和。
∑
u
∈
N
v
h
u
(
k
)
=
A
v
H
(
K
)
\sum_{u \in N_v}h_u^{(k)}=A_vH^{(K)}
u∈Nv∑hu(k)=AvH(K)
求平均的过程只需要除以连接数,Degree matrix正好储存了我们想要的信息。这样,上面复杂的求平均表达式用矩阵可以轻松表达:
∑
u
∈
N
(
v
)
h
u
k
∣
N
(
v
)
∣
)
=
D
−
1
A
v
H
(
K
)
\sum_{u \in N(v)} \frac{h_u^{k}}{|N(v)|})=D^{-1}A_vH^{(K)}
u∈N(v)∑∣N(v)∣huk)=D−1AvH(K)
这里的
D
−
1
A
D^{-1}A
D−1A被称作标准化行矩阵(Row Normalized Matrix),它的最大特征值为1。
还是用上图举例,用代码一步一步算一下,更加形象地展示过程:
import numpy as np
from scipy.linalg import fractional_matrix_power
# 创建A节点的邻接矩阵A
A = np.mat('0,1,1,1,0,0;1,0,1,0,0,0;1,1,0,0,1,1;1,0,0,0,0,0;0,0,1,0,0,1;0,0,1,0,1,0')
# 创建Degree Matrix D
D = np.mat('3,0,0,0,0,0;0,2,0,0,0,0;0,0,4,0,0,0;0,0,0,1,0,0;0,0,0,0,2,0;0,0,0,0,0,2')
# D 1/2
D_halg = fractional_matrix_power(D, 0.5)
# D -1/2
D_half_neg = fractional_matrix_power(D, -0.5)
# 计算Row Normalized Matrix
A_row = np.matmul(D**(-1),A)
>>matrix([[0. , 0.33333333, 0.33333333, 0.33333333, 0. ,0. ],
[0.5 , 0. , 0.5 , 0. , 0. ,0. ],
[0.25 , 0.25 , 0. , 0. , 0.25 ,0.25 ],
[1. , 0. , 0. , 0. , 0. ,0. ],
[0. , 0. , 0.5 , 0. , 0. ,0.5 ],
[0. , 0. , 0.5 , 0. , 0.5 ,0. ]])
在这里先暂停。我们已经计算得到了A的Row Normalized Matrix,但是可以发现这样实际上是一个简单粗暴的计算,只是单纯的按照自己的度,对所有渠道来的信息强行求平均,没有考虑到信息传递的权重。例如B是一个诺奖得主引用了A的论文,D只是A的一个研究生,那显然B对A的权重会极大于D。因此,这里可以使用之前C&S部分使用过的混淆矩阵标准化方法(
A
~
\widetilde{A}
A
):
A
~
s
y
m
=
D
−
1
/
2
A
D
−
1
/
2
\widetilde{A}_{sym}=D^{-1/2}AD^{-1/2}
A
sym=D−1/2AD−1/2
这个矩阵在很多论文都有提到,常见的有这三种表达:Normalized Adjacency Matrix,Normailized Diffusion Matrix, Symmetric Normalized Matrix,如果看到类似的表达记得反应过来,就是这个
A
~
\widetilde{A}
A
矩阵。
A_sym = D_half_neg @ A @ D_half_neg
matrix([[0. , 0.40824829, 0.28867513, 0.57735027, 0. ,0. ],
[0.40824829, 0. , 0.35355339, 0. , 0. ,0. ],
[0.28867513, 0.35355339, 0. , 0. , 0.35355339,0.35355339],
[0.57735027, 0. , 0. , 0. , 0. ,0. ],
[0. , 0. , 0.35355339, 0. , 0. ,0.5 ],
[0. , 0. , 0.35355339, 0. , 0.5 ,0. ]])
使用对称矩阵,既考虑了自己的度,也考虑了对方的度,并且最大特征值为1,不会产生梯度消失(证明过程参考C&S章节)。
至此,最开始的公式可以进一步优化
∑
u
∈
N
(
v
)
h
u
k
∣
N
(
v
)
∣
\sum_{u \in N(v)} \frac{h_u^{k}}{|N(v)|}
∑u∈N(v)∣N(v)∣huk:
∑
u
∈
N
(
v
)
h
u
k
∣
N
(
v
)
∣
=
A
~
s
y
m
H
(
k
)
=
D
−
1
/
2
A
D
−
1
/
2
H
(
k
)
\sum_{u \in N(v)} \frac{h_u^{k}}{|N(v)|} = \widetilde{A}_{sym}H^{(k)}=D^{-1/2}AD^{-1/2}H^{(k)}
u∈N(v)∑∣N(v)∣huk=A
symH(k)=D−1/2AD−1/2H(k)
在GNN论文中的经典公式也就非常容易理解:
H
(
l
+
1
)
=
σ
(
D
−
1
/
2
A
D
−
1
/
2
H
(
l
)
W
(
l
)
)
H^{(l+1)}=\sigma (D^{-1/2}AD^{-1/2}H^{(l)}W^{(l)})
H(l+1)=σ(D−1/2AD−1/2H(l)W(l))
即,第l+1层的隐向量等于l层的隐向量左乘一个
A
~
\widetilde{A}
A
再过一个MLP的权重,再经过一个激活函数。
计算图的改进
在基础的GNN网络中,A的隐向量是他的邻域节点计算得到,但实际上能够表示A信息最好的,就是A他本身。所以可以在第每层加入自己本身的向量进行融合。
在计算B的时候加入了B,在计算C的时候加入了C,在计算D的时候加入了D,另外额外增加了A本身。在这种情况下,邻接矩阵需要做相应的调整:
A
~
=
A
+
E
\widetilde{A} = A + E
A
=A+E
即,之前为0的主对角线元素全部加1。这就是图卷积网络发展到目前的最终形态。
这里的
A
~
\widetilde{A}
A
也就是论文里提到的:
H
k
+
1
=
σ
(
D
~
−
1
/
2
A
~
D
~
−
1
/
2
H
k
W
k
)
H^{k+1} = \sigma (\widetilde{D}^{-1/2} \widetilde{A} \widetilde{D}^{-1/2} H^kW^k)
Hk+1=σ(D
−1/2A
D
−1/2HkWk)
H
i
k
=
σ
(
∑
j
∈
{
N
(
i
)
∪
i
}
A
~
i
j
D
~
i
i
D
~
j
j
H
j
k
−
1
W
k
)
H^k_i=\sigma (\sum_{j\in\{N(i)\cup i\}} \frac{\widetilde{A}_{ij}}{\sqrt {\widetilde{D}_{ii}\widetilde{D}_{jj}}}H_j^{k-1}W^k)
Hik=σ(j∈{N(i)∪i}∑D
iiD
jjA
ijHjk−1Wk)
下面这个公式实际上就是换了个写法,因为如果i和j节点相连,那么他们的
D
−
1
/
2
D^{-1/2}
D−1/2其实就是取倒数再开根号,下面的表示形式。
当然还可以再优化,对于自己信息MLP融合而来的信息可以用一套权重
B
k
B_k
Bk,其他的用一套权重
W
k
W_k
Wk。
H
(
k
+
1
)
=
σ
(
D
−
1
/
2
A
D
−
1
/
2
H
(
k
)
W
k
T
+
H
(
k
)
B
k
T
)
H^{(k+1)}=\sigma ({D}^{-1/2}A{D}^{-1/2}H^{(k)}W_k^T+H^{(k)}B_k^T)
H(k+1)=σ(D−1/2AD−1/2H(k)WkT+H(k)BkT)
GNN损失函数
由于在其他文档并没有整理这部分内容,所以在这里做一个小小的整理。
回归问题:L2损失函数 & 分类问题:交叉熵损失函数
论文中经常提到一个词叫Objective function,这就是目标损失函数。一般化的表达是:
m
i
n
L
(
y
,
f
(
x
)
)
min \ \mathcal L(y,f(x))
min L(y,f(x))
即最小化预测值和真实值之间的差异。
如果是最简单的回归问题,我们可以用最简单的L2 loss。
L
(
y
,
f
(
x
)
)
=
∣
∣
y
−
f
(
x
)
∣
∣
2
\mathcal L (y,f(x))=||y-f(x)||_2
L(y,f(x))=∣∣y−f(x)∣∣2
分类问题最常用的是交叉熵损失函数(corss entropy)。这个在某度,某团,某巴巴的算法岗面试是重灾区。
交叉熵的计算公式是:
C
r
o
s
s
E
n
t
r
o
p
y
=
−
y
i
l
o
g
P
i
CrossEntropy=-y_ilogP_i
CrossEntropy=−yilogPi
其中
y
i
y_i
yi是真实标签值,
P
i
P_i
Pi是预测概率值。
训练样本集编号 | 类型 | 猫 | 狗 | 马 | 猪 | 交叉熵 |
---|---|---|---|---|---|---|
1 | 标签 | 0 | 1 | 0 | 0 | -log0.6 |
1 | 预测 | 0.2 | 0.6 | 0.1 | 0.1 | |
2 | 标签 | 0 | 0 | 1 | 0 | -log0.3 |
2 | 预测 | 0.1 | 0.5 | 0.3 | 0.1 | |
3 | 标签 | 1 | 0 | 0 | 0 | -log0.7 |
3 | 预测 | 0.7 | 0.1 | 0.1 | 0.1 |
我们希望这三个样本都可以预测正确,他的理论概率
P
=
0.6
∗
0.3
∗
0.7
=
0.126
P=0.6*0.3*0.7=0.126
P=0.6∗0.3∗0.7=0.126。很显然,P越大,预测的结果越好,模型也越好。
当然,当样本数量非常庞大,我们把这些概率连乘起来就会很小,因此往往用对数来表示。
l
o
g
P
=
l
o
g
0.6
+
l
o
g
0.3
+
l
o
g
0.7
logP=log0.6+log0.3+log0.7
logP=log0.6+log0.3+log0.7
在数学上,我们并不希望最大化一个优化函数,而希望最小化一个损失函数。我们希望logP最大化,实际上等价于希望-logP最小化。
训练GNN
有监督学习,可以参考其他有标注模型,计算交叉熵即可。在这里GNN的交叉熵损失函数表示如下:
L
=
∑
v
∈
V
y
v
l
o
g
(
σ
(
z
v
T
θ
)
)
+
(
1
−
y
v
)
l
o
g
(
1
−
σ
(
z
v
T
θ
)
)
\mathcal L=\sum_{v \in V}y_vlog(\sigma(z_v^T\theta))+(1-y_v)log(1-\sigma(z_v^T \theta))
L=v∈V∑yvlog(σ(zvTθ))+(1−yv)log(1−σ(zvTθ))
这里的
θ
\theta
θ是分类预测头的权重参数,
y
v
y_v
yv是节点标签类别,
z
v
T
z_v^T
zvT是节点嵌入向量。
图注意力机制
在NLP和CV中有一个非常著名的概念,叫做注意力机制。并且目前主流的顶分算法(NLP中的Transformer架构的GPT BERT等,CV中的MAE等)都能发现注意力机制的身影。所谓注意力机制,即通过某一种计算规则,可以发现输入的繁琐信息中哪些信息重要,哪些信息不重要,进而有偏有侧重的进行学习。
在图中,无非是发现哪些节点的连接重要。
GCN计算的通常方法:
h
i
=
σ
(
∑
W
∗
h
j
)
h_i=\sigma(\sum W*h_j)
hi=σ(∑W∗hj)
即,假设5个节点,每个节点嵌入维度为3,引入一个可学习的[3,8]的矩阵,则可以把嵌入信息更新为一个[5,8]矩阵。那么在这个基础上引入一个权重项即是注意力机制。
h
i
=
σ
(
∑
α
i
j
W
∗
h
j
)
h_i=\sigma(\sum \alpha_{ij}W*h_j)
hi=σ(∑αijW∗hj)
实际上,就是对邻接矩阵进行重构。最初,邻接矩阵都是1,代表每条边的权重一样。通过一个加权处理,实际上就可以计算得到不同的权重。
D
~
−
1
/
2
A
~
D
~
−
1
/
2
\widetilde{D}^{-1/2} \widetilde{A} \widetilde{D}^{-1/2}
D
−1/2A
D
−1/2本质上也是一种注意力机制。
图神经网络相比较传统方法的优点
DeepWalk,Node2Vec,PageRank的优化目标是一个
Φ
\Phi
Φ表,每次都需要维护这么一个庞大的编码表,工作量和参数量都非常大,并且这种直推式学习无法泛化到新节点,神经网络这种归纳式学习,可以泛化到新节点。
具体而言,在DeepWalk中,假如加入了一个新节点,虽然不需要全图重新训练,但是仍然需要增量训练。而图神经网络可以瞬间获得新节点的计算图,直接得到节点向量。此外,DeepWalk这种受到步长限制,对于长距离节点的关系捕捉不够敏锐,而神经网络可以捕捉长距离关系。
对于神经网络,最大的优点在于可以使用属性特征,可以用标注信息,实现多模态的计算。这是传统学习方法鞭长莫及的。
GraphSAGE,让GCN更像NLP和CV
配套代码:PyG-GraphSAGE和池化
整个GCN网络,其实可以归纳为一个公式:
H
k
+
1
=
σ
(
D
~
−
1
/
2
A
~
D
~
−
1
/
2
H
k
W
k
)
H^{k+1} = \sigma (\widetilde{D}^{-1/2} \widetilde{A} \widetilde{D}^{-1/2} H^kW^k)
Hk+1=σ(D
−1/2A
D
−1/2HkWk)
因此在介绍GraphSAGE前,根据这个公式,回顾一下GCN存在的问题。当然这里的问题不一定是GCN才出现的,可能是其他地方就有的问题,但是GCN没有解决或者没有考虑到。
1、GCN没有考虑边的权重。这个问题在Node2Vec解决DeepWalk时就有提到,当边固定权重时,就会忽略一些确信信息,放大一些偶然信息。
2、GCN是计算整张图的邻接矩阵,计算开销非常庞大。
在NLP中,输入模型的是一个又一个的句子,而不会输入整篇文章,CV中输入的是一张一张的图片,而不是全部的图。因此,GraphSAGE就想做这样一件事,他只考虑邻接k阶的节点。
在聚合部分,GraphSAGE采用了重采样/下采样的方法。(这里并没有考虑自相关)
这样,GraphSAGE通过一个简单的采样阶数即可控制输入网络的大小,把一整张图变成一幅一幅的子图,就好像一篇文章拆分成一句一句的句子。
TopKPooling
对输入的
H
k
H^k
Hk,可以通过一个可训练向量选择出最关键的K的节点。(图的剪枝操作)
第一步,计算得分值:
y
=
X
p
∣
∣
p
∣
∣
y=\frac{X_p}{||p||}
y=∣∣p∣∣Xp
第二步,选出Topk的值:
y
=
t
o
p
k
(
y
)
y = top_k(y)
y=topk(y)
第三步、得到筛选后的结果,并且做一个加权处理:
X
′
=
(
X
∘
t
a
n
h
(
y
)
)
i
X'=(X \circ tanh(y))_i
X′=(X∘tanh(y))i
第四步、更新邻接矩阵A:
A
′
=
A
i
,
i
A'=A_{i,i}
A′=Ai,i
图相似度分析
图相似度分析在结构分析、特征匹配、医学材料学等领域有广泛应用。简单来说,就是给了两个图,做一个匹配,看看相似度如何。
当图非常庞大后,储存的
Φ
\Phi
Φ表也会非常庞大,在这种情况下若要完成相似度计算,传统的拓扑方法、特征计算方法都非常困难,哪怕是对传统方法进行优化,仍然很难降低时间复杂度。随着GCN的问世,图神经网络也在发展。SimGNN就是把GCN作为基础,搭建的一个快速图相似度匹配的网络。
跳转阅读链接:SimGNN,图网络的标准处理流程+NTN/PNC实现快速图相似度匹配