第三章 数据准备:风控图数据构造与特征工程

各位同学,欢迎来到第三章。这一章咱们要动真格的了——把原始数据变成图神经网络能吃的“饲料”。

说实话,很多同学在学GCN时,模型原理背得滚瓜烂熟,一上手就懵了:数据怎么组织?特征怎么算?邻接矩阵太大怎么办?

嗯,这些坑我都踩过。今天咱们一个一个填平。

3.1 风控多模图的构造逻辑

先问个问题:为什么风控要用图?

传统风控看的是单点特征——你借过多少钱、逾期过几次。但黑产团伙是“组团作案”的。一个设备挂几十个账号,一个IP关联几百个手机号。这种关联关系,表格根本表达不了。

所以我们要建图。而且是多模图——多种节点类型、多种边关系。

核心思路:把用户、设备、IP、手机号都作为节点,它们之间的关联作为边。

我习惯用这种四类节点:

  • 用户节点(U):每个借款用户是一个节点
  • 设备节点(D):手机IMEI、设备指纹等
  • IP节点(I):登录IP、交易IP
  • 手机号节点(P):注册手机号、联系人手机号

边怎么定义?很简单:谁用过谁,就连一条边。

边类型 含义 示例
U-D 用户使用过某设备 用户A登录过设备X
U-I 用户从某IP登录 用户A的IP是192.168.1.1
U-P 用户使用某手机号 用户A绑定手机号138xxxx
D-I 设备连接过某IP 设备X通过IP 10.0.0.1上网

我在项目中遇到过一个问题:数据量太大,全量构图内存直接爆了。后来我学乖了——按时间窗口切分,只取近30天的关联数据。既保证时效性,又控制图规模。

避坑指南:我曾经把全量历史数据一股脑塞进图里,结果训练一个epoch要3小时。后来发现,3个月前的设备关联对当前风控几乎没贡献。记住:图不是越大越好,时效性比完整性重要。

3.2 特征工程:给节点和边“贴标签”

图建好了,但节点和边都是“裸”的。我们需要给它们赋予特征。

3.2.1 节点特征

节点特征说白了就是“这个节点本身长什么样”。

我一般分三类:

  • 基础属性:用户年龄、性别、注册时长;设备型号、系统版本;IP所属地区、运营商
  • 统计特征:用户历史借款次数、逾期次数;设备关联用户数;IP出现频率
  • 时序特征:最近一次活跃时间、活跃时段分布、行为间隔规律

举个例子,用户节点的特征向量可能是这样:

# 用户节点特征示例
user_features = {
    'age': 28,                    # 年龄
    'gender': 1,                  # 性别编码
    'reg_days': 365,              # 注册天数
    'loan_count': 12,             # 借款次数
    'overdue_rate': 0.08,         # 逾期率
    'device_count': 2,            # 关联设备数
    'ip_change_freq': 0.3,        # IP更换频率
    'night_active_ratio': 0.15    # 夜间活跃比例
}

你想想看,如果一个用户关联了50个设备,IP每天换3次,这正常吗?大概率是黑产在批量操作。这些特征就是GCN判断的依据。

3.2.2 边特征

边特征容易被忽略,但它非常关键。同样是“用户-设备”边,有的用户是正常登录,有的可能是盗号登录。

我常用的边特征:

  • 交互频次:该用户通过该设备操作了多少次
  • 交互时长:平均每次使用时长
  • 交互时段:主要在白天还是深夜
  • 行为一致性:该用户的操作行为是否与设备历史行为一致
个人经验:边特征里,“行为一致性”这个特征特别有用。如果一个设备平时都是白天正常操作,突然凌晨3点被一个新用户登录,这条边的异常分数就应该很高。我把它作为边特征输入GCN后,团伙识别率提升了12%。

3.3 邻接矩阵与稀疏矩阵处理

图在数学上怎么表示?邻接矩阵。但风控图的节点数动辄百万级,全量邻接矩阵存不下。

举个例子:100万个节点,邻接矩阵就是100万×100万,需要约4TB内存。这谁顶得住?

