news 2026/9/2 6:58:51

【深度学习】模型选择、过拟合与欠拟合

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【深度学习】模型选择、过拟合与欠拟合

一、训练误差 vs 泛化误差

训练误差(Training Error):模型在训练数据上的误差。
泛化误差(Generalization Error):模型在从未见过的新数据上的误差。

类比:训练误差 = 平时做课堂练习的正确率;泛化误差 = 期末考试的成绩。

关键结论:

我们真正关心的是泛化误差,而不是训练误差。

因为我们训练模型不是为了在训练集上得高分,而是为了处理将来遇到的新数据。


二、验证数据集 vs 测试数据集

为了正确地估计泛化误差,我们需要把手里的数据拆成三份

2.1 训练数据集(Training Set)

  • 用来训练模型参数。
  • 比如把全部数据的 60% 拿来。

2.2 验证数据集(Validation Set)

  • 用来选择/评估模型、调超参数(学习率、层数、Dropout 比例等)。
  • 不能和训练数据混在一起,否则失去意义。
  • 比如把 60% 训练集里再拿出 20% ~ 50% 作为验证集。
# 从训练集中划分出验证集n_train=int(0.8*len(train_dataset))train_sub=torch.utils.data.Subset(train_dataset,range(0,n_train))val_sub=torch.utils.data.Subset(train_dataset,range(n_train,len(train_dataset)))# train_sub 用于训练,val_sub 用于调参

2.3 测试数据集(Test Set)

  • 只用一次的数据集,最终评估性能用。
  • 在任何情况下都不能用来调超参数,否则结果就是"作弊"。

类比:

  • 验证集 =模拟考试(可以反复做,用来调整复习方向)
  • 测试集 =正式高考(只考一次,考完就定论了)
  • 如果你拿高考真题来练手,那高考成绩就毫无意义了

为什么不能拿测试集调参?
因为一旦你根据测试集的结果调整了模型,测试集就不再"没见过"了,它的评估就失去了泛化意义——就像上过考场见过真题的人,成绩已经不反映真实水平。


三、K-折交叉验证(K-Fold Cross Validation)

数据不够多的时候,单纯拿出一部分做验证集太浪费。这时用K-折交叉验证

3.1 算法过程

算法:K-折交叉验证

  1. 把训练数据均匀分成 K 块
  2. For i = 1, 2, …, K:
    使用第 i 块作为验证数据集
    其余 K-1 块合并作为训练数据集
    训练模型,记录第 i 折的验证误差
  3. 报告 K 个验证误差的平均值

常用 K = 5 或 K = 10。

数据分割示意(K=5): 折1: [验证][训练][训练][训练][训练] 折2: [训练][验证][训练][训练][训练] 折3: [训练][训练][验证][训练][训练] 折4: [训练][训练][训练][验证][训练] 折5: [训练][训练][训练][训练][验证] ↑ 每折轮流当一次"验证集"

3.2 代码实现

importtorchfromtorch.utils.dataimportDataset,DataLoaderdefk_fold_cross_validation(dataset,k,train_fn):""" 对 dataset 做 K-折交叉验证。 train_fn: 训练函数,接收 (train_loader, val_loader),返回验证集误差 """total_len=len(dataset)fold_size=total_len//k val_losses=[]foriinrange(k):# 第 i 折的验证集索引val_indices=list(range(i*fold_size,(i+1)*fold_size))train_indices=list(set(range(total_len))-set(val_indices))train_sub=torch.utils.data.Subset(dataset,train_indices)val_sub=torch.utils.data.Subset(dataset,val_indices)train_loader=DataLoader(train_sub,batch_size=256,shuffle=True)val_loader=DataLoader(val_sub,batch_size=256,shuffle=False)# 每一折重新训练一个模型val_loss=train_fn(train_loader,val_loader)val_losses.append(val_loss)print(f"折{i+1}: 验证误差 ={val_loss:.6f}")# 报告 K 个验证误差的平均returnfloat(torch.tensor(val_losses).mean())K=5avg_loss=k_fold_cross_validation(train_dataset,K,my_train_fn)print(f"{K}-折平均验证误差 ={avg_loss:.6f}")

3.3 K=5 和 K=10 怎么选

选项优点缺点适用
K=5训练快,每折训练 80% 数据验证集稍大,偏差稍高数据中等,追求速度
K=10每折训练 90% 数据,更接近全量训练慢,成本高数据少且珍贵,追求精度

原则:数据越少、越珍贵,K 越大;数据很多时,直接固定一个验证集就行,不必交叉验证。


四、多项式函数拟合实战

