ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

机器学习:线性回归4(欠拟合,正好拟合,过拟合)

机器学习:线性回归4(欠拟合,正好拟合,过拟合)

目录标题

      • 一、概念
      • 二、对应拟合图片
      • 三、过拟合解决方法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_loss=float('inf')# 记录历史最低的验证集 Loss,初始为正无穷best_model_state=None# 记录历史最好状态下的模型权重patience=5# 容忍度:允许验证集 Loss 连续 5 个 epoch 不下降trigger_times=0# 计数器:记录当前连续不下降的次数forepochinrange(100):# ------------------------------------------# 1. 训练阶段# ------------------------------------------model.train()train_loss=0.0forX_batch,y_batchintrain_loader:optimizer.zero_grad()outputs=model(X_batch)loss=criterion(outputs,y_batch)loss.backward()optimizer.step()train_loss+=loss.item()*X_batch.size(0)train_loss/=len(train_loader.dataset)train_losses.append(train_loss)# ------------------------------------------# 2. 验证阶段# ------------------------------------------model.eval()val_loss=0.0withtorch.no_grad():forX_val,y_valinval_loader:val_outputs=model(X_val)loss=criterion(val_outputs,y_val)val_loss+=loss.item()*X_val.size(0)val_loss/=len(val_loader.dataset)val_losses.append(val_loss)print(f"Epoch [{epoch+1}/100], Train Loss:{train_loss:.4f}, Val Loss:{val_loss:.4f}")# ==========================================# 3. 早停机制核心逻辑 (Early Stopping)# ==========================================# 情况 A:验证集 Loss 创了新低(说明模型还在进步)# 注意:这里加一个极小值 1e-4 是防止浮点数精度问题导致误判ifval_loss<best_val_loss-1e-4:best_val_loss=val_loss# 更新历史最低 Lossbest_model_state=copy.deepcopy(model.state_dict())# 把当前表现最好的模型“存档”下来trigger_times=0# 因为有了新突破,把连续没进步的计数器清零print(f" --> 验证集 Loss 创新低,模型已保存!(Best:{best_val_loss:.4f})")# 情况 B:验证集 Loss 没有创新低(说明模型可能在死记硬背,开始过拟合了)else:trigger_times+=1# 没进步,计数器 +1print(f" --> 警告:验证集 Loss 连续{trigger_times}轮未下降。")# 如果连续不下降的次数达到了我们的耐心极限 (patience)iftrigger_times>=patience:print(f"\n触发早停!验证集 Loss 已经连续{patience}轮没有下降,停止训练防止过拟合。")break# 直接跳出训练循环,训练结束!# ==========================================# 4. 恢复最佳模型# ==========================================# 训练中途 break 或者跑完了,我们把之前存下的“最巅峰状态”的模型恢复回来ifbest_model_stateisnotNone:model.load_state_dict(best_model_state)print("已恢复到验证集表现最好的模型状态。")

重点注释解析:

  1. best_val_loss = float('inf'):一开始没有对比对象,所以设定为无限大。第一轮算出来的val_loss肯定比它小,因此第一轮一定会保存模型。

  2. trigger_times的累加与清零:这是早停的灵魂。

    • 只要val_loss下降,trigger_times就被打回0,给你重新计算的机会。
    • 一旦val_loss没下降,trigger_times就+1。
  3. 为什么选择在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 次的评估结果取平均值。

它的核心作用:提供更稳定、更客观的泛化误差评估。它告诉你:“这个模型在多次模拟考中的平均成绩是多少”。如果你发现训练集得分极高,但交叉验证的平均得分很低,这就暴露了过拟合。

找到过拟合,就要调参,那是网格搜索的事,它只负责检测

一句话总结:

  • 交叉验证 = 体检。它告诉你“你是不是过拟合了”,并通过筛选最优超参数,帮你规避过拟合的模型。
  • 正则化/增加数据 = 吃药锻炼。它才是直接防止你过拟合的手段。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进