所以必须用稀疏矩阵。

3.3.1 稀疏矩阵的三种格式

格式 适用场景 存储方式
COO 构建阶段,方便添加边 三个数组:行索引、列索引、值
CSR 矩阵运算,GCN训练 行偏移、列索引、值
CSC 列操作多的场景 列偏移、行索引、值

我建议这样用:构建图时用COO格式,方便动态添加边。训练前转成CSR格式,计算效率最高。

import scipy.sparse as sp
import numpy as np

# 1. 先用COO格式构建
row = np.array([0, 0, 1, 2, 2, 3])
col = np.array([1, 2, 0, 1, 3, 2])
data = np.array([1, 1, 1, 1, 1, 1])  # 无权图用1

coo_matrix = sp.coo_matrix((data, (row, col)), shape=(4, 4))

# 2. 转成CSR用于训练
csr_matrix = coo_matrix.tocsr()

# 3. 添加归一化(GCN必备)
# 计算度矩阵的逆平方根
row_sum = np.array(csr_matrix.sum(axis=1)).flatten()
d_inv_sqrt = np.power(row_sum, -0.5)
d_inv_sqrt[np.isinf(d_inv_sqrt)] = 0

# 对称归一化:D^{-1/2} * A * D^{-1/2}
d_mat_inv_sqrt = sp.diags(d_inv_sqrt)
norm_adj = d_mat_inv_sqrt.dot(csr_matrix).dot(d_mat_inv_sqrt)
注意:归一化这一步很多人会忘。GCN的卷积操作本质是邻居特征的平均,如果不做归一化,度大的节点特征会被稀释,度小的节点特征会被放大。我曾经因为这个原因,模型训练一直不收敛,排查了两天才发现是归一化没做。

3.3.2 大规模图的处理技巧

当节点数超过100万时,连稀疏矩阵都可能装不下。怎么办?

我常用的三种策略:

  • 子图采样:每次训练只取一个子图,比如以目标节点为中心,采样2跳邻居
  • Mini-batch训练:用GraphSAFE或ClusterGCN的思路,分批训练
  • 特征压缩:把高维特征用PCA或AutoEncoder降到64维以下

说实话,在工业级风控场景,我很少见到全图训练的。基本都是采样+分批。你想想看,一个用户的风险只和他直接或间接关联的节点有关,没必要把全图都加载进来。

3.4 本章知识体系总览

下面这张图,是我自己总结的本章核心流程。建议你保存下来,做项目时对照着看。

风控图数据构造与特征工程流程 原始数据源 用户表/设备表/IP表 多模图构建 U-D / U-I / U-P / D-I 特征工程 节点特征 + 边特征 稀疏矩阵 节点特征详解 • 基础属性:年龄/设备型号/IP地区 • 统计特征:借款次数/关联设备数 • 时序特征:活跃时段/行为间隔 • 编码方式:归一化/离散化/Embedding 边特征详解 • 交互频次:操作次数/登录次数 • 交互时长:平均使用时长 • 交互时段:白天/深夜分布 • 行为一致性:与历史行为匹配度 稀疏矩阵处理 • COO格式:构建阶段使用 • CSR格式:训练阶段使用 • 对称归一化:D^{-1/2}AD^{-1/2} • 大规模策略:子图采样/Mini-batch 输出:归一化邻接矩阵 + 节点特征矩阵 + 边特征矩阵 可直接输入GCN模型进行训练 注:实际项目中建议先做小规模验证(1万节点),再扩展到全量数据

这张图把本章的核心逻辑串起来了。从原始数据到最终的稀疏矩阵,每一步都有对应的处理技巧。我个人建议你按照这个流程,先用小规模数据跑通,再上生产环境。

最后说一句:数据准备占了GCN项目80%的工作量。模型调参可能只花20%的时间。所以别急着跑模型,先把数据搞扎实。我在第一个风控GCN项目里,光数据清洗就花了两周,但后面模型一次就上线了。值不值?太值了。

公众号:蓝海资料掘金营,微信deep3321