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