Graph Convolutional Network介紹及簡單實現(一)

本文是https://towardsdatascience.com/how-to-do-deep-learning-on-graphs-with-graph-convolutional-networks-7d2250723780第一部分A High-Level Introduction to Graph Convolutional Networks的翻譯和總結,介紹了簡單的Graph Convolutional Network,並以空手道俱樂部數據集爲示例進行實現分析,部分添加修改。

What is a Graph Convolutional Network?

在這裏插入圖片描述
可以簡單表示爲
Hi+1=f(Hi,A)=σ(AHiWi) H^{i+1} = f(H^i ,A) = \sigma(AH^{i}W^{i})
A(N,N)A: (N,N) 鄰接矩陣
Fi:,iF^{i}: 一維數值 ,第i層的特徵樹目
Hi:(N,Fi),H^{i}: (N ,F^{i}) ,每一行都是一個結點的特徵表示
X=H0:(N,F0)X = H^{0} :(N,F^{0}),輸入向量
Wi:Fi,Fi+1iW^{i}:(F^{i} ,F^{i+1})第i層的權值矩陣

A Simple Graph Example

以下圖爲例
在這裏插入圖片描述
A爲該圖的鄰接矩陣

import numpy as np
A = np.matrix([  ##鄰接矩陣
    [0, 1, 0, 0],
    [0, 0, 1, 1], 
    [0, 1, 0, 0],
    [1, 0, 1, 0]],
    dtype=float
)

X爲輸入的特徵向量,我們直接取值,維度爲(N,F0)(N ,F^0),其中N爲結點個數,F0F^0爲輸入向量的特徵維數

X = np.matrix([
            [i, -i]
            for i in range(A.shape[0])
        ], dtype=float)
X
matrix([[ 0.,  0.],
        [ 1., -1.],
        [ 2., -2.],
        [ 3., -3.]])
A*X
matrix([[ 1., -1.],
        [ 5., -5.],
        [ 1., -1.],
        [ 2., -2.]])

我們可以發現,每行,就是每個結點的特徵是它周圍結點的特徵和
比如,結點1和結點2,3鄰接,所以A*X第2行 5 = [0, 0, 1, 1] *[0 ,1 ,2 ,3]T

但是有兩點問題

  • A*X的結點表示中,並沒有加自己的特徵值。只有有self-loop 的結構纔會把自己特徵值包含在內
  • 鄰接結點多的結點的特徵值會大,少的特徵值就小

第一個問題可以通過加Self-Loops來解決

I = np.matrix(np.eye(A.shape[0]))
I
matrix([[1., 0., 0., 0.],
        [0., 1., 0., 0.],
        [0., 0., 1., 0.],
        [0., 0., 0., 1.]])
A_hat = A + I
A_hat * X
matrix([[ 1., -1.],
        [ 6., -6.],
        [ 3., -3.],
        [ 5., -5.]])

第二個問題可以通過歸一化特徵表示來解決
在這裏插入圖片描述
原因見https://tkipf.github.io/graph-convolutional-networks/

D = np.array(np.sum(A, axis=0))[0]
D = np.matrix(np.diag(D))
D
matrix([[1., 0., 0., 0.],
        [0., 2., 0., 0.],
        [0., 0., 2., 0.],
        [0., 0., 0., 1.]])

歸一化前

A
matrix([[0., 1., 0., 0.],
        [0., 0., 1., 1.],
        [0., 1., 0., 0.],
        [1., 0., 1., 0.]])

歸一化後

D**-1 * A
matrix([[0. , 1. , 0. , 0. ],
        [0. , 0. , 0.5, 0.5],
        [0. , 0.5, 0. , 0. ],
        [1. , 0. , 1. , 0. ]])
D**-1 * A * X
matrix([[ 1. , -1. ],
        [ 2.5, -2.5],
        [ 0.5, -0.5],
        [ 2. , -2. ]])

Putting it All Together

接下來我們把兩個改進方案都運用起來

A_hat  = A + I
D_hat = np.array(np.sum(A_hat, axis=0))[0]
D_hat = np.matrix(np.diag(D_hat))
D_hat
matrix([[2., 0., 0., 0.],
        [0., 3., 0., 0.],
        [0., 0., 3., 0.],
        [0., 0., 0., 2.]])
W = np.matrix([
             [1, -1],
             [-1, 1]
         ])
X
matrix([[ 0.,  0.],
        [ 1., -1.],
        [ 2., -2.],
        [ 3., -3.]])
D_hat**-1 * A_hat*X*W
matrix([[ 1., -1.],
        [ 4., -4.],
        [ 2., -2.],
        [ 5., -5.]])

Back to Reality

