Graph Engine

图机器学习

从图数据、标签与划分出发,理解四类图学习任务的模型结构、训练目标、评估协议,以及模型保存和推理流程。

#任务与模型兼容关系

图机器学习通过 train、infer 和 visualize_model_architecture 工具使用。节点分类、链接预测与预训练读取会话的活动单图;图分类读取独立的多图数据集。这里的节点分类回答‘这个节点属于哪一类’,链接预测估计节点对的连接分数,图分类预测整张图的标签,预训练则生成后续任务可复用的节点嵌入向量。

task / method模型与输入计算目标与结果
node_classification必填 model:APPNP / GCN / GAT / GATV2 / GEN / GIN / SAGE / TRANSFORMER;单图节点特征与标签交叉熵;输出节点类别与 softmax 概率
link_prediction必填 model:APPNP / GCN / GAT / GATV2 / GEN / GIN / SAGE / TRANSFORMER;单图结构正负边二元交叉熵;输出节点对分数与连接概率
graph_classification必填 model:APPNP / GCN / GAT / GATV2 / GEN / GIN / SAGE / TRANSFORMER;HIV / PCBA 多图数据集忽略缺失标签的二元交叉熵;输出图级单任务或多任务预测
pretrain / node2vec不要求 model;读取单图结构,在 CPU 上训练随机游走正负上下文学习;保存节点嵌入向量
pretrain / contrastive_learning、graph_contrastive_learning、mask_autoencoding、graph_mask_autoencoding必填 model:APPNP / GCN / GAT / GATV2 / GEN / GIN / SAGE / TRANSFORMER;单图特征与结构这些名称目前共享同一个 GNN 边重构训练分支

已注册的骨干网络名称为 APPNP / GCN / GAT / GATV2 / GEN / GIN / SAGE / TRANSFORMER;SAGE 对应 GraphSAGE。pretrain 必须填写 method;其余三个监督任务及 GNN 预训练必须填写 model。GNN 预训练方法名称不意味着分别实现了图增广对比学习、特征掩码或掩码自动编码器,应以实际损失函数为准。

#单图张量、特征与标签

设 N 为节点数、E 为存储边数、F 为输入特征维度、D 为输出嵌入维度。模型读取 GraphData 的节点顺序与 edge_index,不先转成文本再重建图。消息传递使用形状为 [2, E] 的边索引;语义为无向的图会生成双向边索引副本,供邻居聚合使用,不改写活动图。边权重和边属性当前没有传入当前 GNN 骨干网络。

输入形状与处理
node_feat转换为 float32。[N] 扩展为 [N, 1];高于二维时按节点展平为 [N, F]。已有特征不会自动标准化。
缺失 node_feat生成 [入度, 出度, 总度, 常数 1],得到 [N, 4];每列除以该列最大值与 1 中的较大者。
node_label:[N]标量类别值映射到连续类别索引;缺失标签不进入监督样本。训练需要足够的已标注节点和至少两个类别。
node_label:[N, C]沿类别维度取 argmax;行和为零的节点视为未标注。这是单标签分类路径,不是多标签节点分类。
data.feature_source = embedding使用 upstream_model_id 的embedding.npy,按 node_names.json 中的节点名对齐;新图中未匹配的节点填零向量。

data.feature_source 默认是 graph。改为 embedding 时,还必须给出同一会话作用域内的 upstream_model_id;上游模型必须实际保存了节点嵌入文件。这个选项复用的是输入特征,不会载入上游 GNN 权重继续优化,也不恢复优化器状态。节点名变化会影响对齐结果,因此复用前应检查名称一致性与缺失匹配数量。

#节点分类:标签划分与训练

节点分类先生成 [N, F] 特征,再由 GNN 得到 [N, D] 嵌入,最后用线性分类头映射到 [N, C] 类别分数。每轮对整张图做一次训练前向传播,仅对训练节点的分数计算交叉熵并反向传播。优化完成后切换到评估模式,在同一张图上重新计算训练准确率与验证准确率,关闭 dropout 对评估的随机影响。

  • data.target_nodes 可将监督范围限制到指定节点名;无法识别或没有标签的节点不会成为有效监督样本。
  • 若图有 train_mask,训练集采用该掩码并与已标注节点相交。验证集优先采用 val_mask;没有 val_mask 时会使用 test_mask;两者都没有时使用其余已标注节点。
  • 若图没有 train_mask,则用 seed 打乱已标注节点,按 train / (train + val) 划分。默认比例为 0.8 / 0.2;兼容字段 train_config.split.test 不参与此流程。
  • 随机划分仅用于该次训练,不会回写成图的 train_mask 或 val_mask。后续 infer 的 query.split 读取原图掩码,不能用它索引一份未持久化的随机划分。

