3. 特征工程基础:特征分类、提取与选择
各位同学好,我是老蓝。今天咱们聊聊特征工程里最基础、也最容易被忽视的一块——特征分类、提取和选择。说实话,我见过太多人一上来就堆特征,结果模型跑出来一团糟。嗯,咱们今天就把这块掰开揉碎了讲清楚。
核心观点:特征工程不是堆砌,而是提炼。好的特征能让模型事半功倍,坏的特征就是噪音。
3.1 特征分类:三种核心类型
我个人习惯把异常交易的特征分成三大类:统计特征、时序特征、行为特征。这三类各有各的用处,也各有各的坑。
3.1.1 统计特征
统计特征是最直观的。说白了,就是算数。比如交易金额的均值、中位数、标准差、最大值、最小值、分位数等等。我在项目中遇到过,有些风控系统只盯着单笔金额,结果被犯罪分子用“蚂蚁搬家”的方式绕过去了。
常见的统计特征:
- 集中趋势:均值、中位数、众数——反映交易的一般水平
- 离散程度:标准差、方差、极差——反映交易的波动性
- 分布形态:偏度、峰度——反映交易是否异常集中
- 分位数:25%分位、75%分位、IQR——抗噪能力强
我的经验:统计特征里,中位数比均值更靠谱。因为均值容易被极端值带偏,而中位数稳如老狗。我曾经处理过一个案子,犯罪分子用几笔大额交易把均值拉高,但中位数纹丝不动,一下子就暴露了。
3.1.2 时序特征
时序特征,就是跟时间挂钩的特征。为什么重要?因为异常交易往往有特定的时间模式。比如深夜交易、高频交易、间隔极短的连续交易等等。
常见的时序特征:
- 时间窗口统计:过去1小时、24小时、7天内的交易次数和金额
- 时间间隔:相邻两笔交易的时间差,以及时间差的统计量
- 时间模式:是否在非工作时间交易、是否在节假日交易
- 趋势特征:交易金额的移动平均、指数平滑值
你想想看,一个正常用户,谁会凌晨3点连续刷10笔大额交易?时序特征就是用来抓这种“反人类”行为的。
3.1.3 行为特征
行为特征是最有“人味儿”的特征。它描述的是用户的操作习惯。比如登录设备、IP地址、操作路径、点击速度等等。
常见的行为特征:
- 设备指纹:是否使用模拟器、是否root、是否频繁更换设备
- IP特征:IP归属地是否频繁变化、是否使用代理/VPN
- 操作习惯:输入速度、页面停留时间、点击顺序
- 社交关系:交易对手是否在黑名单、是否是新注册账户
注意:行为特征虽然强大,但容易涉及隐私问题。我曾经因为采集了过多的行为数据,差点被合规部门约谈。所以,采集行为特征时一定要确认合规边界。
3.2 特征提取方法
特征提取,就是把原始数据变成可用特征的过程。我常用的方法有下面几种:
3.2.1 基于统计的提取
这是最基础的方法。直接对原始字段做统计计算。比如对交易金额字段,直接计算均值、标准差、分位数等。
# 示例:提取统计特征
import pandas as pd
import numpy as np
def extract_stat_features(df, group_col='user_id', value_col='amount'):
"""
提取每个用户的交易金额统计特征
"""
features = df.groupby(group_col)[value_col].agg([
('amount_mean', 'mean'),
('amount_std', 'std'),
('amount_median', 'median'),
('amount_max', 'max'),
('amount_min', 'min'),
('amount_q25', lambda x: x.quantile(0.25)),
('amount_q75', lambda x: x.quantile(0.75)),
('amount_iqr', lambda x: x.quantile(0.75) - x.quantile(0.25)),
('amount_skew', 'skew'),
('amount_kurt', 'kurt')
]).reset_index()
return features
3.2.2 基于时序的提取
时序特征提取,核心是“窗口”。我习惯用滑动窗口来捕捉短期行为,用累积窗口来捕捉长期行为。
# 示例:提取时序特征
def extract_time_features(df, group_col='user_id', time_col='timestamp', value_col='amount'):
"""
提取时序特征:过去1小时、24小时、7天的交易次数和金额
"""
df = df.sort_values([group_col, time_col])
# 过去1小时内的交易次数和金额
df['count_1h'] = df.groupby(group_col)[time_col].transform(
lambda x: x.rolling('1h', min_periods=1).count()
)
df['sum_1h'] = df.groupby(group_col)[value_col].transform(
lambda x: x.rolling('1h', min_periods=1).sum()
)
# 过去24小时内的交易次数和金额
df['count_24h'] = df.groupby(group_col)[time_col].transform(
lambda x: x.rolling('24h', min_periods=1).count()
)
df['sum_24h'] = df.groupby(group_col)[value_col].transform(
lambda x: x.rolling('24h', min_periods=1).sum()
)
return df
3.2.3 基于行为的提取
行为特征提取,往往需要一些“巧思”。比如,我可以通过计算用户切换IP的频率,来判断是否在使用代理。
# 示例:提取行为特征
def extract_behavior_features(df, group_col='user_id'):
"""
提取行为特征:IP切换频率、设备切换频率、操作速度等
"""
features = df.groupby(group_col).agg({
'ip_address': lambda x: x.nunique(), # 不同IP数量
'device_id': lambda x: x.nunique(), # 不同设备数量
'timestamp': lambda x: x.diff().mean() # 平均操作间隔
}).rename(columns={
'ip_address': 'unique_ip_count',
'device_id': 'unique_device_count',
'timestamp': 'avg_interval'
}).reset_index()
# 计算IP切换频率(每分钟切换次数)
features['ip_switch_rate'] = features['unique_ip_count'] / features['avg_interval']
return features
3.3 特征选择策略
特征提取完了,接下来就是选择。为什么需要选择?因为特征太多会带来“维度灾难”。我见过一个项目,工程师提取了500多个特征,结果模型训练时间长了10倍,效果反而下降了。
3.3.1 过滤法
过滤法,就是先算每个特征跟目标变量的关系,然后挑出关系强的。常用的指标有:
| 方法 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 方差阈值 | 数值型特征 | 简单快速 | 忽略特征与目标的关系 |
| 卡方检验 | 分类特征 | 适合离散数据 | 对连续数据不友好 |
| 互信息 | 通用 | 能捕捉非线性关系 | 计算量较大 |
| 相关系数 | 数值型特征 | 直观易懂 | 只能捕捉线性关系 |
我的习惯:先用方差阈值筛掉那些几乎不变的“死特征”,再用互信息挑出跟目标关系强的。这样既快又准。
3.3.2 包裹法
包裹法,就是把特征选择当成一个搜索问题。比如递归特征消除(RFE),每次去掉一个最不重要的特征,直到达到目标特征数。
# 示例:使用RFE进行特征选择
from sklearn.feature_selection import RFE
from sklearn.ensemble import RandomForestClassifier
def select_features_rfe(X, y, n_features_to_select=10):
"""
使用递归特征消除选择特征
"""
estimator = RandomForestClassifier(n_estimators=100, random_state=42)
selector = RFE(estimator, n_features_to_select=n_features_to_select, step=1)
selector.fit(X, y)
selected_features = X.columns[selector.support_].tolist()
return selected_features
3.3.3 嵌入法
嵌入法,就是在模型训练过程中自动选择特征。比如Lasso回归、树模型的特征重要性等。
# 示例:使用树模型的特征重要性进行选择
def select_features_embedded(X, y, threshold=0.01):
"""
使用随机森林的特征重要性进行选择
"""
model = RandomForestClassifier(n_estimators=100, random_state=42)
model.fit(X, y)
importances = pd.Series(model.feature_importances_, index=X.columns)
selected_features = importances[importances > threshold].index.tolist()
return selected_features
避坑指南:我曾经用嵌入法选特征,结果选出来的特征全是跟时间相关的。为什么?因为数据里时间特征跟目标变量有很强的相关性,但实际业务中时间特征往往是“伪相关”。所以,选特征时一定要结合业务理解,不能完全依赖算法。
3.4 本章知识体系
下面这张图,是我自己画的,把本章的核心逻辑串起来了。你一看就明白。
这张图把咱们今天讲的内容串起来了:从原始数据出发,先做特征分类,再提取特征,最后选择特征。每一步都有对应的技术和方法。
好了,特征工程的基础就讲到这里。记住一句话:特征工程不是堆砌,而是提炼。好的特征,能让模型事半功倍;坏的特征,就是噪音。下次咱们聊特征工程里的高级技巧——特征交叉和特征降维。
公众号:蓝海资料掘金营,微信deep3321