1. 特征工程的核心价值
第一次接触机器学习项目时,我犯了个典型错误——把所有时间都花在模型调参上,结果准确率死活上不去。直到导师提醒我"垃圾进,垃圾出"(Garbage in, garbage out),才意识到特征质量才是模型效果的天花板。特征工程就像厨师处理食材,再厉害的厨艺也救不了变质的原料。
在实际工业场景中,数据科学家70%的时间都花在数据清洗和特征工程上。好的特征不仅能提升模型效果,还能降低计算成本。去年我们团队做过对比实验:在同一个电商推荐项目中,仅通过特征优化就将AUC从0.72提升到0.81,同时推理耗时减少了40%。
2. 特征构建方法论
2.1 数值型特征处理
连续数值的尺度问题是最常见的坑。上周刚处理过一个传感器数据集,温度值范围在-20~300之间,而湿度值只有0~1。如果不做标准化直接喂给模型,相当于告诉算法温度比湿度重要几百倍。
我常用的标准化组合拳:
from sklearn.preprocessing import StandardScaler, MinMaxScaler # 对正态分布特征用Z-score标准化 scaler = StandardScaler() df['temperature_normalized'] = scaler.fit_transform(df[['temperature']]) # 对存在边界值的特征用MinMax scaler = MinMaxScaler(feature_range=(0,1)) df['humidity_scaled'] = scaler.fit_transform(df[['humidity']])特别注意:一定要用训练集的统计量来转换测试集!我在早期项目里犯过用全数据集做标准化的错误,导致线上效果严重虚高。
2.2 类别型特征编码
One-Hot编码虽然经典,但在高基数(high-cardinality)特征上会引发维度灾难。去年处理用户地域特征时,200多个城市的one-hot直接让特征矩阵膨胀到内存装不下。
现在我的编码策略会根据特征基数动态调整:
- 基数<10:One-Hot
- 10<基数<50:Target Encoding
- 基数>50:哈希编码(Hashing Trick)
# Target Encoding示例 from category_encoders import TargetEncoder encoder = TargetEncoder(cols=['city']) train_df = encoder.fit_transform(train_df, train_df['target']) test_df = encoder.transform(test_df)2.3 时间特征分解
原始时间戳是最糟糕的特征形式。处理订单数据时,我会把datetime拆解成多个有业务意义的维度:
df['order_hour'] = df['order_time'].dt.hour df['is_weekend'] = df['order_time'].dt.weekday >= 5 df['day_of_month'] = df['order_time'].dt.day最近还发现个有意思的pattern:把小时转换为三角坐标可以保留周期性:
df['hour_sin'] = np.sin(2*np.pi*df['order_hour']/24) df['hour_cos'] = np.cos(2*np.pi*df['order_hour']/24)3. 特征选择实战技巧
3.1 统计过滤法
相关系数矩阵是最快的初筛工具,但要注意两点:
- 只能检测线性关系
- 对异常值敏感
我通常会结合方差阈值先过滤掉不变的特征:
from sklearn.feature_selection import VarianceThreshold selector = VarianceThreshold(threshold=0.01) selected_features = selector.fit_transform(df)3.2 模型特征重要性
树模型的特征重要性是最直观的,但要注意不同模型可能给出不同结果。我的做法是用多个模型交叉验证:
from sklearn.ensemble import RandomForestClassifier from xgboost import XGBClassifier models = [RandomForestClassifier(), XGBClassifier()] for model in models: model.fit(X_train, y_train) print(pd.Series(model.feature_importances_, index=X.columns))3.3 递归特征消除(RFE)
RFE就像反向的特征达尔文主义——从全量特征开始逐步淘汰弱者。在信贷风控项目中,我们用RFE将特征从120个精简到35个,KS值反而提升了5个百分点。
from sklearn.feature_selection import RFE from sklearn.linear_model import LogisticRegression rfe = RFE( estimator=LogisticRegression(), n_features_to_select=30 ) X_rfe = rfe.fit_transform(X, y)4. 高级特征工程技巧
4.1 交互特征挖掘
特征交叉是提升模型表现的大杀器。在广告CTR预测中,用户职业和广告类别的组合特征比单特征重要得多。但要注意组合爆炸问题。
我的经验法则是:
- 先做业务逻辑强相关的交叉
- 再用统计方法验证效果
- 最后考虑二阶/三阶组合
# 用PolynomialFeatures自动生成交互项 from sklearn.preprocessing import PolynomialFeatures poly = PolynomialFeatures(degree=2, interaction_only=True) X_interact = poly.fit_transform(X[['age', 'income']])4.2 自动特征生成工具
现在越来越依赖FeatureTools这类自动化工具了。最近一个零售项目里,用深度特征合成(DFS)自动生成了"用户最近3次购买的平均间隔"这类时序特征,效果远超手工设计。
import featuretools as ft es = ft.EntitySet(id='transactions') es = es.entity_from_dataframe( entity_id='trans', dataframe=trans_df, time_index='purchase_time', variable_types={'user_id': ft.variable_types.Categorical} ) features, _ = ft.dfs( entityset=es, target_entity='trans', max_depth=2 )4.3 特征存储方案
随着特征工程越来越复杂,我建立了特征库来管理不同版本的特征集。使用Feast框架后,线上线下的特征一致性有了保障:
from feast import FeatureStore store = FeatureStore(repo_path=".") training_df = store.get_historical_features( entity_df=entity_df, features=[ "user_stats:avg_order_value", "user_stats:last_purchase_category" ] ).to_df()5. 避坑指南与性能优化
5.1 数据泄露预防
最惨痛的教训来自一个时间序列预测项目:不小心用未来数据做了标准化,导致验证集表现好得离谱。现在我的黄金法则是:
- 任何涉及统计量计算的操作都要分训练/测试集
- 时间序列必须严格按时间切分
- 用Pipeline封装所有预处理步骤
from sklearn.pipeline import Pipeline pipe = Pipeline([ ('scaler', StandardScaler()), ('model', LogisticRegression()) ]) pipe.fit(X_train, y_train) # 自动防止数据泄露5.2 计算效率优化
当特征维度超过百万时,传统方法就会遇到瓶颈。我的应对策略:
- 稀疏矩阵存储:对于one-hot后的高维数据
- 增量学习:处理超出内存的大数据集
- 并行化:用Dask或Spark加速
from scipy.sparse import csr_matrix sparse_matrix = csr_matrix(one_hot_encoded_df)5.3 监控与迭代
特征工程不是一劳永逸的。我们建立了特征监控看板,跟踪:
- 特征缺失率变化
- 数值分布漂移
- 重要性排名波动
当监控到某个关键特征的分布发生显著变化时(用KS检验检测),就需要重新评估特征有效性。