两种节点嵌入的方法:深度游走和 Node2Vec。
随机游走 :随机选择一个邻居,并移动到这个邻居
最简单的随机游走策略是深度游走,即从每个节点开始运行固定长度、无偏的随机游走。
图表示学习
对图 G 中的节点嵌入求和(或平均)
引入“虚拟节点”
匿名步行嵌入,将图表示为这些游走的概率分布
import matplotlib.pyplot as plt # 导入matplotlib库,用于数据可视化
import networkx as nx # 导入networkx库,用于创建和操作复杂网络
import torch # 导入torch库,用于深度学习计算
import torch.nn as nn # 导入torch的神经网络模块
from sklearn.decomposition import PCA # 导入sklearn库的PCA模块,用于数据降维
# 载入空手道俱乐部网络
G = nx.karate_club_graph() # 使用networkx库的karate_club_graph函数加载空手道俱乐部网络
# 可视化图
nx.draw(G, with_labels=True) # 使用networkx库的draw函数绘制空手道俱乐部网络的图形,节点带有标签
torch.manual_seed(1) # 设置torch库的随机数种子,以确保结果可复现
# 初始化嵌入函数
def create_node_emb(num_node=34, embedding_dim=16):
emb = nn.Embedding(num_node, embedding_dim) # 创建一个嵌入层,用于将节点映射到嵌入空间
emb.weight.data = torch.rand(num_node, embedding_dim) # 初始化嵌入层的权重,使用均匀分布填充
return emb
# 初始化嵌入
emb = create_node_emb() # 调用create_node_emb函数初始化嵌入层
# 可视化
def visualize_emb(emb):
X = emb.weight.data.numpy() # 获取嵌入层的权重,转换为numpy数组
pca = PCA(n_components=2) # 创建一个PCA对象,用于将嵌入空间的数据降到2维
components = pca.fit_transform(X) # 对嵌入数据进行PCA降维
plt.figure(figsize=(6, 6)) # 创建一个6x6的绘图窗口
club1_x = [] # 用于存储属于"Mr. Hi"俱乐部的节点的x坐标
club1_y = [] # 用于存储属于"Mr. Hi"俱乐部的节点的y坐标
club2_x = [] # 用于存储属于"Officer"俱乐部的节点的x坐标
club2_y = [] # 用于存储属于"Officer"俱乐部的节点的y坐标
for node in G.nodes(data=True): # 遍历空手道俱乐部网络的所有节点
if node[1]['club'] == 'Mr. Hi': # 判断节点所属的俱乐部
club1_x.append(components[node[0]][0]) # 将节点对应的PCA降维后的x坐标添加到club1_x列表
club1_y.append(components[node[0]][1]) # 将节点对应的PCA降维后的y坐标添加到club1_y列表
else:
club2_x.append(components[node[0]][0]) # 将节点对应的PCA降维后的x坐标添加到club2_x列表
club2_y.append(components[node[0]][1]) # 将节点对应的PCA降维后的y坐标添加到club2_y列表
plt.scatter(club1_x, club1_y, color="red", label="Mr. Hi") # 绘制"Mr. Hi"俱乐部的节点散点图,颜色为红色
plt.scatter(club2_x, club2_y, color="blue", label="Officer") # 绘制"Officer"俱乐部的节点散点图,颜色为蓝色
plt.legend() # 显示图例
plt.show() # 显示图形
# 可视化初始嵌入
visualize_emb(emb) # 调用visualize_emb函数可视化初始嵌入结果
def graph_to_edge_list(G):
# 将 tensor 变成 edge_list
edge_list = []
for edge in G.edges():
edge_list.append(edge)
return edge_list
def edge_list_to_tensor(edge_list):
# 将 edge_list 变成 tesnor
edge_index = torch.tensor([])
edge_index = torch.LongTensor(edge_list).t()
return edge_index
pos_edge_list = graph_to_edge_list(G)
pos_edge_index = edge_list_to_tensor(pos_edge_list)
print("The pos_edge_index tensor has shape {}".format(pos_edge_index.shape))
print("The pos_edge_index tensor has sum value {}".format(torch.sum(pos_edge_index)))
import random
# 采样负边
def sample_negative_edges(G, num_neg_samples):
neg_edge_list = []
# 得到图中所有不存在的边(这个函数只会返回一侧,不会出现逆边)
non_edges_one_side = list(enumerate(nx.non_edges(G)))
neg_edge_list_indices = random.sample(range(0, len(non_edges_one_side)), num_neg_samples)
# 取样num_neg_samples长度的索引
for i in neg_edge_list_indices:
neg_edge_list.append(non_edges_one_side[i][1])
return neg_edge_list
# Sample 78 negative edges
neg_edge_list = sample_negative_edges(G, len(pos_edge_list))
# Transform the negative edge list to tensor
neg_edge_index = edge_list_to_tensor(neg_edge_list)
print("The neg_edge_index tensor has shape {}".format(neg_edge_index.shape))
The pos_edge_index tensor has shape torch.Size([2, 78])
The pos_edge_index tensor has sum value 2535
The neg_edge_index tensor has shape torch.Size([2, 78])