当前位置: 首页 > news >正文

深度学习4.4笔记

《动手学深度学习》-4.4-笔记

验证数据集:通常是从训练集中划分出来的一部分数据,不要和训练数据混在一起,评估模型好坏的数据集

测试数据集:只用一次的数据集

k-折交叉验证(k-Fold Cross-Validation)是一种统计方法,用于评估和比较机器学习模型的性能。它通过将数据集分成k个子集(或“折”)来实现,每个子集都作为一次测试集,而剩余的k-1个子集则作为训练集。这个过程会重复k次,每次选择不同的子集作为测试集,最终将k次测试结果的平均值作为模型的性能评估。常用k=5/10,在没有足够多数据使用时。

总结:

训练数据集:训练模型参数

验证数据集:选择模型超参数

非大型数据集上通常使用k-折交叉验证

欠拟合(Underfitting)

欠拟合是指模型对训练数据的拟合程度不够,无法捕捉到数据中的规律和模式。换句话说,模型过于简单,无法很好地描述数据的特征。

过拟合(Overfitting)

过拟合是指模型对训练数据拟合得过于完美,以至于模型在训练数据上表现很好,但在新的、未见过的数据上表现很差。换句话说,模型过度学习了训练数据中的噪声和细节,而无法泛化到新的数据。

模型容量的定义

表示容量:模型的最大拟合能力,即通过调节参数,模型能够表示的函数族

  1. 模型参数数量:参数越多,模型容量通常越高。

  2. 模型结构复杂度:例如,神经网络的层数和每层的神经元数量。

  3. 数据复杂度:数据的复杂度(如样本数量、特征数量)也会影响模型容量的选择

模型容量与过拟合、欠拟合的关系

  • 容量不足:模型无法很好地拟合训练数据,导致欠拟合。

  • 容量过高:模型可能会过度拟合训练数据中的噪声,导致过拟合

总结;

模型容量需要匹配数据复杂度,否则可能过拟合或欠拟合

统计机器学习提供数学工具来衡量模型复杂度 

代码部分:
 

import math
import numpy as np
import torch
from torch import nn
from d2l import torch as d2l

引入需要的库

max_degree = 20  # 多项式的最大阶数
n_train, n_test = 100, 100  # 训练和测试数据集大小
true_w = np.zeros(max_degree)  # 分配大量的空间
true_w[0:4] = np.array([5, 1.2, -3.4, 5.6])

features = np.random.normal(size=(n_train + n_test, 1))#随机生成200个样本点(服从标准正态分布的x值)。
np.random.shuffle(features)#打乱样本顺序。
poly_features = np.power(features, np.arange(max_degree).reshape(1, -1))
for i in range(max_degree):
    poly_features[:, i] /= math.gamma(i + 1)  # gamma(n)=(n-1)!
# labels的维度:(n_train+n_test,)
labels = np.dot(poly_features, true_w)
labels += np.random.normal(scale=0.1, size=labels.shape)#加上噪音

分析:

生成一个多项式回归的训练/测试数据集。也就是说,我们在模拟一个“隐藏函数”,然后加一点噪声,生成一些数据,来用于模型训练。

多项式阶数:我们打算生成最多20阶的多项式数据(比如 1, x, x², ..., x¹⁹)。

true_w = np.zeros(max_degree)  # 创建一个长度为20的权重数组,初始值全是0
true_w[0:4] = np.array([5, 1.2, -3.4, 5.6])

这一步设置了我们想要“模拟”的真实多项式模型的参数。它实际上模拟了一个三阶多项式:

y = 5 + 1.2x - 3.4x² + 5.6x³

其余的高阶项(x⁴ ~ x¹⁹)的系数为0。

poly_features = np.power(features, np.arange(max_degree).reshape(1, -1))


这一步是关键!构造一个 多项式特征矩阵。

假设 features = [[x1], [x2], ..., [x200]],
我们把它转化为:
[[1, x1, x1², x1³, ..., x1^19],
 [1, x2, x2², x2³, ..., x2^19],
 ...
]

