一、图神经网络基础:图论基本概念、图神经网络发展历程、GraphSAGE 原理介绍
我在反欺诈项目中第一次用到图特征时,说实话效果并不理想。后来复盘才发现,问题出在最基础的地方——对图结构本身的理解不够扎实。嗯,今天我们就从最基础的内容开始梳理。
1.1 图论基本概念:万物皆可连
图是什么?说白了就是一堆点和它们之间的连线。每个点叫节点,每条线叫边。就这么简单。
在反欺诈场景里,节点可以是用户、设备、手机号、银行卡。边就是它们之间的关联——比如同一个 IP 登录过两个账号,或者同一个手机号绑定了多张卡。你想想看,这些关系天然就是一张图。
- 节点(Vertex):图中的实体,反欺诈中通常代表用户、设备、地址等
- 边(Edge):实体之间的关联,如交易、登录、绑定等行为
- 邻接矩阵(Adjacency Matrix):用 0/1 矩阵表示节点之间是否相连
- 度(Degree):一个节点有多少条边连着它
举个例子。假设我们有 3 个用户:A、B、C。A 和 B 是好友,B 和 C 是好友,A 和 C 不认识。那么邻接矩阵就是:
A B C
A 0 1 0
B 1 0 1
C 0 1 0
矩阵里第 i 行第 j 列是 1,就说明节点 i 和节点 j 之间有边。我建议初学者先把邻接矩阵吃透,因为后面 GraphSAGE 的所有操作都离不开它。
图的类型其实也就几种,我列个表格帮你快速区分:
| 类型 | 说明 | 反欺诈场景举例 |
|---|---|---|
| 无向图 | 边没有方向,关系双向 | 两个设备共用同一 Wi-Fi |
| 有向图 | 边有方向,A→B 不代表 B→A | 用户 A 向用户 B 转账 |
| 加权图 | 边上带权重,表示关联强度 | 转账金额、频次作为权重 |
| 异质图 | 节点或边有多种类型 | 用户、设备、地址三种节点混存 |
我在项目里最常用的是异质图。反欺诈场景的实体类型天生多样,硬塞进同质图反而丢失信息。这点后面讲特征工程时还会提到,先留个印象。
1.2 图神经网络发展历程:从谱域到空间域
传统的机器学习方法处理图数据时有个硬伤——它假设样本之间相互独立。但图数据恰恰相反,节点之间是互相关联的。你想想看,一个欺诈团伙里的成员,他们的行为特征肯定互相影响。把每个用户单独建模,等于把团伙拆散了再分析,效果能好才怪。
最早尝试解决这个问题的是图卷积网络(GCN)。GCN 的思路是把卷积操作从图像搬到图上。图像是规则的网格,图是不规则的结构,怎么搬?GCN 用谱域的方法,借助图的拉普拉斯矩阵做特征分解。概念很漂亮,但实际操作中有一个致命问题——它是转导式学习,训练时就必须看到整个图的结构。这意味着如果图里新来了一个节点,整个模型得重新训练。
我记得第一次用 GCN 做反欺诈时,线上不断有新用户注册,每来一批新用户就得全量重训一次模型。那段时间我几乎天天加班,后来实在扛不住了,才开始研究 GraphSAGE。
GraphSAGE 的出现为什么被称为突破?因为它把 GNN 从转导式推向了归纳式。归纳式学习意味着模型在训练时学到的是一套「如何聚合邻居信息」的规则,而不是记住每个节点的具体位置。新节点来了,只要找到它的邻居,套用学好的规则就能生成特征,完全不用重新训练。
我把几个关键模型的发展阶段整理了一下:
| 年代 | 模型 | 核心贡献 | 我的评价 |
|---|---|---|---|
| 2016 | GCN | 谱域卷积,开山之作 | 理论强,但只能做转导 |
| 2017 | GraphSAGE | 归纳式 + 邻居采样 | 实战首选,灵活可扩展 |
| 2018 | GAT | 注意力机制加权邻居 | 效果好但计算量偏大 |
| 2019 | GIN | 理论表达能力更强 | 适合小图,大图采样压力大 |
说实话,GAT 和 GIN 各有各的优势。但在工业级反欺诈场景里,GraphSAGE 的采样机制让它在大规模图上依然能跑得动,这也是我选择它作为本章核心的原因。
1.3 GraphSAGE 原理:采样 — 聚合 — 更新
GraphSAGE 的全称是 Graph Sample and Aggregation,核心就三件事:采样、聚合、更新。下面我逐一拆解。
1.3.1 邻居采样
大图的邻居数量可能成千上万。全部聚合?计算量扛不住。GraphSAGE 的做法是:每层固定采样 S 个邻居。S 是超参数,通常在 2~50 之间。
- 如果节点邻居数 > S,随机抽 S 个
- 如果节点邻居数 < S,就重复采样补齐
这样做的好处很明显:训练时每个 batch 的计算量是可控的。我习惯把第一层采样数设大一点(比如 25),第二层设小一点(比如 10),这样既能捕获局部结构,又不会太吃内存。
1.3.2 聚合函数
采样完邻居之后,要把这些邻居的特征合并成一个向量。GraphSAGE 提供了三种聚合函数:
| 聚合方式 | 做法 | 适用场景 |
|---|---|---|
| Mean 聚合 | 对邻居特征逐元素取平均 | 默认选项,简单稳定 |
| LSTM 聚合 | 把邻居随机排序后输入 LSTM | 邻居顺序隐含结构信息时 |
| Pooling 聚合 | 邻居特征过 MLP 后取 max 或 mean | 需提取最显著邻居特征时 |
Mean 聚合是最稳妥的选择,我 80% 的项目都用它。LSTM 聚合理论上更强,但需要邻居有顺序——图结构本身是无序的,所以得随机排序,这增加了不确定性。Pooling 聚合适合做「某个邻居特别关键」的场景,比如团伙中只跟头目有联系的成员。
1.3.3 节点更新
聚合完邻居特征之后,下一步是把聚合结果和节点自身的特征拼接,再经过一个全连接层加激活函数,得到更新后的节点特征。公式长这样:
h_v^{(k)} = σ( W_k · CONCAT( h_v^{(k-1)}, AGG( { h_u^{(k-1)}, u ∈ N(v) } ) ) )
其中:
- h_v^{(k)} 是节点 v 在第 k 层的特征
- N(v) 是节点 v 的采样邻居集合
- AGG 是聚合函数(Mean / LSTM / Pooling)
- CONCAT 把自身特征和邻居聚合特征拼起来
- σ 是激活函数,一般用 ReLU
注意这里的 CONCAT。很多初学者会忽略这个拼接操作,以为直接把邻居特征聚合一下就行。但实际上,保留自身特征非常关键——它让模型在更新时能区分「我原本是谁」和「我的邻居是谁」。我在调参时试过去掉拼接,准确率直接掉了 7 个点,印象极其深刻。
下面是 GraphSAGE 完整流程的示意图,我把它画成了五步流水线,方便你对照理解: