図(グラフ)拡大畳み込みニューラルネットワーク--LGCNネットワーク(tensorflowバージョンのコード分析)

LGCN(大規模学習可能なグラフ畳み込みネットワーク)

GitHubのプロジェクト(LGCN [tensorflow版])

プロジェクトがまだタスクグラフノード分類の問題を行っている、コーラコーパスがまだ
ノードの総数の図2708である
大規模なマップの場合に対応するために、LGCNは、図にサブサンプリング、サブグラフトレーニング戦略を提案しました。小ロット。
大規模学習可能グラフ畳み込みネットワークは
、従来の一次元畳み込み畳み込みを使用して、図データの格子状構造にデータを変換します。モード変換である:大kと隣人が値をk個の特徴のこのリストは、この数値を取る前に、各フィーチャのサイズ、機能の隣接ノードをソートします。隣人が十分でない場合は、ゼロで埋めます。これは、マトリックスを形成する隣接頂点の情報を与え、次に一次元畳み込みを使用します。

6102062-3c27b89bf7492704.png
大規模学習可能なグラフ畳み込みネットワーク(https://davidham3.github.io/blog/2018/09/17/large-scale-learnable-graph-convolutional-networks/)この記事の説明を参照してください


1.コードをダウンロードし、ローカルのデバッグ
6102062-6203479d05ab83dc.png
コードが正しくローカルに展開されます

2.スーパーモデルパラメータの設定
6102062-cdbc37aad0af0a0f.png
スーパーモデルのコンフィギュレーションパラメータ
3.ロードデータモデルの初期化---- self.process_data()
class GraphNet(object):

    def __init__(self, sess, conf):
        self.sess = sess
        self.conf = conf
        if not os.path.exists(conf.modeldir):
            os.makedirs(conf.modeldir)
        if not os.path.exists(conf.logdir):
            os.makedirs(conf.logdir)
        self.process_data()  ###数据加载
        self.configure_networks()  ###配置网络
        self.train_summary = self.config_summary('train')
        self.valid_summary = self.config_summary('valid')
        self.test_summary = self.config_summary('test')

4.読み込みprocess_data()

    def process_data(self):
        data = load_data('cora')
        adj, feas = data[:2]
        self.adj = adj.todense()
        self.normed_adj = preprocess_adj(adj)
        self.feas = preprocess_features(feas, False)
        self.y_train, self.y_val, self.y_test = data[2:5]
        self.train_mask, self.val_mask, self.test_mask = data[5:]
6102062-f7708fda05832fd8.png
データの種類

self.adj === mtarix === [2078,2078] (图顶点数*图顶点数,整个大图的邻接矩阵)
self.normed_adj === matrix === [2708,2708] (归一化的大图邻接矩阵)
self.feas === matrix === [2078,1433] (所有顶点的特征向量组成的特征矩阵)
self.y_xxxx === ndarray === [2078,7] (顶点的类别标签one-hot向量)


6102062-7f38628fb9dd386a.png
self.y_xxxx标签向量one-hot

5.模型初始化----配置网络self.configure_networks()

    def configure_networks(self):
        self.build_network()
        self.cal_loss()
        optimizer = self.get_optimizer(self.conf.learning_rate)
        self.train_op = optimizer.minimize(self.loss_op, name='train_op')
        self.seed = int(time.time())
        tf.set_random_seed(self.seed)
        self.sess.run(tf.global_variables_initializer())
        trainable_vars = tf.trainable_variables()
        self.saver = tf.train.Saver(var_list=trainable_vars, max_to_keep=0)
        if self.conf.is_train:
            self.writer = tf.summary.FileWriter(self.conf.logdir, self.sess.graph)
        self.print_params_num()

    def build_network(self):
        self.labels_mask = tf.placeholder(tf.int32, None, name='labels_mask')  ###需要遮掩的标签
        self.matrix = tf.placeholder(tf.int32, [None, None], name='matrix')  ###大图邻接矩阵
        self.normed_matrix = tf.placeholder(tf.float32, [None, None], name='normed_matrix')  ###大图归一化处理的邻接矩阵
        self.inputs = tf.placeholder(tf.float32, [None, self.feas.shape[1]], name='inputs')  ###输入数据的特征
        self.labels = tf.placeholder(tf.int32, [None, self.conf.class_num], name='labels') ###输入数据的标签正确答案 
        self.is_train = tf.placeholder(tf.bool, name='is_train')
        self.preds = self.inference(self.inputs)  ###将inputs特征输入模型进行推断,输出self.preds预测标签

    def inference(self, outs):
        ###第一次卷积(一层卷积,simple_conv)
        outs = getattr(ops, self.conf.first_conv)(
            self.normed_matrix, outs, 4*self.conf.ch_num, self.conf.adj_keep_r,
            self.conf.keep_r, self.is_train, 'conv_s', act_fn=None)
        ###第二次卷积(两层卷积,graph_conv)
        for layer_index in range(self.conf.layer_num):
            cur_outs= getattr(ops, self.conf.second_conv)(
                self.normed_matrix, outs, self.conf.ch_num, self.conf.adj_keep_r,
                self.conf.keep_r, self.is_train, 'conv_%s' % (layer_index+1),
                act_fn=None, k=self.conf.k)
            outs = tf.concat([outs, cur_outs], axis=1, name='concat_%s' % layer_index)
        ####第三次卷积(一层卷积,simple_conv)
        outs = ops.simple_conv(
            self.normed_matrix, outs, self.conf.class_num, self.conf.adj_keep_r,
            self.conf.keep_r, self.is_train, 'conv_f', act_fn=None, norm=False)
        return outs

6.simple_conv()
adj_m.shape==(?,?) outs.shape==(?,1433) num_out==32
adj_keep_r==0.999 keep_r==0.16

def simple_conv(adj_m, outs, num_out, adj_keep_r, keep_r, is_train, scope,
                act_fn=tf.nn.elu, norm=True, **kw):
    adj_m = dropout(adj_m, adj_keep_r, is_train, scope+'/drop1')  ###将邻接矩阵按照概率为0.999进行神经元丢弃
    outs = dropout(outs, keep_r, is_train, scope+'/drop2')  ###输入的特征矩阵也进行神经元丢弃
    outs = fully_connected(outs, num_out, scope+'/fully', None)  ###全连接
    outs = tf.matmul(adj_m, outs, name=scope+'/matmul')  ###矩阵相乘
    #if norm:
    #    outs = batch_norm(outs, is_train, scope=scope+'/norm', act_fn=None)
    outs = outs if not act_fn else act_fn(outs, scope+'/act')  ###激活函数处理
    return outs

7.graph_conv()
adj_m.shape==(?,?) outs.shape==(?,32) num_out==8
adj_keep_r==0.999 keep_r==0.16 k=8(程序开始之前,人为设置的值为8)

def graph_conv(adj_m, outs, num_out, adj_keep_r, keep_r, is_train, scope, k=5,
               act_fn=tf.nn.relu6, **kw):
    num_in = outs.shape[-1].value
    adj_m = dropout(adj_m, adj_keep_r, is_train, scope+'/drop1')
    outs = top_k_features(adj_m, outs, k, scope+'/top_k')  ###提取top8的特征
    outs = dropout(outs, keep_r, is_train, scope+'/drop1')
    outs = conv1d(outs, (num_in+num_out)//2, (k+1)//2+1, scope+'/conv1', None, True)
    outs = act_fn(outs, scope+'act1') if act_fn else outs
    outs = dropout(outs, keep_r, is_train, scope+'/drop2')
    outs = conv1d(outs, num_out, k//2+1, scope+'/conv2', None)
    outs = tf.squeeze(outs, axis=[1], name=scope+'/squeeze')
    return batch_norm(outs, True, scope+'/norm2', act_fn)

def top_k_features(adj_m, fea_m, k, scope):
    ###adj_m扩充一个维度,由原来的(?,?)变为了(?,1,?)
    adj_m = tf.expand_dims(adj_m, axis=1, name=scope+'/expand1')
    ###fea_m扩充一个维度,由原来的(?,32)变为了(?,32,1)
    fea_m = tf.expand_dims(fea_m, axis=-1, name=scope+'/expand2')
    ###feas.shape==(?,32,?)
    feas = tf.multiply(adj_m, fea_m, name=scope+'/mul')
    ###feas.shape==(?,32,?)
    feas = tf.transpose(feas, perm=[2, 1, 0], name=scope+'/trans1')
    ###top_k.shape==(?,32,8)
    top_k = tf.nn.top_k(feas, k=k, name=scope+'/top_k').values
    #pre, post = tf.split(top_k, 2, axis=2, name=scope+'/split')
    ###top_k.shape==(?,32,9)
    top_k = tf.concat([fea_m, top_k], axis=2, name=scope+'/concat')
    ###top_k.shape==(?,9,32)
    top_k = tf.transpose(top_k, perm=[0, 2, 1], name=scope+'/trans2')
    return top_k

7.模型损失函数的定义

    def cal_loss(self):
        with tf.variable_scope('loss'):
            self.class_loss = ops.masked_softmax_cross_entropy(
                self.preds, self.labels, self.labels_mask)
            self.regu_loss = 0
            for var in tf.trainable_variables():
                self.regu_loss += self.conf.weight_decay * tf.nn.l2_loss(var)
            self.loss_op = self.class_loss + self.regu_loss  ###分类预测损失+参数正则化损失
            self.accuracy_op = ops.masked_accuracy(self.preds, self.labels, self.labels_mask)

8.训练过程,输入数据
可见,在给模型输入数据的时候,根据中心点的个数和训练批次的大小,大图就被划分为小图处理了

    def pack_trans_dict(self, action):
        feed_dict = {
            self.matrix: self.adj, self.normed_matrix: self.normed_adj,
            self.inputs: self.feas}
        if action == 'train':
            feed_dict.update({
                self.labels: self.y_train, self.labels_mask: self.train_mask,
                self.is_train: True})
            if self.conf.use_batch:
                ###batch_size=2500  indices=644
                indices = get_indice_graph(
                    self.adj, self.train_mask, self.conf.batch_size, 1.0)
                new_adj = self.adj[indices,:][:,indices]  ###将大图邻接矩阵进行了缩小化处理
                new_normed_adj = self.normed_adj[indices,:][:,indices] ###将归一化的大图邻接矩阵进行了缩小化处理
                ###训练模型时,真正的输入数据
                feed_dict.update({
                    self.labels: self.y_train[indices],
                    self.labels_mask: self.train_mask[indices],
                    self.matrix: new_adj, self.normed_matrix: new_normed_adj,
                    self.inputs: self.feas[indices]})
6102062-c8f619873b7ab5ef.png
网络层参数的形状

サイズインデックスは:--------------> 644の
指数はサイズ:--------------> 1484の
指数はサイズ:--------- -----> 2190
大きな操作の9小さな
ここ幅優先することにより、ノードの数が拡大します

def get_indice_graph(adj, mask, size, keep_r=1.0):
    indices = mask.nonzero()[0]  ###获取非0标记的有效数据索引,是train则有140个非零标记索引
    if keep_r < 1.0:
        indices = np.random.choice(indices, int(indices.size*keep_r), False)
    pre_indices = set()
    indices = set(indices)  ###将数据索引有数组形式,转换为set集合形式
    while len(indices) < size:   ###此处的size是batch_size批次大小:2500
        new_add = indices - pre_indices
        if not new_add:
            break
        pre_indices = indices
        candidates = get_candidates(adj, new_add) - indices  ###获取候选集索引,并排除以前的索引,是train则找到504个新的索引
        if len(candidates) > size - len(indices):  ###确保不能超过batch_size=2500个索引
            candidates = set(np.random.choice(list(candidates), size-len(indices), False))
        indices.update(candidates)  ###跟新索引,是train则索引总共是140+504=644个索引
    print('indices size:-------------->', len(indices))
    return sorted(indices)

def get_candidates(adj, new_add):
    return set(adj[sorted(new_add)].sum(axis=0).nonzero()[1])

グラフを考えると、我々は最初のいくつかの初期の頂点をサンプリングしました。それらを開始、我々は繰り返し幅優先探索アルゴリズムを使用して隣接する頂点部分グラフに展開されます。反復と高次ネイバーの数後に初期頂点リストに追加される頂点。私たちは、アルゴリズムに簡単なパラメータNmのを使用することに注意してください。実際には、各反復では、我々はNmが異なる値に設定します。
6102062-4c21d75f8ab79b8c.png
小さな、上記get_indice_graphの大きいアルゴリズムは、()コードに対応します

6102062-852d8d414f594dce.png
例として、ランダムに分割部分グラフ

ランダムな分子グラフカット、深いモデルが大規模に訓練することができます。また、ミニバッチトレーニング方法の完全な使用は、学習プロセスを加速することができます。トレーニングの各ラウンドでは、訓練方法は、複数のサブサンプリングサブグラフ図を使用してもよいし、バッチに入れ。特徴ベクトルは、入力されたネットワークからなる隣接行列に対応します。

ます。https://www.jianshu.com/p/a099dfd46c61で再現

おすすめ

転載: blog.csdn.net/weixin_34259159/article/details/91140283