現在我們在現實中的網絡中運用圖卷集網絡(graph convolutional network技術。選取的網絡爲空手道俱樂部數據集(karate_club_graph)。

from networkx import to_numpy_matrix
import networkx as nx
zkc = nx.karate_club_graph()
order = sorted(list(zkc.nodes()))
A = to_numpy_matrix(zkc, nodelist=order)
I = np.eye(zkc.number_of_nodes())
A_hat = A + I
D_hat = np.array(np.sum(A_hat, axis=0))[0]
D_hat = np.matrix(np.diag(D_hat))
def plot_graph(G, weight_name=None):
    '''
    G: a networkx G
    weight_name: name of the attribute for plotting edge weights (if G is weighted)
    '''
    %matplotlib notebook
    import matplotlib.pyplot as plt
    
    plt.figure()
    pos = nx.spring_layout(G)
    edges = G.edges()
    weights = None
    
    if weight_name:
        weights = [int(G[u][v][weight_name]) for u,v in edges]
        labels = nx.get_edge_attributes(G,weight_name)
        nx.draw_networkx_edge_labels(G,pos,edge_labels=labels)
        nx.draw_networkx(G, pos, edges=edges, width=weights);
    else:
        nodelist1 = []
        nodelist2 = []
        for i in range (34):
            if zkc.nodes[i]['club'] == 'Mr. Hi':
                nodelist1.append(i)
            else:
                nodelist2.append(i)
        #nx.draw_networkx(G, pos, edges=edges);
        nx.draw_networkx_nodes(G, pos, nodelist=nodelist1, node_size=300, node_color='r',alpha = 0.8)
        nx.draw_networkx_nodes(G, pos, nodelist=nodelist2, node_size=300, node_color='b',alpha = 0.8)
        nx.draw_networkx_edges(G, pos, edgelist=edges,alpha =0.4)
plot_graph(zkc)
<IPython.core.display.Javascript object>
W_1 = np.random.normal(
    loc=0, scale=1, size=(zkc.number_of_nodes(), 4))
W_2 = np.random.normal(
    loc=0, size=(W_1.shape[1], 2))
def relu(x):
    return (abs(x) + x) / 2
  
def gcn_layer(A_hat, D_hat, X, W):
    return relu(D_hat**-1 * A_hat * X * W)
H_1 = gcn_layer(A_hat, D_hat, I, W_1)
H_2 = gcn_layer(A_hat, D_hat, H_1, W_2)
output = H_2
feature_representations = {
    node: np.array(output)[node] 
    for node in zkc.nodes()}
feature_representations
{0: array([0.88602091, 0.34237275]),
 1: array([0.40862582, 0.        ]),
 2: array([0.38693926, 0.        ]),
 3: array([0.19478099, 0.10516756]),
 4: array([0.82815959, 0.41738152]),
 5: array([1.1971192 , 0.46978126]),
 6: array([1.2271154 , 0.63378424]),
 7: array([0., 0.]),
 8: array([0.11110005, 0.        ]),
 9: array([0., 0.]),
 10: array([0.6209274 , 0.26495055]),
 11: array([1.60869786, 0.79829349]),
 12: array([0.35029305, 0.56226336]),
 13: array([0.02171053, 0.        ]),
 14: array([0.        , 0.02638456]),
 15: array([0.06979159, 0.68002892]),
 16: array([1.7675629 , 0.82039984]),
 17: array([0.50286326, 0.        ]),
 18: array([0.31509428, 0.29327311]),
 19: array([0.37260057, 0.        ]),
 20: array([0., 0.]),
 21: array([0.70826438, 0.10767323]),
 22: array([0.15022781, 0.25590783]),
 23: array([0.17645064, 0.16650816]),
 24: array([0.29110197, 0.20382017]),
 25: array([0.18688296, 0.14564473]),
 26: array([0.02367803, 0.17550985]),
 27: array([0., 0.]),
 28: array([0.51547931, 0.        ]),
 29: array([0.05318727, 0.16647217]),
 30: array([0.31639705, 0.        ]),
 31: array([0.24761528, 0.03619812]),
 32: array([0.48872535, 0.31039692]),
 33: array([0.62804696, 0.26496685])}
import matplotlib.pyplot as plt
%matplotlib notebook
for i in range (34):
    if zkc.nodes[i]['club'] == 'Mr. Hi':
        plt.scatter(np.array(output)[i,0],np.array(output)[i,1] ,color = 'b',alpha=0.5,s = 100)
    else:
        plt.scatter(np.array(output)[i,0],np.array(output)[i,1] ,color = 'r',alpha=0.5,s = 100)
#plt.scatter(np.array(output)[:,0],np.array(output)[:,1])
<IPython.core.display.Javascript object>

目前來看,這個映射分類效果並不好,待我後續分析補充吧。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章