【第81期】特征缩放怎么选:标准化、MinMax 与 Robust 的原理、泄漏对照和完整实验

系列:《从小白到 AI 大模型开发工程师的进阶之路》 技术点:AI-0303 特征缩放 主人公:小蓝伞 前置:AI-0302 训练集、验证集、测试集 本期产出:四种输入尺度的逻辑回归对照、离群值统计实验、训练集拟合与防泄漏实现

小蓝伞把两个量纲差异很大的特征送进逻辑回归。第一列标准差约为 0.92,第二列约为 111.67;固定数据划分和训练参数后,新版教学实现的未缩放输入准确率为 0.9306,标准化、MinMax 和 Robust 输入也为 0.9306,但四者的对数损失分别为 0.2161、0.1520、0.4014 和 0.1621。测试集参与缩放参数拟合会让评估失去独立性,而离群值又可能把均值、标准差、最小值和最大值拉偏。本文用两个可复现实验回答三个问题:哪些模型值得缩放、缩放器怎样选择、统计量应在哪个数据集上拟合。

一、小蓝伞遇到的问题

小蓝伞先看训练日志,发现优化器没有报错,损失也在下降,于是以为量纲差异不是问题。这个判断漏了两件事。第一,梯度下降能运行不代表收敛效率和概率质量一样;第二,只比较一份合成数据的准确率,不能证明缩放器对所有模型都有效。她需要固定随机划分、损失函数和迭代次数,隔离"尺度变化"这一个变量,再用单独的极端值样本解释不同统计量为何会偏移。

更容易被忽视的是预处理泄漏。若先对全量数据执行 fit_transform 再切分,测试集的均值、标准差、最小值或最大值已经进入训练流程。模型没有直接读取测试标签,仍然不代表评估独立,因为变换参数本身包含了测试分布信息。

二、先给结论

  1. 先判断模型机制。 距离、点积、梯度或正则化会受特征尺度影响时,比较缩放前后效果;基于阈值分裂的树模型通常不把缩放作为首要步骤。

  2. 无明显极端值时,以标准化作基线。 StandardScaler 用训练均值和标准差转换特征,但不把数据变成正态分布。

  3. 需要指定数值范围时,比较 MinMax。 它把训练范围线性映射到目标区间,默认 [0, 1];测试值超出训练范围时,转换结果可能越界。

  4. 离群值明显时,比较 Robust。 它使用中位数和四分位距,降低极端值对中心和尺度估计的影响,但不判断离群值是否为错误数据。

  5. 先划分,再拟合。 缩放参数只由训练集学习;验证、测试和线上输入复用同一参数。交叉验证时把缩放器与模型放在同一个 Pipeline。

三、本篇任务卡

项目 本期设计
核心问题 特征尺度差异会怎样影响优化,缩放器该如何选择?
目标读者 正在训练传统机器学习模型、容易把缩放当作固定模板的开发者
实验变量 原始尺度、Standard、MinMax、Robust;训练拟合与全量拟合统计量
固定条件 同一合成数据、样本划分、初始化、学习率、迭代次数和指标
成功判据 输出可复现;训练统计量可追溯;测试集不参与 fit
不在范围 不把手写逻辑回归性能推广到 scikit-learn 全部算法或生产业务

四、缩放究竟改变了什么

4.1 三种变换的参数从哪里来

对特征第 j 列,标准化可写为:

复制代码
z_j = (x_j - mean_train_j) / std_train_j

这里的均值和标准差只由训练样本计算。标准化让训练列的均值接近 0、标准差接近 1;它不保证正态分布,也不限制取值范围。若某列在训练数据中为常数,标准差为 0,库实现需要定义保护行为,应用时应检查常量列。

MinMax 变换为:

复制代码
x'_j = (x_j - min_train_j) / (max_train_j - min_train_j)

结果只保证训练集的最小值和最大值映射到目标区间。新样本比训练最大值更大时,变换值可以超过 1;是否裁剪是部署策略,不是 MinMax 自动解决的分布漂移。

Robust 变换通常按以下形式计算:

复制代码
x'_j = (x_j - median_train_j) / IQR_train_j
IQR = Q3 - Q1

