机器学习模型训练完以后,最常见的困惑是:训练集分数很高,测试集分数却很低;或者训练集和测试集分数都不高,怎么调都上不去。这两个问题通常对应过拟合和欠拟合。
这篇文章用可运行的 Python 示例解释过拟合、欠拟合和模型调优。读完以后,你应该能通过训练分数和验证分数判断问题类型,并知道优先从数据、模型复杂度、正则化和交叉验证哪一步下手。
如果你刚刚完成 模型训练与评估入门,这篇就是下一步:从“会看指标”进入“会诊断模型”。
一、过拟合和欠拟合是什么意思
过拟合 是模型把训练数据里的细节甚至噪声也记住了。表现是训练集分数很高,但验证集或测试集分数明显低。
欠拟合 是模型太简单,连训练数据里的主要规律都没学好。表现是训练集分数低,验证集分数也低。
可以用一句话记住:
- 训练集高、验证集低:优先怀疑过拟合
- 训练集低、验证集低:优先怀疑欠拟合
- 训练集和验证集都高:模型当前比较健康
二、用决策树演示模型复杂度
决策树很适合演示过拟合,因为树越深,越容易记住训练样本里的细节。下面的代码用不同深度的决策树比较训练集和验证集准确率。
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
X, y = load_breast_cancer(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(
X,
y,
test_size=0.25,
random_state=42,
stratify=y,
)
for depth in [1, 2, 3, 4, 6, 10, None]:
model = DecisionTreeClassifier(max_depth=depth, random_state=42)
model.fit(X_train, y_train)
train_score = model.score(X_train, y_train)
test_score = model.score(X_test, y_test)
print(f"max_depth={depth}: train={train_score:.3f}, test={test_score:.3f}")
保存为 decision_tree_depth_demo.py 后运行:
python decision_tree_depth_demo.py
你通常会看到浅树训练分数不高,说明表达能力有限;树越来越深时,训练分数会上升,但测试分数不一定继续提升。这就是模型复杂度带来的典型权衡。
三、如何判断是过拟合还是欠拟合
不要只看一个测试集分数。更可靠的诊断方式是同时记录训练分数和验证分数。
- 欠拟合:训练分数低,验证分数也低,说明模型没学到足够规律
- 过拟合:训练分数高,验证分数低,说明模型记住了训练数据细节
- 数据不足:训练分数波动大,验证分数不稳定,可能需要更多样本或交叉验证
- 指标选错:accuracy 很高但业务效果差,可能类别不平衡或错误成本不同
这也是为什么 机器学习完整流程 里要强调训练集、验证集和测试集拆分。没有可靠评估,就无法判断模型到底出了什么问题。
四、过拟合怎么解决
过拟合的核心问题是模型太容易记住训练数据。常见解决方向如下:
- 降低模型复杂度:限制树深度、减少特征数量、使用更简单模型
- 增加正则化:线性模型调大正则强度,神经网络可加 weight decay 或 dropout
- 增加数据量:更多样本能减少模型记住偶然噪声的机会
- 做数据增强:图像、文本、语音任务中尤其常见
- 交叉验证:减少只依赖一次划分带来的偶然性
- 提前停止:验证集效果不再提升时停止训练
如果是树模型,先限制 max_depth、min_samples_leaf 和 min_samples_split。如果是逻辑回归,先调正则化参数 C。
五、欠拟合怎么解决
欠拟合的核心问题是模型表达能力不够,或者输入特征没提供足够信息。常见解决方向如下:
- 换更强模型:从线性模型换到树模型、集成模型或神经网络
- 增加有意义特征:补充领域变量、交互特征或时间窗口特征
- 减少过强正则化:正则化太强会让模型学不动
- 训练更久:深度学习任务中,训练轮数太少可能欠拟合
- 检查标签质量:标签噪声严重时,模型很难学到稳定规律
欠拟合不是简单地“把模型调大”。如果原始特征没有信息,模型再复杂也只能学习噪声。
六、用交叉验证减少误判
一次训练测试拆分可能刚好碰到容易或困难的样本,导致判断不稳定。交叉验证可以让模型在多次不同划分上评估,结果更可靠。
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import cross_val_score
from sklearn.tree import DecisionTreeClassifier
X, y = load_breast_cancer(return_X_y=True)
for depth in [2, 3, 4, 6, None]:
model = DecisionTreeClassifier(max_depth=depth, random_state=42)
scores = cross_val_score(model, X, y, cv=5, scoring="accuracy")
print(f"max_depth={depth}: mean={scores.mean():.3f}, std={scores.std():.3f}")
如果要单独验证交叉验证结果,可以把这一段保存为 tree_cross_validation_demo.py 后运行:
python tree_cross_validation_demo.py
mean 代表平均表现,std 代表不同划分之间的波动。平均值高但波动也很大时,说明模型稳定性仍然需要检查。
七、一个实用调优顺序
不要一上来就大范围网格搜索。更稳的调优顺序是:
- 先确认数据拆分没有泄漏。
- 训练一个简单基线模型。
- 记录训练分数和验证分数。
- 判断是欠拟合还是过拟合。
- 只改一类因素,例如模型复杂度或正则化。
- 用交叉验证确认提升不是偶然结果。
- 最后用独立测试集做一次最终评估。
这个顺序比“同时改模型、特征、参数、数据划分”更容易复盘。机器学习调优最怕不可解释的提升,因为你不知道下次能不能复现。
八、常见问题 FAQ
训练准确率 100% 一定是好事吗?
不一定。如果验证集或测试集明显低,训练准确率 100% 反而可能说明模型过拟合。要同时看训练分数和验证分数。
测试集分数低一定是模型不够强吗?
不一定。也可能是数据泄漏修正后分数回落、标签质量差、训练测试分布不同,或者评估指标不适合当前任务。
过拟合时应该删特征吗?
可以考虑,但不要盲删。优先检查特征是否泄漏未来信息、是否重复表达同一信号、是否只在训练数据里有效。保留有业务含义且稳定的特征。
交叉验证能替代测试集吗?
不能。交叉验证适合模型选择和调参,最终仍建议保留一个独立测试集,用来估计模型在未参与选择的数据上的表现。
九、下一步阅读
过拟合和欠拟合是机器学习调优的核心诊断方法。继续学习可以回到 Python 人工智能小实战,把这套诊断方法应用到完整 scikit-learn 分类流程里。
搜索问题
常见问题
这篇文章适合谁读?
这篇文章适合想用 实战 难度理解“过拟合和欠拟合怎么解决:机器学习模型调优实战指南”的读者,预计阅读时间约 10 分钟,重点覆盖 Overfitting, Cross Validation, Model Tuning。
读完后下一步应该看什么?
推荐下一步阅读“神经网络基础:从感知机到多层网络”,这样可以把当前知识点接到更完整的学习路线里。
这篇文章有没有可运行代码或配套资源?
有。页面里的运行说明、资源卡片和下载入口会指向复现实验所需的命令、数据、代码或说明文件。
这篇文章和整个网站的学习路线有什么关系?
它会通过文章上下文、学习路线、资源库和项目时间线连接到同一主题下的其他内容。
文章上下文
人工智能项目
从 AI、机器学习、训练评估、神经网络到 Python 小实战、手写数字识别、CIFAR-10 CNN、对抗性流量防御和 AI 安全攻防,按顺序建立基础。
项目时间线
已发布文章
- 人工智能基础学习路线:先理解什么是 AI、机器学习和深度学习 面向有编程基础的读者,梳理 AI、机器学习、深度学习的关系,并给出可执行的人工智能基础学习路线。
- 机器学习完整流程:从数据、特征到模型预测 从工程视角拆解机器学习完整流程:定义问题、理解数据、处理特征、训练模型、预测和评估。
- 机器学习算法怎么选:分类、回归、聚类和推荐场景对照表 用任务类型、数据规模、解释性和部署成本选择机器学习算法,覆盖逻辑回归、决策树、随机森林、K-means 和表格数据基线模型。
- 特征工程入门实战:用 scikit-learn 处理缺失值、类别变量和数值标准化 用 scikit-learn Pipeline 和 ColumnTransformer 完成特征工程,处理缺失值、类别变量、数值标准化,并避免数据泄漏。
- 模型训练与评估入门:损失函数、过拟合和准确率怎么理解 讲清楚模型训练中的参数、损失函数、梯度下降、过拟合,以及准确率、召回率、F1 等分类评估指标。
- 过拟合和欠拟合怎么解决:机器学习模型调优实战指南 用训练分数和验证分数判断过拟合与欠拟合,并通过模型复杂度、正则化、交叉验证和特征工程调整机器学习模型。
- 神经网络基础:从感知机到多层网络 从一个神经元讲起,解释权重、偏置、激活函数、前向传播、反向传播和典型神经网络训练循环。
- Python 人工智能小实战:用 scikit-learn 完成一个分类任务 使用 scikit-learn 内置教学数据集跑通一个分类任务,覆盖数据加载、拆分、标准化、训练、预测、评估和实验记录。
- 手写数字识别项目入门:先读懂 train.csv、test.csv 和标签结构 从项目文件结构入手,读懂手写数字训练集、测试集、标签列和 784 维像素输入,为后续 C 分类器和实验台打基础。
- 用 C 实现手写数字 Softmax 分类器:从 784 维像素到 submission.csv 结合当前项目源码,讲清楚 softmax 多分类、损失函数、梯度更新、混淆矩阵输出,以及 submission.csv 的生成过程。
- 手写数字实验记录:怎么把离线分类项目接进浏览器实验台 解释浏览器实验台为什么采用轻量预训练模型、它和离线 C 项目的关系,以及如何用样本浏览和手绘输入理解预测结果。
- CIFAR-10 Tiny CNN 教程:用 C 语言实现小型卷积神经网络图像分类 用单文件 C 程序完成 CIFAR-10 小型 CNN 图像分类,讲解数据格式、网络结构、训练命令、loss、accuracy、常见错误和改进方向。
- 构建高熵流量防御:基于 Python 的连接层白噪声混淆与对抗性机器学习实践 以 mld_chaffing_v2.py 虚幻镜项目为例,讲解加密元数据泄漏、信息熵、分布距离、混淆矩阵、空闲窗口微脉冲和性能测试取舍。
- AI 安全威胁建模:用 NIST AML、MITRE ATLAS 和 OWASP 建立攻防地图 用 NIST Adversarial ML、MITRE ATLAS 和 OWASP LLM Top 10 建立 AI 安全威胁模型,覆盖资产、攻击面、证据和剩余风险。
- 对抗样本与鲁棒评估:从 FGSM 公式到 scikit-learn 数字分类实验 从 FGSM 公式解释对抗样本,用 scikit-learn digits toy 实验评估 clean accuracy、perturbed accuracy 和扰动预算。
- 数据投毒与后门攻击防御:污染率、触发器和训练管线隔离 用 toy digits 实验解释数据投毒、后门触发器、attack success rate、数据来源审计和训练管线隔离。
- 模型隐私与模型窃取风险:成员推断、模型抽取和输出接口防护 用本地 toy 实验解释成员推断、模型抽取、membership AUC、surrogate fidelity、输出最小化和查询治理。
- LLM/RAG/Agent 安全:Prompt Injection、工具权限和边界感知防护 从 RAG 和 Agent 架构解释 prompt injection、外部数据降权、工具 allowlist、人工审批和边界感知防护。
- 人工智能 NLP 基础:词袋模型与 TF-IDF 详解 介绍自然语言处理中最基础的文本表示方法:词袋模型(Bag of Words)与 TF-IDF,理解它们的工作原理及优缺点。
- 循环神经网络 (RNN) 基础:处理序列数据的记忆力 理解 RNN 的核心思想、隐藏状态的作用,以及它在处理自然语言序列任务时的优势与挑战。
- Transformer 与自注意力机制:AI 领域的革命性突破 深入浅出地讲解 Transformer 架构的核心:自注意力机制(Self-Attention)及其运作方式。
- 用 C 从零实现 CIFAR-10 Tiny CNN:卷积、池化和反向传播 基于实际 cifar10_tiny_cnn.c 项目,讲解 CIFAR-10 数据格式、3x3 卷积、ReLU、最大池化、全连接层、softmax、反向传播和本地运行方式。
已公开资源
- Python AI 小实战代码说明 文章内包含可直接复制运行的 scikit-learn 分类脚本。
- digit_softmax_classifier.c 手写数字 softmax 分类器的 C 语言源码。
- train.csv.zip 手写数字训练集压缩包,包含 42000 条带标签样本。
- test.csv.zip 手写数字测试集压缩包,包含 28000 条待预测样本。
- sample_submission.csv 官方提交格式示例,可直接对照最终输出字段。
- submission.csv 当前 C 项目跑出的预测结果文件。
- digit-playground-model.json 浏览器实验台使用的轻量 softmax 演示模型与样本。
- digit-sample-grid.svg 从训练集中抽取的小型手写数字预览网格。
- 手写数字项目打包下载 包含源码、压缩数据、提交文件、浏览器模型和样本预览图。
- cifar10_tiny_cnn.c 源码 单文件 C 语言 tiny CNN,包含 CIFAR-10 读取、卷积、池化、softmax 和反向传播。
- model_weights.bin 样例权重 一次本地小样本运行生成的模型权重文件。
- test_predictions.csv 预测样例 CIFAR-10 tiny CNN 输出的测试预测样例。
- CNN 项目说明 PDF 配套 CNN 项目说明材料。
- 虚幻镜脱敏代码骨架 去除控制口令、真实节点和目标列表后的 mld_chaffing_v2.py 控制流程说明。
- 虚幻镜压力测试记录模板 用于记录 CPU、内存、线程峰值、微脉冲速率、延迟和错误数的脱敏 CSV 模板。
- 虚幻镜分类器评估模板 用于记录 TP、FN、FP、TN、accuracy、precision、recall、F1、ROC-AUC、熵和 JS 散度的 CSV 模板。
- 虚幻镜资源说明 说明公开资源为何只提供脱敏代码、测试模板和架构笔记。
- AI Security Lab 说明 说明 AI 安全攻防系列的安全边界、安装命令和 quick-run 实验。
- AI Security Lab 完整实验包 包含安全 toy scripts、结果 CSV、风险登记表、攻防矩阵和架构图。
- AI 安全风险登记表 面向 AI 威胁建模和上线评审的 CSV 风险登记模板。
- AI 攻防矩阵 把攻击面、toy demo、指标和防护控制映射到一张 CSV 表。
- AI Security Lab 架构图 展示威胁建模、鲁棒评估、数据完整性、模型隐私和 RAG 防护之间的关系。
- FGSM digits 鲁棒评估脚本 本地 digits 分类器的 FGSM-style 扰动和准确率下降实验。
- 数据投毒与后门 toy 脚本 用 digits 数据演示污染率、触发器和 attack success rate。
- 模型隐私与抽取 toy 脚本 输出 membership AUC、target accuracy、surrogate fidelity 和 surrogate accuracy。
- RAG prompt injection guard toy 脚本 用确定性 toy agent 演示外部数据降权和工具权限阻断。
- 深度学习专题分享图 用于分享深度学习 / CNN 专题页的 1200x630 SVG 图。
- 从零实现机器学习分享图 用于分享 K-means、Iris 和机器学习流程专题页的 1200x630 SVG 图。
- 学生 AI 项目分享图 用于分享手写数字、C 分类器和浏览器实验台专题页的 1200x630 SVG 图。
- CNN 卷积扫描动画 Remotion 生成的 8 秒短动画,展示 3x3 卷积核如何扫描输入并形成特征图。
当前学习路线
- 人工智能基础学习路线 学习路线节点
- 机器学习完整流程 学习路线节点
- 机器学习算法怎么选 学习路线节点
- 特征工程入门实战 学习路线节点
- 模型训练与评估入门 学习路线节点
- 过拟合和欠拟合怎么解决 学习路线节点
- 神经网络基础 学习路线节点
- Transformer 自注意力机制 学习路线节点
- LLM 可视化教学台 学习路线节点
- Python 人工智能小实战 学习路线节点
- 手写数字数据结构入门 学习路线节点
- 用 C 实现手写数字 Softmax 分类器 学习路线节点
- 手写数字实验台说明 学习路线节点
- CIFAR-10 Tiny CNN 教程 学习路线节点
- 高熵流量防御实验 学习路线节点
- AI 安全威胁建模 学习路线节点
- 对抗样本与鲁棒评估 学习路线节点
- 数据投毒与后门防御 学习路线节点
- 模型隐私与模型抽取防护 学习路线节点
- LLM/RAG/Agent 安全 学习路线节点
下一步计划
- 补充更多图像分类和误差分析案例
- 把常见指标整理成速查表
- 继续补充 AI 安全防御实验记录