模型选择指标是 val_acc。只有本轮验证准确率严格大于 best_val_acc + early_stop_min_delta 才更新最佳训练检查点;相同准确率不会因训练损失更低而替换检查点。训练结束后恢复最佳骨干网络与分类头,返回 train_loss、train_acc、val_acc、best_epoch、trained_epochs、stopped_early 等指标。若 test_mask 被用作验证集,这次运行没有独立的测试评估,应据实报告该评估协议。

#预训练与嵌入向量复用

Node2Vec 根据图结构采样随机游走,通过正负上下文损失学习节点向量。当前使用稀疏参数与 SparseAdam,计算固定在 CPU 上;指定 GPU 不会把这个分支变为 GPU 随机游走训练。walk_length、context_size、walks_per_node、num_negative_samples、p、q 控制游走与采样,批大小读取 algo_config.batch_size,默认 128,而不是图分类使用的 train_config.batch_size。

Node2Vec 默认 walk_length=20、context_size=10、walks_per_node=10、num_negative_samples=1、p=q=1;训练器在未传 embedding_dim 时使用 128。为避免不同任务默认值带来误解,示例显式填写嵌入维度。该分支运行完整 epochs,忽略 weight_decay 和早停选项;结束时保存最后一轮的embedding.npy,不保存可继续训练的 Node2Vec 训练检查点。

GNN 预训练读取图特征和完整边索引,用节点嵌入内积重构正边,并对负采样边计算二元交叉熵。已命名的 contrastive_learning、graph_contrastive_learning、mask_autoencoding、graph_mask_autoencoding 都执行这一流程;没有各自独立的视图增广、掩码率或重构特征损失。它使用 Adam,保存最终骨干网络与节点嵌入,不执行验证集选择或早停。

预训练模型的 infer 是已保存嵌入向量的查询接口:按 query.nodes 返回训练时已有节点的向量,或者按 infer_config.limit 返回前若干项,默认 200。即使 GNN 预训练保存了骨干网络,当前推理路径也不会对新图重新执行 GNN 前向传播。要为更新后的图生成新的向量,需要重新运行预训练;要将这些向量用于节点分类或链接预测,可选择 data.feature_source=embedding并填写上游 model_id。

#分子图分类的数据结构

图分类的数据单元是一张图,HIV 是 1 个二分类任务,PCBA 是 128 个二分类任务。load_graph_classification_dataset 读取会话上传目录中的 .pt 文件,dataset_profile 可设为 auto、hiv 或 pcba;数据集经校验后绑定到会话,并返回 dataset_id。它独立于活动单图:load_graph_classification_graph 可以抽取一张样本用于查看,但不会把多图训练转变为节点分类。

字段必需的数据结构
graphs非空 PyG Data 列表;每张图包含 x、xe、edge_index、y 张量,节点数必须大于零。
x一维整型 [N],存放节点类别编号;编号非负且在节点词表范围内。它不是节点的全局身份编号,也不是二维连续属性矩阵。
xe / edge_indexxe 为一维整型 [E] 边类别编号;edge_index 为 [2, E] 整型索引且端点不越界。边类别必须由边词表覆盖。
y每图 [T] 或 [1, T],归一为 float32 [1, T];允许 0、1、NaN,拒绝无穷值和其他标签值。全部图的 T 一致。
texts至少包含三个字符串列表,依次表示节点、边和标签文本;用于词表和描述,不会自动生成语言模型嵌入。
splitsplit[0] 包含 train、valid(或 val)、test 图索引列表;索引不重复、不重叠并覆盖全部图。split[1] 是 prompt/few-shot 元数据字典,只保留摘要,不改变训练采样。

图分类训练使用数据文件的固定划分,不能提交 train_config.split。它不接受 graph_version、method、upstream_model_id、data.target_nodes、data.target_edges 或嵌入向量特征来源。可显式传 data.dataset_id,也可使用当前绑定的数据集;其他任务不能使用这个字段。加载器计算完整文件 SHA-256 指纹,推理会据此验证数据身份,文件名相同并不代表数据相同。

#模型结构、训练循环与评估

