相比于简单的文本和图像,这种网络类型的非结构化的数据非常复杂,处理它的难点包括:
那么对于这类数据我们该如何建模呢?能否将深度学习进行扩展使得能够建模该类数据呢?这些问题促使了图神经网络的出现与发展。
当然,其实GCN的缺点也是很显然易见的,第一,GCN需要将整个图放到内存和显存,这将非常耗内存和显存,处理不了大图;第二,GCN在训练时需要知道整个图的结构信息(包括待预测的节点), 这在现实某些任务中也不能实现(比如用今天训练的图模型预测明天的数据,那么明天的节点是拿不到的)。
GCN四个特征:
关于GCN常见问题:
找了网上很多GCN代码,都不是太能看得懂,最后找到一个写的很详细的使用DGL图神经网络库来实现的代码。并且根据需要改进了代码,实现节点的多分类问题。https://zhuanlan.zhihu.com/p/93828551
空手道俱乐部是一个包含34个成员的社交网络,有成对的文档交互发生在成员之间。俱乐部后来分裂成两个群体,分别以指导员(节点0)和俱乐部主席(节点33)为首,整个网络可视化如下图:
任务是预测每个节点会加入哪一边(0or33)
创建Zachary’s karate club图如下:
import dgl
def build_karate_club_graph():
g = dgl.DGLGraph()
# add 34 nodes into the graph; nodes are labeled from 0~33
g.add_nodes(34)
# all 78 edges as a list of tuples
edge_list = [(1, 0), (2, 0), (2, 1), (3, 0), (3, 1), (3, 2),
(4, 0), (5, 0), (6, 0), (6, 4), (6, 5), (7, 0), (7, 1),
(7, 2), (7, 3), (8, 0), (8, 2), (9, 2), (10, 0), (10, 4),
(10, 5), (11, 0), (12, 0), (12, 3), (13, 0), (13, 1), (13, 2),
(13, 3), (16, 5), (16, 6), (17, 0), (17, 1), (19, 0), (19, 1),
(21, 0), (21, 1), (25, 23), (25, 24), (27, 2), (27, 23),
(27, 24), (28, 2), (29, 23), (29, 26), (30, 1), (30, 8),
(31, 0), (31, 24), (31, 25), (31, 28), (32, 2), (32, 8),
(32, 14), (32, 15), (32, 18), (32, 20), (32, 22), (32, 23),
(32, 29), (32, 30), (32, 31), (33, 8), (33, 9), (33, 13),
(33, 14), (33, 15), (33, 18), (33, 19), (33, 20), (33, 22),
(33, 23), (33, 26), (33, 27), (33, 28), (33, 29), (33, 30),
(33, 31), (33, 32)]
# add edges two lists of nodes: src and dst
src, dst = tuple(zip(*edge_list))
g.add_edges(src, dst)
# edges are directional in DGL; make them bi-directional
g.add_edges(dst, src)
return g
输出创建的节点和边的数量
G = build_karate_club_graph()
print('We have %d nodes.' % G.number_of_nodes())
print('We have %d edges.' % G.number_of_edges())
>>>We have 34 nodes.
>>>We have 156 edges.
利用networkx画graph
import networkx as nx
import matplotlib.pyplot as plt
fig = plt.figure(dpi=150)
nx_G = G.to_networkx().to_undirected()
pos = nx.kamada_kawai_layout(nx_G)
nx.draw(nx_G, pos, with_labels=True, node_color=[[.7, .7, .7]])
plt.show()
完整绘制网络代码如下:
import dgl
import matplotlib
import torch
# https://zhuanlan.zhihu.com/p/93828551
import networkx as nx
def build_karate_club_graph():
g = dgl.DGLGraph()
# add 34 nodes into the graph; nodes are labeled from 0~33
g.add_nodes(34)
# all 78 edges as a list of tuples
edge_list = [(1, 0), (2, 0), (2, 1), (3, 0), (3, 1), (3, 2),
(4, 0), (5, 0), (6, 0), (6, 4), (6, 5), (7, 0), (7, 1),
(7, 2), (7, 3), (8, 0), (8, 2), (9, 2), (10, 0), (10, 4),
(10, 5), (11, 0), (12, 0), (12, 3), (13, 0), (13, 1), (13, 2),
(13, 3), (16, 5), (16, 6), (17, 0), (17, 1), (19, 0), (19, 1),
(21, 0), (21, 1), (25, 23), (25, 24), (27, 2), (27, 23),
(27, 24), (28, 2), (29, 23), (29, 26), (30, 1), (30, 8),
(31, 0), (31, 24), (31, 25), (31, 28), (32, 2), (32, 8),
(32, 14), (32, 15), (32, 18), (32, 20), (32, 22), (32, 23),
(32, 29), (32, 30), (32, 31), (33, 8), (33, 9), (33, 13),
(33, 14), (33, 15), (33, 18), (33, 19), (33, 20), (33, 22),
(33, 23), (33, 26), (33, 27), (33, 28), (33, 29), (33, 30),
(33, 31), (33, 32)]
# add edges two lists of nodes: src and dst
src, dst = tuple(zip(*edge_list))
g.add_edges(src, dst)
# edges are directional in DGL; make them bi-directional
g.add_edges(dst, src)
return g
G = build_karate_club_graph()
print('We have %d nodes.' % G.number_of_nodes())
print('We have %d edges.' % G.number_of_edges())
G.ndata['feat'] = torch.eye(34)
import networkx as nx
import matplotlib.pyplot as plt
fig = plt.figure(dpi=150)
nx_G = G.to_networkx().to_undirected()
pos = nx.kamada_kawai_layout(nx_G)
nx.draw(nx_G, pos, with_labels=True, node_color=[[.7, .7, .7]])
plt.show()
Graph neural networks associate features with nodes and edges for training. For our classification example, we assign each node an input feature as a one-hot vector: node vivi‘s feature vector is [0,…,1,…,0][0,…,1,…,0], where the ithith position is one.
In DGL, you can add features for all nodes at once, using a feature tensor that batches node features along the first dimension. The code below adds the one-hot feature for all nodes:
联合边和节点信息做图训练。对于整个节点分类的例子,将每个节点的特征转化成one-hot向量:节点变为[0,…,1,…,0][0,…,1,…,0],对应的位置上的数值为1。
在DGL里面,可以使用一个feature张量在第一维上一次性给所有的节点添加特征,代码如下
import torch
G.ndata['feat'] = torch.eye(34)
输出节点看赋值结果
# print out node 2's input feature
print(G.nodes[2].data['feat'])
# print out node 10 and 11's input features
print(G.nodes[[10, 11]].data['feat'])
>>>tensor([[0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]])
>>>tensor([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]])
简单的定义一个图卷积神经网络框架。(推荐阅读原始论文获取更多细节)
上面整个步骤可以看作一个message-passing的范式:每个节点会接受邻居节点的信息从而更新自身的节点表示。一个图形化的例子就是:向目标节点流动
接下来,是一个使用GDL实现GCN的例子
import torch.nn as nn
import torch.nn.functional as F
# 主要定义message方法和reduce方法
# NOTE: 为了易于理解,整个教程忽略了归一化的步骤
def gcn_message(edges):
# 参数:batch of edges
# 得到计算后的batch of edges的信息,这里直接返回边的源节点的feature.
return {'msg' : edges.src['h']}
def gcn_reduce(nodes):
# 参数:batch of nodes.
# 得到计算后batch of nodes的信息,这里返回每个节点mailbox里的msg的和
return {'h' : torch.sum(nodes.mailbox['msg'], dim=1)}
# Define the GCNLayer module
class GCNLayer(nn.Module):
def __init__(self, in_feats, out_feats):
super(GCNLayer, self).__init__()
self.linear = nn.Linear(in_feats, out_feats)
def forward(self, g, inputs):
# g 为图对象; inputs 为节点特征矩阵
# 设置图的节点特征
g.ndata['h'] = inputs
# 触发边的信息传递
g.send(g.edges(), gcn_message)
# 触发节点的聚合函数
g.recv(g.nodes(), gcn_reduce)
# 取得节点向量
h = g.ndata.pop('h')
# 线性变换
return self.linear(h)
通常情况下,节点通过message方法传播计算后的节点特征,reduce方法负责将收集到的节点特征进行聚合。
下面定义一个更深的GCN模型,包含两层 GCN层:
class GCN(nn.Module):
def __int__(self, in_feats, hidden_size, num_classes):
super(GCN, self).__int__()
self.gcn1 = GCNLayer(in_feats, hidden_size)
self.gcn2 = GCNLayer(hidden_size, num_classes)
def forward(self, g, inputs):
h = self.gcn1(g, inputs)
h = torch.relu(h)
h = self.gcn2(g, h)
return h
# 以空手道俱乐部为例
# 第一层将34层的输入转化为隐层为5
# 第二层将隐层转化为最终的分类数2
net = GCN(34,5,2)
我们使用one-hot向量初始化节点。因为是一个半监督的设定,仅有指导员(节点0)和俱乐部主席(节点33)被分配了label,实现如下:
inputs = torch.eye(34)
labeled_nodes = torch.tensor([0, 33]) # only the instructor and the president nodes are labeled
labels = torch.tensor([0, 1]) # their labels are different
训练的步骤和PyTorch模型一样,(1)创建优化器,(2)输入input数据,(3)计算loss,(4)使用反向传播优化模型
optimizer = torch.optim.Adam(net.parameters(), lr=0.01)
all_logits = []
for epoch in range(30):
logits = net(G, inputs)
# we save the logits for visualization later
all_logits.append(logits.detach())
logp = F.log_softmax(logits, 1)
# we only compute loss for labeled nodes
loss = F.nll_loss(logp[labeled_nodes], labels)
optimizer.zero_grad()
loss.backward()
optimizer.step()
print('Epoch %d | Loss: %.4f' % (epoch, loss.item()))
output:
Epoch 0 | Loss: 0.4679
Epoch 1 | Loss: 0.3354
Epoch 2 | Loss: 0.2421
Epoch 3 | Loss: 0.1677
Epoch 4 | Loss: 0.1110
Epoch 5 | Loss: 0.0714
Epoch 6 | Loss: 0.0437
Epoch 7 | Loss: 0.0259
Epoch 8 | Loss: 0.0151
Epoch 9 | Loss: 0.0089
Epoch 10 | Loss: 0.0053
Epoch 11 | Loss: 0.0032
Epoch 12 | Loss: 0.0020
Epoch 13 | Loss: 0.0013
Epoch 14 | Loss: 0.0009
Epoch 15 | Loss: 0.0006
Epoch 16 | Loss: 0.0004
Epoch 17 | Loss: 0.0003
Epoch 18 | Loss: 0.0002
Epoch 19 | Loss: 0.0002
Epoch 20 | Loss: 0.0001
Epoch 21 | Loss: 0.0001
Epoch 22 | Loss: 0.0001
Epoch 23 | Loss: 0.0001
Epoch 24 | Loss: 0.0001
Epoch 25 | Loss: 0.0000
Epoch 26 | Loss: 0.0000
Epoch 27 | Loss: 0.0000
Epoch 28 | Loss: 0.0000
Epoch 29 | Loss: 0.0000
这是一个非常简单的小例子,甚至没有划分验证集和测试集。因此,因为模型最后输出了每个节点的二维向量,我们可以轻易的在2D的空间将这个过程可视化出来,下面的代码动态的展示了训练过程中从开始的状态到到最后所有节点都线性可分的过程。
import matplotlib.animation as animation
import matplotlib.pyplot as plt
def draw(i):
cls1color = '#00FFFF'
cls2color = '#FF00FF'
pos = {}
colors = []
for v in range(34):
pos[v] = all_logits[i][v].numpy()
cls = pos[v].argmax()
colors.append(cls1color if cls else cls2color)
ax.cla()
ax.axis('off')
ax.set_title('Epoch: %d' % i)
nx.draw_networkx(nx_G.to_undirected(), pos, node_color=colors,
with_labels=True, node_size=300, ax=ax)
fig = plt.figure(dpi=150)
fig.clf()
ax = fig.subplots()
draw(0) # draw the prediction of the first epoch
plt.close()
下面的动态过程展示了模型经过一段训练之后能够准确预测节点属于哪个群组。
ani = animation.FuncAnimation(fig, draw, frames=len(all_logits), interval=200)
import dgl
import matplotlib
import torch
# https://zhuanlan.zhihu.com/p/93828551
import networkx as nx
def build_karate_club_graph():
g = dgl.DGLGraph()
# add 34 nodes into the graph; nodes are labeled from 0~33
g.add_nodes(34)
# all 78 edges as a list of tuples
edge_list = [(1, 0), (2, 0), (2, 1), (3, 0), (3, 1), (3, 2),
(4, 0), (5, 0), (6, 0), (6, 4), (6, 5), (7, 0), (7, 1),
(7, 2), (7, 3), (8, 0), (8, 2), (9, 2), (10, 0), (10, 4),
(10, 5), (11, 0), (12, 0), (12, 3), (13, 0), (13, 1), (13, 2),
(13, 3), (16, 5), (16, 6), (17, 0), (17, 1), (19, 0), (19, 1),
(21, 0), (21, 1), (25, 23), (25, 24), (27, 2), (27, 23),
(27, 24), (28, 2), (29, 23), (29, 26), (30, 1), (30, 8),
(31, 0), (31, 24), (31, 25), (31, 28), (32, 2), (32, 8),
(32, 14), (32, 15), (32, 18), (32, 20), (32, 22), (32, 23),
(32, 29), (32, 30), (32, 31), (33, 8), (33, 9), (33, 13),
(33, 14), (33, 15), (33, 18), (33, 19), (33, 20), (33, 22),
(33, 23), (33, 26), (33, 27), (33, 28), (33, 29), (33, 30),
(33, 31), (33, 32)]
# add edges two lists of nodes: src and dst
src, dst = tuple(zip(*edge_list))
g.add_edges(src, dst)
# edges are directional in DGL; make them bi-directional
g.add_edges(dst, src)
return g
G = build_karate_club_graph()
print('We have %d nodes.' % G.number_of_nodes())
print('We have %d edges.' % G.number_of_edges())
G.ndata['feat'] = torch.eye(34)
import torch.nn as nn
import torch.nn.functional as F
# 主要定义message方法和reduce方法
# NOTE: 为了易于理解,整个教程忽略了归一化的步骤
def gcn_message(edges):
# 参数:batch of edges
# 得到计算后的batch of edges的信息,这里直接返回边的源节点的feature.
return {'msg' : edges.src['h']}
def gcn_reduce(nodes):
# 参数:batch of nodes.
# 得到计算后batch of nodes的信息,这里返回每个节点mailbox里的msg的和
return {'h' : torch.sum(nodes.mailbox['msg'], dim=1)}
# Define the GCNLayer module
class GCNLayer(nn.Module):
def __init__(self, in_feats, out_feats):
super(GCNLayer, self).__init__()
self.linear = nn.Linear(in_feats, out_feats)
def forward(self, g, inputs):
# g 为图对象; inputs 为节点特征矩阵
# 设置图的节点特征
g.ndata['h'] = inputs
# 触发边的信息传递
g.send(g.edges(), gcn_message)
# 触发节点的聚合函数
g.recv(g.nodes(), gcn_reduce)
# 取得节点向量
h = g.ndata.pop('h')
# 线性变换
return self.linear(h)
class GCN(nn.Module):
def __init__(self, in_feats, hidden_size, num_classes):
super(GCN, self).__init__()
self.gcn1 = GCNLayer(in_feats, hidden_size)
self.gcn2 = GCNLayer(hidden_size, num_classes)
def forward(self, g, inputs):
h = self.gcn1(g, inputs)
h = torch.relu(h)
h = self.gcn2(g, h)
return h
# 以空手道俱乐部为例
# 第一层将34层的输入转化为隐层为8
# 第二层将隐层转化为最终的分类数2
net = GCN(34, 8, 2)
inputs = torch.eye(34)
labeled_nodes = torch.tensor([0, 33]) # only the instructor and the president nodes are labeled
labels = torch.tensor([0, 1]) # their labels are different
# net = GCN(34, 8, 3)
# inputs = torch.eye(34)
# labeled_nodes = torch.tensor([0, 2, 33]) # only the instructor and the president nodes are labeled
# labels = torch.tensor([0, 1, 2]) # their labels are different
optimizer = torch.optim.Adam(net.parameters(), lr=0.01)
all_logits = []
import matplotlib.animation as animation
import matplotlib.pyplot as plt
nx_G = G.to_networkx().to_undirected()
# Kamada-Kawaii layout usually looks pretty for arbitrary graphs
pos = nx.kamada_kawai_layout(nx_G)
def draw(i):
# fig = plt.figure(dpi=150)
plt.clf()# 此时不能调用此函数,不然之前的点将被清空。
color = [ 'green', 'b', 'r', '#7FFFD4', '#FFC0CB', '#00022e','#F0F8FF']
# cls1color = '#00FFFF'
# cls2color = '#FF00FF'
pos = {}
colors = []
for v in range(34):
pos[v] = all_logits[i][v].numpy()
cls = pos[v].argmax()
colors.append(color[cls])
# colors.append(cls1color if cls else cls2color)
# ax = fig.subplots()
ax.cla()
ax.axis('off')
ax.set_title('Epoch: %d' % i)
nx.draw_networkx(nx_G.to_undirected(), pos, node_color=colors,
with_labels=True, node_size=200)
for epoch in range(40):
logits = net(G, inputs)
# we save the logits for visualization later
all_logits.append(logits.detach())
logp = F.log_softmax(logits, 1)
# we only compute loss for labeled nodes
loss = F.nll_loss(logp[labeled_nodes], labels)
optimizer.zero_grad()
loss.backward()
optimizer.step()
print('Epoch %d | Loss: %.4f' % (epoch, loss.item()))
fig = plt.figure(dpi=150)
fig.clf()
ax = fig.subplots()
# draw(0) # draw the prediction of the first epoch
ani = animation.FuncAnimation(fig, draw, frames=len(all_logits), interval=200)
plt.pause(30)
# plt.close()
半监督学习动态过程:
将整个网络节点分为3类,原来[0, 33]两个分家的人变为,[0, 2, 33]这三个人分家。三者对应的标签为[0, 1, 2],修改这部分代码:
net = GCN(34, 8, 3)
inputs = torch.eye(34)
labeled_nodes = torch.tensor([0, 2, 33]) # only the instructor and the president nodes are labeled
labels = torch.tensor([0, 1, 2]) # their labels are different
直接使用上面的画图,是有错误的,2D的左边不能绘制三维的数据,这里有两种方法绘制分类后的节点:
1、使用PCA降维,在二位坐标中显示出来,但是会有一点的信息损失;
2、直接使用三维的坐标展示;
将原来画图的那部分注释掉,然后之间显示最后的分类点
# fig = plt.figure(dpi=150)
# fig.clf()
# ax = fig.subplots()
# # draw(0) # draw the prediction of the first epoch
# ani = animation.FuncAnimation(fig, draw, frames=len(all_logits), interval=200)
# plt.pause(30)
# # plt.close()
import matplotlib.pyplot as plt #加载matplotlib用于数据的可视化
from sklearn.decomposition import PCA #加载PCA算法包
x, y= [], []
for i in range(34):
x.append(all_logits[39][i].numpy())
y.append(all_logits[39][i].numpy().argmax())
print("{} {}".format(all_logits[39][i].numpy(), all_logits[39][i].numpy().argmax()))
pca=PCA(n_components=2) #加载PCA算法,设置降维后主成分数目为2
reduced_x=pca.fit_transform(x)#对样本进行降维
print(reduced_x)
# #可视化
color = ['b', 'r', '#7FFFD4', '#FFC0CB', '#00022e','#F0F8FF', 'green']
for index, item in enumerate(reduced_x):
plt.scatter(item[0], item[1], c= color[y[index]])
plt.show()
GCN训练的最后一步的结果与对应的分类值:
3D绘图代码:
# 三维显示
import math
import numpy as np
import matplotlib.pyplot as plt
import time
from mpl_toolkits.mplot3d import Axes3D #绘制3D坐标的函数
x, c= [], []
color = ['b', 'r','#7FFFD4','#FFC0CB', '#00022e','#F0F8FF', 'green']
for i in range(34):
x.append(all_logits[39][i].numpy())
c.append(all_logits[39][i].numpy().argmax())
print("{} {}".format(all_logits[39][i].numpy(), all_logits[39][i].numpy().argmax()))
x1 = [i[0] for i in x]
x2 = [i[1] for i in x]
y = [i[2] for i in x]
d = []
for i in range(len(x1)):
d.append(color[c[i]])
fig1=plt.figure()#创建一个绘图对象
ax=Axes3D(fig1)#用这个绘图对象创建一个Axes对象(有3D坐标)
ax.scatter(x1, x2, y, c = d)#用取样点(x,y,z)去构建曲面
plt.show()#显示模块中的所有绘图对象
import dgl
import matplotlib
import torch
# https://zhuanlan.zhihu.com/p/93828551
import networkx as nx
def build_karate_club_graph():
g = dgl.DGLGraph()
# add 34 nodes into the graph; nodes are labeled from 0~33
g.add_nodes(34)
# all 78 edges as a list of tuples
edge_list = [(1, 0), (2, 0), (2, 1), (3, 0), (3, 1), (3, 2),
(4, 0), (5, 0), (6, 0), (6, 4), (6, 5), (7, 0), (7, 1),
(7, 2), (7, 3), (8, 0), (8, 2), (9, 2), (10, 0), (10, 4),
(10, 5), (11, 0), (12, 0), (12, 3), (13, 0), (13, 1), (13, 2),
(13, 3), (16, 5), (16, 6), (17, 0), (17, 1), (19, 0), (19, 1),
(21, 0), (21, 1), (25, 23), (25, 24), (27, 2), (27, 23),
(27, 24), (28, 2), (29, 23), (29, 26), (30, 1), (30, 8),
(31, 0), (31, 24), (31, 25), (31, 28), (32, 2), (32, 8),
(32, 14), (32, 15), (32, 18), (32, 20), (32, 22), (32, 23),
(32, 29), (32, 30), (32, 31), (33, 8), (33, 9), (33, 13),
(33, 14), (33, 15), (33, 18), (33, 19), (33, 20), (33, 22),
(33, 23), (33, 26), (33, 27), (33, 28), (33, 29), (33, 30),
(33, 31), (33, 32)]
# add edges two lists of nodes: src and dst
src, dst = tuple(zip(*edge_list))
g.add_edges(src, dst)
# edges are directional in DGL; make them bi-directional
g.add_edges(dst, src)
return g
G = build_karate_club_graph()
print('We have %d nodes.' % G.number_of_nodes())
print('We have %d edges.' % G.number_of_edges())
G.ndata['feat'] = torch.eye(34)
import torch.nn as nn
import torch.nn.functional as F
# 主要定义message方法和reduce方法
# NOTE: 为了易于理解,整个教程忽略了归一化的步骤
def gcn_message(edges):
# 参数:batch of edges
# 得到计算后的batch of edges的信息,这里直接返回边的源节点的feature.
return {'msg' : edges.src['h']}
def gcn_reduce(nodes):
# 参数:batch of nodes.
# 得到计算后batch of nodes的信息,这里返回每个节点mailbox里的msg的和
return {'h' : torch.sum(nodes.mailbox['msg'], dim=1)}
# Define the GCNLayer module
class GCNLayer(nn.Module):
def __init__(self, in_feats, out_feats):
super(GCNLayer, self).__init__()
self.linear = nn.Linear(in_feats, out_feats)
def forward(self, g, inputs):
# g 为图对象; inputs 为节点特征矩阵
# 设置图的节点特征
g.ndata['h'] = inputs
# 触发边的信息传递
g.send(g.edges(), gcn_message)
# 触发节点的聚合函数
g.recv(g.nodes(), gcn_reduce)
# 取得节点向量
h = g.ndata.pop('h')
# 线性变换
return self.linear(h)
class GCN(nn.Module):
def __init__(self, in_feats, hidden_size, num_classes):
super(GCN, self).__init__()
self.gcn1 = GCNLayer(in_feats, hidden_size)
self.gcn2 = GCNLayer(hidden_size, num_classes)
def forward(self, g, inputs):
h = self.gcn1(g, inputs)
h = torch.relu(h)
h = self.gcn2(g, h)
return h
# 以空手道俱乐部为例
# 第一层将34层的输入转化为隐层为8
# 第二层将隐层转化为最终的分类数2
# net = GCN(34, 8, 2)
# inputs = torch.eye(34)
# labeled_nodes = torch.tensor([0, 33]) # only the instructor and the president nodes are labeled
# labels = torch.tensor([0, 1]) # their labels are different
net = GCN(34, 8, 3)
inputs = torch.eye(34)
labeled_nodes = torch.tensor([0, 2, 33]) # only the instructor and the president nodes are labeled
labels = torch.tensor([0, 1, 2]) # their labels are different
optimizer = torch.optim.Adam(net.parameters(), lr=0.01)
all_logits = []
import matplotlib.animation as animation
import matplotlib.pyplot as plt
nx_G = G.to_networkx().to_undirected()
# Kamada-Kawaii layout usually looks pretty for arbitrary graphs
pos = nx.kamada_kawai_layout(nx_G)
def draw(i):
# fig = plt.figure(dpi=150)
plt.clf()# 此时不能调用此函数,不然之前的点将被清空。
color = [ 'green', 'b', 'r', '#7FFFD4', '#FFC0CB', '#00022e','#F0F8FF']
# cls1color = '#00FFFF'
# cls2color = '#FF00FF'
pos = {}
colors = []
for v in range(34):
pos[v] = all_logits[i][v].numpy()
cls = pos[v].argmax()
colors.append(color[cls])
# colors.append(cls1color if cls else cls2color)
# ax = fig.subplots()
ax.cla()
ax.axis('off')
ax.set_title('Epoch: %d' % i)
nx.draw_networkx(nx_G.to_undirected(), pos, node_color=colors,
with_labels=True, node_size=200)
for epoch in range(40):
logits = net(G, inputs)
# we save the logits for visualization later
all_logits.append(logits.detach())
logp = F.log_softmax(logits, 1)
# we only compute loss for labeled nodes
loss = F.nll_loss(logp[labeled_nodes], labels)
optimizer.zero_grad()
loss.backward()
optimizer.step()
print('Epoch %d | Loss: %.4f' % (epoch, loss.item()))
# 动态显示
# fig = plt.figure(dpi=150)
# fig.clf()
# ax = fig.subplots()
# # draw(0) # draw the prediction of the first epoch
# ani = animation.FuncAnimation(fig, draw, frames=len(all_logits), interval=200)
# plt.pause(30)
# # plt.close()
# 降维二维显示
# import matplotlib.pyplot as plt #加载matplotlib用于数据的可视化
# from sklearn.decomposition import PCA #加载PCA算法包
#
# x, y= [], []
# for i in range(34):
# x.append(all_logits[39][i].numpy())
# y.append(all_logits[39][i].numpy().argmax())
# print("{} {}".format(all_logits[39][i].numpy(), all_logits[39][i].numpy().argmax()))
#
#
# pca=PCA(n_components=2) #加载PCA算法,设置降维后主成分数目为2
# reduced_x=pca.fit_transform(x)#对样本进行降维
# print(reduced_x)
#
# # #可视化
# color = ['b', 'r', '#7FFFD4', '#FFC0CB', '#00022e','#F0F8FF', 'green']
# for index, item in enumerate(reduced_x):
# plt.scatter(item[0], item[1], c= color[y[index]])
# plt.show()
# 三维显示
import math
import numpy as np
import matplotlib.pyplot as plt
import time
from mpl_toolkits.mplot3d import Axes3D #绘制3D坐标的函数
x, c= [], []
color = ['b', 'r','#7FFFD4','#FFC0CB', '#00022e','#F0F8FF', 'green']
for i in range(34):
x.append(all_logits[39][i].numpy())
c.append(all_logits[39][i].numpy().argmax())
print("{} {}".format(all_logits[39][i].numpy(), all_logits[39][i].numpy().argmax()))
x1 = [i[0] for i in x]
x2 = [i[1] for i in x]
y = [i[2] for i in x]
d = []
for i in range(len(x1)):
d.append(color[c[i]])
fig1=plt.figure()#创建一个绘图对象
ax=Axes3D(fig1)#用这个绘图对象创建一个Axes对象(有3D坐标)
ax.scatter(x1, x2, y, c = d)#用取样点(x,y,z)去构建曲面
plt.show()#显示模块中的所有绘图对象
https://zhuanlan.zhihu.com/p/93828551
https://blog.csdn.net/liuweiyuxiang/article/details/98957612
http://tkipf.github.io/graph-convolutional-networks/
GitHub图卷积网络的代码:https : //github.com/tkipf/gcn
图神经网络库DGL零基础上手指南_01(半监督学习) https://zhuanlan.zhihu.com/p/93828551
https://github.com/TobiasSkovgaardJepsen/posts
详解GCN源码:https://blog.csdn.net/yyl424525/article/details/100634211?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-4.compare&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-4.compare
GCN图卷积网络简单实现 https://blog.csdn.net/u014281392/article/details/90174664?utm_medium=distribute.pc_relevant.none-task-blog-baidulandingword-5&spm=1001.2101.3001.4242