中位数和四分位距比均值、标准差更不容易被少数极端值支配。它仍会保留极端样本的相对差异,也不会替代异常原因调查。

4.2 距离模型为什么敏感

KNN 的欧氏距离为 sqrt(sum((x_j - y_j)^2))。若某一列范围在数百,另一列范围在个位数,平方和通常由大尺度列主导。缩放后距离权重改变,所以近邻集合可能改变。

RBF 核 SVM 使用 exp(-gamma * ||x-y||^2);距离尺度变化会改变核值分布。如果继续使用同一个 gamma,模型实际感受到的相似度范围也会改变。缩放和 gamma 调参应在训练/验证流程内共同完成,不能先看测试分数再反复挑选。

4.3 梯度下降为什么敏感

逻辑回归的线性部分是 w0 + w1*x1 + w2*x2。若 x2 的尺度远大于 x1,损失对 w2 的梯度可能大很多。固定学习率时,优化器可能沿陡峭方向来回震荡,同时沿平缓方向前进很慢。缩放把坐标尺度拉近后,等高线通常更接近圆形,梯度下降更容易使用统一步长。

缩放不会凭空增加信息。若问题本来由数据噪声、标签错误或错误切分造成,缩放不能修复根因。

4.4 正则化的相对意义会变化

L1/L2 正则化直接惩罚权重大小。特征单位变化时,同一个预测函数所需的权重数值会变;固定正则化系数于是对不同特征施加不同的有效约束。缩放后再调正则化超参数,模型比较才有清楚的解释。不要把"缩放提高分数"单独归因于优化变快,正则化和超参数搜索也可能同时影响结果。

树模型通常按单个特征的排序位置选择切分阈值。严格单调的线性缩放一般保留该列的排序,所以许多树模型不需要标准化。极端精度、量化、稀疏表示或特定实现仍可能带来差异,最终以当前算法和库版本验证为准。

五、实验一:四种输入尺度的逻辑回归对照

实验固定种子 81,构造 240 个样本,其中 168 个训练、72 个测试。第一列服从标准差约为 1 的正态分布,第二列标准差设置为 100;标签由两列的线性组合加噪声生成。每种变换都只从训练样本拟合统计量,再应用到测试数据。优化器、学习率、训练轮数、初始化和数据划分完全相同。

将下列代码保存为 main.py。环境为 Windows 11、Python 3.12.14、NumPy 2.3.5;本实验未调用 scikit-learn,不把手写逻辑回归结果写成该库实测。

复制代码
import numpy as np
​
rng = np.random.default_rng(81)
n = 240
x1 = rng.normal(size=n)
x2 = rng.normal(scale=100, size=n)
y = (x1 + 0.015 * x2 + rng.normal(scale=0.35, size=n) > 0).astype(float)
indices = np.arange(n)
rng.shuffle(indices)
train_indices, test_indices = indices[:168], indices[168:]
X = np.c_[x1, x2]
​
def scale_from_train(X_train, X_all, method):
    """仅用训练行拟合参数,再转换全部行。"""
    if method == "standard":
        center = X_train.mean(axis=0)
        spread = X_train.std(axis=0)
    elif method == "minmax":
        center = X_train.min(axis=0)
        spread = X_train.max(axis=0) - center
    elif method == "robust":
        center = np.median(X_train, axis=0)
        quartiles = np.percentile(X_train, [25, 75], axis=0)
        spread = quartiles[1] - quartiles[0]
    elif method == "raw":
        return X_all.copy(), None, None
    else:
        raise ValueError(f"unknown method: {method}")
​
    spread = np.where(spread == 0, 1.0, spread)
    return (X_all - center) / spread, center, spread
​
def evaluate(X_train, X_test, y_train, y_test):
    """训练同一逻辑回归教学实现,返回准确率和对数损失。"""
    train_matrix = np.c_[np.ones(len(X_train)), X_train]
    test_matrix = np.c_[np.ones(len(X_test)), X_test]
    weights = np.zeros(train_matrix.shape[1])
    for _ in range(3000):
        probability = 1 / (1 + np.exp(-np.clip(train_matrix @ weights, -40, 40)))
        gradient = train_matrix.T @ (probability - y_train) / len(y_train)
        weights -= 0.08 * gradient
