Flink反洗钱PSI漂移告警实战课 - 第4节
4.1 反洗钱特征工程:核心特征体系
反洗钱特征工程是将原始交易数据转化为可量化、可监控的数值指标的过程。在实时流处理场景下,特征必须满足低延迟、可增量计算、可回溯三个基本要求。本节聚焦三类最核心的反洗钱特征:交易频次、金额波动与地域偏移。
4.1.1 交易频次特征
交易频次是衡量账户在单位时间内发起交易数量的基础指标。洗钱行为往往通过高频小额交易(Smurfing)或突发性低频大额交易来规避监管阈值。
| 特征名称 | 计算窗口 | 业务含义 | 典型告警阈值 |
|---|---|---|---|
| 交易笔数(1h) | 滑动窗口 1 小时 | 检测短时高频异常 | > 均值 + 3σ |
| 交易笔数(24h) | 滑动窗口 24 小时 | 检测日累计拆分交易 | > 历史 P99 |
| 交易笔数(7d) | 滑动窗口 7 天 | 检测周度模式突变 | > 历史 P95 且环比增长 > 200% |
| 交易间隔均值 | 最近 10 笔交易 | 检测机器化自动交易 | < 5 秒且标准差 < 1 秒 |
实战要点:在 Flink 中实现滑动窗口频次统计时,优先使用
SlidingEventTimeWindows 配合 aggregate 函数,避免使用 process 函数手动维护状态,以利用 Flink 内置的 watermark 对齐机制。
4.1.2 金额波动特征
金额波动特征刻画交易金额的离散程度与异常偏离。洗钱账户常呈现“金额聚集”或“金额跳跃”两种模式。
- 单笔金额偏离度:
(当前金额 - 历史均值) / 历史标准差,用于检测单笔大额异常。 - 金额变异系数(CV):
标准差 / 均值,用于检测金额分布是否过于集中或分散。 - 金额熵值:基于最近 N 笔交易的金额分布计算信息熵,低熵值暗示金额高度重复。
- 整数金额占比:交易金额为整百/整千的比例,异常账户常出现大量整数金额。
案例:某账户过去 30 天单笔金额均在 4,800–5,200 元区间,变异系数仅 0.03,金额熵值 0.12。该账户被判定为“金额聚集型”可疑账户,经查实为拆分洗钱。
4.1.3 地域偏移特征
地域偏移特征追踪交易对手的地理位置变化。洗钱行为常涉及跨地域、跨国家的资金快速转移,或刻意回避高风险地区。
| 特征名称 | 计算方式 | 洗钱信号 |
|---|---|---|
| 交易对手地域熵 | 最近 20 笔交易对手所在城市的分布熵 | 熵值突增 → 地域分散化洗钱 |
| 跨省交易占比 | 跨省交易笔数 / 总交易笔数 | 占比突增 → 资金跨区域流动 |
| 高风险地区交易占比 | 与 FATF 高风险地区交易笔数占比 | 占比 > 50% → 涉恐/涉毒风险 |
| 地域跳跃距离 | 连续两笔交易对手所在地的球面距离 | 单次跳跃 > 1000km → 异常转移 |
4.2 特征分布漂移的监控需求
特征分布漂移是指模型训练时使用的特征分布与实时生产环境中的特征分布之间出现显著差异。在反洗钱场景中,漂移可能由以下原因触发:
- 洗钱手法演变:犯罪分子调整交易模式以规避现有模型。
- 业务规则变更:银行调整转账限额、手续费或开户政策。
- 季节性因素:节假日、购物季导致正常交易模式变化。
- 数据质量问题:上游系统字段变更、缺失率上升。
4.2.1 漂移监控的核心指标
| 监控指标 | 适用特征类型 | 计算方式 | 告警阈值 |
|---|---|---|---|
| PSI(群体稳定性指标) | 连续型 / 分箱型 | ∑(实际占比 - 期望占比) * ln(实际占比/期望占比) | > 0.1 弱漂移;> 0.25 强漂移 |
| KL 散度 | 概率分布型 | ∑P(x) * log(P(x)/Q(x)) | > 0.2 |
| KS 统计量 | 连续型 | max(|累积分布差异|) | > 0.3 |
| 均值漂移(Z-score) | 连续型 | (当前均值 - 历史均值) / 历史标准差 | |Z| > 3 |
重要提醒:PSI 对空箱(占比为 0 的分箱)敏感。在 Flink 中实现 PSI 计算时,必须对分箱占比做 Laplace 平滑处理:
p_i = (count_i + 1) / (total + num_bins),避免出现 ln(0) 导致计算异常。
4.2.2 实时漂移监控架构
在 Flink 流处理中,特征漂移监控需要构建“参考分布”与“实时分布”的双流对比管道。参考分布通常从历史数据中预计算并存入状态后端或外部存储;实时分布通过滑动窗口持续更新。
4.2.3 监控频率与分箱策略
漂移监控的粒度直接影响计算开销与告警时效性。以下为生产环境推荐配置:
| 特征类型 | 监控频率 | 分箱数量 | 窗口大小 | 状态后端建议 |
|---|---|---|---|---|
| 交易频次(1h) | 每 10 分钟 | 10 等频分箱 | 1 小时滑动 | RocksDB |
| 交易频次(24h) | 每小时 | 20 等频分箱 | 24 小时滑动 | RocksDB |
| 金额波动 | 每 30 分钟 | 15 等距分箱 | 7 天滑动 | RocksDB + 增量快照 |
| 地域偏移 | 每小时 | 按城市编码分箱 | 7 天滑动 | RocksDB + MapState |
Flink 实现片段:PSI 计算核心逻辑
// 参考分布(预计算,从状态或外部存储读取)
Map<Integer, Double> refDist = getRefDistribution(featureName);
// 实时分布(滑动窗口聚合结果)
Map<Integer, Long> realCounts = getRealTimeCounts(featureName);
long total = realCounts.values().stream().mapToLong(Long::longValue).sum();
int numBins = refDist.size();
double psi = 0.0;
for (int bin : refDist.keySet()) {
double p = refDist.get(bin);
double q = (realCounts.getOrDefault(bin, 0L) + 1.0) / (total + numBins);
psi += (p - q) * Math.log(p / q);
}
// 判断漂移等级
if (psi > 0.25) {
emitAlert(featureName, psi, "STRONG_DRIFT");
} else if (psi > 0.1) {
emitAlert(featureName, psi, "WEAK_DRIFT");
}
4.2.4 漂移告警的降噪策略
直接对每个特征独立计算 PSI 会产生大量误报。生产环境必须引入降噪机制:
- 连续漂移确认:连续 3 个监控周期均触发弱漂移才产生告警,避免单次抖动。
- 特征重要性加权:仅对模型贡献度 Top 20 的特征进行漂移监控,降低计算开销。
- 业务日历对齐:对比参考分布时使用同期数据(如同比上周同日),消除周期性影响。
- 漂移根因定位:当 PSI 告警时,自动输出贡献最大的 Top 3 分箱,辅助人工研判。
生产建议:将 PSI 计算结果也写入 Kafka,供下游可视化看板(如 Grafana)实时展示。同时将告警事件持久化到 Elasticsearch,便于事后回溯与模型迭代分析。