去今年刚出就买了. 一查豆瓣评分比我想的还低(我这种小白都能看出一些错误), 有1说1对于入门还是可以的, 至少能知道GNN大概的发展路线, 如图卷积 → \rightarrow GCN → \rightarrow GNN等. 如果小白直接上手GNN啥的, 连图滤波, 空域频域等概念都不知道, 也只能瞎搞.

其实看书很容易也很快, 难得是把参考文献里的论文看完, 精华都在论文里.

笔记

GNN

  • Laplasian矩阵
    • 先导知识
      • [[梯度、散度、旋度、Jacobian、Hessian、Laplacian]]
    • 用散度的概念解读
      • ∇ f = 0 \nabla f=0 f=0
        • 中心点 的势和其周围点的势是相等的, 局部范围内不存在势差。所以该点无源
      • ∇ f > 0 \nabla f>0 f>0
        • 可以近似认为中心点 的势低于周围点,可以想象成中心点如恒星一样发出能量,补给周围的点,所以该点是正源
      • ∇ f < 0 \nabla f<0 f<0
        • 可以近似认为中心点 的势高于周围点,可以想象成中心点如吸引子一样在吸收能量,所以该点是负源
    • 推导
      • 元素形式
        • Δ f i = ∑ j ∈ N i W i j ( f i − f j ) \Delta f_{i}=\sum_{j \in N_{i}} W_{i j}\left(f_{i}-f_{j}\right) Δfi=jNiWij(fifj)
        • Δ f i = ∑ j ∈ N w i j ( f i − f j ) \Delta f_{i}=\sum_{j \in N} w_{i j}\left(f_{i}-f_{j}\right) Δfi=jNwij(fifj)
          • = ∑ j ∈ N w i j f i − ∑ j ∈ N w i j f j =\sum_{j \in N} w_{i j} f_{i}-\sum_{j \in N} w_{i j} f_{j} =jNwijfijNwijfj
          • = d i f i − w i : f =d_{i} f_{i}-w_{i:} f =difiwi:f
      • 矩阵形式
        • Δ f = ( Δ f 1 ⋮ Δ f N ) = ( d 1 f 1 − w 1 : f ⋮ d N f N − w N : f ) \Delta f=\left(\begin{array}{c}\Delta f_{1} \\ \vdots \\ \Delta f_{N}\end{array}\right)=\left(\begin{array}{c}d_{1} f_{1}-w_{1:} f \\ \vdots \\ d_{N} f_{N}-w_{N:} f\end{array}\right) Δf=Δf1ΔfN=d1f1w1:fdNfNwN:f
        • = ( d 1 ⋯ 0 ⋮ ⋱ ⋮ 0 ⋯ d N ) f − ( w 1 : ⋮ w N : ) f =\left(\begin{array}{ccc}d_{1} & \cdots & 0 \\ \vdots & \ddots & \vdots \\ 0 & \cdots & d_{N}\end{array}\right) f-\left(\begin{array}{c}w_{1:} \\ \vdots \\ w_{N:}\end{array}\right) f =d100dNfw1:wN:f
        • = diag ⁡ ( d i ) f − W f =\operatorname{diag}\left(d_{i}\right) f-W f =diag(di)fWf
        • = ( D − W ) f =(D-W) f =(DW)f
        • = L f =L f =Lf
      • 拉普拉斯矩阵中的第 i i i行实际上反应了第 i i i个节点在对其他所有节点产生扰动时所产生的增益累积。
      • 图拉普拉斯反映了当我们在节点 i i i上施加一个势,这个势以哪个方向能够多顺畅的流向其他节点。
    • TV(x) , Total Variation
      • T V ( x ) = x T L x = Σ e i j ∈ E ( x i − x j ) ² TV(x)=x^TLx=\Sigma_{e_{ij}\in E}(x_i-x_j)² TV(x)=xTLx=ΣeijE(xixj)²
      • 刻画了图信号的整体平滑度
      • laplacian矩阵二次型
      • 对总变差进行改写
        • T V ( x ) = x T L x = x T V Λ V T x TV(x)=x^TLx=x^TV\Lambda V^Tx TV(x)=xTLx=xTVΛVTx
        • x = V x ^ x=V\hat{x} x=Vx^
        • x ^ = V T x \hat{x}=V^Tx x^=VTx
        • T V ( x ) = x ^ T Λ x ^ = Σ k N λ k x ^ k ² TV(x)=\hat{x}^T\Lambda \hat{x}=\Sigma_k^N \lambda_k \hat{x}_k² TV(x)=x^TΛx^=ΣkNλkx^k²
      • 总变差是图所有特征值的一个线性组合
        • 权重是图信号对应fourier系数的平方
  • 图滤波器
    • 公式与推导
      • y = H x = Σ k = 1 N ( h ( λ k ) x ^ k ) v k y=Hx=\Sigma^N_{k=1}(h(\lambda_k)\hat{x}_k)v_k y=Hx=Σk=1N(h(λk)x^k)vk
      • H = V Λ h V T H=V\Lambda_hV^T H=VΛhVT
        • 相比与laplacian矩阵, H只改动了对焦线上的值, 故其所有定义与laplacian矩阵相同
      • Λ h \Lambda_h Λh图滤波器H的频率响应矩阵
    • 性质
      • 线性
        • H ( x + y ) = H x + H y H(x+y)=Hx+Hy H(x+y)=Hx+Hy
      • 顺序无关
        • H ₁ ( H ₂ ( x ) ) H₁(H₂(x)) H(H(x))
      • 可逆
        • 如果 h ( λ ) ≠ 0 h(\lambda)\neq 0 h(λ)=0
    • 泰勒展开
      • H = Σ k = 0 K h k L k H=\Sigma_{k=0}^Kh_kL^k H=Σk=0KhkLk
    • 空域角度
      • y = H x = Σ k = 0 K h k L k x y=Hx=\Sigma_{k=0}^Kh_kL^k x y=Hx=Σk=0KhkLkx
      • 性质
        • 局部性
          • 每个节点的输出信号值只需要考虑K阶子图
        • 可通过K步迭代的矩阵乘法完成滤波
    • 频域角度
      • y = H x = V ( Σ k = 0 K h k Λ k ) V T x y=Hx=V(\Sigma_{k=0}^Kh_k\Lambda^k)V^Tx y=Hx=V(Σk=0KhkΛk)VTx
      • 性质
        • 需要做特征分解, 与空域的矩阵乘法相比有工程上的局限性
  • GCN
    • 图卷积等价于图滤波
      • 空域视角: 引入一个自适应图位移算子, 完成对输入图信号的变换操作
      • 频域视角: 自适应的图滤波器, 频率响应函数
    • 问题
      • 学习参数=节点数, 容易过拟合
      • 信息蕴含在低频段
    • 卷积层定义
      • K=1
      • X ′ = σ ( L s y m X W ) X^{\prime}=\sigma(L_{sym}XW) X=σ(LsymXW)
      • L s y m ∈ ( − 1 , 1 ] L_{sym}\in (-1,1] Lsym(1,1] 防止梯度消失爆炸
      • 等价于对邻居节点的特征响亮进行聚合操作
      • 时间复杂度:
        • O ( ∣ E ∣ d ) O(|E|d) O(Ed)
    • 性质
      • 与CNN的区别与联系
        • 1.图像是一种特殊的图数据
        • 2.局部连接
          • 但GCN权重参数只有1组, CNN的权重参数为卷积核size
          • (在我看来GCN根本没有权重参数, 边权是预设的)
        • 3.二者卷积核权重是处处共享的
        • 4.感受野随着卷积层增加而变大
      • 图数据的信息刻画
        • 属性信息
        • 结构信息
      • 非端到端的传统图数据学习方法
        • 手工特征
          • 节点的度
          • 中心度
          • PageRank值
        • 随机游走
      • GCN能解决图同构问题(graph isomorphism problem)
        • Weisfeiler-Lehman
        • GIN(Graph Isomorphism Network)
          • https://arxiv.org/abs/1810.00826
          • How Powerful are Graph Neural Networks?
      • GCN是个低通滤波器
        • 半监督学习中, 需要在损失函数中加个正则项
          • L r e g = Σ e i j ∈ E A i j ∣ ∣ f ( x i ) − f ( x j ) ∣ ∣ ² = f ( X ) T L f ( X ) \mathcal{L}_{reg}=\Sigma_{e_{ij}\in E}A_{ij}||f(x_i)-f(x_j)||²=f(X)^TLf(X) Lreg=ΣeijEAijf(xi)f(xj)²=f(X)TLf(X)
            • laplacian的二次型
          • 希望相邻结点分类标签尽量一致
        • GCN不需要正则项
          • 重归一化的laplasian矩阵的频率响应函数 p ( λ ) = 1 − λ ^ ∈ ( − 1 , 1 ] p(\lambda)=1-\hat{\lambda}\in (-1,1] p(λ)=1λ^(1,1]
          • p ( λ ) p(\lambda) p(λ)是一个线性收缩函数
          • 直接将多层GCN退化为 σ ( L k X W ) \sigma(L^kXW) σ(LkXW)
      • 低频信号包含对学习更有效的信息
        • Revisiting Graph Neural Networks: All We Have is Low-Pass Filters
        • https://arxiv.org/abs/1905.09550
        • https://www.cnblogs.com/liuxiangyan/p/12669728.html
      • 容易造成的问题: 过平滑(over smooth)
        • 所有节点信号趋同
        • 解决方法
          • 空域视角
            • 保留每层输出,最后进行聚合
              • 拼接
              • 平均池化
              • 最大池化
              • LSTM
            • Representation Learning on Graphs with Jumping Knowledge Networks
            • https://arxiv.org/abs/1806.03536
          • 频域视角
            • 重新分配权重
              • A i j ′ = { p / ∑ j = 1 C A i j ,  if  i ≠ j 1 − p ,  if  i = j \boldsymbol{A}_{i j}^{\prime}=\left\{\begin{array}{ll}p / \sum_{j=1}^{C} \boldsymbol{A}_{i j}, & \text { if } i \neq j \\ 1-p, & \text { if } i=j\end{array}\right. Aij={p/j=1CAij,1p, if i=j if i=j
              • p → 1 p\rightarrow1 p1
                • 模型趋向于不使用自身信息
                • 加速低通滤波效应
              • p → 0 p\rightarrow0 p0
                • 模型趋向于不聚合邻居信息
                • 缓解低通滤波效应
            • Multi-Label Image Recognition with Graph Convolutional Networks
            • https://arxiv.org/abs/1904.03582
            • https://github.com/Megvii-Nanjing/ML-GCN
  • GNN
    • GraphSAGE
      • Inductive Representation Learning on Large Graphs
      • https://arxiv.org/abs/1706.02216
      • implement
        • https://github.com/twjiang/graphSAGE-pytorch
        • https://github.com/dsgiitr/graph_nets
      • 两种学习方法
        • 归纳学习 (Inductive Learning)
          • 没有Laplasian矩阵的参与, 每个节点的特征学习仅与k阶邻居有关
          • 对训练阶段见不到的数据进行直接预测而不用重新训练
        • 转导学习 (transductive learning)
          • deepwalk
          • 图半监督算法
          • lda2vec
    • GAT
      • Graph Attention Networks
      • https://arxiv.org/abs/1710.10903
      • 保留了非常完整的局部性, 一样能进行归纳学习
    • R-GCN
      • Modeling Relational Data with Graph Convolutional Networks
      • https://arxiv.org/abs/1703.06103
    • DeepWalk
      • DeepWalk: Online Learning of Social Representations
      • https://arxiv.org/abs/1403.6652
代码

https://github.com/FighterLYL/GraphNeuralNetwork

chapter 5 | GCN

分析

train_index.shape
Out[30]: (140,)
val_index.shape
Out[31]: (500,)
test_index.shape
Out[32]: (1000,)
x = dataset.x / dataset.x.sum(1, keepdims=True)  # 归一化数据,使得每一行和为1

x是每个节点(论文文本)的特征, 词带特征

Laplasian矩阵的规范化

L = D − 1 2 ( A + I ) D − 1 2 L=D^{-\frac{1}{2}}(A+I)D^{-\frac{1}{2}} L=D21(A+I)D21

def normalization(adjacency):
    """计算 L=D^-0.5 * (A+I) * D^-0.5"""
    adjacency += sp.eye(adjacency.shape[0])    # 增加自连接
    degree = np.array(adjacency.sum(1))
    d_hat = sp.diags(np.power(degree, -0.5).flatten())
    return d_hat.dot(adjacency).dot(d_hat).tocoo()

最后的操作tocoo()是把csr变成coo

TODO

kaiming_uniform_ 是否合理

数据集切分, 训练集测试集都在一个图上(信息可传递, 共享) 是否会造成数据泄漏

之考虑词袋特征, 表现如何? 只考虑图结构特征, 表现如何?

改代码得到的结论

论文引用关系实质上是有向图, 数据处理时改为了无向图. 我将其改为有向图后, 无明显变化, 除了学习曲线有些波动

这图貌似就是无向图, 改代码没用

graph[999]
Out[2]: [1358, 346]
graph[346]
Out[3]: [1358, 999]

chapter 6 | GraphSage

分析

sampling.multihop_sampling

len(sampling_result[0])
Out[3]: 16
len(sampling_result[1])
Out[4]: 160
batch_sampling_x[2].shape
Out[9]: torch.Size([1600, 1433])

16表示batch, 10 表示采样数

self.gcn
Out[11]: 
ModuleList(
  (0): SageGCN(
    in_features=1433, out_features=128, aggr_hidden_method=sum
    (aggregator): NeighborAggregator(in_features=1433, out_features=128, aggr_method=mean)
  )
  (1): SageGCN(
    in_features=128, out_features=7, aggr_hidden_method=sum
    (aggregator): NeighborAggregator(in_features=128, out_features=7, aggr_method=mean)
  )
)

NeighborAggregator 可以理解为对所有邻居进行了聚合然后做仿射变换

src_node_features.shape
Out[17]: torch.Size([16, 1433])
neighbor_node_features.shape
Out[18]: torch.Size([16, 10, 1433])

SageGCN层与子结构NeighborAggregator 的weight不共享

hidden[0].shape
Out[26]: torch.Size([16, 1433])
hidden[1].shape
Out[27]: torch.Size([160, 1433])
hidden[2].shape
Out[28]: torch.Size([1600, 1433])
next_hidden[0].shape
Out[30]: torch.Size([16, 128])
next_hidden[1].shape
Out[31]: torch.Size([160, 128])
next_hidden[0].shape
Out[36]: torch.Size([16, 7])

如码所示, 最后得到了这批batch 16个样本(图结点)的logits

需要注意的点:

  1. 聚合操作, 对以目标点为弧尾的点进行聚合
  2. 重要的超参有聚合邻居方法聚合隐层方法.
  • 聚合邻居方法
    • mean
    • sum
    • max
  • 聚合隐层方法
    • sum
    • concat

Test Accuracy: 0.7470000386238098

TODO

改写代码, 比较使用其他聚合方法的区别