我们用一个三次多项式做实验

4.1 生成带噪声的真实数据

importtorchimportmatplotlib.pyplotasplt n_train=100# 训练样本n_test=100# 测试样本# 真实函数:y = 1.2x - 3.4x^2 + 5.6x^3 + 5,再加一点噪声deftrue_fn(x):return1.2*x-3.4*x**2+5.6*x**3+5# 生成 x 和带噪声的 yfeatures=torch.randn(n_train+n_test,1)noise=torch.normal(0,0.5,size=features.shape)labels=true_fn(features)+noise# 用多项式特征扩展:把一列 x 变成 [x, x^2, x^3, ...]defpoly_features(x,degree):returntorch.cat([x**iforiinrange(1,degree+1)],1)# 我们用次数 3 的多项式特征去拟合(和真实函数次数一致)degree=3poly_features=poly_features(features,degree)

4.2 三个模型对比:欠拟合 / 合适 / 过拟合

我们用三种"模型容量"来拟合——次数不足、刚好、过高

模型用它拟合结果
线性(次数 1)三次数据欠拟合
三次多项式(次数 3)三次数据刚好合适
高次多项式(次数 e.g.7)三次数据过拟合
importtorchfromtorchimportnndeftrain_poly(degree,epochs=500,lr=1e-2):"""用指定次数的多项式模型去拟合三次函数的数据"""# 多项式特征features_poly=torch.cat([features**iforiinrange(1,degree+1)],1)n_train=100train_f=features_poly[:n_train]train_l=labels[:n_train]test_f=features_poly[n_train:]test_l=labels[n_train:]# 线性模型(对多项式特征做线性组合,等价于多项式拟合)net=nn.Linear(degree,1,bias=False)net.weight.data.normal_(0,0.01)loss=nn.MSELoss()trainer=torch.optim.SGD(net.parameters(),lr=lr)forepochinrange(epochs):trainer.zero_grad()l=loss(net(train_f),train_l)l.backward()trainer.step()train_loss=loss(net(train_f),train_l).item()test_loss=loss(net(test_f),test_l).item()returntrain_loss,test_lossfordegreein[1,3,7]:tr,te=train_poly(degree)print(f"次数{degree}: 训练误差{tr:.6f}| 泛化(测试)误差{te:.6f}")

五、过拟合与欠拟合

  • 欠拟合(Underfitting):模型过于简单,训练误差都无法降低。连课堂例题都做不对。
  • 过拟合(Overfitting):模型过于复杂,训练误差很低但泛化误差很高。把例题全背下来了,换个问法就废。

六、模型容量与 VC 维

7.1 模型容量(Model Capacity)

模型容量 = 模型拟合各种函数的能力。

  • 低容量模型:难以拟合训练数据(欠拟合)。
  • 高容量模型:可以记住所有训练数据(但往往过拟合)。

两种极端都不好。我们的目标是:容量刚好够拟合真实规律,但又不会大到把噪声也背下来。

7.2 VC 维(VC Dimension)

怎么定量衡量模型容量?一个常用指标就是VC 维

VC 维的定义:一个模型最多能"打散(shatter)"多少个数据点——即不管这些点的标签怎么排列(正/负),模型都能找到一个参数把这组点完全分开。

  • 二维平面上的线性分类器:VC 维 = 3。它最多能完全分开 3 个点(任意正负组合都能用一条直线分开),但 4 个点就不行了(比如 XOR 布局)。
  • 数据点越多、越分散,需要的 VC 维越大。

VC 维 = 3(二维直线的例子)
3 个点:无论正负怎么排,总能画一条直线分开 → 能打散,VC维至少3
4 个点:存在排法(如 XOR)无法用直线分开 → 打不散,所以 VC维 < 4

结论:二维线性分类器 VC 维 = 3

直觉:VC 维越大,模型越"能记",但也越容易过拟合。而在我们的代码里,多项式次数越高,等价于 VC 维越高。

八、触发过拟合的典型场景

场景为什么容易过拟合
训练数据太少模型把少量样本的噪声当成了规律
模型参数太多容量过大,拟合能力超过真实复杂度
训练轮数过多(epoch 过长)越训越"背"训练集,验证误差开始反弹
特征维度远大于样本数如基因数据、图像展平向量

八、应对过拟合的策略

8.1 降低模型容量

  • 少几层网络 / 少几个隐藏单元
  • 高次多项式 → 降到合适的次数(前面实验里次数 7 → 次数 3)

8.2 数据增强(Data Augmentation)

让数据"变多变花",降低对噪声的记忆:

