生产环境的机器学习系统:什么时候该转换数据#
两个时机#
原始数据必须先经过特征工程(也就是转换)才拿去用于训练模型。那么问题就来了:转换应该在什么时候做呢?
笼统地说,特征工程可以落在下面两个时段进行:
训练模型之前
训练模型的过程中
乍一听起来,好像是直到模型训练结束之前都可以一样,两者有什么区别呢?接下来我们分别看看这两种做法各自的优点和缺点。
训练前转换数据#
先看第一种:模型还没开始训练,数据就已经先转好了。这种做法要分两步走:
写代码或者用专门做这件事的工具,把原始数据转换掉。
把转换后的数据放到模型能读到的某个地方,比如磁盘上。
优点:
整套系统只需要转换一次原始数据。
系统可以分析整个数据集,再据此决定用哪种转换策略最合适。
缺点:
- 到了预测推理的时候,你必须把同样的转换再做一遍,否则可能会出现训练-服务偏离的问题。
点击进一步了解 训练-服务偏离(Training-Serving Skew)
MLWM: 训练-服务偏离(Training-Serving Skew)指的是模型在训练阶段接触到的数据,与模型在实际上线预测时接触到的数据,在特征处理方式或数据分布上存在差异。 举个例子,假设我们正在开发一个房价预测模型,原始数据中有一个特征:房屋面积(单位:平方米)。 为了让模型更好地学习,我们决定对这个特征进行归一化处理:
$$x'=\frac{x-\mu}{\sigma}$$其中 $x$ 表示原始房屋面积,$\mu$ 表示训练数据中房屋面积的均值,$\sigma$ 表示训练数据中房屋面积的标准差, $x'$ 表示归一化后的特征。 假设训练数据的均值是 100,标准差是 20,那么,一套面积为 120 平方米的房屋,经过转换后:
$$x'=\frac{120-100}{20}=1$$模型在训练过程中,看到的就是这个归一化后的数值 1。 但是,模型上线后,负责处理用户请求的服务程序可能使用了另一套归一化参数,或者直接忘记了归一化。 例如,线上服务错误地使用均值 80、标准差 20:
$$x'=\frac{120-80}{20}=2$$这样,同样一套 120 平方米的房屋,训练时输入模型的特征是 1,推理时输入模型的特征却是 2。 模型训练时学到的规律建立在原来的特征尺度上,现在输入数据的处理方式发生了变化,自然可能导致预测结果不准确,这就是典型的训练-服务偏离。
训练-服务偏离在系统做动态(在线)推理的时候格外危险。在用动态推理的系统里,负责转换原始数据的模块通常和负责推理预测的模块并不在一起,于是很容易出现训练时和线上预测推理时的处理对不上。相比之下,走静态(离线)推理的系统,有时倒是可以复用同一套逻辑。
训练中转换数据#
第二种做法是把转换放进训练过程里。这种情况下,转换逻辑就写在模型代码中,模型自己读入原始数据,顺手把它转掉。
优点:
就算你改了转换策略,原始数据文件还是原来那一份,不用重新准备。
训练和预测这两个阶段,一定用的是同一套转换逻辑。
缺点:
转换写得复杂,会拉长模型的延迟。
每来一个批次,转换都要重做一遍。
按批次来转换数据,这件事本身就有点麻烦。举个例子:假设你想用 Z-score 归一化 来转换原始数值数据。Z-score 归一化要用到这个特征的均值和标准差,可如果按批次转换意味着你手里只有一个批次的数据,而不是整个数据集。所以,如果各批次之间的差异很大,那么同一个 -2.5 的 Z-score,在这个批次里和在另一个批次里就不是同一个意思了。
当然,此时变通的办法是让系统先在整个数据集上把均值和标准差算出来,再把它们当成常量写进模型里。
关键术语:
- 训练-服务偏离(Training-serving skew)
- Z-score 归一化(Z-score normalization)