​
    probability = 1 / (1 + np.exp(-np.clip(test_matrix @ weights, -40, 40)))
    accuracy = float(((probability >= 0.5) == y_test).mean())
    loss = float(-(
        y_test * np.log(probability + 1e-9)
        + (1 - y_test) * np.log(1 - probability + 1e-9)
    ).mean())
    return accuracy, loss
​
X_train = X[train_indices]
y_train = y[train_indices]
y_test = y[test_indices]
​
for method in ("raw", "standard", "minmax", "robust"):
    X_scaled, center, spread = scale_from_train(X_train, X, method)
    result = evaluate(
        X_scaled[train_indices], X_scaled[test_indices], y_train, y_test
    )
    print(method, result)
    if method == "standard":
        print("train_mean", center.tolist())
        print("train_std", spread.tolist())
        print("all_data_mean_do_not_fit", X.mean(axis=0).tolist())

实际输出:

复制代码
raw (0.9305555555555556, 0.2160760373373203)
standard (0.9305555555555556, 0.15198105386196034)
minmax (0.9305555555555556, 0.40137888604626726)
robust (0.9305555555555556, 0.1620680646603428)
train_mean [0.08294656121764418, -5.624243745092431]
train_std [0.9248892548263401, 111.66800925009704]
all_data_mean_do_not_fit [0.12575450637482288, -8.497416928721448]

这组结果里,四种输入的准确率相同,但损失不同。准确率只统计阈值 0.5 下预测对错,损失还会惩罚置信度差的错误,因此"分类边界给出同样数量的正确样本"不代表模型概率输出一样。当前手写优化器下,Standard 和 Robust 的损失较低;这是特定种子、数据、学习率和损失函数的实验结果,不是缩放方法排名。

六、实验二:离群值怎样扭曲统计量

使用一列数据 [1, 2, 3, 4, 5, 100]。前五个数集中在 1 到 5,最后一个数代表极端观测。全部六个数的均值为 19.1667、标准差为 36.1728;中位数为 3.5、四分位距为 2.5。若去掉 100,均值变为 3、标准差变为 1.4142。这个小例子说明均值和标准差对极端值敏感,不能证明 100 一定是错误数据。

MinMax 的范围会被 100 拉到 1 到 100,原本 1 到 5 的样本都挤在窄区间。Robust 用中位数和四分位距,不会让中心与尺度被这个点大幅拉走,但转换后的极端值仍然很大。处理前应先区分真实稀有事件、测量故障和数据录入错误;三者对应保留、修复或业务规则处理,不能统一删除。

七、故障复盘:全量拟合会偷看测试分布

假设训练特征只有 [0, 1, 2],测试特征是 [100]。训练均值为 1;若先在全量数据拟合,均值变为 25.75。测试样本没有提供标签,仍然影响了训练阶段使用的变换参数。这样评估回答的就不是"只根据训练数据学到的流程能否泛化"。

错误写法:

复制代码
X_scaled = scaler.fit_transform(X)  # 测试统计量进入拟合
X_train, X_test = split(X_scaled)

正确顺序:

复制代码
X_train, X_test = split(X)
scaler.fit(X_train)
X_train_scaled = scaler.transform(X_train)
X_test_scaled = scaler.transform(X_test)

在交叉验证里,如果缩放器在交叉验证前拟合,验证折的信息也会泄漏到该折训练过程。scikit-learn 官方推荐把预处理放进 Pipeline,使每个折独立拟合训练部分,再转换验证部分。本文没有在环境中安装并运行 scikit-learn;此 API 建议依据官方文档核对。

八、缩放方法选择表

数据或模型条件 候选做法 重点检查
线性模型、SVM、KNN、PCA,分布无明显极端值 先试 Standard 标准差为零的常量列、正则化参数
网络输入或业务约定要求固定范围 试 MinMax 训练外新值是否越界、是否裁剪
离群值存在且有业务意义 比较 Robust 中位数/IQR 是否稳定,极端值是否需监控
稀疏矩阵 避免默认中心化 例如 Standard 的 with_mean=False,否则可能破坏稀疏性
决策树、随机森林等阈值模型 先建立不缩放基线 以当前实现和数据实测,不套用距离模型结论