# 图像随机翻转 + 旋转,制造更多样本transform=torchvision.transforms.Compose([torchvision.transforms.RandomHorizontalFlip(),torchvision.transforms.RandomRotation(10),torchvision.transforms.ToTensor(),])

8.3 正则化(Regularization)

给损失函数加上对"大权重"的惩罚,让模型不要太"张狂"。

L2 正则化(权重衰减 Weight Decay)

# 优化器自带 weight_decaytrainer=torch.optim.SGD(net.parameters(),lr=0.01,weight_decay=0.05)# weight_decay 让每一步更新时权重都往 0 缩一点,抑制过拟合

8.4 Dropout

训练时随机"失活"一部分神经元,防止过度依赖某些特征:

net=nn.Sequential(nn.Linear(784,256),nn.ReLU(),nn.Dropout(0.5),# 训练时随机丢弃 50% 神经元nn.Linear(256,128),nn.ReLU(),nn.Dropout(0.5),nn.Linear(128,10),)

8.5 早停(Early Stopping)

在验证误差开始回升的那一刻停止训练:

best_val=float('inf')forepochinrange(num_epochs):train_one_epoch()val_loss=evaluate(model,val_loader)ifval_loss<best_val:# 出现了更低的验证误差best_val=val_loss torch.save(model.state_dict(),'best.pt')# 存最好的# 如果连续 N 轮验证误差不再下降,就早停ifno_improve_count>patience:break


参考资料

  • 《动手学深度学习》第 4 章:模型选择、欠拟合和过拟合
  • Bishop, C. (2006). Pattern Recognition and Machine Learning.
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/2 6:56:23

Mac看图工具Pixea:极简高效,替代预览的轻量之选

简介&#xff1a;这是一份专门为苹果电脑用户准备的极简看图工具安装包&#xff0c;用来替代系统自带预览程序&#xff0c;解决日常浏览图片时启动慢、占用高、格式支持少的问题。工具主打轻量启动和低内存占用&#xff0c;原生兼容 WebP、HEIC、AVIF、PSD、RAW、SVG 等多种现代…

作者头像 李华
网站建设 2026/9/2 6:55:13

Claude Code 深度使用指南:从“会用“到“用好“的7个进阶心法

导语:Claude Code 不是更聪明的自动补全,它是一个能读文件、跑命令、改代码的AI代理。但很多人用了几周后才发现:真正决定效率的,不是提示词技巧,而是你给AI搭的"轨道"够不够稳。 一、先认清本质:Claude Code 到底是什么? 很多人第一次用 Claude Code 时,把…

作者头像 李华
网站建设 2026/9/2 6:53:27

AI时代开发者转型指南:从代码执行者到解决方案架构师

最近在技术社区看到不少关于AI未来发展的讨论&#xff0c;其中DeepMind创始人Demis Hassabis博士关于“旧世界”时间窗口的预言引发了广泛思考。作为一名长期关注技术演进的后端开发者&#xff0c;我深感这并非危言耸听&#xff0c;而是对技术浪潮即将重塑产业格局的深刻洞察。…

作者头像 李华
网站建设 2026/9/2 6:53:24

从《我的世界》史诗工程看模块化与自动化设计:以Unstable SMP为例

这次我们来看一个名为“Unstable SMP”的《我的世界》&#xff08;Minecraft&#xff09;服务器系列视频的熟肉&#xff08;中文字幕&#xff09;内容。这个项目本身并非一个软件工具或AI模型&#xff0c;而是一系列由创作者“Wemmbu”制作的、记录在“Unstable SMP”服务器上进…

作者头像 李华
网站建设 2026/9/2 6:50:47

网易我的世界函数指令完全指南:从零构建自动化系统

在网易版《我的世界》中&#xff0c;你是否曾羡慕过那些能一键建造宏伟建筑、瞬间召唤千军万马、或是实现复杂自动化流程的玩家&#xff1f;这些看似神奇的操作&#xff0c;背后往往离不开一个强大但常被新手忽视的功能——函数&#xff08;Function&#xff09;。与单条指令的…

作者头像 李华
网站建设 2026/9/2 6:50:29

基于PyTorch与LSTM的共享单车需求时序预测实战解析

简介&#xff1a;本资源是一个面向智能交通与城市数据科学领域的深度学习实践项目&#xff0c;专为具备Python与PyTorch基础的中高级学习者设计&#xff0c;解决共享单车多站点停放数量的时序预测难题。项目基于LSTM神经网络建模历史使用数据&#xff08;含时间戳、位置、车辆数…

作者头像 李华