LGCN(Large-Scale Learnable Graph Convolutional Networks )
GitHub項目(LGCN[tensorflow版])
該項目做的任務仍是圖中節(jié)點分類問題,語料仍是Cora
圖總共的節(jié)點數(shù)量是2708個
為了適應大規(guī)模圖的情況,LGCN提出了一種子圖訓練策略迟蜜,將采樣的子圖放入一個小批處理中。
Large-Scale Learnable Graph Convolutional Networks
將圖結(jié)構數(shù)據(jù)變換到網(wǎng)格狀數(shù)據(jù)中植酥,使用傳統(tǒng)的一維卷積進行卷積。變換的方式是:針對每個特征的大小悬襟,對鄰居結(jié)點進行排序缓窜,取這個特征前k大的數(shù)作為它鄰居這列特征的k個值管闷。如果鄰居不夠粥脚,那就用0來補。這樣就能得到該頂點的鄰居信息包个,組成一個矩陣刷允,然后使用一維卷積。
1.下載代碼碧囊,本地調(diào)試
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() ###數(shù)據(jù)加載
self.configure_networks() ###配置網(wǎng)絡
self.train_summary = self.config_summary('train')
self.valid_summary = self.config_summary('valid')
self.test_summary = self.config_summary('test')
4.數(shù)據(jù)加載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:]
self.adj === mtarix === [2078树灶,2078] (圖頂點數(shù)*圖頂點數(shù),整個大圖的鄰接矩陣)
self.normed_adj === matrix === [2708糯而,2708] (歸一化的大圖鄰接矩陣)
self.feas === matrix === [2078天通,1433] (所有頂點的特征向量組成的特征矩陣)
self.y_xxxx === ndarray === [2078,7] (頂點的類別標簽one-hot向量)
5.模型初始化----配置網(wǎng)絡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') ###輸入數(shù)據(jù)的特征
self.labels = tf.placeholder(tf.int32, [None, self.conf.class_num], name='labels') ###輸入數(shù)據(jù)的標簽正確答案
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進行神經(jīng)元丟棄
outs = dropout(outs, keep_r, is_train, scope+'/drop2') ###輸入的特征矩陣也進行神經(jīng)元丟棄
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') ###激活函數(shù)處理
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擴充一個維度,由原來的(?,?)變?yōu)榱??,1,?)
adj_m = tf.expand_dims(adj_m, axis=1, name=scope+'/expand1')
###fea_m擴充一個維度阐虚,由原來的(?,32)變?yōu)榱??,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.模型損失函數(shù)的定義
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 ###分類預測損失+參數(shù)正則化損失
self.accuracy_op = ops.masked_accuracy(self.preds, self.labels, self.labels_mask)
8.訓練過程,輸入數(shù)據(jù)
可見蚌卤,在給模型輸入數(shù)據(jù)的時候实束,根據(jù)中心點的個數(shù)和訓練批次的大小奥秆,大圖就被劃分為小圖處理了
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] ###將歸一化的大圖鄰接矩陣進行了縮小化處理
###訓練模型時,真正的輸入數(shù)據(jù)
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]})
indices size:--------------> 644
indices size:--------------> 1484
indices size:--------------> 2190
9.大圖化小的操作
此處是通過廣度優(yōu)先咸灿,擴展節(jié)點數(shù)量
def get_indice_graph(adj, mask, size, keep_r=1.0):
indices = mask.nonzero()[0] ###獲取非0標記的有效數(shù)據(jù)索引构订,是train則有140個非零標記索引
if keep_r < 1.0:
indices = np.random.choice(indices, int(indices.size*keep_r), False)
pre_indices = set()
indices = set(indices) ###將數(shù)據(jù)索引有數(shù)組形式,轉(zhuǎn)換為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])
給定一個圖亥宿,我們先采樣出一些初始頂點。從它們開始砂沛,我們使用廣度優(yōu)先搜索算法烫扼,迭代地將鄰接頂點擴充到子圖內(nèi)。經(jīng)過一定次數(shù)的迭代后碍庵,初始頂點的高階鄰居頂點就會被加進去映企。注意,我們在算法中使用一個簡單的參數(shù)Nm静浴。實際上在每個迭代中堰氓,我們將Nm設置為了不同的值。隨機的切分子圖豆赏,可以在大尺度的圖上訓練深層模型。此外富稻,充分利用mini-batch訓練方法可以加速學習過程掷邦。在每輪訓練中,可以使用子圖訓練方法采樣多個子圖椭赋,然后把它們放到batch中抚岗。對應的特征向量和鄰接矩陣組成了網(wǎng)絡的輸入。