【机器学习】(38)—— 特征变换的时机与训练

摘要:原始数据进入模型前,几乎总要做特征变换。变换可以发生在训练前,也可以嵌在训练 / 服务路径里;两种做法各有利弊。本文说明两种时机的取舍,重点解释 训练—服务偏差(training-serving skew),并用汽车重量标准化、品牌词表与工单文本清洗给出可复现的一致做法与核对清单。适合读完生产系统入门、准备上线特征管道的读者。


1. 原始数据还要先变成特征

第 37 篇把生产系统拆成数据采集、特征、模型、服务与监控。特征这一环的核心问题是:变换写在哪里,训练与推理是否走同一套逻辑

请添加图片描述

时机大致做法
训练前变换离线管道把原始数据变成特征表 / 文件,模型直接读入已变换结果
训练中变换变换代码成为模型输入路径的一部分,训练与推理共用同一段逻辑

贯穿示例仍是汽车:车重(千克)做标准化、品牌 ID 查 Embedding 词表、工单文本做清洗再编码。这些步骤在笔记本里很容易「先 fit 再训练」;一旦训练脚本与在线服务拆成两套仓库,就容易出现训练时看过的输入,与线上真实喂给模型的输入不一致。

前文见 【机器学习】(37)—— 生产环境中的机器学习系统。本篇把「特征能否与训练时一致」展开成可检查的工程选择题。


2. 训练前完成变换

训练前变换通常分两步:先写管道或工具把原始数据变换好,再把结果存到模型能读取的位置。

请添加图片描述

优点说明
变换可只做一次全量刷特征后,多次实验可复用同一份特征表
便于看全量统计均值、分位数、稀有类别占比可以基于更大窗口估计
代价说明
推理必须复现同变换服务侧要使用同一套均值、词表、清洗规则
路径易分裂尤其是动态推理:离线特征作业与在线预处理常常是两套代码

静态推理有时能缓解一些问题:若批量打标与训练共用同一管道生成特征,再把预测写入缓存,训练与「生成预测」两侧更容易对齐。但缓存刷新、特征版本升级时,仍要把版本配对写清楚。

汽车例子:离线任务把全库车型的 weight_kg 标准化后写入特征表,油耗模型只读标准化列。上线若有人「临时」用原始千克去调在线接口,分数会整体错位——这不是模型突然变笨,而是输入协议被改了。

训练前变换还有一个隐蔽坑:特征表很久不更新,模型却按「当前业务语义」理解列名。例如列名仍叫 weight_norm,口径已从「按训练集 μ/σ」改成「按滚动 30 天 μ/σ」。文件还在,含义已变,偏差同样成立。因此物化特征时,建议在元数据里写明统计窗口、生成时间与代码提交号。


3. 训练过程中完成变换

训练中变换把预处理嵌进模型输入图:模型吃原始或近原始字段,内部完成缩放、查表等步骤。

请添加图片描述

优点说明
训练与推理更易一致同一段变换代码随模型一起导出 / 部署
改变换不必重刷全表仍可保留原始数据文件,换逻辑后重训即可
代价说明
可能抬高推理延迟复杂清洗、多段查表都算在请求路径上
每个 batch 都要变换吞吐与实现复杂度上升

特别要注意:依赖全局统计的变换,不能只按当前 batch 现算。例如 Z-score 需要特征的均值与标准差;若每个 batch 各自估计,batch 之间数值含义会漂,-2.5 在 A 批与 B 批可能不是同一尺度。常见做法是:在训练集上预先算好均值 / 标准差,当作常量写进模型或配置,训练与服务都引用同一组常量。

这与专栏一贯要求一致:缩放器、词表只在训练集上 fit,验证 / 测试 / 线上只 transform。见 【机器学习】(28)—— 神经网络小结【机器学习】(32)—— Embedding 串讲


4. 训练—服务偏差的典型表现

**训练—服务偏差(training-serving skew)**指:模型在训练时见到的特征分布或变换逻辑,与上线服务时不一致,从而导致离线指标好看、线上效果变差。

