FIG. (Graph) Larger convolutional neural network --LGCN network (tensorflow Version code analysis)

LGCN(Large-Scale Learnable Graph Convolutional Networks )

GitHub project ( LGCN [tensorflow version])

The project is still doing the task graph node classification problems, Cora corpus is still the
view of the total number of nodes is 2708
in order to meet the case of large-scale map, LGCN proposed a subgraph training strategies, sub-sampled into a Fig. small batch.
Large-Scale Learnable Graph Convolutional Networks
converting the data into a grid-like structure of FIGS data, using a conventional one-dimensional convolution convolution. Mode conversion is: for sorting the neighbor node for the size of each feature, feature before taking this number as a large k this list of features which neighbors k values. If the neighbor is not enough, then fill with zeros. This will give the information of the neighbor vertex to form a matrix, and then using a one-dimensional convolution.

6102062-3c27b89bf7492704.png
Large-Scale Learnable Graph Convolutional Networks (https://davidham3.github.io/blog/2018/09/17/large-scale-learnable-graph-convolutional-networks/) see description of this article


1. Download the code, local debugging
6102062-6203479d05ab83dc.png
The code is correctly deployed to the local

2. Super model parameter configuration
6102062-cdbc37aad0af0a0f.png
Super model configuration parameters
3. Load data model initialization ---- 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. Loading 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
type of data

self.adj === mtarix === [2078,2078] (* FIG Vertices FIG Vertices, to enlarge the entire adjacency matrix)
self.normed_adj Matrix === === [2708,2708] (normalized large FIG adjacency matrix)
self.feas matrix === === [2078,1433] (all features vectors at the vertices of the matrix composition)
self.y_xxxx ndarray === === [2078,7] (vertices category labels one-hot vector)


6102062-7f38628fb9dd386a.png
Label one-hot vector self.y_xxxx

The configuration of the network model initialization ---- 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.16 == == K = 0.999 keep_r before. 8 (program begins, the set value of artificial 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. Model Loss Function definitions

    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. The training process, the input data
can be seen, when the input data to the model, depending on the size and number of the training center of the batch, large image was processed into small FIG.

    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
The shape of the network layer parameters

size indices: --------------> 644
indices size: --------------> 1484
indices size: --------- -----> 2190
9. FIG small of big operation
here by a breadth-first, the number of nodes expanded

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])

Given a graph, we first sampled some initial vertex. Starting them, we use breadth-first search algorithm iteratively will expand into the adjoining vertex subgraph. After a number of iterations and higher-order neighbor vertices initial vertex will be added to the list. Note that we use a simple parameter Nm in the algorithm. In fact, in each iteration, we will Nm set to different values.
6102062-4c21d75f8ab79b8c.png
Larger algorithm of small, above get_indice_graph () corresponds to the code

6102062-852d8d414f594dce.png
Examples randomly divided subgraph

Random molecular graph cut, deep model can be trained on a larger scale. In addition, full use of the mini-batch training method can accelerate the learning process. In each round of training, a training method may be used subgraph FIG sampling a plurality of sub, and put them in the batch. And the feature vector corresponding adjacency matrix composed of the input network.

Reproduced in: https: //www.jianshu.com/p/a099dfd46c61

Guess you like

Origin blog.csdn.net/weixin_34259159/article/details/91140283