for i in range(max_degree):
    poly_features[:, i] /= math.gamma(i + 1)  # gamma(n)=(n-1)!

这一步是做多项式特征的缩放处理,用的是数学中的Gamma函数

举例:

  • gamma(1) = 0! = 1

  • gamma(2) = 1! = 1

  • gamma(3) = 2! = 2

  • gamma(4) = 3! = 6 ...

所以这是在做归一化的处理,让高阶项不会变得太大

labels = np.dot(poly_features, true_w)

这一步是最核心的:根据我们设定的权重 true_w 计算标签 y 值

可以理解为:
对每一行的多项式特征向量和权重向量做内积(点乘),
也就是:

  • 所以最终的标签是:
    真实标签 + 小范围扰动

# NumPy ndarray转换为tensor
true_w, features, poly_features, labels = [torch.tensor(x, dtype=
    torch.float32) for x in [true_w, features, poly_features, labels]]
#这句用列表推导式,把之前的 NumPy 数组全部 转换成 PyTorch 的 tensor(张量)格式,这样就可以用 PyTorch 来训练模型啦!
features[:2], poly_features[:2, :], labels[:2]#这个不是赋值语句,而是查看前两个样本的输入特征、多项式特征和标签的值,

已经把 NumPy 的数组转成了 PyTorch 的张量

def evaluate_loss(net, data_iter, loss):  #@save
    """评估给定数据集上模型的损失"""
    metric = d2l.Accumulator(2)  # 损失的总和,样本数量
    for X, y in data_iter:
        out = net(X)#前向传播 + 计算损失 让模型对输入 X 做预测,得到输出 out
        y = y.reshape(out.shape)
        l = loss(out, y)#计算预测结果和真实值之间的损失
        metric.add(l.sum(), l.numel())#计算预测结果和真实值之间的损失
    return metric[0] / metric[1]#计算预测结果和真实值之间的损失

评估模型在某个数据集(data_iter)上的平均损失

