目录标题一、概念二、对应拟合图片三、过拟合解决方法L1和L2正则四、(可以暂时不看涉及后面知识)如何防止过拟合训练那么判断五、(可以暂时不看涉及后面知识)除了上面正则过拟合预解决方法(预防并处理,用后面会学的知识 交叉验证和网格搜索)一、概念二、对应拟合图片三、过拟合解决方法L1和L2正则Lasso 回归回归模型带 L1 正则的线性回归岭回归回归模型带 L2 正则的线性回归四、(可以暂时不看涉及后面知识)如何防止过拟合训练那么判断既然不能用测试集来决定什么时候停止训练那我们怎么知道模型什么时候开始“过拟合”了怎么找到这个停止训练点呢? 引入验证集Validation Set正确的做法是把训练集切分成两份——训练集Train Set和验证集Validation Set。训练集 Loss用来更新模型参数(w, b)。验证集 Loss用来观测模型泛化能力指导早停和调参。测试集 Loss只在所有训练和调参结束后最后评估一次。patience耐心值是深度学习中实现早停Early Stopping最核心的机制简单来说:如果验证集 Loss 连续 patience 次训练都没有创下新低我们就认为模型已经越过“红线”开始过拟合了此时直接 break 结束训练。代码实现:importtorchimportcopyimportmatplotlib.pyplotasplt# 假设 model, train_loader, val_loader, criterion, optimizer 已定义train_losses[]val_losses[]# # 早停机制相关的变量初始化# best_val_lossfloat(inf)# 记录历史最低的验证集 Loss初始为正无穷best_model_stateNone# 记录历史最好状态下的模型权重patience5# 容忍度允许验证集 Loss 连续 5 个 epoch 不下降trigger_times0# 计数器记录当前连续不下降的次数forepochinrange(100):# ------------------------------------------# 1. 训练阶段# ------------------------------------------model.train()train_loss0.0forX_batch,y_batchintrain_loader:optimizer.zero_grad()outputsmodel(X_batch)losscriterion(outputs,y_batch)loss.backward()optimizer.step()train_lossloss.item()*X_batch.size(0)train_loss/len(train_loader.dataset)train_losses.append(train_loss)# ------------------------------------------# 2. 验证阶段# ------------------------------------------model.eval()val_loss0.0withtorch.no_grad():forX_val,y_valinval_loader:val_outputsmodel(X_val)losscriterion(val_outputs,y_val)val_lossloss.item()*X_val.size(0)val_loss/len(val_loader.dataset)val_losses.append(val_loss)print(fEpoch [{epoch1}/100], Train Loss:{train_loss:.4f}, Val Loss:{val_loss:.4f})# # 3. 早停机制核心逻辑 (Early Stopping)# # 情况 A验证集 Loss 创了新低说明模型还在进步# 注意这里加一个极小值 1e-4 是防止浮点数精度问题导致误判ifval_lossbest_val_loss-1e-4:best_val_lossval_loss# 更新历史最低 Lossbest_model_statecopy.deepcopy(model.state_dict())# 把当前表现最好的模型“存档”下来trigger_times0# 因为有了新突破把连续没进步的计数器清零print(f -- 验证集 Loss 创新低模型已保存(Best:{best_val_loss:.4f}))# 情况 B验证集 Loss 没有创新低说明模型可能在死记硬背开始过拟合了else:trigger_times1# 没进步计数器 1print(f -- 警告验证集 Loss 连续{trigger_times}轮未下降。)# 如果连续不下降的次数达到了我们的耐心极限 (patience)iftrigger_timespatience:print(f\n触发早停验证集 Loss 已经连续{patience}轮没有下降停止训练防止过拟合。)break# 直接跳出训练循环训练结束# # 4. 恢复最佳模型# # 训练中途 break 或者跑完了我们把之前存下的“最巅峰状态”的模型恢复回来ifbest_model_stateisnotNone:model.load_state_dict(best_model_state)print(已恢复到验证集表现最好的模型状态。)重点注释解析best_val_loss float(inf)一开始没有对比对象所以设定为无限大。第一轮算出来的val_loss肯定比它小因此第一轮一定会保存模型。trigger_times的累加与清零这是早停的灵魂。只要val_loss下降trigger_times就被打回0给你重新计算的机会。一旦val_loss没下降trigger_times就1。为什么选择在break之后恢复模型当你连续 5 轮patience发现 Loss 没下降并决定break时当前的模型其实已经是“过拟合”的烂模型了。真正的“红线”是在这 5 轮之前那个创下best_val_loss的瞬间。所以我们需要用best_model_state把它救回来。补充一点工业界的进阶常识除了监控 Loss有时候我们也会监控准确率Accuracy。因为有时候 Loss 在下降但准确率可能停滞或者 Loss 震荡但准确率在突破。逻辑是一模一样的把判断条件从val_loss best_val_loss改成val_acc best_val_acc即可。五、(可以暂时不看涉及后面知识)除了上面正则过拟合预解决方法(预防并处理,用后面会学的知识 交叉验证和网格搜索)交叉验证Cross-Validation本身不能直接防止过拟合它是用来“评估”模型是否过拟合以及“帮你挑选”最不容易过拟合的超参数的工具(这个交叉验证只负责检查具体修改超参的是网格搜索)交叉验证法是划分数据集的一种方法目的就是为了得到更加准确可信的模型评分举个列子看看交叉验证是干啥怎么找到过拟合的交叉验证是干什么的它是体检仪如果你只用一次简单的“训练集/测试集”切分万一运气不好切出来的测试集特别简单模型考了 95或者测试集特别难模型考了 50 分。这都不能真实反映模型的泛化能力。交叉验证最常用的是 K 折交叉验证的做法是把数据分成 K 份轮流拿其中 1 份当测试集剩下 K-1 份当训练集训练 K 次最后把 K 次的评估结果取平均值。它的核心作用提供更稳定、更客观的泛化误差评估。它告诉你“这个模型在多次模拟考中的平均成绩是多少”。如果你发现训练集得分极高但交叉验证的平均得分很低这就暴露了过拟合。找到过拟合就要调参那是网格搜索的事它只负责检测一句话总结交叉验证 体检。它告诉你“你是不是过拟合了”并通过筛选最优超参数帮你规避过拟合的模型。正则化/增加数据 吃药锻炼。它才是直接防止你过拟合的手段。