如何用电网真实拓扑喂给 PyG:从状态估计到故障定位的完整实践
【免费下载链接】pytorch_geometricGraph Neural Network Library for PyTorch项目地址: https://gitcode.com/GitHub_Trending/py/pytorch_geometric
智能电网的拓扑天然是"节点+连线":变电站是节点,输电线路是边,量测值挂在上面。用PyTorch Geometric(PyG)做电力网络建模,本质就是把这张拓扑喂给图神经网络,让它按电气连接关系交换信息,而不是把每条线路孤立成一条记录。下面用一张 10 节点、3 级电压等级的简化电网,把状态估计、故障定位、负荷预测三条任务线走一遍,代码全部是 PyG 的最小可运行形态。
第一步:把简化电网建成 PyG 的 Data 对象
电力网络图数据的最小构建方式,只需要三个字段:节点特征x、边索引edge_index、边特征edge_attr。这里构造一张"220kV 枢纽站 → 110kV 站 → 10kV 配电站 → 0.4kV 低压台区"的 10 节点电网,节点特征取电压、有功功率、三相不平衡度三个量,边特征取线路电阻、电抗:
import torch from torch_geometric.data import Data from torch_geometric import transforms as T x = torch.tensor([ [220.0, 45.0, 0.03], # 220kV 枢纽站 [110.0, 28.0, 0.05], # 110kV A 站 [110.0, 22.0, 0.07], # 110kV B 站 [10.5, 8.0, 0.12], # 10kV 城东配电站 [10.4, 6.5, 0.15], # 10kV 城南配电站 [10.6, 7.2, 0.10], # 10kV 开发区配电站 [0.4, 1.8, 0.31], # 0.4kV 低压台区 1 [0.4, 1.5, 0.27], # 0.4kV 低压台区 2 [0.4, 2.1, 0.35], # 0.4kV 低压台区 3 [0.4, 1.6, 0.29], # 0.4kV 低压台区 4 ], dtype=torch.float) edge_index = torch.tensor([[0, 0, 1, 1, 2, 3, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6, 7, 8, 9]], dtype=torch.long) edge_attr = torch.tensor([[0.42, 1.15], [0.51, 1.32], [0.38, 0.96], [0.62, 1.48], [0.71, 1.55], [0.29, 0.68], [0.24, 0.57], [0.33, 0.81], [0.18, 0.42], [0.16, 0.38]], dtype=torch.float) data = Data(x=x, edge_index=edge_index, edge_attr=edge_attr) # 先标准化再建图:电压 220 与 0.4 差三个数量级,不归一会淹没小量纲 transform = T.Compose([T.NormalizeFeatures(), T.AddSelfLoops()]) data = transform(data)两个容易踩的坑:AddSelfLoops不是可选项——GCN 的聚合规则是"邻居均值里带上自己",没有自环时节点自身信息会被邻居稀释;另外如果电网里同时存在线路、变压器、开关等多种边类型,单用Data表达不了边类型差异,可以升级到 torch_geometric/data/hetero_data.py 的异构图,把不同设备关系拆成多个边类型分别聚合。
GCN 做电压状态估计:关键调用只有三步
GCN 可以理解为让每个节点和邻居"交换一次信息":每一层卷积把邻居特征加权汇总回来,几层之后,一个节点的表示就覆盖了它的整个电气邻域。对状态估计来说,这个邻域恰好对应故障或负荷波动会沿着线路传播的范围。模型本体只有两层卷积,实现见 torch_geometric/nn/conv/gcn_conv.py:
import torch.nn.functional as F from torch_geometric.nn import GCNConv class StateEstimator(torch.nn.Module): def __init__(self, hidden=32): super().__init__() self.conv1 = GCNConv(x.shape[1], hidden) # 3 个量测特征 -> 隐藏层 self.conv2 = GCNConv(hidden, 1) # -> 偏差输出 def forward(self, x, edge_index): x = self.conv1(x, edge_index).relu() x = F.dropout(x, p=0.2, training=self.training) return self.conv2(x, edge_index).squeeze(-1) model = StateEstimator() optimizer = torch.optim.Adam(model.parameters(), lr=0.01) # 训练循环的关键三步:前向 -> 只对量测节点算 MSE -> 反向 out = model(data.x, data.edge_index) loss = F.mse_loss(out[mask], y_true[mask]) # mask 标记有量测的节点 loss.backward(); optimizer.step(); optimizer.zero_grad()这里的关键是损失只对有量测的节点求,无量测节点的"真值"由网络从拓扑中插值出来——这正是状态估计里"量测不完整、全网状态都要算"的数学形态,而 PyG 里它只是mask下标的事。评估侧同理:model.eval()后同一份data前向一次,输出的逐节点偏差就是估计结果。
GATConv 的注意力权重直接用来定位故障边
故障定位和状态估计的区别在于:状态估计问"全网量测现在是多少",故障定位问"哪条线路最可疑"。GAT 把"信息交换"升级成"带权交换"——每条边先算一个注意力分数再聚合,分数越高代表这条边对当前节点越重要。这里借用的直觉是:负荷异常传播时,电流最集中的线路就是嫌疑最大的线路,而 GAT 的注意力恰好是在学这种集中度。
from torch_geometric.nn import GATConv loc = GATConv(3, 32, heads=4) # 4 个头各自看一遍注意力 out, (attn, edge_idx) = loc(data.x, data.edge_index, return_attention_weights=True) # 每条输入边在 4 个头里被关注度最高的那条边 suspicious = edge_idx[torch.argmax(attn, dim=0)]return_attention_weights=True会让卷积多返回一个注意力张量,形状是(头数, 边数),edge_idx标明每列对应输入中的哪条边——拿到这两个量,无需任何额外后处理就能把"最可疑的边"直接映射回电网里的具体线路。实现细节在 torch_geometric/nn/conv/gat_conv.py。注意一个工程细节:多注意力头会改变维度(隐藏维乘以头数),后接卷积或全连接时输入维要写hidden * heads,这是 GAT 模型里最常见的报错来源。
负荷预测:给采样器挂上时间轴
负荷是随时间滚动的,电网的量测也是分时到达的。PyG 对这类数据的支持藏在采样器里:只要边或节点上带t时间戳属性,NeighborLoader就能按时间窗口做采样,而不是把整张图一次性搬进显存:
from torch_geometric.loader import NeighborLoader # 节点特征按时间步堆叠,time_attr 告诉采样器时间戳存在哪 loader = NeighborLoader( data, # 假设节点/边上已有 t 字段 num_neighbors=[4, 2], # 每层采样邻居数 input_nodes=torch.arange(data.num_nodes), batch_size=64, temporal_strategy='uniform', # 在时间窗内均匀采样 time_attr='t', ) for batch in loader: # 每个 batch 自带边集和时间戳 out = model(batch.x, batch.edge_index)换句话说,动态负荷预测不需要你手写时序图结构,时间维度是采样器参数,temporal_strategy='uniform'表示在目标时间之前均匀抽点,适合负荷这类平稳性较强的序列。实现见 torch_geometric/loader/neighbor_loader.py。
大规模电网:邻居采样先于模型选型
先别急着调参。电网拓扑一变大,瓶颈从来不是模型结构而是图本身:百万节点电网的完整邻接聚合,显存和通信都扛不住。邻居采样的做法是:每层只抽少量邻居,把"全网聚合"降级成"小邻域聚合 + 逐层回溯",GNN 每层看到的只是半径一层展开的子图。
图中把节点按机器分区后,本地邻居直接取、远端邻居异步拉取——采样器对模型完全透明,NeighborLoader每个 batch 递过来的就是带edge_index的小子图,模型代码一行不用改。单卡场景把num_neighbors从[20, 20]起步调就够了;多机训练可以参考 examples/multi_gpu/ 下的分布式采样示例。一个容易误入歧途的点:torch_geometric.distributed包自 2.7 起已标记弃用,新代码不要再从那里取分布式采样器,直接看官方多卡示例即可。
延伸方向
- 异构图建模:把"线路/变压器/开关"拆成不同边类型,用
HeteroData分别聚合,比单图更贴近一次接线图 - 多任务联训:状态估计与故障定位共享底层卷积、只分头输出,样本效率通常高于两个独立模型
相关文档:docs/source/(模块总览与教程入口),示例代码集中在 examples/。
【免费下载链接】pytorch_geometricGraph Neural Network Library for PyTorch项目地址: https://gitcode.com/GitHub_Trending/py/pytorch_geometric
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考