从 Tikhonov 正则化到 Dropout理解防过拟合的数学本质开头模型越「聪明」越容易「死记硬背」上一篇我们学习了 SVM。SVM 通过最大间隔和正则化参数 C 来防止过拟合。今天我们系统地学习正则化——防止过拟合的核心技术。你有没有遇到过这种情况训练集准确率99.9% 测试集准确率75% 模型在训练集上「学得太好了」 → 把噪声也学进去了 → 遇到新数据就不行 这就是「过拟合」正则化就是解决这个问题的「数学武器」。一、过拟合的数学解释1.1 偏差-方差分解偏差-方差分解 ═══════════════════════════════════════════════════════════════════ 期望误差 偏差² 方差 噪声 偏差Bias模型预测值与真实值的差距 → 高偏差 欠拟合 方差Variance模型对不同训练集的敏感度 → 高方差 过拟合 噪声Noise数据本身的随机性 → 无法避免 图示 欠拟合 最佳 过拟合 高偏差 低方差 适中 低偏差 高方差 ───────── ───────── ───────── ───────── ───────── ───────── ───────── ───────── ───────── 太简单 刚好 太复杂1.2 模型复杂度 vs 泛化能力模型复杂度与泛化能力 ═══════════════════════════════════════════════════════════════════ 误差 ↑ │ ╲训练误差 ╱ 测试误差 │ ╲ ╱ │ ╲ ╱ │ ╲──────────╱ │ ↑ │ 最佳点 └──────────────────→ 模型复杂度 简单 复杂 训练误差随着模型复杂度增加而减小 测试误差先减小后增大U 型曲线 最佳模型测试误差最小的点二、Tikhonov 正则化2.1 基本思想Tikhonov 正则化 ═══════════════════════════════════════════════════════════════════ 原始目标最小化损失函数 min J(w) 正则化目标在损失函数中加入惩罚项 min J(w) λ Ω(w) 其中 λ正则化系数控制正则化强度 Ω(w)正则化项通常是权重的范数 λ 的作用 λ 0没有正则化 λ 小轻度正则化 λ 大强正则化2.2 L2 正则化RidgeL2 正则化Ridge 回归 ═══════════════════════════════════════════════════════════════════ 正则化项Ω(w) ||w||² Σ wᵢ² 目标函数 min J(w) λ ||w||² 几何解释 L2 正则化约束区域是一个「圆」二维或「球」高维 w₂ ↑ │ ○ 最优解 │ ╱ │ ╱ │ ╱ ╲ │╱ ╲ 正则化约束圆 └────────→ w₁ 最优解在损失函数等高线与约束圆的切点 效果 权重趋向于小值但不为 0 → 权重衰减Weight Decay2.3 L1 正则化LassoL1 正则化Lasso 回归 ═══════════════════════════════════════════════════════════════════ 正则化项Ω(w) ||w||₁ Σ |wᵢ| 目标函数 min J(w) λ ||w||₁ 几何解释 L1 正则化约束区域是一个「菱形」二维或「多面体」高维 w₂ ↑ │ ○ 最优解 │ ╱ │ ╱ │ ╱ ╲ │╱ ╲ 正则化约束菱形 └────────→ w₁ 最优解在损失函数等高线与菱形的顶点 效果 部分权重变为 0稀疏化 → 特征选择2.4 L1 vs L2 对比特性L1LassoL2Ridge正则化项Σ |wᵢ|Σ wᵢ²约束区域菱形圆形权重效果稀疏化部分为 0衰减趋向小值特征选择是否可解释性高中2.5 弹性网络Elastic Net弹性网络 ═══════════════════════════════════════════════════════════════════ 结合 L1 和 L2 的优点 Ω(w) α ||w||₁ (1-α) ||w||² 其中 α ∈ [0, 1] 控制 L1 和 L2 的比例 α 1纯 L1Lasso α 0纯 L2Ridge 0 α 1弹性网络三、Dropout3.1 基本思想Dropout ═══════════════════════════════════════════════════════════════════ 训练时随机丢弃一部分神经元概率为 p 正常网络 Dropout 后 ┌────────┐ ┌────────┐ │ ● ● ● │ │ ● ○ ● │ │ ● ● ● │ → │ ○ ● ○ │ │ ● ● ● │ │ ● ○ ● │ └────────┘ └────────┘ ● 活跃神经元 ○ 被丢弃的神经元 推理时使用所有神经元但权重乘以 (1-p)3.2 数学解释Dropout 的数学解释 ═══════════════════════════════════════════════════════════════════ 集成学习的视角 每次 Dropout 相当于训练一个不同的子网络 推理时相当于对所有子网络取平均 2ⁿ 个可能的子网络n 个神经元 → 相当于集成了 2ⁿ 个模型 正则化的视角 Dropout 迫使神经元不依赖特定的其他神经元 → 学到更鲁棒的特征3.3 实现classDropoutLayer:Dropout 层def__init__(self,dropout_rate0.5):self.dropout_ratedropout_rate self.maskNoneself.trainingTruedefforward(self,x):ifnotself.training:returnx# 生成 Dropout 掩码self.mask(np.random.rand(*x.shape)self.dropout_rate)# 应用掩码并缩放returnx*self.mask/(1-self.dropout_rate)defbackward(self,grad_output):ifnotself.training:returngrad_outputreturngrad_output*self.mask/(1-self.dropout_rate)四、Python 实现正则化4.1 L2 正则化classRegularizedMLP:带 L2 正则化的 MLPdef__init__(self,input_size,hidden_size,output_size,reg_lambda0.01,learning_rate0.01):self.lrlearning_rate self.reg_lambdareg_lambda# Xavier 初始化self.W1np.random.randn(input_size,hidden_size)*np.sqrt(2.0/input_size)self.b1np.zeros((1,hidden_size))self.W2np.random.randn(hidden_size,output_size)*np.sqrt(2.0/hidden_size)self.b2np.zeros((1,output_size))defrelu(self,z):returnnp.maximum(0,z)defsoftmax(self,z):exp_znp.exp(z-np.max(z,axis1,keepdimsTrue))returnexp_z/np.sum(exp_z,axis1,keepdimsTrue)defforward(self,X):self.z1X self.W1self.b1 self.a1self.relu(self.z1)self.z2self.a1 self.W2self.b2 self.a2self.softmax(self.z2)returnself.a2defcompute_loss(self,y_pred,y_true):计算损失带 L2 正则化my_true.shape[0]# 交叉熵损失cross_entropy-np.mean(np.log(y_pred[range(m),y_true]1e-8))# L2 正则化项l2_regself.reg_lambda*(np.sum(self.W1**2)np.sum(self.W2**2))returncross_entropyl2_regdefbackward(self,X,y):mX.shape[0]# 输出层梯度dz2self.a2.copy()dz2[range(m),y]-1# 加入 L2 正则化梯度dW2self.a1.T dz2/mself.reg_lambda*self.W2 db2np.sum(dz2,axis0,keepdimsTrue)/m# 隐藏层梯度da1dz2 self.W2.T dz1da1*(self.z10)# 加入 L2 正则化梯度dW1X.T dz1/mself.reg_lambda*self.W1 db1np.sum(dz1,axis0,keepdimsTrue)/m# 更新权重self.W2-self.lr*dW2 self.b2-self.lr*db2 self.W1-self.lr*dW1 self.b1-self.lr*db1deffit(self,X,y,n_iterations1000):self.train_history[]forepochinrange(n_iterations):y_predself.forward(X)lossself.compute_loss(y_pred,y)self.train_history.append(loss)ifepoch%1000:accuracynp.mean(np.argmax(y_pred,axis1)y)print(fEpoch{epoch}: Loss{loss:.4f}, Accuracy{accuracy:.2%})self.backward(X,y)4.2 带 Dropout 的 MLPclassDropoutMLP:带 Dropout 的 MLPdef__init__(self,input_size,hidden_size,output_size,dropout_rate0.5,learning_rate0.01):self.lrlearning_rate self.dropout_ratedropout_rate self.trainingTrueself.W1np.random.randn(input_size,hidden_size)*np.sqrt(2.0/input_size)self.b1np.zeros((1,hidden_size))self.W2np.random.randn(hidden_size,output_size)*np.sqrt(2.0/hidden_size)self.b2np.zeros((1,output_size))defrelu(self,z):returnnp.maximum(0,z)defsoftmax(self,z):exp_znp.exp(z-np.max(z,axis1,keepdimsTrue))returnexp_z/np.sum(exp_z,axis1,keepdimsTrue)defdropout(self,x):ifnotself.training:returnx mask(np.random.rand(*x.shape)self.dropout_rate)returnx*mask/(1-self.dropout_rate)defforward(self,X):self.z1X self.W1self.b1 self.a1self.relu(self.z1)self.a1_droppedself.dropout(self.a1)# Dropoutself.z2self.a1_dropped self.W2self.b2 self.a2self.softmax(self.z2)returnself.a2defbackward(self,X,y):mX.shape[0]dz2self.a2.copy()dz2[range(m),y]-1dW2self.a1_dropped.T dz2/m db2np.sum(dz2,axis0,keepdimsTrue)/m da1dz2 self.W2.Tifself.training:da1*(self.a10)*(np.random.rand(*da1.shape)self.dropout_rate)/(1-self.dropout_rate)else:da1*(self.a10)dW1X.T da1/m db1np.sum(da1,axis0,keepdimsTrue)/m self.W2-self.lr*dW2 self.b2-self.lr*db2 self.W1-self.lr*dW1 self.b1-self.lr*db14.3 对比实验fromsklearn.datasetsimportmake_classificationfromsklearn.model_selectionimporttrain_test_split# 生成数据有噪声X,ymake_classification(n_samples1000,n_features20,n_informative10,n_redundant5,random_state42)X_train,X_test,y_train,y_testtrain_test_split(X,y,test_size0.3)# 标准化fromsklearn.preprocessingimportStandardScaler scalerStandardScaler()X_train_scaledscaler.fit_transform(X_train)X_test_scaledscaler.transform(X_test)# 无正则化mlp_no_regRegularizedMLP(20,64,2,reg_lambda0,learning_rate0.01)mlp_no_reg.fit(X_train_scaled,y_train,n_iterations500)acc_no_regnp.mean(np.argmax(mlp_no_reg.forward(X_test_scaled),axis1)y_test)# L2 正则化mlp_l2RegularizedMLP(20,64,2,reg_lambda0.01,learning_rate0.01)mlp_l2.fit(X_train_scaled,y_train,n_iterations500)acc_l2np.mean(np.argmax(mlp_l2.forward(X_test_scaled),axis1)y_test)print(f\n无正则化测试准确率:{acc_no_reg:.2%})print(fL2 正则化测试准确率:{acc_l2:.2%})五、工业应用5.1 深度学习中的正则化深度学习中的正则化技术 ═══════════════════════════════════════════════════════════════════ 1. Dropout - 训练时随机丢弃神经元 - 最常用的正则化技术 2. Batch Normalization - 归一化每层的输入 - 间接起到正则化作用 3. 数据增强 - 增加训练数据的多样性 - 提高泛化能力 4. 早停Early Stopping - 训练过程中监控验证集性能 - 性能不再提升时停止 5. 权重衰减Weight Decay - L2 正则化的另一种形式 - PyTorch 中的 weight_decay 参数5.2 传统机器学习中的正则化传统机器学习中的正则化 ═══════════════════════════════════════════════════════════════════ 1. 线性回归 - RidgeL2防止权重过大 - LassoL1特征选择 2. 逻辑回归 - L1/L2 正则化 - C 参数与 λ 成反比 3. SVM - C 参数正则化强度 - 核函数选择控制复杂度 4. 决策树 - 剪枝限制树的深度 - 叶子节点最小样本数5.3 AOI 中的应用AOI 项目中的正则化 ═══════════════════════════════════════════════════════════════════ YOLO 训练中的正则化 - weight_decayL2 正则化 - dropout分类头中的 Dropout - data augmentation数据增强 - early stopping早停 参数设置 weight_decay0.0005 # 默认值 dropout0.5 # 分类头 patience10 # 早停耐心值六、避坑指南使用正则化的 3 个陷阱坑 1λ 太大 → 欠拟合错误做法reg_lambda10# ❌ λ 太大mlpRegularizedMLP(20,64,2,reg_lambda10)# 正则化太强模型学不到东西# 训练集和测试集效果都很差正确做法从 0.01 开始用交叉验证选择# ✅ 交叉验证选择 λfromsklearn.model_selectionimportGridSearchCV param_grid{reg_lambda:[0.001,0.01,0.1,1.0]}# 用 sklearn 的 MLPClassifier 包装grid_searchGridSearchCV(mlp_sklearn,param_grid,cv5)grid_search.fit(X_train,y_train)print(f最佳 λ:{grid_search.best_params_[reg_lambda]})坑 2λ 太小 → 过拟合错误做法reg_lambda0# ❌ 没有正则化mlpRegularizedMLP(20,64,2,reg_lambda0)# 训练集 99%测试集 70%正确做法始终使用正则化# ✅ 始终使用正则化mlpRegularizedMLP(20,64,2,reg_lambda0.01)坑 3不加正则化 → 训练集效果好测试集效果差错误做法只看训练集准确率# ❌ 只看训练集print(f训练准确率:{accuracy})# 99% 以为很好其实过拟合了正确做法始终监控验证集性能# ✅ 监控验证集train_accaccuracy_score(y_train,model.predict(X_train))val_accaccuracy_score(y_val,model.predict(X_val))print(f训练准确率:{train_acc:.2%})print(f验证准确率:{val_acc:.2%})# 如果训练 验证说明过拟合了七、正则化参数选择指南7.1 λ 的选择λ 选择指南 ═══════════════════════════════════════════════════════════════════ 方法 1网格搜索 λ ∈ [0.001, 0.01, 0.1, 1.0, 10] 用交叉验证选择 方法 2经验法则 - 数据量小λ 大一些0.1 ~ 1.0 - 数据量大λ 小一些0.001 ~ 0.01 - 特征多λ 大一些 方法 3可视化 画出不同 λ 对应的训练/验证误差 选择验证误差最小的 λ7.2 Dropout 率的选择Dropout 率选择指南 ═══════════════════════════════════════════════════════════════════ 常用值0.3 ~ 0.5 经验法则 - 隐藏层大dropout 大一些0.5 - 隐藏层小dropout 小一些0.2 - 过拟合严重增大 dropout - 欠拟合减小 dropout 或不使用 注意 - 只在训练时使用 Dropout - 推理时不使用但权重要缩放八、本篇总结核心要点回顾过拟合模型在训练集上效果好但泛化能力差偏差-方差分解误差 偏差² 方差 噪声L2 正则化Ridge权重衰减趋向于小值L1 正则化Lasso稀疏化部分权重为 0Dropout随机丢弃神经元集成学习效果参数选择用交叉验证选择正则化参数下篇预告下一篇我们学习主分量分析PCA。正则化防止过拟合但有时候我们需要降维来减少数据的复杂度。下一篇你将学到PCA 的直觉理解特征值分解和 SVD核 PCA 处理非线性数据用 Python 手写 PCA本期互动你对正则化有什么看法你常用哪种正则化技术你遇到过过拟合问题吗怎么解决的你觉得 Dropout 为什么有效欢迎在评论区留言。系列目录篇标题状态01Haykin 精讲开篇从「只会调参」到「理解神经网络的灵魂」✅ 完成02感知器神经网络的「鼻祖」为什么它能「学会」分类✅ 完成03LMS 算法从最小二乘到随机梯度下降工业自适应滤波的核心✅ 完成04反向传播神经网络为什么能「学习」用 NumPy 手写 BP✅ 完成05核方法为什么 SVM 能处理非线性问题理解「升维」的本质✅ 完成06支持向量机最大间隔的「艺术」为什么它是「小数据之王」✅ 完成07正则化为什么模型越复杂越容易过拟合L1/L2/Dropout✅ 当前08PCA为什么降维能「去噪」从特征值分解到核 PCA⏳ 下一篇09SOM无监督学习的「聚类之王」为什么它能「自组织」⏳ 待写10信息论为什么「信息最大化」能学特征从熵到 ICA⏳ 待写11玻尔兹曼机深度学习的「前世」从统计力学到 RBM⏳ 待写12动态规划强化学习的「数学基础」从 MDP 到值迭代⏳ 待写13Hopfield 网络联想记忆的「鼻祖」为什么它能「回忆」⏳ 待写14卡尔曼滤波为什么它能「预测」从贝叶斯推断到粒子滤波⏳ 待写15Haykin 精讲终篇从感知器到深度学习——一部神经网络的「进化史」⏳ 待写点赞收藏转发是我持续更新的动力