
DDGK 深度散度图核基于图自编码器的无监督图表示学习与图核实战指南【免费下载链接】google-researchGoogle Research项目地址: https://gitcode.com/gh_mirrors/go/google-research本文以 google-research 仓库中的 graph_embedding/ddgk 实现为主线系统讲解 WWW 2019 论文《DDGK: Learning Graph Representations for Deep Divergence Graph Kernels》的开源复现从环境搭建、TUDatasets 数据加载到图自编码器的编码Encode与注意力评分Score两个阶段的源码级原理再到基于支持向量机的端到端图分类实验。读完本文你将掌握 DDGK 的完整算法链路、全部命令行参数与超参数语义并能基于 MUTAG 等公开图核数据集复现论文实验、评估分类精度。一、DDGK 是什么从图核到深度散度图核图核Graph Kernel是一类用于图分类的经典技术其核心思想是通过某种相似度/距离度量将图嵌入到可被核方法如支持向量机直接消费的特征空间。传统图核如 Weisfeiler-Lehman 核、最短路径核等通常依赖人工设计的结构特征而DDGKDeep Divergence Graph Kernels提出了一种全新的思路通过训练一个图自编码器来度量把一个图映射到另一个图的节点空间时产生的重建误差该误差即两个图之间的深度散度Divergence可直接用作图核。DDGK 的完整表述来自 WWW 2019 论文《DDGK: Learning Graph Representations for Deep Divergence Graph Kernels》作者 Rami Al-Rfou、Dustin Zelle、Bryan Perozzi本仓库 graph_embedding/ddgk/README.md 即该论文的官方参考实现。与论文使用 Google 内部分布式库不同这份开源代码使用单机实现替换了分布式网格搜索并借助 scikit-learn 完成 SVM 的网格搜索README.md 明确说明。相应地README 也提醒大规模数据集上分布式版本是必要的单机版更适合中小规模图数据集。二、仓库结构与代码总览DDGK 模块位于仓库的graph_embedding/ddgk/目录下结构非常精简仅包含 4 个文件文件职责graph_embedding/ddgk/main.py命令行入口数据下载解析、并行编码/评分、距离矩阵构造、SVM 网格搜索graph_embedding/ddgk/model.py核心模型超参数类MutagHParams、图自编码器Encode、注意力评分Score及各类损失函数graph_embedding/ddgk/requirements.txtPython 依赖清单graph_embedding/ddgk/init.py包初始化文件main.py与model.py的职责划分清晰model.py 只关心如何用 TensorFlow 训练自编码器并计算散度main.py 则负责数据集编排与下游分类任务。后续章节将分别深入剖析。三、环境准备虚拟环境与依赖安装按照 README.md 的说明在仓库根目录google-research/下创建全新的虚拟环境并安装依赖# 在 google-research/ 目录下执行 virtualenv -p python3 . source ./bin/activate pip3 install -r graph_embedding/ddgk/requirements.txtgraph_embedding/ddgk/requirements.txt 中锁定的依赖版本如下依赖版本用途absl-py0.7.0命令行参数解析absl.flags与app入口networkx2.3图数据结构与邻接矩阵构造numpy1.16.4数值计算scipy1.3.0距离矩阵计算scipy.spatial.distancesklearn0.0支持向量机与网格搜索svm.SVC、GridSearchCVtensorflow1.14图自编码器训练1.x APItqdm4.32.2训练/评分进度条注意版本适用前提从 model.py 可以看到模型直接使用tensorflow.compat.v1 as tf与tensorflow.contrib.training这是 TensorFlow 1.x 时代的 API依赖清单同样锁定在 TF 1.14 与 networkx 2.3 等旧版本。若在现代 Python / TensorFlow 2.x 环境中运行需要自行搭建对应兼容环境代码本身不做版本迁移。四、数据准备TUDatasets 格式与自动下载解析DDGK 使用由 TUDatasets 收集整理的图核数据集如 MUTAG、PTC 等。这些数据集以 zip 包形式发布每个包内包含一组固定命名的文本文件。main.py中的load()函数graph_embedding/ddgk/main.py#L44-L93实现了完整的下载 → 解压 → 解析为 networkx 图流程def load(): resp urllib.request.urlopen(FLAGS.data_set) unzipped zipfile.ZipFile(io.BytesIO(resp.read())) ...其解析逻辑与数据集文件一一对应zip 内文件解析内容处理方式_node_labels.txt每个节点的整数标签g.add_node(i, labelint(line))文件缺失时跳过_A.txt图的边每行u,v读取所有边加入图_edge_labels.txt每条边的整数标签存在时为每条边附带label属性缺失时忽略_graph_indicator.txt每个节点所属的图编号按编号将节点分组成多个子图_graph_labels.txt每个图的分类标签写入g.graph[label]解析完成后代码打印数据集概况Loaded data_set_url with N graphs.随后通过nx.convert_node_labels_to_integers(g, first_label0)将节点重新编号为从 0 开始的整数graph_embedding/ddgk/main.py#L90-L93以适配后续 one-hot 编码。值得留意的是load()对每个文件都做了存在性容错缺失时静默跳过因此该加载器可以兼容 TUDatasets 中无节点标签或无边标签的数据集变体。五、算法原理与源码实现剖析DDGK 的核心算法分为两个阶段Encode编码与Score评分二者均由 model.py 实现。5.1 图自编码器Encode 阶段Encode(source, ckpt_prefix, hparams)graph_embedding/ddgk/model.py#L183-L216针对每一个源图source graph训练一个独立的图自编码器目标是从节点集合中重建该图的邻接矩阵A nx.adjacency_matrix(source, weightNone) # 邻接矩阵作为监督信号 x tf.one_hot(list(source.nodes()), source.number_of_nodes(), dtypetf.float64) y tf.convert_to_tensor(A.todense(), dtypetf.float64) layer tf.layers.dense(x, hparams.embedding_size, use_biasFalse) # 输入 → embedding for _ in range(hparams.num_dnn_layers): # 多层 DNN layer tf.layers.dense(layer, hparams.embedding_size * 4, activationtf.nn.tanh) logits tf.layers.dense(layer, source.number_of_nodes(), activationtf.nn.tanh) loss AdjMatrixLoss(logits, y) # 逐边 sigmoid 交叉熵 train_op contrib_training.create_train_op( loss, tf.train.AdamOptimizer(hparams.learning_rate), summarize_gradientsFalse)网络结构可以概括为one-hot 节点 → dense(embedding_size) → 4×tanh DNN 层 → dense(N) → 重建邻接矩阵。损失函数AdjMatrixLossgraph_embedding/ddgk/model.py#L87-L89对每条边计算 sigmoid 交叉熵后取均值def AdjMatrixLoss(logits, labels): losses tf.nn.sigmoid_cross_entropy_with_logits(labelslabels, logitslogits) return tf.reduce_mean(losses) # Report loss per edge自编码器以hparams.train_num_epochs默认 600个 epoch 训练完毕后tf.train.Saver(tf.trainable_variables()).save(session, ckpt_prefix)将全部可训练变量保存为 checkpoint供后续评分阶段复用graph_embedding/ddgk/model.py#L216。5.2 注意力映射与评分Score 阶段Score(source, target, ckpt_prefix, hparams)graph_embedding/ddgk/model.py#L219-L286)是 DDGK 的灵魂它把目标图target graph的节点通过注意力机制映射到源图的节点空间再用源图预训练好的自编码器试图重建目标图的邻接矩阵——重建损失越大说明两个图的散度越大。with tf.variable_scope(attention): attention tf.layers.dense(x, source.number_of_nodes(), use_biasFalse) source_node_prob tf.nn.softmax(attention) # 每个 target 节点对 source 节点的注意力分布 layer tf.layers.dense(source_node_prob, hparams.embedding_size, use_biasFalse) for _ in range(hparams.num_dnn_layers): layer tf.layers.dense(layer, hparams.embedding_size * 4, activationtf.nn.tanh) logits tf.layers.dense(layer, source.number_of_nodes(), activationtf.nn.tanh) with tf.variable_scope(attention_reverse): attention_reverse tf.layers.dense(logits, target.number_of_nodes()) target_neighbors_pred tf.nn.sigmoid(attention_reverse) target_neighbors_prob ProbFromCounts(target_neighbors_pred) loss AdjMatrixLoss(attention_reverse, y) # 重建 target 邻接矩阵的损失关键的变量恢复与冻结逻辑graph_embedding/ddgk/model.py#L266-L275vars_to_restore tf.get_collection( tf.GraphKeys.GLOBAL_VARIABLES, scope(?!attention)) # 恢复自编码器全部变量 vars_to_train tf.get_collection( tf.GraphKeys.TRAINABLE_VARIABLES, scopeattention) # 仅训练 attention 变量 train_op contrib_training.create_train_op( loss, tf.train.AdamOptimizer(hparams.learning_rate), variables_to_trainvars_to_train, summarize_gradientsFalse)也就是说评分阶段从 checkpoint 恢复源图自编码器的全部权重并冻结仅训练新加入的attention与attention_reverse变量经过hparams.score_num_epochs默认 600个 epoch 优化后返回最后score_window默认 10个 epoch 的重建损失均值作为该 target 相对该 source 的散度得分graph_embedding/ddgk/model.py#L281-L286for _ in range(hparams.score_num_epochs): losses.append(session.run([train_op, loss])[1]) return losses[-hparams.score_window:]5.3 可选的标签保持损失MutagHParams还暴露了node_label_loss_coefficient与incident_label_loss_coefficient两个标签保持损失的系数开关默认均为 0即关闭。当系数非 0 时评分阶段会追加两类辅助损失graph_embedding/ddgk/model.py#L252-L264节点标签损失NodeLabelLossNeighborNodesLabelLoss约束映射后的节点标签分布与目标图一致关联边标签损失EdgeLabelLossNeighborEdgesLabelsLoss约束映射后的边标签分布与目标图一致。这两类损失分别通过NodesLabels、NeighborNodesLabelsmodel.py与EdgesLabels、NeighborEdgesLabelsmodel.py统计图内标签分布归一化为概率即ProbFromCounts再与映射结果做 softmax 交叉熵。需要强调的是MUTAG 实验中的默认配置为关闭这两个损失系数 0.0即纯粹的邻接矩阵重建散度。六、命令行参数详解main.py 通过absl.flags定义了 4 个命令行参数参数类型默认值含义--data_setstring无必填数据集的 zip 包 URLTUDatasets 格式--num_sourcesint16随机采样的源图数量--num_threadsint32并行编码/评分使用的线程数--working_dirstring无必填工作目录用于存放每个源图的 checkpointmain()入口对四个参数均有断言检查graph_embedding/ddgk/main.py#L97-L100其中working_dir必须是已存在的目录os.path.isdir校验。完整运行命令README 中的示例需在 google-research/ 根目录执行python3 -m graph_embedding.ddgk.main \ --data_sethttps://ls11-www.cs.tu-dortmund.de/people/morris/graphkerneldatasets/MUTAG.zip \ --working_dir~/tmp七、超参数配置详解MutagHParams模型全部超参数集中在MutagHParams()graph_embedding/ddgk/model.py#L47-L68中使用tensorflow.contrib.training.HParams管理超参数默认值含义embedding_size4节点嵌入向量的维度num_dnn_layers4自编码器中 DNN 隐藏层数score_window10评分阶段用于计算损失与准确率的平均窗口learning_rate0.01Adam 优化器学习率train_num_epochs600Encode 阶段自编码器的训练步数score_num_epochs600Score 阶段注意力映射的训练步数node_label_loss_coefficient0.0节点标签保持损失系数0 表示关闭num_node_labels7节点标签类别数对应 MUTAG 数据集的标签规模incident_label_loss_coefficient0.0关联边标签保持损失系数0 表示关闭num_edge_labels4边标签类别数对应 MUTAG 数据集的标签规模从num_node_labels7、num_edge_labels4这两个默认值以及函数名MutagHParams可以推断该超参配置面向 MUTAG 数据集定制若迁移到其他标签数不同的数据集需要同步调整这两个参数否则标签保持损失将无法正确计算。八、端到端实验流程从数据到分类精度main()的完整执行链路graph_embedding/ddgk/main.py#L96-L151可以分为四个阶段阶段一加载数据并采样源图。调用load()解析数据集然后用random.sample(graphs.items(), FLAGS.num_sources)随机抽取num_sources16个图作为源图main.py#L104-L105。阶段二并行编码源图。使用multiprocessing.pool.ThreadPool(FLAGS.num_threads)并行对每个源图执行model.Encode将 checkpoint 保存到working_dir/{图编号}/ckptmain.py#L110-L119。阶段三并行评分所有图。对数据集中每个图含源图自身分别用 16 个源图的 checkpoint 执行model.Score取返回散度序列的最后一个值构成该图的16 维 DDGK 特征向量main.py#L121-L132。阶段四SVM 分类与网格搜索。计算特征向量两两之间的欧氏距离矩阵作为图间相似度特征X np.array([[scores[i][j] for j in sources.keys()] for i in graphs.keys()]) X distance.squareform(distance.pdist(X, metriceuclidean)) Y np.array([v.graph[label] for v in graphs.values()])随后用 scikit-learn 的GridSearchCV对svm.SVC()做交叉验证网格搜索main.py#L138-L149params { C: np.logspace(0, 8, 17).tolist(), # 17 个 C 取值10^0 ~ 10^8 kernel: [linear, rbf, poly, sigmoid], gamma: [auto], max_iter: [-1], } cv ShuffleSplit(n_splits10, test_size0.1, random_state8191) # 10 次随机划分10% 测试 clf GridSearchCV(svm.SVC(), params, cvcv, iidFalse) clf.fit(X, Y) print(10-fold CV score: {:.4f}..format(clf.best_score_))最终输出形如Loaded data_set_url with N graphs. Encoding 16 source graphs... Scoring N target graphs... Performing GridSearchCV w/ DDGK... 10-fold CV score: 0.9104.由于每次运行都会随机采样源图random.sample未固定随机种子不同运行的源图集合不同最终精度会有波动这是该实验流程的固有特性。九、复现结果与论文差异说明README.md 明确披露了复现差异Discrepancies between the codes results and those reported in the paper may occur (e.g., the paper reports91.58forMUTAGbut with this code we attained only91.04).即论文在 MUTAG 上报告的分类精度为91.58%而本仓库单机实现的复现精度约为91.04%。README 给出的解释要点如下论文使用 Google 内部分布式实现进行大规模网格搜索而本仓库代码用单机实现替换并改由 scikit-learn 的GridSearchCV完成 SVM 网格搜索交叉验证策略也不完全相同本仓库使用ShuffleSplit(n_splits10, test_size0.1)的随机划分且固定了random_state8191因此代码结果与论文报告值存在合理范围内的偏差属于预期现象不应视为 bug。同时 README 也强调大规模数据集需要分布式版本单机实现仅适用于中小规模场景。十、引用与联系方式如果你在研究中使用了 Deep Divergence Graph KernelsREADME.md 建议引用以下论文Al-Rfou, R., Zelle, D., Perozzi, B., (2019). DDGK: Learning Graph Representations for Deep Divergence Graph Kernels. InThe Web Conference.对应的 BibTeX 条目inproceedings{47867, title {DDGK: Learning Graph Representations for Deep Divergence Graph Kernels}, author {Rami Al-Rfou and Dustin Zelle and Bryan Perozzi}, year {2019}, booktitle {Proceedings of the 2019 World Wide Web Conference on World Wide Web} }关于该实现的问题或评论可通过 dzellegoogle.com 或 rmyeidgoogle.com 联系作者联系方式见 README.md。【免费下载链接】google-researchGoogle Research项目地址: https://gitcode.com/gh_mirrors/go/google-research创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考