第三章:反洗钱数据特征工程
各位同学,今天我们来聊聊反洗钱建模中最“脏”也最“关键”的一步——特征工程。说实话,我在这个领域摸爬滚打这么多年,见过太多模型因为特征没做好而翻车的案例。你想想看,数据质量不行,再牛的深度学习模型也是白搭。
核心观点:特征工程决定了模型效果的上限,而模型算法只是在逼近这个上限。我个人习惯把70%的时间花在特征工程上,剩下的30%才给模型调参。
3.1 交易数据清洗——别让脏数据毁了你的模型
数据清洗这事儿,听起来简单,做起来全是坑。我记得刚入行那会儿,接手一个银行的反洗钱项目,数据量大概有5000万条交易记录。我心想,这不就是去去空值、删删重复嘛,结果一跑出来,模型AUC只有0.52,跟抛硬币差不多。
后来排查才发现,问题出在数据清洗上。具体来说,有这几个地方特别容易翻车:
- 缺失值处理:交易对手信息、交易渠道这些字段,缺失率可能高达30%以上。我建议不要直接删掉,而是根据业务逻辑填充。比如交易对手为空,可以标记为“未知账户”或填充为“0”。
- 异常值检测:单笔交易金额动辄几个亿,这正常吗?不一定。有些是机构间的大额调拨,有些纯粹是数据录入错误。我习惯用IQR(四分位距)方法先粗筛,再结合业务规则人工复核。
- 重复交易:同一个交易ID出现多次,可能是系统重试导致的。我曾经遇到过一个案例,某笔交易被重复记录了17次,直接导致该账户的“交易频率”特征暴涨。
我的小技巧:清洗完成后,一定要做一次“数据质量报告”。统计每个字段的缺失率、异常值比例、重复率。这个报告能帮你快速定位问题,也方便跟业务方沟通。
3.2 特征构建——从原始数据中挖出金子
特征构建是反洗钱建模的核心。说白了,就是把原始的交易流水,转化成能反映洗钱行为的数值特征。我一般从四个维度入手:金额、频率、地域、时间。
3.2.1 金额特征
金额是最直观的特征。但光用原始金额是不够的,需要做一些变换:
- 对数变换:金额分布通常呈长尾分布,取log后更接近正态分布,模型更容易学习。
- 统计量特征:过去7天、30天的平均交易金额、最大金额、最小金额、标准差。这些能反映账户的资金流动模式。
- 整额交易占比:洗钱分子喜欢用整数金额(比如10万、50万)来规避检测。计算一下账户中整额交易的比例,这个特征在实战中非常有效。
# 金额特征构建示例
import pandas as pd
import numpy as np
def build_amount_features(df):
# 对数变换
df['amount_log'] = np.log1p(df['amount'])
# 过去30天统计量
df['avg_amount_30d'] = df.groupby('account_id')['amount'].transform(
lambda x: x.rolling(30).mean()
)
df['std_amount_30d'] = df.groupby('account_id')['amount'].transform(
lambda x: x.rolling(30).std()
)
# 整额交易占比(金额为10000的整数倍)
df['is_round_amount'] = (df['amount'] % 10000 == 0).astype(int)
df['round_amount_ratio'] = df.groupby('account_id')['is_round_amount'].transform('mean')
return df
3.2.2 频率特征
频率特征反映的是交易活跃度。洗钱账户通常有“快进快出”的特点,交易频率会异常高。
- 交易次数:过去1天、7天、30天内的交易笔数。
- 交易间隔:相邻两笔交易的时间差。间隔越短,越可疑。
- 夜间交易占比:凌晨0点到5点的交易占比。正常用户很少在这个时间段频繁交易。
注意:频率特征容易受到“僵尸账户”的干扰。有些账户长期不用,突然活跃起来,这本身就是一个强信号。我建议把“休眠期”也作为一个特征加入模型。
3.2.3 地域特征
地域特征在跨境洗钱场景中特别重要。我做过一个项目,发现大量可疑交易都指向同一个离岸金融中心。
- 高风险地区交易占比:根据FATF(反洗钱金融行动特别工作组)名单,标记高风险国家/地区,计算交易占比。
- 地域分散度:交易对手涉及的不同城市/国家数量。分散度越高,越可疑。
- 地理距离:交易双方的地理距离。比如一个上海的小商户,频繁跟新疆的账户交易,这就不太正常。
3.2.4 时间特征
时间特征能捕捉交易的时间规律。洗钱分子往往会避开正常的工作时间。
- 小时特征:交易发生在几点?可以分桶处理(0-6点、6-12点、12-18点、18-24点)。
- 星期特征:周末交易占比。正常企业很少在周末进行大额交易。
- 节假日特征:节假日前后往往是洗钱高发期。我习惯把“距离最近节假日的天数”作为一个特征。
3.3 标签生成策略——没有标签怎么办?
反洗钱领域最大的痛点是什么?标签稀缺。真正被确认的洗钱案例少之又少,正负样本比例可能达到1:10000甚至更低。
我常用的标签生成策略有三种:
| 策略类型 | 方法描述 | 适用场景 | 优缺点 |
|---|---|---|---|
| 监督学习 | 使用已确认的可疑交易作为正样本 | 有历史案例积累的机构 | 准确率高,但样本量少 |
| 半监督学习 | 用规则引擎生成“疑似标签”,再人工复核 | 规则明确但人力有限 | 样本量扩大,但噪声增加 |
| 无监督学习 | 用聚类或异常检测发现离群点 | 完全没有标签的新业务 | 可以发现新模式,但解释性差 |
实战经验:我个人最推荐“半监督+人工复核”的方式。先用规则引擎(比如单笔超过100万、频繁换卡等)生成一批候选标签,然后让风控专家审核。这样既能保证标签质量,又能快速扩大样本量。
另外,时间窗口标签也很重要。比如“交易发生后30天内是否被举报”,这个窗口期设置得太短会漏掉案例,太长又会引入噪声。我一般会设置7天、30天、90天三个窗口,分别训练模型,然后做模型融合。
3.4 数据不平衡处理——让模型关注少数派
数据不平衡是反洗钱建模的“老熟人”了。正样本可能只有0.1%,直接训练的话,模型会学成一个“永远预测正常”的废物。
我常用的处理方法有这几种:
- 过采样(SMOTE):合成少数类样本。但要注意,SMOTE在反洗钱场景中容易生成“不真实”的样本。我曾经试过,生成的样本在业务上完全不合理,比如一个从未出过国的用户,突然有了跨境交易记录。
- 欠采样:随机删除多数类样本。简单粗暴,但会丢失大量信息。我建议用“NearMiss”方法,只保留离正样本最近的负样本。
- 代价敏感学习:给正样本更高的权重。比如设置class_weight='balanced',让模型更关注少数类。这个方法在深度学习框架中很容易实现。
- 异常检测方法:把反洗钱问题转化为异常检测问题。用Isolation Forest、AutoEncoder等模型,直接检测离群点。
# 代价敏感学习示例(PyTorch)
import torch.nn as nn
# 计算类别权重
pos_weight = torch.tensor([100.0]) # 正样本权重设为负样本的100倍
criterion = nn.BCEWithLogitsLoss(pos_weight=pos_weight)
# 训练时,模型会自动给正样本更高的惩罚
避坑指南:我曾经犯过一个错误——在测试集上也做了过采样。结果模型在测试集上表现很好,上线后直接崩了。记住:永远只在训练集上做数据不平衡处理,测试集要保持原始分布。
好了,关于反洗钱数据特征工程,核心内容就是这些。数据清洗、特征构建、标签生成、不平衡处理,这四个环节环环相扣,任何一个环节出问题,模型效果都会大打折扣。希望这些经验能帮你少走一些弯路。