Convolutional Neural Networks on Graphs with Fast Localized Spectral Filtering论文解读( and code)

4 篇文章 1 订阅
1 篇文章 0 订阅

 

《Convolutional Neural Networks on Graphs with Fast Localized Spectral Filtering》 提供了已实现的GCN,并且针对 《Spectral Networks and Deep Locally Connected Networks on Graphs》存在的问题:1 计算复杂度高 2 filter并不局部 做出了相应的改进。学习GCN避免不了查看该篇文章。 

目录

1 论文贡献:

2 filter and pooling推广

2.1 卷积滤波器 filter

2.2 图池化

2.3 整个GCN过程

3. GCN代码解析

3.1 如何使用GCN -- by MNIST示例

3.2 图卷积具体刨析



1 论文贡献:

a 定义并实现了一个图谱域的卷积公式。

b 所定义的卷积公式严格局部定位,PS他的感受域可以理解为一个直径K的球。

c 计算复杂度低,滤波器复杂度线性。

d 一个的图池化

e 以上都有实现证明,代码公开哦。

2 filter and pooling推广

在graph上推广 CNN需要 1 在graph上设计卷积滤波器,2图的池化(图粗化).

2.1 卷积滤波器 filter

卷积公式(不清楚的话参照url: )

改进变化如下图,左为《Spectral Networks and Deep Locally Connected Networks on Graphs》

右为《Convolutional Neural Networks on Graphs with Fast Localized Spectral Filtering》

改进简单说就是将卷积核(or滤波器)的表示函数(可理解为参数θ左)换位多项式函数h(λ)右。

上图第一行左表示卷积核(or滤波器)中的单个单元(PS:在实现时,该单个单元用一个参数θ代表了一个函数,是一种”粗暴近似“,θ可学习)。

第一行右表示卷积核中的单个单元(PS:在实现时,该单个单元实际为多项式函数,α可学习,λ为Laplacian特征值)

第二行表示卷积整个过程的变化(即把改变的卷积滤波器带入整个卷积公式)。

第三行表示卷积核所有单元的变化(即把卷积核中的所有单元替换后的公式形式)。

第四行表示卷积整个过程的公式简洁形式。

PS:不清楚可以借助后文代码解析理解。

2.2 图池化

上图左部分表示使用METIS算法粗化图的方法(借助顶点、顶点之间的关系-边等将图简化),

每级粗化我们可以得到一个粗化图(g0,g1,g2,g3,g4),将该粗化图添加上一些辅助节点(上图右部分上,蓝色圆),使粗化图中每个顶点都有2个孩子,从而构成一个二叉树,将二叉树摊平构成一维信号(上图右部分下),然后对该信号采样即可表示为一次图pooling。这部分作者借助的是之前的工作,我没怎么仔细看,只是大致看代码了解这个构建二叉树于与摊平操作是依靠METIS时的粗话图之间的关系。

2.3 整个GCN过程

 

3. GCN代码解析

这部分是我最想写的部分,因为之前的太过枯燥,全是公式啥的。

这里分为两部分,1 分析提供的MNIST代码让你明白GCN大致如何用。2 图卷积如何实现的。

3.1 如何使用GCN -- by MNIST示例

本来作者提供了一个使用说明usage.py,但是他是随机构建了数据来解释,这样不免导致难以理解关系,所以这没解释usage.py部分,如需要,推荐阅读 https://blog.csdn.net/duyue3052/article/details/82315463 ,包含了中间数据的直观展示。

作者提供的MNIST包含多个模型对比,我这里直接抽出作者的经典GCN相关,其他除去,方便专注。

mnist.py

# -*- coding: utf-8 -*-

import sys, os
sys.path.insert(0, '..')
from lib import models, graph, coarsening, utils
import tensorflow as tf
import numpy as np
import time

