GCN
GCN originates from the article SEMI-SUPERVISED CLASSIFICATION WITH GRAPH CONVOLUTIONAL NETWORKS. Suppose there is a undirected graph G(V,E)
with vertices V and edges E. It has an adjacency matrix A
(VxV). Each node has a feature and the feature matrix is X
(VxN) We want to classify the nodes into M categories using neural network f(X,A)
.
$$
f(X,A) = softmax(\hat{A} ReLU(\hat{A} XW(0))W(1))
$$
where
$$
\hat{A} = D^{-\frac{1}{2}}AD^{-\frac{1}{2}}
$$
It computes the symmetrically normalized adjacency matrix of G.
D denotes as degree matrix of G
$$ D_{ii} = \sum_{j\in nbr(i)}A_{ij} $$
code
I first generated code from ChatGPT and then I modified it. GPT makes mistakes!
class GraphConvolution(tf.keras.layers.Layer):
def __init__(self, input_dim, output_dim):
super(GraphConvolution, self).__init__()
self.output_dim = output_dim
self.input_dim = input_dim
# self.support_dim = support_dim
# self.activation = activation
self.w = self.add_weight("weight0", [input_dim, output_dim], initializer='glorot_uniform', trainable = True)
print(self.w)
# self.W = self.add_weight(name='W', shape=(self.input_dim, self.output_dim), initializer='glorot_uniform', trainable=True)
def call(self, inputs):
X, A = inputs
outputs = list()
# AXW
# for i in range(self.support_dim):
# pre = tf.matmul(X[:,i,:], self.weights[i])
# outputs.append(tf.matmul(A[:,i,:], pre))
pre = tf.matmul(X,self.w)
outputs = tf.matmul(A, pre)
return outputs
class GCN(tf.keras.models.Model):
def __init__(self, input_dim, hidden_dim, output_dim):
super(GCN, self).__init__()
self.input_dim = input_dim
self.hidden_dim = hidden_dim
self.output_dim = output_dim
self.gc1 = GraphConvolution(input_dim, hidden_dim)
self.gc2 = GraphConvolution(hidden_dim, output_dim)
def call(self, inputs):
X, A = inputs
H = tf.nn.relu(self.gc1([X, A]))
Y = self.gc2([H, A])
return Y
def normalize_adjacency_matrix(adj_matrix):
# Compute the degree matrix
degree_matrix = np.diag(np.sum(adj_matrix, axis=1))
# Compute the inverse of the degree matrix
inverse_degree_matrix = np.linalg.inv(degree_matrix)
# Compute the symmetrically normalized adjacency matrix
sqrt_inverse_degree_matrix = np.sqrt(inverse_degree_matrix)
normalized_adj_matrix = np.dot(np.dot(sqrt_inverse_degree_matrix, adj_matrix), sqrt_inverse_degree_matrix)
return normalized_adj_matrix
# Example usage
# Generate a simple graph with 4 nodes and 5 edges
adj_matrix = np.array([[0, 1, 0, 1],
[1, 0, 1, 0],
[0, 1, 0, 1],
[1, 0, 1, 0]])
# Normalize the adjacency matrix
normalized_adj_matrix = np.zeros([2,4,4])
normalized_adj_matrix[0,:,:] = tf.convert_to_tensor(np.reshape(normalize_adjacency_matrix(adj_matrix+np.identity(adj_matrix.shape[0])),[1,4,4]))
normalized_adj_matrix[1,:,:] = tf.convert_to_tensor(np.reshape(normalize_adjacency_matrix(adj_matrix+np.identity(adj_matrix.shape[0])),[1,4,4]))
print(normalized_adj_matrix)
# Define the input features for each node
X = np.array([[[1, 0],
[0, 1],
[1, 1],
[0, 0]],
[[1, 0],
[0, 1],
[1, 1],
[0, 0]]])
# Define the target outputs for each node
Y = np.array([[[1],
[0],
[1],
[0]],
[[1],
[0],
[1],
[0]]])
X = tf.cast(X, tf.float32)
# Define the GCN model
gcn = GCN(input_dim=2, hidden_dim=4, output_dim=1)
# Compile the model
gcn.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.01), loss='binary_crossentropy')
# print(X, normalized_adj_matrix)
# Train the model on the graph data
# gcn.fit(x=[X, normalized_adj_matrix], y=Y, epochs=50)
gcn([X,normalized_adj_matrix])
Reference
great books about GCN: GRL
Thank you, ChatGPT, you help me understand the GCN.