这里写目录标题
- GG-NN
- GGS-NNs
- 应用举例
GG-NN
GGNN网络使用了GRU(Gate Recurrent Units),在固定的 T 时间步中展开RNN,并使用BPTT算法(Back Propagation Through Time)以计算梯度。
补充:
GGNN并不能保证图的最终状态会到达不动点。由于更新次数 T 变成了固定值,因此GGNN可以直接使用BPTT算法来进行梯度的计算。相比于一般的GNN使用Almeida-Pineda算法需要更多的内存,但是不需要约束参数以满足压缩映射(contraction map)的要求。
补充一下GRU的基本传播步骤:
GGNN模型是为解决图上定义的问题而设计的——这些图需要输出序列,而现有模型则专注于产生单个输出。
补充:
对于不同任务,GGNN模型具有不同的输出:
对于节点层级(node-focused)的任务,模型对每个节点都有一个输出向量;
对于图级别(graph-focused)的任务,模型可以在节点向量基础上获得图的表示向量。
GGS-NNs
应用举例
使用图神经网络做基于会话的推荐
本文介绍的论文题目为:《Session-based Recommendation with Graph Neural Networks》
论文下载地址为:https://arxiv.org/abs/1811.00855
代码地址为:https://github.com/princewen/tensorflow_practice/tree/master/recommendation/Basic-SRGNN-Demo(代码和参考代码一致,写了点注释)
参考代码地址为:https://github.com/CRIPAC-DIG/SR-GNN
import tensorflow as tf
import mathclass Model(object):def __init__(self, hidden_size=100, out_size=100, batch_size=100, nonhybrid=True):self.hidden_size = hidden_sizeself.out_size = out_sizeself.batch_size = batch_sizeself.mask = tf.placeholder(dtype=tf.float32)self.alias = tf.placeholder(dtype=tf.int32) # 给给每个输入重新self.item = tf.placeholder(dtype=tf.int32) # 重新编号的序列构成的矩阵self.tar = tf.placeholder(dtype=tf.int32)self.nonhybrid = nonhybridself.stdv = 1.0 / math.sqrt(self.hidden_size)self.nasr_w1 = tf.get_variable('nasr_w1', [self.out_size, self.out_size], dtype=tf.float32,initializer=tf.random_uniform_initializer(-self.stdv, self.stdv))self.nasr_w2 = tf.get_variable('nasr_w2', [self.out_size, self.out_size], dtype=tf.float32,initializer=tf.random_uniform_initializer(-self.stdv, self.stdv))self.nasr_v = tf.get_variable('nasrv', [1, self.out_size], dtype=tf.float32,initializer=tf.random_uniform_initializer(-self.stdv, self.stdv))self.nasr_b = tf.get_variable('nasr_b', [self.out_size], dtype=tf.float32, initializer=tf.zeros_initializer())def forward(self, re_embedding, train=True):rm = tf.reduce_sum(self.mask, 1)last_id = tf.gather_nd(self.alias, tf.stack([tf.range(self.batch_size), tf.to_int32(rm)-1], axis=1))last_h = tf.gather_nd(re_embedding, tf.stack([tf.range(self.batch_size), last_id], axis=1))seq_h = tf.stack([tf.nn.embedding_lookup(re_embedding[i], self.alias[i]) for i in range(self.batch_size)],axis=0) #batch_size*T*dlast = tf.matmul(last_h, self.nasr_w1)seq = tf.matmul(tf.reshape(seq_h, [-1, self.out_size]), self.nasr_w2)last = tf.reshape(last, [self.batch_size, 1, -1])m = tf.nn.sigmoid(last + tf.reshape(seq, [self.batch_size, -1, self.out_size]) + self.nasr_b)coef = tf.matmul(tf.reshape(m, [-1, self.out_size]), self.nasr_v, transpose_b=True) * tf.reshape(self.mask, [-1, 1])b = self.embedding[1:]if not self.nonhybrid:ma = tf.concat([tf.reduce_sum(tf.reshape(coef, [self.batch_size, -1, 1]) * seq_h, 1),tf.reshape(last, [-1, self.out_size])], -1)self.B = tf.get_variable('B', [2 * self.out_size, self.out_size],initializer=tf.random_uniform_initializer(-self.stdv, self.stdv))y1 = tf.matmul(ma, self.B)logits = tf.matmul(y1, b, transpose_b=True)else:ma = tf.reduce_sum(tf.reshape(coef, [self.batch_size, -1, 1]) * seq_h, 1)logits = tf.matmul(ma, b, transpose_b=True)loss = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(labels=self.tar - 1, logits=logits))self.vars = tf.trainable_variables()if train:lossL2 = tf.add_n([tf.nn.l2_loss(v) for v in self.vars if v.name notin ['bias', 'gamma', 'b', 'g', 'beta']]) * self.L2loss = loss + lossL2return loss, logitsdef run(self, fetches, tar, item, adj_in, adj_out, alias, mask):return self.sess.run(fetches, feed_dict={self.tar: tar, self.item: item, self.adj_in: adj_in,self.adj_out: adj_out, self.alias: alias, self.mask: mask})class GGNN(Model):def __init__(self,hidden_size=100, out_size=100, batch_size=300, n_node=None,lr=None, l2=None, step=1, decay=None, lr_dc=0.1, nonhybrid=False):super(GGNN,self).__init__(hidden_size, out_size, batch_size, nonhybrid)self.embedding = tf.get_variable(shape=[n_node, hidden_size], name='embedding', dtype=tf.float32,initializer=tf.random_uniform_initializer(-self.stdv, self.stdv))self.adj_in = tf.placeholder(dtype=tf.float32, shape=[self.batch_size, None, None])self.adj_out = tf.placeholder(dtype=tf.float32, shape=[self.batch_size, None, None])self.n_node = n_nodeself.L2 = l2self.step = stepself.nonhybrid = nonhybridself.W_in = tf.get_variable('W_in', shape=[self.out_size, self.out_size], dtype=tf.float32,initializer=tf.random_uniform_initializer(-self.stdv, self.stdv))self.b_in = tf.get_variable('b_in', [self.out_size], dtype=tf.float32,initializer=tf.random_uniform_initializer(-self.stdv, self.stdv))self.W_out = tf.get_variable('W_out', [self.out_size, self.out_size], dtype=tf.float32,initializer=tf.random_uniform_initializer(-self.stdv, self.stdv))self.b_out = tf.get_variable('b_out', [self.out_size], dtype=tf.float32,initializer=tf.random_uniform_initializer(-self.stdv, self.stdv))with tf.variable_scope('ggnn_model', reuse=None):self.loss_train, _ = self.forward(self.ggnn())with tf.variable_scope('ggnn_model', reuse=True):self.loss_test, self.score_test = self.forward(self.ggnn(), train=False)self.global_step = tf.Variable(0)self.learning_rate = tf.train.exponential_decay(lr, global_step=self.global_step, decay_steps=decay,decay_rate=lr_dc, staircase=True)self.opt = tf.train.AdamOptimizer(self.learning_rate).minimize(self.loss_train, global_step=self.global_step)gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=0.8)config = tf.ConfigProto(gpu_options=gpu_options)config.gpu_options.allow_growth = Trueself.sess = tf.Session(config=config)self.sess.run(tf.global_variables_initializer())def ggnn(self):fin_state = tf.nn.embedding_lookup(self.embedding, self.item)cell = tf.nn.rnn_cell.GRUCell(self.out_size)with tf.variable_scope('gru'):for i in range(self.step):fin_state = tf.reshape(fin_state, [self.batch_size, -1, self.out_size])fin_state_in = tf.reshape(tf.matmul(tf.reshape(fin_state, [-1, self.out_size]),self.W_in) + self.b_in, [self.batch_size, -1, self.out_size])fin_state_out = tf.reshape(tf.matmul(tf.reshape(fin_state, [-1, self.out_size]),self.W_out) + self.b_out, [self.batch_size, -1, self.out_size])av = tf.concat([tf.matmul(self.adj_in, fin_state_in),tf.matmul(self.adj_out, fin_state_out)], axis=-1)state_output, fin_state = \tf.nn.dynamic_rnn(cell, tf.expand_dims(tf.reshape(av, [-1, 2*self.out_size]), axis=1),initial_state=tf.reshape(fin_state, [-1, self.out_size]))return tf.reshape(fin_state, [self.batch_size, -1, self.out_size])
1、Gated Graph Sequence Neural Networks,ICLR 2016
2、链接:https://arxiv.org/abs/1511.05493
3、官方实现(Lua):https://github.com/yujiali/ggnn
4、第三方实现(pytorch):https://github.com/calebmah/ggnn.pytorch