请添加图片描述

常见分裂点例子
单位 / 量纲训练用吨,服务用千克
统计量来源训练用训练集均值,服务用「当天窗口」重算均值
词表映射训练有 UNK,服务把未见品牌映射成错误 ID 或直接丢弃
文本清洗训练去了标点与大小写,服务原样送入
缺失填充训练填中位数,服务填 0
特征版本模型是 v3,特征表仍是 v2 口径

动态推理时风险通常更高:训练管道与在线预处理更容易由不同人、不同语言实现。静态推理若全程复用同一离线作业,偏差机会更少,但「人工改了缓存表字段含义」仍会造成隐性偏差。

可以把偏差理解成:模型在用一套坐标系做决策,线上却把点画到了另一套坐标系里。调学习率解决不了这类问题,只能对齐变换与版本。

一个可操作的发现办法是:从线上抽样原始请求,同时跑「训练管道的 transform」与「服务管道的 transform」,比较向量差。若某一维长期差一个数量级,优先怀疑单位;若只有离散 ID 对不上,优先怀疑词表;若文本向量整体乱,优先怀疑清洗与分词版本。


5. Z-score 与按批变换

数值特征常用 Z-score:

z = x − μ σ z = \frac{x - \mu}{\sigma} z=σxμ

符号含义
x x x原始特征值,例如车重千克
μ \mu μ参照均值,应来自训练集统计
σ \sigma σ参照标准差,同样应固定,避免除零
z z z标准化后的值

若在训练循环里对每个 mini-batch 现算 μ , σ \mu,\sigma μ,σ,batch 越小、越不稳定,同一辆车在不同 batch 里的 z z z 可能跳变。更稳妥的路径是:

1. 仅在训练集上估计 μ、σ
2. 把 μ、σ 存进配置或模型附属文件
3. 验证、测试、在线服务都使用同一对 μ、σ

Embedding 词表同理:只在训练集定词表与 UNK;服务不得偷偷扩展「今天新出现的品牌 ID」却不更新模型侧表,除非你们明确设计了可在线扩展的流程并做了评估。


6. 汽车场景下的一致做法

特征更稳妥的一致做法
车重 / 马力训练集算 μ、σ;训练前写入特征表,或训练中用常量缩放
品牌 / 车型 ID训练集词表 + UNK;服务只查表,不另写映射
工单文本清洗与分词函数做成共享库;训练与服务 import 同一版本
可预打标字段静态推理:离线管道生成特征并打标,缓存带特征版本号
新工单即时分类动态推理:服务路径必须调用与训练相同的变换模块

组合建议:

  • 统计量大、可离线算清的变换:可以训练前做,但要把「参数文件」当作一等产物版本化。
  • 希望强一致、且变换不算太重:优先训练中变换或「共享预处理库 + 两端调用」。
  • 无论哪种,都要能回答:这一版模型配对的是哪一版 μ/σ、词表与清洗规则

实践里也很常见第三种折中:训练前物化「重」特征,训练中只保留轻量、必须一致的步骤。例如品牌 Embedding 查表放在模型内,而耗时的文本归一化结果先离线写好;两端仍用同一清洗函数生成那一列,避免「离线用 A 清洗、在线又用 B 清洗」。折中的关键仍是单一真相来源:统计量、词表与清洗代码有明确版本,而不是笔记本里各写一份。


7. 核对清单与最小示例

请添加图片描述

1. 标准化均值 / 方差是否只来自训练集,并已版本化
2. 词表与 UNK 在服务侧是否同一映射
3. 单位与量纲在训练、服务文档中是否写死
4. 文本清洗 / 分词是否同一代码版本
5. 缺失填充默认值两侧是否一致
6. 模型版本与特征版本是否配对发布
7. 动态推理路径是否与训练预处理做过对照抽样
8. 静态缓存刷新后,是否避免旧特征口径混入新模型

概念示意:用同一套统计量做训练侧与服务侧变换,并做一次对照断言。