已注册骨干网络包括多层图卷积、注意力聚合和 APPNP 传播结构。常用默认值为 hidden_dim=64、num_layers=2、dropout=0.2;GNN 的 embedding_dim 省略时跟随 hidden_dim,因此在 hidden_dim 未改动时为 64。GAT、GATV2 与 TRANSFORMER 使用 gat_heads=2、concat=False。APPNP 先做多层线性预测,再执行个性化传播,默认 appnp_k=10、appnp_alpha=0.1。use_residual_connection 与 use_normalization 默认关闭;归一化支持 rms_norm、layer_norm、batch_norm,norm_pos 为 pre 或 post。

图分类先对节点类别做可训练的 nn.Embedding 编码,再经过 GNN、global_mean_pool 和线性图分类头。一个批次将多张图的节点与边拼接,用 batch 向量记录节点所属图:节点嵌入为 [ΣN, D],池化结果为 [B, D],最终 logits 为 [B, T]。train_config.batch_size 默认 64,num_workers 默认 0;训练批次打乱,验证与测试按固定顺序读取,不丢弃最后一个不足批大小的批次。

图分类采用带缺失标签掩码的 BCEWithLogitsLoss:仅在标签非 NaN 的位置取平均,一个批次全部缺失时跳过优化。训练集整轮没有有效标签会失败。HIV 用 ROC-AUC 选最佳模型;PCBA 对可评估任务分别计算 AP,再对这些任务取宏平均,同时记录有效与跳过的任务数。只有单一类别或全部缺失的任务无法贡献该指标,不能把 PCBA 指标表述为无条件平均全部 128 项。

三个监督任务使用 Adam。early_stop_patience=0 关闭早停,正整数表示连续多少轮没有改进后停止;early_stop_min_delta 默认 0,两个参数不能为负。图分类每轮只评估验证集,验证指标严格超过最佳值加 min_delta 才更新检查点;最终恢复最佳权重,再对测试集评估一次。预训练两个分支均没有这套验证选择和早停逻辑,不能把统一请求字段的存在理解为所有任务都执行它。

设置行为与适用范围
epochs / lr / weight_decay默认 200 / 0.05 / 0.0;示例显式设置学习率,数值不是调参结论。Node2Vec 使用 SparseAdam,忽略 weight_decay。
seed默认 42,设置 Python、NumPy、PyTorch 与 CUDA 随机种子;多图 DataLoader 也使用种子与工作进程初始化。
algo_config.devicecpu / cuda / auto;请求不可用的 CUDA 会失败。auto 无 GPU 时使用 CPU,多 GPU 时按空闲显存及可用利用率信息选择。Node2Vec 实际固定 CPU。
指标与进度训练逐轮报告损失与对应评估指标;监督任务报告 best_epoch、trained_epochs、stopped_early。检查这些字段以区分计划轮数、实际轮数和采用的权重。

#工具调用:提交训练与读取结果

以下 JSON 都是工具参数对象,在已验证身份并绑定会话的 MCP / 智能体工具环境中使用。参数示例展示数据结构,不包含测得的性能或最佳超参数。单图训练前先加载具备相应标签或结构的数据;分子图分类则先上传数据文件并执行数据集加载工具。示例中的文件名、dataset_id、model_id 与节点名应替换为实际返回值和真实数据名称。

节点分类:调用 train,使用活动图特征,按验证准确率选择模型。对于没有 train_mask 的图,split 控制本次随机划分。

json
{
  "task": "node_classification",
  "model": "GCN",
  "data": {
    "feature_source": "graph"
  },
  "train_config": {
    "split": {
      "train": 0.8,
      "val": 0.2
    },
    "epochs": 100,
    "lr": 0.001,
    "weight_decay": 0.0005,
    "seed": 42,
    "early_stop_patience": 10,
    "early_stop_min_delta": 0.0001
  },
  "algo_config": {
    "hidden_dim": 64,
    "embedding_dim": 64,
    "num_layers": 2,
    "dropout": 0.2,
    "device": "auto"
  }
}

链接预测:调用 train,使用图中的逻辑边作为正样本。需要限定监督正边时可提供 data.target_edges,其元素是两个节点名组成的数组。

json
{
  "task": "link_prediction",
  "model": "SAGE",
  "data": {
    "feature_source": "graph"
  },
  "train_config": {
    "epochs": 100,
    "lr": 0.001,
    "seed": 42,
    "early_stop_patience": 10
  },
  "algo_config": {
    "hidden_dim": 64,
    "embedding_dim": 64,
    "num_layers": 2,
    "device": "auto"
  }
}

Node2Vec:调用 train,显式给出嵌入维度、随机游走配置和该分支的批大小。

