一、DGL库实现版本
utilis.py
- parse_arguments()
- load_ogb_dataset(dataset)
ogb:神经网络基准数据集库
返回:一张图,划分好的边(测试,训练,验证) - drnl_node_labeling(subgraph, src, dst)
双半径节点标记法,获得节点在子图里的位置标记
返回:节点位置标记 - evaluate_hits(name, pos_pred, neg_pred, K)
正负样本命中数值
sampler.py
import 节点标记函数 drnl_node_labeling(subgraph, src, dst)
-
class GraphDataSet(Dataset)
节点list,节点tensor,节点和tensor的索引 -
class PosNegEdgesGenerator(object)
生成正负样本
参数:- 图,
- 划分好的边,
- 每一个正样本对应的负样本个数,
- 子样本的比例
- subsample(self, edges, subsample_ratio)
返回:子样本所有边的tensor
-
class EdgeDataSet(Dataset):
用于加速SEALSampler的辅助数据集 -
class SEALSampler(object):
两节点周围的K跳邻居采样(构成封闭子图)- sample_subgraph(self, target_nodes):
两个节点的tensor,返回两节点所在的封闭子图 - _collate(self, batch):
将 n 张小图打包在一起,生成一张含 n 个不相连小图的大图
将几个label的tensor生成一个新tensor
返回:一张大图,一个新tensor
- sample_subgraph(self, target_nodes):
-
class SEALData(object):
生成正负样本
打印:
Load existing processed {} files :加载已处理的{}文件
Generate {} edges totally :完全生成{}边
Save preprocessed subgraph to :将预处理的子图保存到{}
model.py
- class GCN(nn.Module):
利用邻居更新节点emb - class DGCNN(nn.Module):
整个GN块的处理包含了三次update操作和三次聚合操作:
1.通过原始Edge,起始Node信息,全局信息共同update Edge;
2.通过当前Node 的信息,更新后Edge的信息,全局信息共同update Node;
3.通过更新后Edge、Node信息,和当前的全局信息共同update global;
4.每次更新后,聚合更新前的特征信息和更新后的特征信息作为当前的特征信息。
main
from model import DGCNN, GCN
from sampler import SEALData (生成正负样本)
from utils import evaluate_hits, load_ogb_dataset, parse_arguments
(计算正负样本命中率,/ 一张图,划分好的边(测试,训练,验证)/ 参数解析器)
- def train():
- def evaluate(model, dataloader, device):
- def main(args, print_fn=print):
if name == “main”:
args = parse_arguments()
logger = LightLogging(log_name=“SEAL”, log_path=“./logs”)
main(args, logger.info)
核心:学习一个映射,将子图模式 -> 边的存在性
输入:(A,X)
A是封闭子图的邻接矩阵
X是节点信息矩阵,其中每一行是一个节点的特征向量
如何构造X是重点:
首先,X由:结构节点标签、节点嵌入,节点属性三部分组成
第一个组件是每个节点的结构标签
- 构造一个映射函数:给封闭子图里的每一个节点赋予一个整数标签
- 利用代码:utilis.py 里的 drnl_node_labeling(subgraph, src, dst)
- 双半径节点标记法,获得节点在子图里的位置标记
返回:节点位置标记 7/2/3
1)中心节点x和y是链路所处的目标节点( src, dst )
2)与中心相对位置不同的节点对链路具有不同的结构重要性。
-
在得到标签后,我们使用它们的one-hot编码向量来构造X
-
节点标签函数用在了 def sample_subgraph(self, target_nodes): 里
target_nodes(Tensor):两个目标节点的张量
返回:子图(DGLGraph):子图,返回一个节点数据视图,用于设置/获取节点特性
z = drnl_node_labeling(subgraph, u_id, v_id)
subgraph.ndata["z"] = z
以两个目标节点为基准,利用双半径节点标记法,为子图里的所有节点标记,最终获得子图的节点特征(结构标签)。
第二个组件:节点嵌入
- Given:网络G = (V, E),一组正样本训练边集合Ep⊆E,一组负样本边集合En( En∩E =∅ ,负样本边不属于边集)
- 如果直接在G上生成节点嵌入,则节点嵌入将包含:训练边集的边存在信息,因为正样本边属于边集。
- 我们观察到GNN进行节点嵌入时,可以快速发现这类链路存在信息, 导致正负样本获得的信息有差异。
- 我们的方法是:暂时把负样本En加到E中,并生成节点在新图上的嵌入:G0 = (V, E∪En),使得正负训练集都能在嵌入中加入相同的链路存在信息。
两种方法获得节点嵌入:使用GNN或节点嵌入。
- DGCNN作为默认GNN
- node2vec作为默认嵌入
总的来说,Node2vec的流程如下:
- 使用改进的随机游走的方法(BFS和DFS的混合)产生采样节点序列。
- 将生成的序列通过word2vec模型进行embedding
Official实现版本
总结:
【Main.py使用】
找data name = none 的代码(75-82, 98-112, 120-128) train-name test-name is not none 的,没有use embedding 和use attributes
利用util里的sample_neg生成不重叠的neg_train和neg_test
链路预测代码的困难:服务器崩溃(经常需要重新训练),
社团发现算法的实现困难:先调试类似的小数据集跑社团发现代码,再修改我们的数据集作为输入调试,数据集和测试数据相差十倍,导致可视化运行的时候速度很慢(还在找解决方法),提供了六种社团发现算法,还要逐步应用于数据集比较可视化的性能
八个数据集:SAir NS PB Yeast Celegans Power Router Ecoli
python Main.py --data-name XXX --hop 'auto' --batch-size 1
1.输入“python Main.py——data-name USAir”在USAir网络上尝试SEAL。
在命令后面附加“——seed Y”来使用其他种子。在数据集XXX上再现本文表1中的实验结果。
默认种子为1。
- python Main.py --data-name NS –test-ratio 0.5 --hop ‘auto’ –use-embedding
2.在NS网络上运行SEAL
- 随机移除50% 观察到的链路作为测试链路
- 跳数自动从{1,2}设置
- 并包括node2vec嵌入
- python Main.py --data-name PPI_subgraph –test-ratio 0.5 --use-embedding --use-attribute
- 在包含节点属性的PPI_subgraph上运行SEAL。
- 并包括node2vec嵌入
- 随机移除50% 观察到的链路作为测试链路
- 假定节点属性保存在.mat文件的组中。
- python Main.py –train-name PB_train.txt --test-name PB_test.txt --hop 1
- 在自定义分割上的训练和测试边集上运行SEAL
- “PB_train.txt”的每一行是一个观察到的训练边
- “PB_test.txt”的每一行是一个未观察到的测试边
- 注意,“PB_train.txt”中的边将用于构建观测到的网络,但不需要用“PB_train.txt”中的所有边训练SEAL,特别是当观测到的链接数量很大时。
-
例如,要设置要训练的最大边数,可以附加“—max-train-num 10000”
-
当1跳邻居过多时,例如twitter网络中的一个中心节点拥有数百万个追随者。添加**“——max-nodes-per-hop 100”**使用随机抽样将1hop邻居数量限制在100以下。
- python Main.py –data-name PB –train-name PB_train.txt --test-name PB_test.txt --hop 1
- 仍然使用正训练边集“PB_train.txt”和测试边集“PB_test.txt”
- 但使用“PB. mat”中包含的网络。
(如果“PB.mat”中有一个组,则可以使用 ——use-attribute)
如果希望使用SEAL输出未知链路的链路存在概率,请使用4.
- python Main.py --train-name PB_train.txt –test-name PB_val.txt --hop 1 –save-model
- PB_val.txt是验证模型的边集
- 而 —save-model”将最终模型保存到data/PB_model.pth
- python Main.py --train-name PB_train.txt –test-name PB_test.txt --hop 1 –only-predict
- PB_test.txt 是想要输出边存在概率的节点对(它可能同时包含正负连边)
- 加载已保存的模型,并将“PB_test.txt”的预测结果输出到“data/PB_test_pred.txt”
SEAL代码(从子图、嵌入和属性中学习链接预测)。
SEAL是一种新的链路预测框架,它将链路预测系统地转化为子图分类问题。
对于每个目标链路,SEAL提取其h-hop封闭子图A,并构建其节点信息矩阵X(包含结构节点标签、潜在嵌入和节点的显式属性)。
然后,SEAL将(A, X)输入到图神经网络(GNN)中对链路存在性进行分类,使其可以同时从图结构特征(从A)和潜在/显式特征(从X)中学习,进行链路预测。
- 对于SEAL来说,嵌入和属性都不是必需的。
- 在大多数网络中,SEAL可以学习一个非常好的模型,而不需要使用任何嵌入或属性(因此利用纯图结构)。
- 正如实验所示,在X中包含嵌入甚至可能会损害性能。
- 如果X中不包含节点嵌入,SEAL将成为归纳式链路预测模型。
util_function.py
def sample_neg(net, test_ratio=0.1, train_pos=None, test_pos=None, max_train_num=None, all_unknown_as_negative=False):
返回:正负样本的测试集和训练集
train_pos, train_neg, test_pos, test_neg
def links2subgraphs(A, train_pos, train_neg, test_pos, test_neg, h=1, max_nodes_per_hop=None, node_information=None, no_parallel=False):
返回:训练封闭子图集,测试封闭子图集,?
return train_graphs, test_graphs, max_n_label[‘value’]
def parallel_worker(x):
返回:提取的子图标签
subgraph_extraction_labeling
subgraph_extraction_labeling(ind, A, h=1, max_nodes_per_hop=None, node_information=None):
返回:子图,字体节点标签,节点特征
return g, labels.tolist(), features
def neighbors(fringe, A):
返回:边缘节点集的一跳邻居集
def node_label(subgraph):
提出的双半径节点标记(DRNL)的实现
返回:子图节点位置标签集
def generate_node2vec_embeddings(A, emd_size=128, negative_injection=False, train_neg=None):
返回:node2vec嵌入
def AA(A, test_pos, test_neg):
返回:Adamic-Adar score
return CalcAUC(sim, test_pos, test_neg)
def CN(A, test_pos, test_neg):
返回:共同邻居得分
def CalcAUC(sim, test_pos, test_neg):
返回:AUC
return auc
Pytorch实现版本
-
这个存储库用PyTorch-Geometric库重新实现了SEAL,并在开放图形基准测试(OGB)数据集上测试SEAL。
在提交时,SEAL在OGB排行榜的4个链接预测数据集中的3个中排名第一。
它还支持类似plantoid的数据集,如Cora, CiteSeer和PubMed,其中使用随机0.85/0.05/0.1分割和AUC度量。
使用自定义数据集也很容易,可以用自己的数据集替换Planetoid数据集。 -
前文提到X中可以不需要节点嵌入和节点属性,aka:
SEAL是一种基于gnn的链路预测方法:- 为每个目标链路提取一个k跳包围子图
- 应用一种称为双半径节点标记(DRNL)的标记技巧给每个节点一个整数标签作为其附加特征。
- 这些标记的封闭子图被馈送到图神经网络DGCNN来预测链接的存在。
-
第三步GNN可以用启发式方法替代:Common Neighbor (CN)和adam Adar (AA)
它们在ogbl-ppa和ogbl-collab上的性能比许多GNN方法好得多。
Common Neighbor应用命令:python seal_link_pred.py --use_heuristic CN --dataset ogbl-ppa
utils.py
def neighbors(fringe, A, outgoing=True):
- 找到图A的一跳邻居集
A是scipy的CSR类型压缩矩阵
outgoing表示是否是有向图
def k_hop_subgraph(src, dst, num_hops, A, sample_ratio=1.0,
max_nodes_per_hop=None, node_features=None,
y=1, directed=False, A_csc=None):
- 从图A中找到(u,v )边的k-hop封闭子图
返回:节点集,子图,节点特征
def drnl_node_labeling(adj, src, dst):
- 返回:子图节点的双半径节点标记(DRNL)
def de_node_labeling(adj, src, dst, max_dist=3):
- 返回:节点距离编码
def de_plus_node_labeling(adj, src, dst, max_dist=100):
距离编码plus,本质与DRNL相同
def construct_pyg_graph(node_ids, adj, dists, node_features, y, node_label=‘drnl’):
- 返回:从scipy csr邻接矩阵构造pytorch_geometric图
def extract_enclosing_subgraphs(link_index, A, x, y, num_hops, node_label=‘drnl’,
ratio_per_hop=1.0, max_nodes_per_hop=None,
directed=False, A_csc=None):
- 从图A中为link_index中的所有边提取封闭子图
def do_edge_split(dataset, fast_split=False, val_ratio=0.05, test_ratio=0.1):
- 数据集边分割,验证训练测试
def get_pos_neg_edges(split, split_edge, edge_index, num_nodes, percent=100):
- 获得正负样本边集
计算共同邻居启发式得分,亚当-阿达尔启发式评分,个性化PageRank启发式评分。
models.py
构造了GCN,SAGE,DGCNN,GIN四个网络
seal_link_pred.py
构造了SEALDataset类:
- 初始化
- 处理文件名
- 处理数据集
- 用权重将多条边压缩成单边
- 提取正负样本边集的封闭子图
构造SEALDynamicDataset类:
- 初始化
- 用权重将多条边压缩成单边