数据采集与预处理:打好反欺诈的第一仗
说实话,做反欺诈算法这几年,我最大的体会就是——数据质量决定了模型的天花板。模型再花哨,喂进去的是脏数据,出来的就是垃圾。今天咱们就聊聊数据采集与预处理这块硬骨头。
我个人习惯把这一步叫做「地基工程」。地基没打好,后面盖的楼再漂亮也得塌。你想想看,欺诈检测里,一个缺失值可能让你漏掉一笔欺诈交易,一个异常值可能触发一堆误报。嗯,咱们一步步来拆解。
一、数据源分类:你的数据从哪来?
做反欺诈,数据源通常分三大类。我刚开始入行时,以为数据越多越好,结果被一堆冗余字段坑惨了。后来才明白,选对数据源比堆数据量重要得多。
1. 内部日志
说白了就是自家系统产生的数据。比如用户登录日志、交易流水、操作行为记录。这些数据最可靠,因为完全可控。我在项目中遇到过,内部日志里时间戳格式不统一,有的用毫秒,有的用秒,差点导致特征计算全错。
- 用户ID、IP地址、User-Agent
- 登录时间、登出时间、会话时长
- 交易金额、交易类型、商户ID
- 操作序列(点击、页面跳转、表单填写)
2. 第三方数据
征信报告、黑名单库、社交网络数据这些。我建议你注意一点:第三方数据往往有延迟。我记得有一次,我们用的黑名单库更新周期是24小时,结果当天新增的欺诈分子完全没被拦截。后来我们加了实时接口才解决。
| 数据源类型 | 优点 | 缺点 |
|---|---|---|
| 内部日志 | 实时、可控、成本低 | 维度有限、可能有偏差 |
| 第三方数据 | 维度丰富、权威性强 | 有延迟、成本高、依赖接口 |
| 设备指纹 | 唯一性强、难以伪造 | 隐私合规风险、采集复杂 |
3. 设备指纹
这个很有意思。设备指纹不是简单的设备ID,而是通过浏览器或APP采集的多维特征组合。比如屏幕分辨率、操作系统版本、字体列表、时区、甚至显卡型号。为什么需要这么多?因为欺诈分子可以伪造单个特征,但很难同时伪造几十个特征的组合。
二、数据清洗:脏数据是欺诈模型的毒药
数据清洗占了我整个项目时间的60%以上。别嫌烦,这一步省了,后面模型上线你会哭的。
1. 缺失值处理
缺失值在反欺诈里特别常见。比如用户没填手机号、设备指纹没采集到、第三方接口超时。处理方法有三种:
- 直接删除:缺失比例超过70%的字段,我一般直接砍掉。留着也是噪音。
- 填充:数值型用中位数或均值,类别型用众数。但要注意——欺诈场景下,缺失本身可能就是一个特征。比如「手机号缺失」这个字段,在欺诈样本中出现的概率远高于正常用户。
- 模型预测填充:用其他字段预测缺失值。这个方法效果好,但计算量大。我一般只在关键字段上使用。
# 缺失值处理示例
import pandas as pd
import numpy as np
# 读取数据
df = pd.read_csv('transaction_data.csv')
# 查看缺失情况
print(df.isnull().sum())
# 数值型字段:用中位数填充
df['amount'].fillna(df['amount'].median(), inplace=True)
# 类别型字段:用众数填充,并新增缺失标记
df['phone_missing'] = df['phone'].isnull().astype(int)
df['phone'].fillna('UNKNOWN', inplace=True)
# 删除缺失率超过70%的字段
threshold = 0.7
df = df.loc[:, df.isnull().mean() < threshold]
2. 异常值检测
异常值在反欺诈里分两种:一种是数据录入错误(比如年龄填了200岁),另一种是真正的欺诈行为(比如单笔交易金额是平时的100倍)。怎么区分?我教你一个笨办法:先做统计分布,再结合业务规则。
常用的异常值检测方法:
- 3σ原则:适用于正态分布的数据
- 箱线图(IQR):更稳健,不受极端值影响
- 孤立森林:适合高维数据,我在设备指纹异常检测中常用
# 使用IQR检测异常值
Q1 = df['amount'].quantile(0.25)
Q3 = df['amount'].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
# 标记异常值,但不删除
df['amount_outlier'] = ((df['amount'] < lower_bound) | (df['amount'] > upper_bound)).astype(int)
3. 重复值去重
重复数据在反欺诈里是个大坑。你以为同一个用户操作了两次,其实可能是系统重试导致的重复记录。我建议你:先区分「业务重复」和「数据重复」。
- 数据重复:所有字段完全一样,直接删除
- 业务重复:关键字段一样但时间戳不同,需要结合业务判断。比如同一笔交易在1秒内重复提交,大概率是系统问题,保留第一条即可
# 去重示例
# 完全重复:删除
df.drop_duplicates(inplace=True)
# 业务重复:按用户ID和交易ID去重,保留最早的一条
df.sort_values('timestamp', inplace=True)
df.drop_duplicates(subset=['user_id', 'transaction_id'], keep='first', inplace=True)
三、数据标准化与归一化:让特征站在同一起跑线
为什么需要标准化?你想想看,交易金额可能是几万块,而登录次数只有个位数。如果不做处理,模型会天然认为金额更重要——但实际可能恰恰相反。我见过太多新手直接拿原始数据训练,结果模型全被量纲大的特征带偏了。
1. Z-score标准化
把数据变成均值为0、标准差为1的分布。适合数据本身近似正态分布的场景。我在做信用评分卡时特别喜欢用这个。
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
df['amount_scaled'] = scaler.fit_transform(df[['amount']])
2. Min-Max归一化
把数据缩放到[0,1]区间。适合有明确上下界的特征,比如年龄、评分。但要注意——如果有异常值,Min-Max会被严重拉偏。
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
df['age_scaled'] = scaler.fit_transform(df[['age']])
3. Robust缩放
这个是我在反欺诈场景下的首选。它使用中位数和四分位数,对异常值不敏感。欺诈数据里异常值太多了,用这个最稳。
from sklearn.preprocessing import RobustScaler
scaler = RobustScaler()
df['amount_robust'] = scaler.fit_transform(df[['amount']])
- 数据近似正态分布 → Z-score
- 数据有明确边界、无异常值 → Min-Max
- 数据含大量异常值(反欺诈常见) → Robust缩放
- 树模型(XGBoost、LightGBM)→ 其实可以不做标准化,但为了特征可比性,我习惯做一下
好了,数据采集与预处理这块就聊到这儿。记住一句话:干净的数据是反欺诈模型的灵魂。下一章咱们会深入特征工程,到时候这些预处理过的数据就能派上大用场了。