json
{
  "task": "pretrain",
  "method": "node2vec",
  "train_config": {
    "epochs": 30,
    "lr": 0.01,
    "seed": 42
  },
  "algo_config": {
    "embedding_dim": 64,
    "walk_length": 20,
    "context_size": 10,
    "walks_per_node": 10,
    "num_negative_samples": 1,
    "batch_size": 128,
    "p": 1.0,
    "q": 1.0,
    "device": "cpu"
  }
}

分子图分类:先调用 load_graph_classification_dataset;加载完成后把返回的 dataset_id 填入第二个对象,并用它调用 train。

json
{
  "filename": "hiv.pt",
  "dataset_profile": "hiv"
}
json
{
  "task": "graph_classification",
  "model": "GCN",
  "data": {
    "dataset_id": "DATASET_ID_FROM_LOAD"
  },
  "train_config": {
    "epochs": 100,
    "lr": 0.001,
    "weight_decay": 0.0,
    "seed": 42,
    "early_stop_patience": 10,
    "early_stop_min_delta": 0.0001,
    "batch_size": 64,
    "num_workers": 0
  },
  "algo_config": {
    "node_embedding_dim": 64,
    "hidden_dim": 64,
    "embedding_dim": 64,
    "num_layers": 2,
    "dropout": 0.2,
    "device": "auto"
  }
}

直接使用 MCP 时,长任务可能先返回 job_id。用 get_job_status 查询作业;首次传 after_seq=0,后续将返回的 progress_cursor 作为 after_seq 增量读取进度;progress_has_more=true 时继续翻页。完成状态为 job.status=done,训练结果位于 job.result;确认结果 success=true 后读取 model_id,再提交 infer。智能体的 GraphMCPTools 包装会自动等待并解析这些作业,通常直接交付最终结果。

json
{
  "job_id": "JOB_ID_FROM_TRAIN",
  "after_seq": 0,
  "progress_limit": 100
}

也可用 get_job_result(job_id=...) 读取最终输出。查询成功不等于训练完成:先确认返回 status=done,再检查内层训练结果的 success 与 model_id。大图算法作业的结果是摘要、预览和结果文件引用,不应当作图学习 model_id 使用。

#推理查询与返回结构

infer 根据模型元数据选择任务分支。节点分类与链接预测重新构造骨干网络、加载训练检查点,在当前图上执行前向传播;图分类重新加载模型对应的数据集并检查身份;预训练返回保存的节点向量。返回的 model_id、task 与图版本或数据集标识可用于确认结果来自哪个模型和输入。

节点分类按节点名查询:predictions 包含 pred_class_idx、pred_label 等类别信息,标签可用时还返回真实标签及正确性;开启 return_prob 可取各类别概率。result_limit 与 result_offset 仅分页明细,评估基于请求的完整节点集合。空 query 返回划分评估摘要和抽样预测;query.split 仅能选择图中实际存在的掩码。

json
{
  "model_id": "NODE_MODEL_ID_FROM_TRAIN",
  "query": {
    "nodes": [
      "node_a",
      "node_b"
    ]
  },
  "infer_config": {
    "return_prob": true,
    "result_limit": 20,
    "result_offset": 0
  }
}

链接预测可指定 query.edges,也可给出 source_node 与 candidate_nodes 做候选排序。每条结果包含 score、prob、pred 与 exists_in_graph;判定使用模型保存的验证阈值。候选列表可以包含已有边,调用方可根据 exists_in_graph 筛选。空 query 会搜索非边候选,虽然 max_candidate_pairs 限制保留和评分的候选量,生成阶段仍遍历节点对,大图上优先提供明确候选范围。

json
{
  "model_id": "LINK_MODEL_ID_FROM_TRAIN",
  "query": {
    "source_node": "node_a",
    "candidate_nodes": [
      "node_b",
      "node_c"
    ],
    "topk": 2
  }
}

图分类 query.split 可选 train、val、test,空 query 默认 test;也可用 graph_indices 指定图索引,二者不能同时出现。HIV 明细含 probability、predicted_label 和 true_label;PCBA 默认返回概率最高的 5 个任务,可用 top_k_tasks 调整,return_all_task_probs=true 返回完整任务概率。evaluation 对完整请求集合计算,count 为请求图数,returned_count 为本页数量,truncated 表示仍有明细未显示。

json
{
  "model_id": "GRAPH_MODEL_ID_FROM_TRAIN",
  "query": {
    "split": "test"
  },
  "infer_config": {
    "batch_size": 128,
    "device": "auto",
    "result_limit": 20,
    "result_offset": 0,
    "threshold": 0.5
  }
}