分析:

  • net: 模型(PyTorch 中定义的神经网络)

  • data_iter: 数据迭代器(通常是训练集或测试集的 DataLoader

  • loss: 损失函数(比如 nn.MSELoss()

def train(train_features, test_features, train_labels, test_labels,
          num_epochs=400):#定义了一个训练函数
    loss = nn.MSELoss(reduction='none')#均方误差损失函数(MSE),但不求平均,保留每个样本的损失值。
    input_shape = train_features.shape[-1]
    # 不设置偏置,因为我们已经在多项式中实现了它
    net = nn.Sequential(nn.Linear(input_shape, 1, bias=False))
    batch_size = min(10, train_labels.shape[0])
    train_iter = d2l.load_array((train_features, train_labels.reshape(-1,1)),
                                batch_size)#把训练和测试数据打包成 DataLoader,方便模型一批一批训练
    test_iter = d2l.load_array((test_features, test_labels.reshape(-1,1)),
                               batch_size, is_train=False)
    trainer = torch.optim.SGD(net.parameters(), lr=0.01)#使用随机梯度下降(SGD)优化模型参数,学习率为 0.01
    animator = d2l.Animator(xlabel='epoch', ylabel='loss', yscale='log',
                            xlim=[1, num_epochs], ylim=[1e-3, 1e2],
                            legend=['train', 'test'])#用 D2L 里的 Animator 动态绘图类,记录训练过程的 loss 曲线
    for epoch in range(num_epochs):#训练一个 epoch,用的是 D2L 中封装好的 train_epoch_ch3(每轮完整训练一遍所有 batch)
        d2l.train_epoch_ch3(net, train_iter, loss, trainer)
        if epoch == 0 or (epoch + 1) % 20 == 0:#每隔20轮(或第1轮),就评估一下训练集和测试集上的平均损失,然后加到图上
            animator.add(epoch + 1, (evaluate_loss(net, train_iter, loss),
                                     evaluate_loss(net, test_iter, loss)))
    print('weight:', net[0].weight.data.numpy())  #打印最终训练得到的权重

PyTorch + 多项式特征训练一个线性模型,并可视化训练过程的

  • 用线性模型拟合你设计的多项式数据(多阶特征)

  • 使用 MSELoss + SGD 训练

  • 可视化训练和测试集上的损失变化

  • 打印最终训练好的模型参数,看看学得准不准

按书中的报错然后,你调用了 l.backward() 来反向传播,但这个 l 是一个 不需要梯度 的张量(requires_grad=False),所以无法反向传播!

还是之前的做法:

loss = nn.MSELoss(reduction='none')返回的是一个 每个样本的损失 的张量,而不是所有样本损失的平均或总和。

看看 train_epoch_ch3 的定义),它里面可能是直接用了 l = loss(y_hat, y),然后 l.backward()

修改后

正常:

欠拟合

如果用不同复杂度的模型来拟合这个函数,表现会怎样?

# 只用 1 和 x 两项(线性模型)
train(poly_features[:n_train, :2], poly_features[n_train:, :2],
      labels[:n_train], labels[n_train:])

这意味着你在训练一个线性模型

这个模型完全忽略了二阶项 和三阶项 ,所以它根本学不出原来的复杂模式

结果就是:

  • 训练损失很高

  • 测试损失也高

  • 模型欠拟合:学得太简单,跟不上真实的非线性函数

# 使用与真实模型相同的特征阶数
train(poly_features[:n_train, :4], poly_features[n_train:, :4],
      labels[:n_train], labels[n_train:])

 这次你用了前4项:

注意:你训练的时候也会拟合这几个特征,也就是:

而我们真实函数 y = 5 + 1.2x - 3.4x^2 + 5.6x^3,刚好就是3阶多项式

  • 训练损失下降得更快

  • 最终损失更低

  • 模型可以很好地拟合数据,不欠拟合也不过拟合

 

poly_features[:, :] 表示使用 所有20阶的多项式特征,也就是:

  • 训练了一个 20维输入的线性模型

  • 训练次数设为 1500 轮(比前面更多)

  • 但现在用一个 包含20阶的模型 去拟合这些数据,虽然原函数只有3阶,后面17个高阶项都是“多余的”。

  • 训练集表现很好(损失很低),但在测试集上 泛化能力变差

http://www.dtcms.com/a/98693.html

相关文章:

  • 游戏AI实现-GOAP
  • 【华为OD技术面试真题 - 技术面】- Java面试题(16)
  • 【免费】中国电子学会2025年03月份青少年软件编程Python等级考试试卷六级真题(含答案)
  • 解决obsidian无法加载社区的解决的尝试
  • DeepSeek-R1国产大模型实战:从私有化部署到内网穿透远程使用全攻略
  • 【Java】Java核心知识点与相应面试技巧(七)——类与对象(二)
  • c++游戏开发第一期
  • 【大模型基础_毛玉仁】5.3 附加参数法:T-Patcher
  • 《新凯来 :中国半导体设备制造行业从“跟随者”向“挑战者”的角色转变》
  • 【蓝桥杯速成】| 15.完全背包
  • docker pull lss233/one-api:latest 在哪里运行,作用是什么
  • 【VUE】day08黑马头条小项目
  • (三)点亮LED
  • ngx_http_core_merge_srv_conf
  • 外观模式(Facade Pattern):复杂系统的“统一入口”
  • Qwen-0.5b linux部署
  • 订单超时自动关闭:电商系统的技术难题与解决方案
  • 常用登录Linux系统的方法以及操作指南
  • Spring Cloud Gateway详细介绍简单案例
  • Js 主线程和异步队列哪个先执行
  • Yolo系列之Yolov4的改进及网络框架
  • Java入门知识总结——章节(二)
  • bluecode-20240913_1_数据解码
  • 【Kafka】分布式消息队列的核心奥秘
  • 3.29前端模拟面试
  • 【区块链安全 | 第八篇】多签机制及恶意多签
  • org.apache.maven.surefire:surefire-junit-platform:jar:2.22.2 Maven打包失败
  • 逗万DareWorks|创意重构书写美学,引领新潮无界的文创革命
  • 什么是贴源库
  • unique_ptr的详细介绍