3、数据工程基础:数据采集、数据清洗、特征工程、数据版本管理
3.1 核心原理:数据是模型质量的天花板
在模型交付流程中,数据工程往往决定了一个项目的成败上限。业界常说“Garbage in, garbage out”,但更准确的说法是:数据质量决定了模型性能的上限,而模型算法只是逼近这个上限的手段。一个在训练集上表现优异的模型,如果其数据采集存在偏差、清洗逻辑不一致、特征分布漂移或版本不可追溯,那么在生产环境中几乎必然出现性能衰减。
数据工程的核心目标可以拆解为四个维度:完整性(覆盖所有关键业务场景)、一致性(同一实体在不同时间/系统中的定义统一)、时效性(数据能反映当前业务状态)、可复现性(任何历史模型都能追溯到其训练数据的确切版本)。这四个维度分别对应数据采集、数据清洗、特征工程和数据版本管理四大模块。
关键认知:数据工程不是一次性任务,而是贯穿模型开发、评估、上线和监控全生命周期的持续过程。每一次数据更新,都应当像代码提交一样被记录、审查和版本化。
3.2 数据采集:从源头保证数据质量
数据采集是数据工程的起点,其核心挑战在于覆盖度与偏差控制。常见的采集方式包括:业务数据库抽取(如MySQL、PostgreSQL)、日志采集(如Flume、Logstash)、埋点上报(前端/客户端事件)、消息队列接入(Kafka、Pulsar)以及第三方API拉取。在设计采集方案时,需要明确以下关键参数:
- 采集频率:实时(秒级)、准实时(分钟级)还是离线(T+1),取决于业务对时效性的要求。
- 采集粒度:全量快照还是增量变更(CDC),增量采集需考虑删除事件的捕获。
- 数据格式:JSON、Avro、Parquet等,需兼顾可读性与压缩效率。
- 采集监控:记录采集延迟、丢失率、重复率,设置告警阈值。
一个典型的采集链路配置示例如下:
# 使用Debezium捕获MySQL变更(CDC)
{
"connector.class": "io.debezium.connector.mysql.MySqlConnector",
"database.hostname": "prod-db-01.internal",
"database.port": "3306",
"database.user": "cdc_user",
"database.server.id": "5401",
"database.include.list": "orders,users",
"table.include.list": "orders.payments,orders.shipments",
"snapshot.mode": "schema_only_recovery",
"tombstones.on.delete": "false"
}
3.3 数据清洗:建立可复用的清洗规则库
数据清洗不是简单的“删除坏数据”,而是通过一系列规则将原始数据转换为符合模型输入要求的“干净数据”。清洗工作通常分为四个层次:
- 格式清洗:统一日期格式、去除首尾空格、转换字符编码、标准化手机号/身份证等。
- 缺失值处理:根据缺失机制(随机缺失/非随机缺失)选择删除、均值/中位数填充、模型预测填充或置为特殊值。
- 异常值检测:基于统计方法(3σ原则、IQR)、距离方法(DBSCAN)或业务规则(如年龄>120岁)识别并处理。
- 去重与一致性校验:基于业务主键去重,并验证跨表数据的一致性(如订单金额=商品单价×数量)。
清洗规则应当以配置文件或规则引擎的形式管理,而非散落在代码中。这样既能复用,也便于审计。以下是一个清洗规则配置的示例:
# 清洗规则定义(YAML格式)
rules:
- name: "user_age_range"
field: "age"
type: "range_check"
min: 0
max: 120
action: "flag_and_filter"
- name: "order_amount_positive"
field: "amount"
type: "custom_sql"
condition: "amount > 0"
action: "reject"
- name: "phone_format"
field: "phone"
type: "regex"
pattern: "^1[3-9]\\d{9}$"
action: "standardize"
常见误区:在清洗阶段直接删除大量“异常”数据,可能导致训练集与真实分布产生偏差。建议对删除操作保留审计日志,并定期统计清洗前后的数据分布变化。
3.4 特征工程:从原始数据到模型输入
特征工程是将清洗后的数据转换为模型可学习的数值表示。其核心方法包括:
| 特征类型 | 常用方法 | 适用场景 | 注意事项 |
|---|---|---|---|
| 数值特征 | 标准化、归一化、分箱、对数变换 | 连续值如金额、年龄、距离 | 分箱边界需结合业务含义 |
| 类别特征 | One-Hot、Label Encoding、Target Encoding | 城市、品类、用户等级 | 高基数类别需谨慎使用One-Hot |
| 时序特征 | 滑动窗口统计、时间衰减、滞后特征 | 用户行为序列、传感器数据 | 防止未来信息泄漏 |
| 交叉特征 | 笛卡尔积、特征组合、FM/FFM | 用户×商品、渠道×时段 | 组合爆炸需配合特征选择 |
特征工程中最重要的原则是防止数据泄漏。例如,在构建用户购买预测模型时,如果使用“用户是否已购买”作为特征,或者使用全量数据的均值进行归一化,都会导致模型在训练集上表现虚高,而线上效果大幅下降。正确的做法是:所有统计特征(如均值、最大值)必须基于训练集单独计算,并保存统计参数供验证集和测试集复用。
3.5 数据版本管理:让每一次实验可复现
数据版本管理是模型交付中常被忽视却至关重要的环节。没有版本管理,就无法回答“当前线上模型是用哪份数据训练的”这一基本问题。推荐使用专门的数据版本管理工具(如DVC、LakeFS)或结合对象存储的不可变快照机制。核心实践包括:
- 数据快照:每次训练前对数据集生成不可变的版本标识(如哈希值),并记录数据集的生成时间、来源SQL或采集任务ID。
- 数据血缘:记录每个特征字段的来源表、清洗规则和转换逻辑,便于排查问题。
- 训练/验证/测试集划分:划分逻辑必须固定(如按时间或按用户ID哈希),并随数据版本一起记录。
- 版本对比:当数据更新后,对比新旧版本的特征分布、样本量、标签分布,评估是否需要重新训练模型。
一个典型的数据版本记录结构如下:
# DVC 数据版本记录示例
version: v3.2.1
created_at: 2025-06-18T10:30:00Z
dataset_name: "user_purchase_prediction"
source:
- type: "sql"
query: "SELECT * FROM orders WHERE created_at >= '2025-01-01'"
- type: "kafka_topic"
topic: "user_events"
offset_range: "100234-105678"
cleaning_rules_version: "1.4.0"
feature_pipeline_version: "2.1.3"
train_test_split:
method: "by_user_id_hash"
train_ratio: 0.8
seed: 42
checksum: "a1b2c3d4e5f6..."
metrics:
row_count: 1250000
feature_count: 87
label_positive_rate: 0.032
3.6 案例复盘:推荐系统数据工程中的典型问题
某电商推荐团队在模型上线后一周内,CTR(点击率)从3.2%骤降至2.1%。排查过程如下:
- 初步排查:模型代码未变更,特征逻辑未调整,排除代码问题。
- 数据对比:对比训练集与线上实时数据的特征分布,发现“用户最近7天浏览商品数”这一特征在线上明显偏低。
- 根因定位:数据采集任务在凌晨2点后因Kafka消费者组rebalance导致部分日志丢失,且清洗规则中“浏览数>100视为异常并置为0”的规则误伤了正常用户。
- 修复措施:调整采集任务的消费者并发数,优化清洗规则为“浏览数>500才视为异常”,并增加数据质量监控(每日特征分布对比告警)。
- 版本回滚:使用DVC回滚到上一版本数据,重新训练模型并灰度上线,CTR恢复至3.1%。
这个案例说明:数据工程中的任何一个环节(采集稳定性、清洗规则合理性、版本可回滚性)都可能成为模型交付的瓶颈。建立完善的数据质量监控体系,比追求更复杂的模型算法往往能带来更稳定的业务收益。
最佳实践建议:在项目启动初期就建立数据版本管理机制,哪怕团队规模很小。从第一版数据开始记录,后续的每一次迭代都会受益于这种“数据可追溯”的习惯。