做了这么多年机器学习项目,我越来越确信一句话:特征决定了模型的上限,调参只是在逼近这个上限。但手工特征工程太耗人了,尤其是在数据源多、字段乱、关系复杂的项目里。后来我把一套基于Python的自动特征工程流程沉淀成固定管线,从原始数据到模型就绪,基本全自动跑完。今天就把这套东西掰开揉碎讲给你听,包括工具选型、核心原理、完整代码和踩过的坑。
这篇文章不是从教科书里抄来的理论,而是我在真实项目里反复调整后的实操总结。不管你是刚入门机器学习,还是已经在处理高维表格数据的老手,应该都能从里面找到可复用的东西。我会先讲清楚自动特征工程解决什么问题,再对比主流Python工具,然后给出全套自动化流程和代码,最后把我实际遇到过的问题和排查方法整理成清单。
1. 先想清楚:自动特征工程到底在解决什么问题
1.1 手工特征工程为什么成了项目瓶颈
很多团队搭建机器学习项目时,习惯把精力放在模型选型和调参上,结果做到一半发现卡死在特征环节。比如数据仓库里同时有用户基本信息、登录日志、订单流水、优惠券使用记录,业务方要求下周上线一个用户复购预测模型,你要手工构造“最近30天订单金额的均值”“最近30天订单金额的标准差”“不同支付方式的订单占比”这类特征。字段一多一乱,整个开发周期就会被这类重复计算拖垮。
更麻烦的是,手工特征工程里的很多动作是无法复用的。换一个业务场景,换一批数据,之前的SQL和pandas代码就得重新写。哪怕只是增加一个时间窗口,比如从“最近7天”改成“最近14天”,你也要挨个检查相关的特征生成逻辑有没有遗漏。模型训练本身往往几分钟就结束,可特征工程却要消耗几周时间,这就是项目最大的瓶颈。
我见过不少团队把大量时间花在“手工拼特征”上,最后得出的特征集却不够稳定。原因很简单:人脑能够同时组合的字段和算子数量非常有限,通常只会想到均值、最大值、最小值、求和等几个聚合函数,很难发散出真正有价值的高阶交互特征。于是模型能力被特征质量卡住,再怎么调参也上不去。
1.2 自动化的目标不是取代你,而是把重复动作标准化
自动特征工程并不是说以后完全不需要人参与,而是把重复性高、规则明确的动作交给代码去执行。比如类型推断、缺失值填充、基础聚合特征生成、相关性过滤等,这些步骤几乎在每个机器学习项目里都会用到,完全可以固化成一套标准流程。
标准化带来的好处很直接:第一,开发效率大幅提升,以前需要手工写几十段特征计算逻辑,现在只要定义好实体关系,调用一次深度特征合成就能得到成百上千个候选特征;第二,流程可复现,每次跑出来的特征定义都能导出,任何人拿到同一份数据和同一套配置,都能得到完全一致的结果;第三,便于实验管理,当你要对比不同特征集对模型效果的影响时,自动化流水线可以帮你快速切换和回溯。
自动化不是要取代特征工程师,而是把你从重复劳动里解放出来,把精力转移到真正需要业务理解的环节,比如选择哪些原始表参与建模、定义字段的业务含义、判断哪些自动生成的特征在逻辑上是否合理。
1.3 “发散创新”在自动特征工程里的含义
项目标题里有个词我觉得很贴切:发散创新。手工特征工程的问题恰恰是不够发散,你很容易被“人均消费金额”“订单总数”这类常规特征定住思维。自动特征工程的优势在于,它可以在你给定的基础算子和字段关系之上,组合出大量你没想到过的候选特征。
举个例子,如果原始数据里有订单表和用户表,自动特征工程不仅会生成“每个用户的订单数”“订单金额均值”,还会尝试构造“每个用户最后一次订单距离当前时间的天数”“用户在工作日下单的平均金额”“订单金额在地区内的分位数”等。这些高阶特征往往是模型性能的关键来源,因为它们捕捉到了隐藏的模式。
但发散也意味着风险,特征数量可能爆炸,无关特征可能混进来,所以必须配上一套严格的特征筛选和验证机制。这也是为什么我强调“全流程自动化”:发散生成只是其中一环,后续的清洗、筛选、泄漏检测、数据划分,每一环都不能省。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Python生态里的自动特征工程工具,选型是第一步
2.1 四类工具的能力边界
Python生态里有不少跟自动特征工程相关的库,但它们的定位差异非常大,选错工具很容易绕远路。我在实际项目里主要对比过下面几类,正好可以帮你理清它们在能力边界上的区别。
| 工具/库 | 擅长解决的问题 | 不适合的场景 |
|---|---|---|
| Featuretools | 多表关系型数据、深度特征合成、时间索引约束 | 单条离散时间序列的特征抽取,配置略重 |
| tsfresh | 时间序列批量特征提取,自动筛选相关特征 | 多实体关系建模,场景比较单一 |
| AutoFeat | 回归/分类任务中的非线性高阶特征生成 | 高基数多表数据,速度较慢 |
| feature_engine / sklearn | 缺失值填充、编码、缩放、多项式特征 | 无法自动处理多表关系,组合能力有限 |
Featuretools最吸引我的是它对“关系型数据”的原生支持,这是大多数机器学习项目的现实形态:多张表通过外键关联。它能把多表结构、时间索引、聚合规则统一建模,然后自动扩展出跨表的特征。tsfresh则更偏向纯时间序列场景,比如传感器读数、股票行情、点击流,它会从每个时间序列里抽取几百个统计特征,再用假设检验筛掉无关特征。
AutoFeat的思路也很特别,它会把已有的数值特征通过加减乘除组合成新的高阶特征,同时给出特征重要度,适合特征维度不大但需要非线性交互的场景。feature_engine和sklearn里的许多预处理器则更像是“工具箱”,负责清洗和编码,不能承担完整的自动特征生成任务。
2.2 深度特征合成(DFS)是怎么“发散”出特征的
我最终把Featuretools作为核心,是因为它的核心算法深度特征合成(DFS)非常契合“发散创新”这个思路。DFS的基本单位有三个:实体集、聚合原语和转换原语。
实体集可以理解成一个存放所有数据表和表间关系的数据结构。比如用户表和订单表通过“用户ID”关联,那么在实体集里就可以明确记录这对关系。只要关系定义清楚,DFS就能沿着关系路径,从目标表出发向相邻表聚合信息,再继续向更远的关系扩展。聚合原语是对一组数值执行汇总操作的函数,比如sum、mean、std、count、max、min,它们会把多行的信息压缩成一行目标实体的特征。
转换原语则是在单条记录上做的变换,比如从时间戳里提取“星期几”“小时”“月份”,对数值做“取对数”“差分”“累积占比”。DFS的深度特征合成,就是在聚合结果之上继续做转换,或者在转换结果之上再做聚合,这样层层堆叠,就能生成像“每个用户最近30天消费金额的周均值变化率”这样的高阶特征。
我用一个很简单的例子说明:假设原始特征只有订单金额和下单时间,第一步可以聚合出“用户平均订单金额”,这是一个深度为1的特征;第二步在这个平均金额基础上再转换出“用户平均订单金额与全站平均订单金额的比值”,这就是深度为2的特征。深度越大,组合的抽象层次越高,但特征数量也会指数级增长,所以必须控制max_depth参数。
2.3 我的选型判断:什么场景选哪个工具
如果是几张表有明确主外键关系,并且需要自动构造跨表特征,我建议直接用Featuretools,再搭配sklearn或feature_engine做清洗和编码。如果项目核心只有一条长周期时间序列,比如设备振动信号或股票分钟线,tsfresh更方便,它在提取峰峰值、频谱能量这类时序特征时非常高效。
如果是单纯的表格数据,字段不多但希望补充一些高阶交互特征,可以先试试AutoFeat,它能自动组合出类似“收入除以年龄”的衍生特征,但要注意特征维度过高时计算开销会很大。如果是追求生产环境稳定,我更推荐Featuretools加自定义特征原语的组合,因为它能结合领域知识,同时保证流程可追溯。
我的习惯是先用Featuretools跑一个基线特征矩阵,如果时间序列成分很重,再单独用tsfresh补充时序特征,最后交给特征筛选模块统一去重。多工具配合往往比单工具硬撑效果好得多。
3. 全流程自动化的骨架:从原始数据到模型就绪
3.1 数据接入层的规范设计
全流程自动化的第一步不是直接造特征,而是把数据接入层做好。我见过太多项目因为表结构不统一、字段命名混乱,导致自动特征工程脚本无法复用。所以我在项目里会先约定一套输入规范:每张原始数据表必须是标准表格格式,CSV或Parquet都行;每张表必须有稳定的主键;涉及时间的字段必须统一成datetime类型;外键关系需要提前在配置文件中声明。
配置文件的威力在于,它把业务知识从代码里剥离出来。你可以把“用户表的主键是user_id”“订单表与用户表通过user_id关联”“订单表中的下单时间是时间索引”等信息写成YAML或JSON。这样换一个业务场景,只需要改配置,不需要改特征工程代码。
配置文件的示例如下:
yaml复制entities:
customers:
dataframe: customers.parquet
index: customer_id
transactions:
dataframe: transactions.parquet
index: transaction_id
time_index: created_at
relationships:
- parent: customers
parent_column: customer_id
child: transactions
child_column: customer_id
有了这个配置,数据接入层就可以统一加载表结构、校验主键、检查时间字段,保证后续自动特征生成不会因为脏数据而中断。
3.2 类型推断与脏数据清洗的自动化策略
原始数据最大的问题就是脏和乱:ID字段被读成了数值、日期字段混入字符串、类别字段里同时出现“male”和“男”、缺失值比例高达80%。如果这些不提前处理,自动特征工程会把错误信息放大,最后生成一堆没有意义的特征。
我采用的策略是分两层清洗。第一层是通用清洗,比如把所有字段名转成统一风格,全角转半角,字符串首尾去空格,识别常见时间格式并统一为datetime;第二层是基于“列角色”的清洗,也就是在配置里人工声明每个字段的业务角色,例如user_id是ID列、gender是类别列、order_amount是数值列、created_at是时间列。有了这个角色清单,代码就能自动决定是否删除高基数ID列、是否对类别列做编码、是否把数值列里的异常值替换成缺失。
高基数的ID列是类型推断阶段最典型的坑。比如用户ID本身是数值型,但把它当作数值特征参与聚合就会产生“用户ID均值”这种毫无意义的特征。所以我在自动清洗里会增加一条规则:对主键和外键字段默认排除在特征计算范围之外,除非你在配置里显式声明它具备业务含义。
3.3 特征生成:自动发散的同时加约束
特征生成是整个流程的核心,也是最需要“既发散又克制”的环节。完全发散会产生几千甚至几万个特征,内存和训练时间都扛不住;约束太死又回归到了手工特征工程。我的做法是用Featuretools的DFS,但明确控制四个参数,分别是max_depth、agg_primitives、trans_primitives和max_features。
max_depth控制特征堆叠的层数,我用2作为默认值,只有在候选特征明显不够时才尝试3。agg_primitives控制聚合函数集合,除了常规的sum、mean、std、max、min、count,还会根据业务场景加入trend、avg_time_between这类时序相关原语。trans_primitives控制转换操作,比如提取日期里的星期、月份,或者计算数值的百分位数。
max_features是最重要的约束阀门,它可以限制最终保留到特征矩阵里的特征数量。Featuretools会优先保留在训练集上表现较好的候选特征,这样一来,发散生成的特征数量虽然很大,但最终进入模型的特征矩阵规模是可控的。
3.4 特征筛选:防止维度爆炸和多重共线性
DFS生成的候选特征里,真正有用的往往是少数,所以特征筛选是必做的一道工序。我的筛选流程通常是多级串联:先删除缺失率超过50%的特征,再删除方差接近于0的特征,然后计算特征之间的相关系数,当两个特征相关性超过0.95时,优先保留与目标变量相关性更高的那个。
如果完成了上面三步之后特征数量依然偏大,我还会继续用基于模型的特征重要性或者互信息做一个粗筛。随机森林或LightGBM在训练集上跑一次,提取feature_importances_,保留累计贡献达到90%的特征集合。这个方法不算严谨,但是在候选特征过多时非常实用。
特征筛选的另一个好处是自动化的可解释检查。当我看到筛选后保留的特征列表时,能快速判断AutoML生成的候选特征是否符合业务逻辑。如果某个特征在逻辑上说不通,但数据上表现很好,我要么怀疑数据泄漏,要么会专门花时间做业务分析,而不是直接丢进模型。
3.5 数据集划分与模型就绪输出
很多自动特征工程流程到生成特征矩阵就结束了,但“模型就绪”其实还差最后两步:数据集划分和输出规范。对于时序型项目,我不建议用随机切分,而是按时间先后划分训练集和测试集,防止未来信息泄漏。
数据划分可以用TimeSeriesSplit或者直接按日期截断。划分完成后,我需要把三个东西保存下来:特征矩阵训练集和测试集、特征定义列表、预处理管线的参数文件。特征定义列表保存的是Featuretools生成的特征名和对应原语,这是后续解释模型和排查问题的重要依据。
最后用Parquet格式保存数据,能显著减少磁盘占用,读取速度也比CSV快很多。同时把训练集和测试集放到同一个目录下,命名规范为train.parquet和test.parquet,再把特征列表保存成feature_defs.json。这样一套“模型就绪”数据集就完成了,后续无论接LightGBM、XGBoost还是深度学习框架,都可以直接读取。
4. 核心代码实现:一套可直接改的流水线
4.1 安装依赖与最小环境
在开始写代码前,先把环境准备好。我这里只列最小依赖,具体版本建议根据自己环境调整:
bash复制pip install pandas numpy scikit-learn featuretools pyarrow
Pandas和NumPy负责基础数据处理,scikit-learn用于特征筛选和数据划分,Featuretools负责深度特征合成,PyArrow用于读写Parquet文件。需要注意的是,Featuretools的API在1.0之后有过较大调整,老版本里的entity_from_dataframe、relationship函数在新版本中已改名,所以我下面给出的代码默认使用较新的API风格。
4.2 定义实体集和数据关系:自动特征工程的地基
我习惯把数据加载和实体集定义放在一个函数里,这样每次换数据时只需要改配置部分。下面是核心代码:
python复制import pandas as pd
import featuretools as ft
transactions = pd.read_parquet("transactions.parquet")
customers = pd.read_parquet("customers.parquet")
transactions["created_at"] = pd.to_datetime(transactions["created_at"])
customers["signup_date"] = pd.to_datetime(customers["signup_date"])
es = ft.EntitySet(id="customer_demo")
es.add_dataframe(
dataframe_name="transactions",
dataframe=transactions,
index="transaction_id",
time_index="created_at",
)
es.add_dataframe(
dataframe_name="customers",
dataframe=customers,
index="customer_id",
)
es.add_relationship(
"customers", "customer_id",
"transactions", "customer_id",
)
这段代码里最关键的是设置了time_index。time_index在自动特征工程里非常重要,它告诉Featuretools哪些时间戳可以用于时间窗口计算,从而尽量避免用未来的数据去预测过去。很多新手会忽略这一步,结果模型在验证集上表现极好,上线后效果却一塌糊涂。
4.3 用DFS批量生成“发散”特征
实体集定义好之后,一行代码就能生成大量候选特征:
python复制feature_matrix, feature_defs = ft.dfs(
entityset=es,
target_dataframe_name="customers",
max_depth=2,
agg_primitives=[
"sum", "avg_time_between", "mean",
"std", "max", "min", "count",
],
trans_primitives=[
"day", "month", "weekday",
"percentile", "diff",
],
max_features=300,
n_jobs=-1,
verbose=True,
)
max_depth=2是我比较推荐的起点,代表特征最多由两层聚合或转换操作叠加而成。如果设成3,特征数量会急剧膨胀,训练时间也成倍增加。max_features=300用来限制最终进入特征矩阵的特征数量,避免一次性生成太多列导致内存问题。
运行后,feature_matrix是一个DataFrame,索引是对应的customer_id,每一列是一个特征。feature_defs里保存着每个特征的具体生成路径,可以通过打印Featuretools对象看到它是由哪个原始字段、哪个聚合原语、哪个转换原语组合而来的。
4.4 特征后处理与自动筛选
DFS返回的结果通常不能直接建模,还需要做后处理。首先是类型压缩和缺失值填充。因为我是对训练集调用DFS,得到的特征矩阵会同时包含训练和测试,这里先做整体清洗,划分后再填充缺失值,避免信息泄漏。
python复制def clean_feature_matrix(fm, target_col=None):
fm = fm.copy()
for col in fm.columns:
if fm[col].dtype == "object":
fm[col] = fm[col].astype("category")
elif "float" in str(fm[col].dtype):
fm[col] = fm[col].astype("float32")
return fm
fm_clean = clean_feature_matrix(feature_matrix)
然后是特征筛选。下面是低方差和相关性筛选的代码逻辑:
python复制from sklearn.feature_selection import VarianceThreshold
vt = VarianceThreshold(threshold=0.01)
fm_vt = vt.fit_transform(fm_clean)
selected_cols = fm_clean.columns[vt.get_support()].tolist()
fm_vt = pd.DataFrame(fm_vt, columns=selected_cols, index=fm_clean.index)
corr_matrix = fm_vt.corr().abs()
upper = corr_matrix.where(
np.triu(np.ones(corr_matrix.shape), k=1).astype(bool)
)
to_drop = [
column
for column in upper.columns
if any(upper[column] > 0.95)
]
fm_reduced = fm_vt.drop(columns=to_drop)
这段代码的逻辑是先用VarianceThreshold删除几乎恒定不变的特征,再用相关系数矩阵剔除高度相关的冗余特征。如果过滤完特征数量还是太多,可以继续接一个随机森林重要性排序,只保留累计重要度达到90%的top特征。
4.5 输出“模型就绪”数据集
后处理完成后,我会把数据统一输出为模型可以直接读取的格式:
python复制from sklearn.model_selection import train_test_split
X = fm_reduced.drop(columns=["target"], errors="ignore")
y = fm_reduced["target"]
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, shuffle=False
)
X_train.to_parquet("train.parquet")
X_test.to_parquet("test.parquet")
fm_feature_names = list(X_train.columns)
如果是时序预测项目,train_test_split里必须设置shuffle=False,否则随机切分会把未来数据混进训练集,造成严重的数据泄漏。训练集和测试集按时间顺序切分之后,把特征名称列表保存成feature_names.json,这样后续调参和模型复现时都能快速定位到实际参与建模的特征。
到这里,从原始数据到模型就绪数据集的自动化流程就完成了。你只需要维护好配置文件和实体关系,后续更换数据集时,运行同一个脚本就能得到一套标准的train.parquet、test.parquet和feature_names.json。
5. 实测中踩过的坑和排查技巧实录
5.1 特征数量爆炸,内存直接爆掉
我第一次用DFS跑一个用户行为项目时,没有设置max_features,只设了max_depth=2,结果生成了6000多个特征,直接导致内存占满、进程被杀。后来我总结了一套控制方案:一是限制max_features,先设成300跑一版;二是减少agg_primitives的数量,例如只保留sum、mean、count、std,不要一开始就把所有原语都放进去。
还有一个小技巧是先在小样本上跑通流程,再上全量数据。比如随机抽取5万条订单,快速看一下候选特征数量和质量,确认没问题后再用全量数据生成。这样能节省大量调试时间,也避免反复爆内存。
5.2 时间泄漏:自动特征工程里最隐蔽的坑
自动特征工程虽然能自动生成特征,但它并不知道哪些信息在预测时是可得的。如果不设置time_index,DFS就可能使用整条时间线上的聚合统计,包括未来的数据,导致模型在训练集上表现异常优秀,上线后瞬间崩塌。
我经历过一次非常诡异的模型回测,AUC能到0.99,但真实环境里几乎没有区分度。排查了很久才发现,订单表里有个“支付完成时间”字段,我没有设置time_index,自动特征工程就把它当成普通数值参与聚合,结果生成了包含未来信息的特征。
现在我的做法是,所有涉及时间序列的字段都要明确声明time_index,并检查是否需要按时间窗口计算特征。Featuretools的cutoff_time参数可以指定预测时点,让DFS只使用时点之前的数据,这是防止时间泄漏最直接的手段。
5.3 类型推断错误导致的特征污染
自动类型推断并不总是可靠的。比如用户ID是数字,但它是主键而不是数值特征,Featuretools可能会把它当成连续值参与mean、std等聚合,生成一堆“用户ID均值”这种无意义特征。这些特征不仅浪费资源,还可能干扰特征筛选。
我的解决办法是在配置里声明列角色,把主键和外键从特征生成中排除。如果想让代码自动识别,也可以增加一条简单规则:对于整型列,如果唯一值数量占比超过90%,就默认它是ID列而不是数值特征,不参与聚合。这样能在一定程度上减少类型推断错误。
5.4 特征名变得不可读,模型解释性怎么办
Featuretools生成的默认特征名非常长,比如“SUM(transactions.order_amount) BY customers.customer_id”,在生产环境或业务汇报时很难直接阅读。而且DFS的深度特征合成会把多个操作组合在一起,特征名往往会变成一大串难以理解的文本。
这里我的经验是,特征定义列表feature_defs才是真正可解释的资产,特征名只是临时标签。排查问题时,我会解析feature_defs,把每个特征关联回原始字段和操作原语,整理成一张业务可读的表格。如果要交付模型给业务方,我还会用业务术语对最终保留的100个特征做重命名,例如“SUM(transactions.order_amount)”更名为“累计订单金额”。这样既保留了自动生成的能力,又保证了可解释性。
5.5 快速定位问题的调试清单
| 症状 | 可能原因 | 检查方法 | 解决办法 |
|---|---|---|---|
| 单机内存暴涨 | 候选特征数量过多 | 查看DFS生成列数 | 减小max_depth、max_features或减少原语 |
| 验证集效果远低于训练集 | 存在时间泄漏 | 检查特征矩阵中是否出现未来统计量 | 设置time_index并指定cutoff_time |
| 特征中出现大量NaN | 缺失值过多或原语不适配 | 查看特征缺失率 | 调整原语组合,增加缺失填充 |
| 重要特征全被过滤 | 特征筛选阈值过严 | 检查相关性阈值和方差阈值 | 适当放宽阈值,保留部分弱相关性特征 |
| 相同配置但特征数不稳定 | 数据版本或抽样不一致 | 对比原始数据shape和唯一值 | 固定数据抽样种子,固化配置版本 |
这张表是我在排查问题时最常用的起点,难度不高但很实用。遇到新问题,我一般先对照这张表定位大概方向,再深入具体模块。
6. 从自动特征工程到 AutoML:我现在的完整链路
6.1 与 Optuna / Hyperopt 的整合思路
自动特征工程解决了“特征从哪里来”的问题,但后面还有模型调参和特征选择的联合优化。现在我会把整条流程封装进一个sklearn风格的Pipeline,让特征生成、特征筛选、模型训练可以一起参与调参。
比如用Optuna时,我可以把DFS的max_depth作为超参数参与搜索。虽然每次跑DFS都要重新计算特征矩阵,但样本量不大时完全可行。另一种更轻量的做法是先生成一批固定特征,然后把特征数量和模型参数一起交给Optuna搜索,特征数量通过SelectKBest或随机森林重要度阈值动态控制。
整合后的好处是,我不再需要手工做“特征工程-调参-再特征工程”的多轮循环,而是让自动化流程自动搜索到相对合适的配置组合。当然这种搜索的代价是计算量增加,所以生产环境里我通常只对max_depth或筛选阈值做小范围搜索,不会做太大规模的全链路搜索。
6.2 自定义特征原语,把领域知识加回去
自动特征工程最大的争议是它可能生成大量无业务含义的特征,所以我在实际项目中会主动把领域知识注入进去。Featuretools允许通过装饰器自定义特征原语,这是一个非常好用的扩展点。
比如在电商项目里,我希望自动生成“用户最近一次交易距今的小时数”这类业务特征,可以定义如下:
python复制from featuretools.primitives import make_agg_primitive
def hours_since_last_transaction(transaction_times, cutoff_time):
times = pd.to_datetime(transaction_times)
cutoff = pd.to_datetime(cutoff_time)
delta = (cutoff - times.max()).total_seconds() / 3600
return delta
HoursSinceLastTransaction = make_agg_primitive(
hours_since_last_transaction,
name="hours_since_last_transaction",
input_types=[ft.variable_types.Datetime],
return_type=ft.variable_types.Numeric,
)
自定义原语的好处是,你可以让缺乏业务直觉的自动发散过程,朝指定方向偏置。它会融入DFS的整体搜索框架,自动与其它原语组合出更高阶的特征,而不是只能生成天马行空的数学组合。领域知识和自动发散两者结合,是我目前最推荐的做法。
6.3 写在最后:一点个人体会
做了这么多项目之后,我的体会是自动特征工程并不是银弹,它最大的价值不是取代人,而是帮你把候选特征空间扩大一个量级,让你能快速验证新思路。以前要花一周才能验证“最近30天订单金额方差对复购预测有没有用”,现在只要把原始数据接入配置,DFS跑一遍,十几分钟就能看到效果。
我自己最常用的方式,是先让DFS生成一批基线特征,再在基线基础上用自定义原语叠加少量业务关键特征。这样一来,既覆盖了人工容易忽略的高阶组合,又保留了业务上的解释性。自动特征工程最怕的就是一把梭,生成几千个特征然后丢给模型,最后又无法解释。把“发散”和“约束”平衡好,这套流程才能真正在项目里落地复用。
