可以看做是对这篇博客备忘:-----hyperLink-------
一个超容易理解的例子,
一个简单的有向图G:

考虑图G中各结点的出度,
结点0发出的边指向结点1,
结点1发出的边指向结点2与结点3,
结点2发出的边指向结点1,
结点3发出的边指向结点0与结点2,
该图的邻接矩阵A则表示如下,
A = np.matrix([
[0, 1, 0, 0],
[0, 0, 1, 1],
[0, 1, 0, 0],
[1, 0, 1, 0]],
dtype = float
)
接下来,根据每个结点的索引为每个结点生成两个整形特征,
X = np.matrix([
[i, -i]
for i in range(A.shape[0])
], dtype = float)
matrix([
[ 0., 0.],
[ 1., -1.],
[ 2., -2.],
[ 3., -3.]
])
现在我们有了图G,它的邻接矩阵A,以及结点的输入特征X,
接下来应用简单的传播规则,
A * X
matrix([
[ 1., -1.],
[ 5., -5.],
[ 1., -1.],
[ 2., -2.]]
从 A*X 的结果可以看出,
现在每个结点的特征表示(每一行)都是它们邻居结点的特征之和,
图卷积层将每个结点表示为它的邻居结点的聚合
我们可以发现,
1.目前结点的集成表示并不包含结点本身的特征,
2. 结点的度数大那么它的特征表示最后往往也大,这可能会造成梯度消失或爆炸
为了解决第一个问题,为图中每个结点添加自循环,
做法是在应用传播规则前将单位矩阵I添加到邻接矩阵A中
I = np.matrix( np.eye( A.shape[0] ) )
matrix([
[1., 0., 0., 0.],
[0., 1., 0., 0.],
[0., 0., 1., 0.],
[0., 0., 0., 1.]
])
A_hat = A + I
A_hat * X
matrix([
[ 1., -1.],
[ 6., -6.],
[ 3., -3.],
[ 5., -5.]])
添加了结点自循环后,
现在集成每个结点的邻居特征表示时,
每个结点本身的特征表示也包含在内了
解决第二个问题,可以用结点的度来正则化每个结点的特征
通过将邻接矩阵A与逆度矩阵D相乘可以转换邻接矩阵A,
传播规则则变为:
首先计算一下图的入度矩阵D,即考虑图中每个结点的入度
D = np.array( np.sum(A, axis=0) )[0]
D = np.matrix( np.diag( D ) )
matrix([
[1., 0., 0., 0.],
[0., 2., 0., 0.],
[0., 0., 2., 0.],
[0., 0., 0., 1.]
])
初始的邻接矩阵A
A = np.matrix([
[0, 1, 0, 0],
[0, 0, 1, 1],
[0, 1, 0, 0],
[1, 0, 1, 0]],
dtype=float
)
通过逆度矩阵将邻接矩阵A变换为
D**-1 * A
matrix([
[0. , 1. , 0. , 0. ],
[0. , 0. , 0.5, 0.5],
[0. , 0.5, 0. , 0. ],
[1. , 0. , 1. , 0. ]
])
可以看到邻接矩阵中每行的权重已除以该行所对应结点的度
继续传播规则
D**-1 * A * X
matrix([
[ 1. , -1. ],
[ 2.5, -2.5],
[ 0.5, -0.5],
[ 2. , -2. ]
])
从结果可以看出,每个结点的特征表示对应于其邻居结点的特征表示的均值
现在将以上整合,结合自循环与正则化方法,并引入先前省略的权重与激活函数
先计算下添加了自循环的邻接矩阵的度矩阵,
D_hat = np.array( np.sum( A_hat, axis=0 ) )[0]
D_hat = np.matrix( np.diag( D_hat ) )
matrix([[2., 0., 0., 0.],
[0., 3., 0., 0.],
[0., 0., 3., 0.],
[0., 0., 0., 2.]])
将之前省略的权重加上
W = np.matrix([
[1, -1],
[-1, 1]
])
D_hat**-1 * A_hat * X * W
matrix([
[ 1., -1.],
[ 4., -4.],
[ 2., -2.],
[ 5., -5.]
])
如果想要降低输出特征表示的维度,可通过操作权重矩阵W来实现
W = np.matrix([
[1],
[-1]
])
D_hat**-1 * A_hat * X * W
matrix([[1.],
[4.],
[2.],
[5.]]
)
将激活函数加上
这里保留了特征表示的维数,应用ReLU激活函数
W = np.matrix([
[1, -1],
[-1, 1]
])
relu( D_hat**-1 * A_hat * X * W )
matrix([[1., 0.],
[4., 0.],
[2., 0.],
[5., 0.]])
至此完成了一个带有邻接矩阵、输入特征、权重以及激活函数的完整的隐藏层