正则化、归一化、标准化这三个词,在机器学习和深度学习的调参清单里出现的频率高得惊人,但能把它们说得清清楚楚的人其实不多。正则化是给模型加约束、抑制过拟合;归一化和标准化则是把特征数据缩放到合理范围,让模型训练更稳。这篇文章就围绕这三件事,把我的实操经验掰开揉碎讲一遍,适合正在调参、总被各种“normalization”绕晕的读者。
1. 先搞懂三者分别解决什么问题
1.1 正则化:模型跑得太野,得拽回来
正则化(Regularization)从来不是数据预处理,它是直接作用在模型目标函数上的“约束力”。通俗点说,模型在训练集上学得太投入,把噪声和个别样本的细节都背下来了,换一批数据立刻翻车,这就是过拟合。正则化的思路很朴素:我不想让你学得那么极端,所以要给最终损失函数加一笔惩罚费用,参数越离谱,惩罚越高,模型就不敢肆意妄为。
最常见的做法是在原始损失函数后面加一项权重惩罚项。假如原来的损失是 J(w),加上正则化项后变成:
J(w) + λ·Ω(w)
这里的 Ω(w) 是某种关于参数的范数,λ 是控制惩罚强弱的系数。λ 越大,模型越保守;λ 越小,模型越可能放飞自我。所以正则化的本质是“用偏差换方差”,用一点点训练集上的表现下降,换取测试集上的稳定提升。它不是用来让训练 loss 更低的,恰恰相反,它会让训练 loss 略高,但让验证 loss 更低。
很多人刚接触时有个误区:正则化“减少参数”或“帮助收敛”。其实它并不直接提速,也不直接降维,而是让大家别走极端。真正让它看起来像“调参”的原因在于,λ 这个超参数直接决定了模型在“记忆”和“泛化”之间的站位。过小等于没加,过大会把模型压成“躺平”,连正常模式都学不出来。后面我会专门说怎么选 λ。
1.2 归一化与标准化:让数据从“五花八门”变成“一个量级”
归一化与标准化处理的对象不是模型,而是数据。很多业务数据天生尺度就不统一:年龄可能是 0 到 100,收入可能是 3000 到 50000,点击量可能是 0 到几百万。如果把这些原始数值直接丢给模型,数值大的特征天然会在距离计算或梯度更新中占据主导地位,模型会误以为“这个特征最重要”,但实际上只是它的单位大。
归一化(Normalization)通常指把数据压到某一固定区间,最常见的区间是 [0, 1]。公式是:
x_scaled = (x - x_min) / (x_max - x_min)
标准化(Standardization)则是把数据变成均值为 0、标准差为 1 的分布,公式是:
x_scaled = (x - μ) / σ
标准化不保证数据落在某个固定区间,比如有些值可能是 -2.5,有些是 3.2,这都正常。但它们有一个共同目的:让不同特征的尺度可比,让优化过程更公平。特别是在使用梯度下降的模型里,特征尺度一致之后,损失函数等高线更接近于圆形,梯度指向最优解的方向更准确,收敛自然更快。
这还不是全部。像 SVM、K-Means、KNN 这类依赖距离的算法,对特征尺度极其敏感;神经网络里的权重初始化、正则化惩罚、学习率选择,也会因为数据尺度不稳而变得难以控制。所以归一化和标准化常常是建模流程里的“地基工程”。
1.3 命名的纠缠:Regularization、Normalization、Standardization
三个英文词分别是 Regularization、Normalization、Standardization。中文翻译里,Normalization 既可以翻译成“归一化”,也可以翻译成“规范化”;Standardization 通常叫“标准化”,有时候也被人叫“归一化”。这就导致很多人觉得它们是同一件事。
我见过不少刚入行的人把 Batch Normalization 和“数据归一化”划等号。Batch Normalization 是在神经网络的每一层内部对一批数据做规范化,它的确有“Normalization”这个词,但它属于模型结构层面,跟你在喂数据前做 StandardScaler 是两码事。Regularization 跟 Normalization 更是八竿子打不着,一个管模型复杂度,一个管数据尺度。这三者的共同点只有“normal”这个拉丁词根,意思大概是“规范化、正常化”,但应用层次完全不同。先分清对象,再谈调参,才不会越调越乱。
2. 正则化:约束模型的“缰绳”
2.1 L1 和 L2:两种最常用的惩罚逻辑
L1 正则化,惩罚项是权重绝对值的和,也就是 λ·Σ|w_j|。它的特点是会把一部分权重压缩到正好为 0,得到稀疏解。稀疏意味着模型自动做了特征选择,很多不重要的特征权重变成 0,模型更简洁。L2 正则化,惩罚项是权重平方和,写作 λ·Σw_j²,它会让权重整体变小,但很少变到 0,是一种平滑的收缩。
为什么 L1 会产生稀疏而 L2 不会?可以从几何直觉理解:L1 的约束形状是菱形,L2 的约束形状是圆形。损失函数的等高线在同一点与约束边界相切时,菱形角点正好落在坐标轴上,所以更容易出现某个维度权重为 0;圆形光滑,切点很少恰好落在坐标轴上。另一个理解角度是导数:L1 对权重的梯度是一个常数(±λ),权重每次更新都会均匀地朝 0 移动,直到归零;L2 的梯度是 2λw,权重越大收缩越快,权重小的时候几乎不动,所以很难真正归零。
实际选择时,我一般遵循一个原则:特征数量很多、且大多数可能无关时,优先 L1;特征之间高度相关、希望权重稳定时,优先 L2;想要两者兼顾就用 Elastic Net,它是 L1 和 L2 的线性组合。具体到代码里,如果你用的是线性模型,Ridge 是 L2,Lasso 是 L1,ElasticNet 是混合。深度学习框架里的 weight_decay 参数也对应 L2 的思想。
2.2 深度学习里的正则化不止加惩罚项
在深度学习中,“正则化”这个词的范围被扩大了。因为深层网络参数量巨大,光靠 L2 惩罚不一定够,大家会组合使用多种手段。Dropout 是最典型的一种:训练时随机让一部分神经元输出置零,强迫网络不依赖个别节点,这等于每轮都在训练一个不同的“子网络”,最后在测试时集成所有子网络。Dropout 的核心超参是丢弃概率 p,一般在 0.2 到 0.5 之间。它和 L2 互不冲突,可以叠加,但不要一上来就把两者都调到很强,很容易欠拟合。
早停(Early Stopping)也是一种被低估的正则化手段。模型在训练集上的 loss 不断下降,验证集 loss 却可能先降后升,那个开始升的拐点附近,模型已经进入了过拟合区。保存验证 loss 最好的那一轮权重,就是最简单的正则化。我习惯在深度学习训练中同时打开早停和模型保存,用验证集指标作为监控对象。还有一种数据增强,比如图像的随机裁剪、翻转、加噪声,它本质上是“免费”的正则化,因为模型看到了更多具备差异的样本,记忆难度变大,泛化能力也就相应提高。
2.3 正则化系数怎么调才靠谱
调 λ 的第一步,是先让模型“裸奔”到过拟合。如果你发现训练 loss 很低、验证 loss 很高,这时候才需要加正则化。第二步是设一个 λ 的搜索范围,常用的有 0.0001、0.001、0.01、0.1、1.0 这些量级,建议在对数尺度上做网格搜索或随机搜索。为什么用对数尺度?因为 λ 对模型的影响通常跨越好几个数量级,在 0.01 附近和 0.02 附近差别不大,但在 0.01 和 0.1 之间差别可能非常明显。用线性均匀取值会错过合适的量级。
还有一个容易被忽视的细节:加入正则化之后,最好同步把学习率略微调低。因为惩罚项会增加梯度波动,学习率太大容易让权重在最优解附近震荡。反过来,如果用了自适应学习率的优化器(比如 Adam),L2 惩罚的等价形式会跟“权重衰减”不完全一致,这时最好使用框架里单独的 weight_decay 参数,而不是手动往 loss 里加 L2。至少在 PyTorch 里,Adam + L2 直接加 loss 的效果和真正的解耦权重衰减是有差异的。
3. 归一化与标准化:数据的“对齐工程”
3.1 Min-Max 与 Z-Score 怎么选
Min-Max 归一化适合你明确知道特征取值范围、且没有太多极端离群值的情况。它实现简单,结果直观,所有值都落在 [0,1] 内,对图像像素这种天然有界的数值非常友好。但它的命门是“怕极端值”。如果某个特征出现一个特别大的异常值,整个缩放区间会被拉宽,正常样本会被压到非常窄的小区间里,信息被压缩。而且新来的测试数据如果比训练集里的最大值还大,转换后就会超出 [0,1],处理起来很尴尬。
Z-Score 标准化不要求数据一定服从正态分布,很多人误解了这一点。它只是把数据变成零均值、单位方差,即使原始分布是偏态的,标准化后的分布也未必变成正态。它适合大多数连续特征,尤其当特征分布比较均匀、存在少量离群值时,效果优于 Min-Max,因为均值和标准差受极端值的影响比最大值最小值小一些。但也不是没有代价:标准化后的数据会出现负值,这对稀疏数据很不友好。比如文本 TF-IDF 矩阵里大部分值是 0,标准化后变成负的,稀疏性就被破坏了。
如果你担心离群值,还有更稳的 RobustScaler,它用中位数和四分位距(IQR)做缩放,对异常值有很强的抵抗力。日常建模时,我会先看数据分布和模型类型再决定。树模型不需要任何特征缩放,因为分裂点只依赖排序;线性模型、神经网络、KNN、SVM 则强烈建议做缩放。深度学习里如果处理图像,通常直接除以 255 就够;处理表格数据,首选 StandardScaler。
3.2 标准化在流程里的正确位置
Feature scaling 最容易被忽略的是“时机”。你必须在划分训练集和测试集之后,只用训练集去 fit 缩放器的参数(均值和标准差,或者最小值最大值),然后用同一套参数去 transform 训练集、验证集和测试集。原因很简单:测试集代表未来的数据,你在建模时本不该“偷看”它。如果先把全量数据合并再算均值和标准差,测试集的信息就渗入了训练过程,验证结果会偏乐观,上线后真实效果打折扣。
具体的操作可以用 scikit-learn 的 Pipeline 把缩放器和模型串起来,比如先 StandardScaler 再接一个分类器。Pipeline 会在每一折交叉验证中只对训练折做 fit,再转换验证折,这样就不会数据泄漏。我见过很多新手在 Kaggle 上直接把所有数据 read_csv 之后,先.fit_transform再train_test_split,看起来方便,实际上埋了大雷。这类错误在特征工程越复杂的时候越危险,因为泄漏的不只是均值,而是整个预处理链条上的统计量。
3.3 Batch Norm、Layer Norm 和“数据标准化”到底什么关系
神经网络里同样会出现“Normalization”家族:Batch Normalization(BN)、Layer Normalization(LN)、Instance Normalization、Group Normalization。它们跟前置的数据标准化有相似之处,都是把一组数值变成均值为 0、方差为 1,但作用的维度完全不同。
Batch Norm 是在每个特征维度上,对一个 batch 里的所有样本求均值和方差,然后归一化。它让每一层的输入分布稳定一些,模型对初始化、学习率的敏感度会大幅降低。这也是为什么很多人发现“加了 BN 之后,学习率可以调大点”。但它也有软肋:batch size 太小的时候,统计量噪声很大,训练不稳定;如果数据本身分布变化剧烈,效果也会打折。Layer Norm 则是对单个样本的所有特征维度做归一化,跟 batch 大小无关,所以在 RNN、Transformer 里更常用。它们的共同点是都属于“模型内部结构”,不是简单的数据预处理。你可以在数据标准化之后再用 BN,两者不冲突,解决问题层次不同。
4. 调参顺序:数据、模型、正则化一把梭
4.1 一个可以直接套用的流程骨架
我给自己定了一套固定流程,顺序基本围绕“先让数据干净,再让模型能跑,最后加约束”。第一步,划分训练/验证/测试集,测试集锁死不看。第二步,在训练集上选择标准化或归一化方案,办好 fit 和 transform。第三步,先用一个小模型不加正则化,快速训练几十轮,看训练集和验证集的表现。第四步,如果明显过拟合,按优先级依次引入 Dropout、L2/weight decay、早停。第五步,在验证集上搜索关键超参数:学习率、正则化系数、网络宽度或深度。
这个顺序的核心逻辑是:如果你连数据尺度都没对齐,正则化调得再仔细也很难找到真正有效的 λ。因为未标准化的特征会让 L2 惩罚“偏心”——数值范围大的特征权重被惩罚得更重,数值范围小的特征几乎不受约束,正则化就没法均匀发力。反过来,如果你一上来就同时调学习率和 λ,万一效果变差,很难判断是哪个参数拖的后腿。一步一动,变量隔离,是调参里最省时间的策略。
4.2 用代码演示:标准化 + 正则化的小闭环
先看一个表格数据场景。假设我们已经切好了 X_train、X_val、X_test,接下来用 scikit-learn 搭一个带标准化的逻辑回归,并且通过 Pipeline 避免泄漏:
from sklearn.pipeline import Pipeline from sklearn.preprocessing import StandardScaler from sklearn.linear_model import LogisticRegression pipe = Pipeline([ ("scaler", StandardScaler()), ("model", LogisticRegression(penalty="l2", C=0.1)) ]) pipe.fit(X_train, y_train) val_score = pipe.score(X_val, y_val)这里 LogisticRegression 里的 C 是正则化强度的倒数,C 越小,正则化越强。C=0.1 相当于比默认 1.0 更强的约束。因为 Pipeline 中 StandardScaler 会在每一折交叉验证中自动只对训练部分 fit,所以不会泄漏。如果换到深度学习,用 PyTorch 的写法可能长这样:
import torch.nn as nn model = nn.Sequential( nn.Linear(64, 128), nn.BatchNorm1d(128), nn.ReLU(), nn.Dropout(0.3), nn.Linear(128, 10) ) optimizer = torch.optim.Adam(model.parameters(), lr=1e-3, weight_decay=1e-4)这里的weight_decay=1e-4就是 L2 正则化的等价实现,Dropout(0.3)表示随机丢弃 30% 的神经元。注意,把 Dropout 放在激活函数之后是常见做法,也有少数情况放在激活之前,需要看具体网络结构。一般建议先按经典结构搭,再根据验证结果微调位置。
4.3 调参中一定要记录的三个维度
调参不是玄学,而是实验管理。我会为每轮实验记录三样东西:数据集是否经过标准化、当前模型结构、正则化相关参数。用表格比对结果,比只记住“好像调过一次”高效得多。尤其是 λ 和 dropout 的联动:dropout 调大时,模型容量降低,可能需要同时调大网络宽度或者降低 L2 系数,否则会出现欠拟合。这就像跷跷板,正则化手段不是无穷叠加的,需要互相平衡。
我自己的经验是:每次只改一个变量,跑完一轮再动下一个。比如这一轮只改 λ,下一轮只改学习率,再把两者交互验证。如果时间有限,就用随机搜索而不是网格搜索,因为超参数空间里真正有效的区间往往比较窄,随机搜索能覆盖更多不同组合。最后一定要在测试集上只验一次,不要反复看着测试集调参,否则测试集就变成了另一个验证集,你的“泛化评估”就失真了。
5. 常见踩坑与速查
5.1 数据泄漏:先 fit 再 transform,顺序不能反
这个问题值得单拎出来说。错误示范:scaler.fit(X_all); X_all_scaled = scaler.transform(X_all); train_test_split(...)。这样做的后果是,训练集里混入了测试集的均值和方差信息,模型在验证时看到的“归一化后分布”是被修正过的,性能自然虚高。而真实线上环境没有测试集给你算均值,于是推理效果大打折扣。正确做法是在切分后,只对训练集调用fit,再依次transform训练集、验证集、测试集。如果用了交叉验证,就让 Pipeline 来做这件事。
5.2 归一化破坏了稀疏性
另一个高频问题出现在文本或推荐场景。TF-IDF、One-Hot 这类特征大部分是 0,如果用 StandardScaler,0 会变成 -μ/σ,原本的稀疏矩阵变成了稠密负数,内存和计算量暴涨,而且负值在语义上没有意义。解决办法:要么用专门针对稀疏数据的 MaxAbsScaler,它把每个特征除以最大绝对值,保留零元素;要么保持原始稀疏矩阵,仅在需要时使用归一化。还要注意,对稀疏矩阵做 StandardScaler 默认会产生警告,因为它会破坏稀疏性。
5.3 正则化系数和特征缩放的关系
正则化和标准化看起来是两件独立的事,但它们会互相影响。假设某个特征取值在 0 到 10000,另一个特征在 0 到 1,未标准化时 L2 惩罚对大数值特征的权重惩罚会严重失衡。后者权重即使很大,对 loss 的影响也不大;前者的值稍微一动,惩罚就迅速上升。于是模型会尽量把前者的权重压小,这跟你想要“公平地”约束所有参数是矛盾的。所以,标准化往往是你调正则有意义的前提。在实际项目里,我发现很多人花了很多轮在调 weight_decay,结果问题只是忘了缩放数据。
5.4 速查表:三大策略到底怎么用
下面这张表是我自己整理用的,浓缩了前文所有关键点,适合贴在手边。
| 策略 | 作用对象 | 常见实现 | 核心公式 | 典型应用场景 | 主要坑点 |
|---|---|---|---|---|---|
| 正则化 | 模型参数 | L1、L2、Dropout、早停 | loss + λ·Σ|w| 或 λ·Σw² | 高维稀疏、深度网络、大模型 | λ 过小无效,过大欠拟合 |
| 归一化 | 输入数据 | Min-Max、MaxAbs | (x - x_min)/(x_max - x_min) | 图像像素、有界特征、稀疏数据 | 怕离群值,测试数据可能越界 |
| 标准化 | 输入数据 | Z-Score、RobustScaler | (x - μ)/σ | 连续特征、神经网络、距离模型 | 破坏稀疏性,非正态分布也适用 |
最后分享一个我自己的体会:这三个策略虽然常被放在一起讨论,但调参时不要试图一次全调到位。先把数据标准化做了,再跑一个基线模型,观察过拟合程度;然后只加一种正则化手段,记录验证集变化;最后再回头看学习率和网络结构。你会发现,很多看似需要“玄学调参”的问题,其实是基础环节没做扎实。正则化、归一化、标准化,就像做饭时的火候、刀工和调味,火候不对菜会糊,刀工不好熟不均匀,调味过头则前功尽弃,但只要有条不紊地一样样来,模型效果不会差到哪里去。