图分类推理接受 infer_config.device;节点分类与链接预测的推理路径当前自动选择设备,不能假定相同字段对它们具有相同作用。对于预训练模型,下面的调用只读取已有节点向量,返回 embeddings 数组,每项包含 node 与 embedding(数值向量)。

json
{
  "model_id": "PRETRAIN_MODEL_ID_FROM_TRAIN",
  "query": {
    "nodes": [
      "node_a",
      "node_b"
    ]
  },
  "infer_config": {
    "limit": 200
  }
}

#模型文件、版本绑定与复现

训练产物存放在 Graph Engine 的模型目录中,默认根目录为 artifacts/graph_ml,可通过 GRAPH_ML_ARTIFACT_ROOT 配置。模型目录按运行时的用户与会话作用域隔离,model_id 指向具体一次训练产物。图机器学习模块按需初始化;受控工作进程执行训练时先写入暂存目录,Graph Engine 验证作业仍有效后发布模型文件。收到 job_id 本身不等于模型已经可用。

任务模型目录中的主要文件
节点分类checkpoint.pt:backbone 与 head;meta.json:模型结构、特征来源、类别映射、graph_version;metrics.json 与 node_names.json。
链接预测checkpoint.pt:backbone;meta.json 另记录 threshold;metrics.json 与 node_names.json。
Node2Vec 预训练embedding.npy、node_names.json、meta.json、metrics.json;没有 checkpoint.pt。
GNN 预训练checkpoint.pt:backbone;另有embedding.npy、node_names.json、meta.json、metrics.json。
图分类checkpoint.pt:model 权重;meta.json:数据集指纹、词表大小、任务数、模型配置与划分大小;metrics.json、per_task_metrics.json。

单图 train 的 graph_version 是对当前活动版本的校验,并不会自动加载历史图。需要旧版本时先调用 load_graph_version,再训练。节点分类与链接预测推理默认要求当前图版本等于训练版本;infer_config.allow_version_mismatch=true 可跳过这项版本限制,但不会解决输入维度、标签语义或上游嵌入不兼容。预训练向量查询校验的是已保存模型版本,不依赖当前活动图;图分类则绑定 dataset_id 与完整数据集指纹。

复现实验时应另行保存完整 train 请求、源数据文件及指纹、图版本、上游 model_id、依赖版本和实际设备。现有元数据记录模型结构及结果,但没有完整保存所有训练超参数、随机数状态和优化器状态,也没有公开的断点续训接口。seed 控制主要随机源,却没有开启强制确定性算法;GPU 内核和软件环境变化仍可能造成结果差异,因此不能仅凭相同 seed 保证逐位一致。

visualize_model_architecture 接收 model_id,读取保存的模型配置用于检查结构。结构图描述网络层与计算路径,不代替任务指标。比较实验时同时查看最佳轮次、实际训练轮数、数据划分协议、验证与测试样本是否独立,以及 PCBA 有效任务数量。

#能力边界与实验解释

  • 图分类使用节点类别嵌入、已注册骨干网络(APPNP / GCN / GAT / GATV2 / GEN / GIN / SAGE / TRANSFORMER)、固定全局均值池化和图级线性头。xe 已读取、校验和保留,但不进入前向传播;模型元数据记录 uses_edge_features=false。不能据此宣称模型利用了分子键类型。
  • GIN 已注册,GINE 与可切换图池化尚未提供;HIV / PCBA 加载路径也不等价于支持任意 OGB 数据集。多图任务要求约定的 .pt 数据结构与固定划分,不能在 train 中任意指定新指标或重划测试集。
  • 单图监督任务和 GNN 预训练使用整图前向传播,没有邻居采样小批训练。train_config.batch_size 不会降低这些路径的整图特征和中间激活显存需求;它用于图分类,Node2Vec 则使用自己的批大小字段。
  • 二维节点标签经 argmax 进入单标签分类;分子图分类的 [B, T] 独立二分类才具有多任务标签语义。不要把两者的数据形状相似理解为同一种训练目标。
  • 链接预测验证边仍参与消息传递,内积解码不区分方向;GNN 预训练方法名当前共享边重构实现。这些实现边界需要与指标一起说明,才能正确比较模型与研究基线。
  • 当前接口没有提供自动超参数搜索、类别加权损失、学习率调度器、优化器状态恢复或跨环境确定性保证。本页示例是可理解的调用配置,不包含尚未执行的性能测试结论。
iGraph技术文档