不平衡类别、缺失值填充、特征选择和缩放的顺序也应放进同一训练流水线。只有训练阶段需要估计参数的变换才存在拟合边界;固定单位换算通常不估计数据分布,但若换算规则是在观察测试数据后挑选,仍可能产生选择泄漏。

九、常见问题与避坑

标准化会让数据变成正态分布吗?

不会。它只线性平移、缩放训练列的均值和标准差。偏态分布经过标准化仍可能偏态,异常值也仍存在。若模型依赖分布形态,应使用适合的变换并在训练折内部拟合。

MinMax 后测试数据为什么大于 1?

映射边界来自训练集。新样本超过训练最大值时自然会越界。这可能意味着正常扩展,也可能意味着漂移或单位错误。先监控并排查原因,再决定裁剪、重训或拒绝输入。

为什么树模型通常不需要缩放?

严格单调变换会保留单列样本次序,许多树分裂候选由排序决定,因此结构常不变。浮点精度、直方图分箱和实现细节仍可能改变结果,所以"通常不需要"不是绝对保证。

稀疏数据能否减均值?

均值中心化会把大量零变成非零,可能令稀疏矩阵变成稠密矩阵,导致内存骤增。稀疏输入应选择保留稀疏性的变换或关闭中心化,并在目标库当前版本确认参数行为。

十、工程验收清单

  • 数据划分在任何有统计拟合的预处理之前完成。

  • 缩放器只在训练数据拟合;测试和生产输入只转换。

  • 预处理、特征顺序、单位、缺失值策略与模型共同版本化。

  • 通过同一数据划分、随机种子和指标公平比较缩放策略。

  • 检查常量列、离群值、稀疏格式、测试越界和线上分布漂移。

  • 交叉验证时,把缩放器和模型绑定在同一 Pipeline。

十一、面试题与追问

  1. 标准化是否会改变样本顺序? 单列正标准差缩放是严格单调变换,不改变该列排序;但距离和点积会变。

  2. 缩放是否改变逻辑回归函数族? 对无约束线性模型,线性可逆缩放可通过重新参数化表达等价预测函数;固定正则化和有限步优化会让实际训练结果不同。

  3. 准确率相同为什么损失不同? 阈值决策相同不代表预测概率相同,对数损失衡量置信度质量。

  4. RobustScaler 是否能修复脏数据? 不能,它只用稳健统计量设定中心和尺度,不识别错误记录。

  5. 固定随机种子为什么挡不住泄漏? 种子控制随机抽样,不控制预处理拟合时看到了哪些行。

十二、工程金句与下一期

工程金句:缩放器学到的不是格式,而是训练数据的统计边界;边界一旦偷看测试集,评估就失去独立性。

这句话来自本期训练均值与全量均值的实测对照。它提醒我们,预处理步骤也有信息边界,不能因为"没用标签"就忽略测试数据。

下一期是 AI-0304「类别特征编码」,继续处理训练阶段学到的参数如何应对线上未知类别。

官方资料

适用边界

数值实验只覆盖合成二分类数据、固定迭代次数的 NumPy 教学实现和已记录环境。本文不声称三种缩放在其他数据集上效果相同,也未执行 scikit-learn 代码兼容性测试。生产使用还需验证漂移、稀疏内存、训练工件、输入 schema、模型回滚和业务指标。

相关推荐
新*1 小时前
从一段 WebSocket 代码聊开:bytes 不是 zlib、binaryType 决定一切,15MB 大消息的零拷贝优化
javascript·python·websocket·性能优化·blob·zlib·arraybuffer
网易云信1 小时前
销冠跑了,经验留下:AI Agent 如何进入医美机构的日常工作?
人工智能
lucas_AI1 小时前
RL 训练的树枝该长在哪?别看哪里不确定,要看哪里的「梯度」最值钱
机器学习·强化学习
武子康1 小时前
驾驶轨迹换个坐标,就能给移动机器人用吗?
人工智能·算法·机器学习
jay神2 小时前
基于深度学习的交通流量预测系统(Web 版)
前端·人工智能·深度学习·yolo·计算机毕业设计