目录
- 方式一:Min-Max 归一化
- 方式二:Z-score 标准化
- 两者如何选择(工业界经验)
- zscore的 mean计算
- 在实际项目中怎么算
- 工业界的一个重要细节:只能用训练集算
- 线上服务时怎么用
这两种归一化公式解决的核心问题是一样的:让不同量级的特征"站在同一起跑线上",避免数值大的特征把数值小的特征的影响力淹没。
方式一:Min-Max 归一化
$$age_normalized = \frac{raw_age - age_min}{age_max - age_min}$$
直观理解:把原始值线性拉伸/压缩到 [0, 1] 这个固定区间内。
假设年龄范围是 18~80 岁:
| 原始年龄 | 计算过程 | 归一化结果 |
|---|---|---|
| 18 岁(最小) | (18-18) / (80-18) = 0/62 |
0.0 |
| 49 岁(中间) | (49-18) / (80-18) = 31/62 |
0.5 |
| 80 岁(最大) | (80-18) / (80-18) = 62/62 |
1.0 |
- 优点:结果范围固定在 [0, 1],直觉上最好理解。
- 缺点:非常怕异常值。如果有个用户年龄填了 999 岁,
age_max就变成 999,导致正常用户的归一化值全部被压缩到 0 附近,失去区分度。
方式二:Z-score 标准化
$$asset_normalized = \frac{raw_asset - mean_asset}{std_asset}$$
直观理解:把原始值换算成"距离平均值有多少个标准差"的衡量单位。
假设资产均值是 50 万,标准差是 20 万:
| 原始资产 | 计算过程 | 标准化结果 | 含义 |
|---|---|---|---|
| 50 万(均值) | (50-50) / 20 = 0 |
0.0 | 正好处于平均水平 |
| 70 万(均值+1σ) | (70-50) / 20 = 1 |
+1.0 | 比平均高 1 个标准差 |
| 10 万(均值-2σ) | (10-50) / 20 = -2 |
-2.0 | 比平均低 2 个标准差 |
- 优点:对异常值更鲁棒,结果以 0 为中心,神经网络的 ReLU/BatchNorm 等层对这种分布的处理效果更好。
- 缺点:结果没有固定的上下界(可能是 -3、+5 等),不如 Min-Max 直观。
两者如何选择(工业界经验)
| 场景 | 推荐方式 |
|---|---|
| 知道明确边界(如年龄 0~120) | Min-Max |
| 数据有异常值或分布不均匀(如资产、收入) | Z-score |
| 深度学习里的隐藏层激活 | 通常 Z-score 更稳定 |
一句话:Min-Max 是"把尺子缩放到 0~1",Z-score 是"把单位换算成标准差"。两者目的都是让不同特征的数值尺度一致,让模型更容易学习。
zscore的 mean计算
mean_asset 就是全体训练样本的资产算术平均值,计算方式非常直接:
$$mean_asset = \frac{1}{N} \sum_{i=1}^{N} raw_asset_i$$
在实际项目中怎么算
方式一:用 NumPy(离线/批量数据)
import numpy as npraw_asset = np.array([10, 50, 100, 200, 500, ...]) # 所有用户的原始资产mean_asset = raw_asset.mean() # 均值
std_asset = raw_asset.std() # 标准差# 归一化
asset_normalized = (raw_asset - mean_asset) / std_asset
方式二:用 scikit-learn(工业界推荐)
from sklearn.preprocessing import StandardScalerscaler = StandardScaler()
scaler.fit(train_asset) # 只用训练集来算 mean 和 std
asset_normalized = scaler.transform(raw_asset) # 用训练集的统计量来转换
工业界的一个重要细节:只能用训练集算
这是面试常考的点:
✅ 正确做法 ❌ 错误做法
计算 mean/std 用 只用「训练集」数据 用「全量数据」(含测试集)
为什么不能用全量数据(含测试集)来算 mean?
因为测试集模拟的是"未来的用户",在真实上线场景里你是看不到未来数据的。如果用了测试集数据来算 mean,相当于模型提前"偷看了答案",这在机器学习里叫做数据泄露(Data Leakage),会导致离线评估虚高,线上效果一落千丈。
线上服务时怎么用
训练完成后,mean_asset 和 std_asset 这两个统计量需要持久化保存下来(比如存成 JSON 或 pkl 文件),线上推理时直接加载使用:
# 离线训练时保存
stats = {"mean": float(mean_asset), "std": float(std_asset)}
json.dump(stats, open("asset_stats.json", "w"))# 线上推理时加载
stats = json.load(open("asset_stats.json"))
asset_normalized = (user_raw_asset - stats["mean"]) / stats["std"]
一句话:mean_asset 就是把训练集里所有用户的资产加起来除以人数,然后把这个值存下来,线上遇到新用户时直接拿来用。
