ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

反欺诈里那些抓不到的团伙,交给图神经网络:PyG GraphSAGE 交易反欺诈实战

反欺诈里那些抓不到的团伙,交给图神经网络:PyG GraphSAGE 交易反欺诈实战 反欺诈里那些抓不到的团伙交给图神经网络PyG GraphSAGE 交易反欺诈实战【免费下载链接】pytorch_geometricGraph Neural Network Library for PyTorch项目地址: https://gitcode.com/GitHub_Trending/py/pytorch_geometric用 PyTorch GeometricPyG的HeteroData把信用卡、商户、交易、设备装进一张异构图用 GraphSAGE 按节点做欺诈分类再用NeighborLoader小批量训练一路走到分布式扩展。本文走一遍从建模到落地的完整链路。速览本文讲如何用 PyG 的HeteroDataSAGEConvGraphSAGE做交易节点级反欺诈分类用NeighborLoader小批量采样处理百万级交易图并用AUC对到风控口径。适合读者有 Python/PyTorch 基础、想给交易/资金网络做反欺诈或团伙挖掘的风控与算法工程师。一笔笔欺诈怎么装进一张交易异构图HeteroData单看一笔交易金额、渠道、时段都正常可欺诈从来不是孤立事件——同一批黑产会复用几张卡、几台设备、几个商户批量刷单。这种团伙信号藏在关系里散在多张表里根本连不起来一旦拆成 SQL 视图分别分析传导路径就断了。图的价值就是把卡—交易—商户—设备画在一张纸上让共享同一台设备的交易这类证据能沿着边传出去。PyG 用HeteroData表达这种多实体多关系结构节点和边都用「类型 索引」写import torch from torch_geometric.data import HeteroData data HeteroData() # 节点特征从风控数仓取现成字段做 z-score 归一化 data[card].x torch.randn(num_cards, 16) # 发卡行/地区/额度/账户年龄 data[merchant].x torch.randn(num_merchants, 16) # MCC行业/地区/交易规模 data[device].x torch.randn(num_devices, 8) # 机型/IP/设备指纹 data[transaction].x torch.randn(num_tx, 16) # 金额/时段/渠道/是否退款 # 边2xE 的 index第 0 行起点、第 1 行终点只保留有语义的几条 data[card, made, transaction].edge_index card_tx_idx data[transaction, at, merchant].edge_index tx_merchant_idx data[device, used_in, transaction].edge_index device_tx_idx节点特征直接取风控数仓字段归一化纯关系型节点如只有 ID 没有属性的可以先用独热 ID 顶上去。边不用穷举所有实体组合只留卡做了交易交易发生在商户设备参与了交易这几条主干。注意边方向别乱写——made/at/used_in的起点终点要和后续取特征、做归一化对齐方向写反会让消息从错误方向传播团伙信号被稀释。欺诈节点怎么编码GraphSAGE to_hetero 自动展开反欺诈的落地点是节点级二分类给每笔交易打一个欺诈概率而不是预测某条边的数值。思路先用 GraphSAGE 把每笔交易聚合它周围卡、商户、设备的信号编码成向量再过一个分类头输出 logit。这里不手写每类节点的卷积而是用to_hetero把一个同质SAGEConv编码器按元数据自动展开成异构图模型——每类边独立一套参数。仓库示例examples/hetero/to_hetero_mag.py做的就是这件事它拿 MAG 异构数据SAGEConv((-1, -1), 64)建编码器再to_hetero本文把它改到交易反欺诈上。from torch_geometric.nn import SAGEConv, to_hetero class FraudDetector(torch.nn.Module): def __init__(self): super().__init__() # -1 表示输入维度由数据自动推断to_hetero 才能按每类边配独立参数 self.conv1 SAGEConv((-1, -1), 64) self.conv2 SAGEConv(64, 64) self.encoder to_hetero(self, data.metadata(), aggrsum) self.head torch.nn.Linear(64, 1) # 只给交易节点接分类头 def forward(self, x_dict, edge_index_dict): z self.encoder(x_dict, edge_index_dict) # 各节点类型都有嵌入 return self.head(z[transaction]).view(-1) # 每笔交易的欺诈 logitSAGEConv((-1, -1), ...)里的-1是关键让输入维度由真实数据推断to_hetero才能给每种边类型生成独立的参数矩阵。分类头只挂在transaction上卡/商户/设备只做特征传播源、不直接出分。注意z[transaction]返回的是本批次所有被采样到的交易节点前batch_size个才是这次要打分/反传的种子交易切片见下一节漏切会拿邻居的 label 去算 loss训练直接乱掉。大交易图怎么小批量训练NeighborLoader 采样 类别不平衡真实交易图动辄上亿条交易、上千万节点全图一次前向内存根本扛不住。PyG 的NeighborLoader做 mini-batch 邻居采样每次只取一小撮种子交易往回采两跳邻居用这个采样子图训练。仓库examples/multi_gpu/mag240m_graphsage.py就是用 GraphSAGE 在亿级 MAG 图上做采样的路子本文单机版直接复用它的参数风格。交易欺诈是典型的极不平衡问题欺诈占比常在千分之几。直接上交叉熵模型全判正常就能拿到 99.9% 的准确率AUC 却是垃圾。用pos_weight把负样本权重拉起来from torch_geometric.loader import NeighborLoader import torch.nn.functional as F train_loader NeighborLoader( data, num_neighbors[10, 5], # 两跳采样10 - 5 batch_size1024, input_nodes(transaction, train_mask), # 只从训练集交易节点起步 shuffleTrue, ) optimizer torch.optim.Adam(model.parameters(), lr0.01) pos_weight torch.tensor([num_neg / max(num_pos, 1)]) # 压制不平衡 def train_epoch(): model.train() total 0. for batch in train_loader: optimizer.zero_grad() out model(batch.x_dict, batch.edge_index_dict) n batch[transaction].batch_size # 前 n 个是种子交易 loss F.binary_cross_entropy_with_logits( out[:n], batch[transaction].y[:n], pos_weightpos_weight) loss.backward(); optimizer.step() total loss.item() return total / len(train_loader)注意input_nodes一定只传train_mask且num_neighbors的跳数要和模型卷积层数对齐——这里两层卷积配两跳[10, 5]若层数比跳数多要设directedFalse补全采样节点间所有边否则深层卷积取不到够用的邻域。欺诈评估怎么对得上风控口径AUC 换算成漏损风控不关心你 loss 多低只关心我按风险分排序去拦截能抓住多少欺诈、误伤多少正常。最稳的口径是AUC 固定误报率下的召回而不是准确率。AUC可以直接读AUC0.91意味着随机取一笔欺诈交易和一笔正常交易模型把欺诈分排在前面的概率是 91%。换算到钱from sklearn.metrics import roc_auc_score import numpy as np torch.no_grad() def eval_auc(loader): model.eval() logits, labels [], [] for batch in loader: out model(batch.x_dict, batch.edge_index_dict) n batch[transaction].batch_size logits.append(out[:n].cpu()) labels.append(batch[transaction].y[:n].cpu()) logits torch.cat(logits).numpy() return roc_auc_score(labels, 1 / (1 np.exp(-logits))) # 转概率再算 AUC拿到 AUC 后按风控口径落地在同样 1% 误报率下AUC 从 0.83 提到 0.91欺诈召回往往从 62% 提到 79%。假设月欺诈总额 500 万召回每提升 1 个点 ≈ 每月少漏 5 万这个数拿去和现在凭规则 人工复核的拦截率比就能算出模型值不值得上。⚠️ 这里有个反欺诈特有的坑训练/验证/测试必须按交易时间切分且不能随机切。随机切会把同一台设备/同一个商户的欺诈交易同时塞进训练和测试评估 AUC 虚高一上线就现原形——欺诈是时序演化的未来的交易不能当历史特征用。交易图大到装不进内存分布式邻居采样单机采样的极限是特征矩阵和邻接表装不进内存。当节点过千万、边过亿PyG 的torch_geometric/distributed/提供两级扩展离线切图Partitionerdistributed/partition.py把节点、特征、边按分片落盘每个partN/下是切好的图与特征在线采样DistNeighborLoaderdistributed/dist_neighbor_loader.py绑定本机分片本地邻居直接读跨分片邻居走 RPC 从远端拉。from torch_geometric.distributed import DistNeighborLoader # 先离线 Partitioner 切图落盘训练时每台机器加载自己的 part 分片 loader DistNeighborLoader( partition_rootdist_graph/, # 切图输出目录 edge_type(card, made, transaction), num_neighbors[10, 5], batch_size1024, )采样开销从全图降到本机分片 一跳远程训练吞吐随机器数近似线性扩展。对交易这种边数远超单机的图这一步基本是必选项。注意切图会引入跨分片的采样延迟num_neighbors和batch_size建议比单机调小一档并把采样放子进程num_workers 0与训练重叠否则 RPC 等待会吃掉吞吐。要点回顾与下一步要点回顾建模HeteroData把卡/商户/设备/交易装成异构图边用 2×E index 只留主干语义编码SAGEConv((-1, -1), ...)维度自推断to_hetero按元数据展开成独立参数的异构图模型采样NeighborLoaderinput_nodestrain_mask做 mini-batch 两跳采样num_neighbors跳数对齐层数不平衡binary_cross_entropy_with_logits配pos_weight别用裸交叉熵评估AUC 对齐风控口径按交易时间切分防实体泄漏扩展Partitioner离线切图 DistNeighborLoader在线跨机采样下一步可以做的事按优先级给交易节点加时序切分 时间感知的邻居采样防止未来交易泄漏进历史特征在解码端加团伙聚合把同设备/同卡的交易嵌入聚合成团伙分输出可解释的风险簇把pos_weight换成焦点损失让模型在高危但少的大额欺诈上召回更高【免费下载链接】pytorch_geometricGraph Neural Network Library for PyTorch项目地址: https://gitcode.com/GitHub_Trending/py/pytorch_geometric创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表