from dataclasses import dataclass

@dataclass(frozen=True)
class Standardize:
    mean: float
    std: float

    def transform(self, x: float) -> float:
        denom = self.std if self.std > 1e-8 else 1e-8
        return (x - self.mean) / denom


# 只允许来自训练集的统计量
weight_scaler = Standardize(mean=1450.0, std=220.0)

def train_features(raw_weight: float) -> float:
    return weight_scaler.transform(raw_weight)

def serve_features(raw_weight: float) -> float:
    # 服务必须复用同一对象 / 同一配置,而不是重新估均值
    return weight_scaler.transform(raw_weight)

assert abs(train_features(1670.0) - serve_features(1670.0)) < 1e-12
print("train/serve 对齐:", round(serve_features(1670.0), 4))

真正项目里,断言应扩展到一批黄金样本:同一原始行,训练管道与服务管道输出的特征向量逐维比对。发现某一维系统性偏移,优先查单位、默认值与词表,而不是先加倍模型宽度。

若暂时无法共用同一份 Python / C++ 库,至少固定「交换契约」:列出字段名、类型、单位、缺失约定与示例向量,并在 CI 里用黄金样本对两端输出做数值比对。契约比口头约定「我们都做了标准化」可靠得多。


8. 能力边界与常见误区

情况说明
全表先标准化再切分泄漏;μ、σ 被验证 / 测试信息污染
服务用「今日均值」重算 Z-score与训练坐标系不一致,属于典型偏差
以为静态训练就没有偏差推理路径仍可能复现失败
以为训练中变换就零延迟代价复杂清洗仍会吃掉在线预算
按 batch 现算全局统计尺度漂移,优化不稳定
特征表升级但模型未重训版本错配,表现难解释
只对准确率告警、不对特征分布告警偏差可能先表现为输入漂移

适用前提:团队能把预处理参数与代码纳入版本管理。若变换散落在笔记本单元格、一次性 SQL 与线上临时脚本里,偏差几乎必然出现,只是发现得早晚不同。

也要承认边界:有些业务特征本身就依赖「请求时刻」的上下文,例如当前库存、当日活动价。这类特征不是靠 μ/σ 对齐能消掉的,需要在训练数据里显式构造同时刻可用的字段,并在服务侧保证能取到同定义的值;否则看起来像变换偏差,根因其实是标签与特征的时间对齐问题。


9. 术语与延伸阅读

术语含义
特征变换 / 特征工程把原始字段变成模型可用输入的过程
训练前变换先离线生成特征,再交给训练
训练中变换变换嵌在模型输入路径,随推理一起执行
训练—服务偏差训练与服务的数据变换或输入分布不一致
Z-score 标准化按均值与标准差缩放数值特征
特征版本变换口径与统计量的版本标识,需与模型配对
资源说明
【机器学习】(37)—— 生产环境中的机器学习系统静态 / 动态训练与推理
【机器学习】(32)—— Embedding 串讲词表与 UNK 纪律
【机器学习】(28)—— 神经网络小结划分后 fit、验证纪律
【机器学习】(35)—— 微调、蒸馏与提示离线推理与缓存

10. 小结与下一主题

特征变换时机可以概括为:

  1. 训练前变换:适合全量统计、一次物化;必须在服务侧严格复现,并版本化参数。
  2. 训练中变换:更易保持训练—服务一致;要注意延迟,以及全局统计不能按 batch 乱估。
  3. 训练—服务偏差是上线后「指标骗人」的常见根因;对齐变换代码、统计量与特征版本,比盲目换更大模型更优先。

请添加图片描述

下一篇会进入 部署测试:上线前如何验证输入数据、特征工程、新模型质量、服务基础设施,以及流水线组件之间的集成是否可靠。汽车例子会从「变换是否一致」,扩展到「新版本能不能安全替掉旧版本」。

系列导航


如果本篇对你有帮助,欢迎点赞、收藏、关注博主,机器学习专栏持续更新中,下次更新不迷路。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值