# %matplotlib inline
flags = tf.app.flags
FLAGS = flags.FLAGS
flags.DEFINE_integer('number_edges', 8, 'Graph: minimum number of edges per vertex.')
flags.DEFINE_string('metric', 'euclidean', 'Graph: similarity measure (between features).')  #相似度测量
flags.DEFINE_bool('normalized_laplacian', True, 'Graph Laplacian: normalized.')
flags.DEFINE_integer('coarsening_levels', 4, 'Number of coarsened graphs.')
flags.DEFINE_string('dir_data', os.path.join('..', 'data', 'mnist'), 'Directory to store data.')



"""# Feature graph 图结构描述,即准备邻接矩阵A的拉普拉斯L"""

def grid_graph(m, corners=False):
    
    z = graph.grid(m) #该函数说白了就产生一个28*28网格的每个点的坐标
    dist, idx = graph.distance_sklearn_metrics(z, k=FLAGS.number_edges, metric=FLAGS.metric) #顶点K邻近点计算
    A = graph.adjacency(dist, idx) #构建表示图的邻接矩阵 A

    # Connections are only vertical or horizontal on the grid. 网格上的连接只有水平或者垂直
    # Corner vertices are connected to 2 neightbors only. 转角处的顶点只与两个邻接点连接
    if corners:
        import scipy.sparse
        A = A.toarray()
        A[A < A.max()/1.5] = 0
        A = scipy.sparse.csr_matrix(A)
        print('{} edges'.format(A.nnz))

    print("{} > {} edges".format(A.nnz//2, FLAGS.number_edges*m**2//2))
    return A

t_start = time.process_time()
A = grid_graph(28, corners=False) # "邻接矩阵"
A = graph.replace_random_edges(A, 0) #"添加噪声的邻接矩阵"
graphs, perm = coarsening.coarsen(A, levels=FLAGS.coarsening_levels, self_connections=False) #粗化图
L = [graph.laplacian(A, normalized=True) for A in graphs] #对邻接矩阵进行拉普拉斯变换
print('Execution time: {:.2f}s'.format(time.process_time() - t_start))
#graph.plot_spectrum(L)
del A


"""# Data 准备"""
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets(FLAGS.dir_data, one_hot=False)

train_data = mnist.train.images.astype(np.float32)
val_data = mnist.validation.images.astype(np.float32)
test_data = mnist.test.images.astype(np.float32)
train_labels = mnist.train.labels
val_labels = mnist.validation.labels
test_labels = mnist.test.labels

t_start = time.process_time()
train_data = coarsening.perm_data(train_data, perm)
val_data = coarsening.perm_data(val_data, perm)
test_data = coarsening.perm_data(test_data, perm)
print('Execution time: {:.2f}s'.format(time.process_time() - t_start))
del perm

"""# Neural networks"""

common = {}
common['dir_name']       = 'mnist/'
common['num_epochs']     = 20
common['batch_size']     = 100
common['decay_steps']    = mnist.train.num_examples / common['batch_size']
common['eval_frequency'] = 30 * common['num_epochs']
common['brelu']          = 'b1relu'
common['pool']           = 'mpool1'
C = max(mnist.train.labels) + 1  # number of classes
model_perf = utils.model_perf()  #模型对比函数

# 参数 for LeNet5-like networks.
common['regularization'] = 5e-4
common['dropout']        = 0.5
common['learning_rate']  = 0.02  # 0.03 in the paper but sgconv_sgconv_fc_softmax has difficulty to converge
common['decay_rate']     = 0.95
common['momentum']       = 0.9
common['F']              = [32, 64]  #每次卷积的输出feature大小
common['K']              = [25, 25]  #每个卷积核的多项式项数
common['p']              = [4, 4]    #每次卷积后的池化大小,池化次数与卷积次数一致 
common['M']              = [512, C]  #全连接层输出

# Architecture of TF MNIST conv model (LeNet-5-like).

if True:
    name = 'Chebyshev_test'  # 'Chebyshev'
    params = common.copy()
    params['dir_name'] += name
    params['filter'] = 'chebyshev5'  #使用chebyshev5所建的滤波器
    model_perf.test(models.cgcnn(L, **params), name, params,
                    train_data, train_labels, val_data, val_labels, test_data, test_labels)

model_perf.show()
''' 
#or 使用下面的代码可以在训练后不显示评价结果,而是直接训练。 
model = models.cgcnn(L, **params)
accuracy, loss, t_step = model.fit(train_data, train_labels, val_data, val_labels)
'''

由上述代码可知数据关系为:

为了构建网络,我们需要提供描述graph的邻接矩阵A(这个图的描述设置将影响GCN性能),以便帮助网络构成滤波器。

而原始数据会由粗化返回的重排关系重新组织为3D数据(样例编号N,顶点编号M,该顶点上特征F),他们将输入到网络中真正进行卷积。

3.2 图卷积具体刨析

本章将逐级展开GCN,分析其实现

1 从 mnist.py 开始,发现网络构建是借助 来自models.py的class cgcnn(base_model)

models.cgcnn(L, **params)

2 models.py中的 class cgcnn(base_model)

这里的cgcnn继承自models.py中的base_model(作者构建了几种网络对比,而其中的卷积是不同实现,所以用基类来省去重复部分),base_model包含了一个“神经网络类”的一些通用函数,如fit(),predict(),evaluate(),build_graph()等函数,自然我们需要首先看base_model中的build_graph() 

    def build_graph(self, M_0):
        """Build the computational graph of the model."""
        self.graph = tf.Graph()
        with self.graph.as_default():

            # Inputs.
            with tf.name_scope('inputs'):
                self.ph_data = tf.placeholder(tf.float32, (self.batch_size, M_0), 'data')
                self.ph_labels = tf.placeholder(tf.int32, (self.batch_size), 'labels')
                self.ph_dropout = tf.placeholder(tf.float32, (), 'dropout')

            # Model.
            op_logits = self.inference(self.ph_data, self.ph_dropout)  #!!!!!!#
            self.op_loss, self.op_loss_average = self.loss(op_logits, self.ph_labels, self.regularization)
            self.op_train = self.training(self.op_loss, self.learning_rate,
                    self.decay_steps, self.decay_rate, self.momentum)
            self.op_prediction = self.prediction(op_logits)

            # Initialize variables, i.e. weights and biases.
            self.op_init = tf.global_variables_initializer()
            
            # Summaries for TensorBoard and Save for model parameters.
            self.op_summary = tf.summary.merge_all()
            self.op_saver = tf.train.Saver(max_to_keep=5)
        
        self.graph.finalize()

上面的是一般正常构建步骤,唯一特殊点的是,self.inference调用self._inference直接返回预测,所以必然每个子类的_inference实现将是关键

op_logits = self.inference(self.ph_data, self.ph_dropout)  #!!!!!!#

----------排版分割-------------------
   def inference(self, data, dropout):
        """
        something .....
        """
        logits = self._inference(data, dropout)
        return logits

3 以cgcnn中的_inference为例分析(models.py中的其他是论文中提到的其他模型或配置实现),cgcnn为作者提出的基准模型。

    def _inference(self, x, dropout):
        # Graph convolutional layers.
        x = tf.expand_dims(x, 2)  # N x M x F=1
        for i in range(len(self.p)):  #依据池化次数,设置卷积(池化and卷积一对一)
            with tf.variable_scope('conv{}'.format(i+1)):  #此scope中的为一次图卷积
                with tf.name_scope('filter'):              #卷积中的滤波器
                    x = self.filter(x, self.L[i], self.F[i], self.K[i])
                with tf.name_scope('bias_relu'):           #卷积中的激活函数
                    x = self.brelu(x)
                with tf.name_scope('pooling'):             #卷积后的池化
                    x = self.pool(x, self.p[i])
        
        # Fully connected hidden layers.
        N, M, F = x.get_shape()
        x = tf.reshape(x, [int(N), int(M*F)])  # N x M
        for i,M in enumerate(self.M[:-1]):
            with tf.variable_scope('fc{}'.format(i+1)):
                x = self.fc(x, M)
                x = tf.nn.dropout(x, dropout)
        
        # Logits linear layer, i.e. softmax without normalization.
        with tf.variable_scope('logits'):
            x = self.fc(x, self.M[-1], relu=False)
        return x

4 分解上述代码scope('filter'),发现他是借助getattr()重新与指定函数绑定,如和chebyshev2()绑定,这里我们以chebyshev2()(PS 该函数即为filter,且为作者提出的经典类型,功能应该和chebyshv5()相同,只不过通过numpy实现,而chebyshev5借助TF实现,)为例展开解释filter如何编写的(numpy比TF实现看起来更好理解)。

    def chebyshev2(self, x, L, Fout, K):
        """
        Filtering with Chebyshev interpolation
        Implementation: numpy.
        
        Data: x of size N x M x F
            N: number of signals
            M: number of vertices
            F: number of features per signal per vertex
        """
        N, M, Fin = x.get_shape()
        N, M, Fin = int(N), int(M), int(Fin)
        # Rescale Laplacian. Copy to not modify the shared L.
        L = scipy.sparse.csr_matrix(L)
        L = graph.rescale_L(L, lmax=2)
        # Transform to Chebyshev basis
        x = tf.transpose(x, perm=[1, 2, 0])  # M x Fin x N
        x = tf.reshape(x, [M, Fin*N])  # M x Fin*N   #将X所有点所有特征放到一个维度上,得X
        def chebyshev(x):     #在X上用用chebyshev,返回T_k X(py_func将X转到numpy array运行)
            return graph.chebyshev(L, x, K)        
        x = tf.py_func(chebyshev, [x], [tf.float32])[0]  # K x M x Fin*N
        x = tf.reshape(x, [K, M, Fin, N])  # K x M x Fin x N            # 维度转换
        x = tf.transpose(x, perm=[3,1,2,0])  # N x M x Fin x K
        x = tf.reshape(x, [N*M, Fin*K])  # N*M x Fin*K                          
        # Filter: Fin*Fout filters of order K, i.e. one filterbank per feature.
        W = self._weight_variable([Fin*K, Fout], regularization=False)  #权重乘法
        x = tf.matmul(x, W)  # N*M x Fout
        return tf.reshape(x, [N, M, Fout])  # N x M x Fout

从上面可以看到,先将x转换到稀疏矩阵的形式,然后维度变形,之后在其上使用graph.chebyshev()应用chebyshev多项式的迭代计算函数,获得T_k,即论文中使用chebyshev来计算T_k---下图左, 来近似diag(λ)^k---下图右),同时我们注意到下面公式里含有求和(关于k),而上面代码中并没有,而是通过增加权重参数(w本为【Fin*Fout】,现为【Fin*k,Fout】)将其从求和变为x*w(shape(w) = [Fin*k,Fout])的形式。

def chebyshev(L, X, K):
    """Return T_k X where T_k are the Chebyshev polynomials of order up to K.
    Complexity is O(KMN)."""
    '''来自于graph.py
    返回T_k X,其中T_k是最多为K阶的Chebyshev多项式。'''
    M, N = X.shape
    assert L.dtype == X.dtype

    # L = rescale_L(L, lmax)
    # Xt = T @ X: MxM @ MxN.
    Xt = np.empty((K, M, N), L.dtype)
    # Xt_0 = T_0 X = I X = X.
    Xt[0, ...] = X
    # Xt_1 = T_1 X = L X.
    if K > 1:
        Xt[1, ...] = L.dot(X)
    # Xt_k = 2 L Xt_k-1 - Xt_k-2.
    for k in range(2, K):
        Xt[k, ...] = 2 * L.dot(Xt[k-1, ...]) - Xt[k-2, ...]
    return Xt

上面的chebyshev即借助chebyshev的多项式递推性质而写。获得T_k后经过适当变形处理,即可和需要学习的参数θ相乘,然后返回结果,至此一次filter()结束,PS 这里为何可用chebyshev多项式近似我不太熟悉,没有深究,有熟悉的同学请评论或私信我。

5 之后的self.brelu(x),self.pool(x, self.p[i]) 并不是作者提出(PS:pool也挺有意思,其重新组织为1D信号时需要采取的粗化与重排),此次并不过多说明。(PS主要是太懒了)

 

 

 

  • 16
    点赞
  • 83
    收藏
    觉得还不错? 一键收藏
  • 52
    评论
### 回答1: 这句话的意思是,使用快速局部谱滤波在图上进行卷积神经网络。在这个过程中,图像被表示为一个图,节点表示像素,边表示它们之间的关系。然后使用谱滤波器来处理这些图像,以便更好地捕捉它们之间的关系。由于使用了快速局部谱滤波器,因此可以有效地减少计算量,并提高计算效率。 ### 回答2: 卷积神经网络(CNN)在计算机视觉领域中被广泛应用,而针对图像上的卷积运算也得到了很好的改进。但是,对于图结构数据,卷积操作却变得更加困难。近年来出现了一些新的关于卷积神经网络用于图结构数据的方法,如基于图卷积网络(GCN)等。本文要介绍的“convolutional neural networks on graphs with fast localized spectral filtering”,即基于图谱的局部快速滤波的卷积神经网络,是另一种针对图结构数据的卷积方法。 传统的CNN通常采用局部的、线性的滤波器来提取图像的空间特征。而对于图结构数据,由于图上两个节点之间的关系是任意的,以及节点的特征不一定是有序的,因此无法直接地应用局部的卷积操作。但是,与图结构数据相对应的,是一个特殊的函数——图谱,它提供了丰富的图结构信息。 图谱(即拉普拉斯矩阵)是一个对称的稀疏矩阵,反映了图结构和节点特征之间的关系。将图谱的特征值和特征向量作为滤波器,就可以将图上的卷积操作转化为图谱卷积的形式。尤其是,利用局部滤波器就可以实现对图上节点嵌入向量的快速计算。 该方法涉及到了图谱嵌入、拉普拉斯矩阵、小批量图谱卷积核的设计等方面的内容。其中,图谱嵌入是将图结构数据映射为一个低维向量表示的过程,具有降维和特征抽取的作用;拉普拉斯矩阵是反应了图结构的一类矩阵,与图谱嵌入有密切关系;在卷积核设计方面,考虑到图结构的多样性和规模,将设计小批量卷积核进行快速的局部卷积操作,以提高计算效率。 该方法的优点在于,可以处理任意结构的图像和非图像数据,并且具有较好的鲁棒性和泛化能力。是否可以进一步提高计算效率,仍需更多的研究来探索。 ### 回答3: 卷积神经网络是一种基于多层神经元的深度学习算法,被用于图像、文本和声音等领域。最近,学者们开始研究如何将卷积神经网络应用于图形数据,如社交网络、交通网络和化学分子。其中,卷积神经网络特别适合处理图形数据,因为它可以学习局部特征,并保持局部空间关系。因此,卷积神经网络在图形任务上取得了许多优秀成果。 然而,之前的卷积神经网络模型存在一些不足,比如缺乏设计可解释性、效率低下、过度拟合等。为了解决这些问题,一种新的基于谱滤波的图形卷积神经网络被提出,即convolutional neural networks on graphs with fast localized spectral filtering。 这种方法在卷积层引入了局部谱滤波器,能够提高模型的效率和可解释性。谱滤波器可以学习图形数据的空间结构特征,能够捕捉节点之间的相邻关系和密度。而局部谱滤波器则针对每个节点的邻居子图进行滤波,使模型能够更好地识别图形数据中的局部特征。 此外,该方法还能够解决过拟合问题。过拟合是神经网络经常遇到的问题,即模型在训练集上表现极佳,但在测试集上表现不佳。谱滤波器可以在输入数据中学习的特征不够显著时,利用图形数据的全局谱信息进行补充,并减少过拟合的发生。 总之,convolutional neural networks on graphs with fast localized spectral filtering是一种高效、可解释、稳定的图形卷积神经网络。此方法在实际应用中有很大的潜力,如社交网络分析、城市交通预测、生物学和化学分子分析等领域。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 52
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值