• 04.4. 模型选择、欠拟合和过拟合


    04.4. 模型选择、欠拟合和过拟合

    4.4.1. 训练误差和泛化误差

    • 训练误差(training error)
      是指, 模型在训练数据集上计算得到的误差。

    • 泛化误差(generalization error)
      是指, 模型应用在同样从原始样本的分布中抽取的无限多数据样本时,模型误差的期望

    4.4.1.1. 统计学习理论

    独立同分布假设(i.i.d. assumption)
    假设训练数据和测试数据都是从相同的分布中独立提取

    4.4.1.2. 模型复杂性

    统计学家认为,能够轻松解释任意事实的模型是复杂的, 而表达能力有限但仍能很好地解释数据的模型可能更有现实用途。

    介绍几个倾向于影响模型泛化的因素:

    • 可调整参数的数量。当可调整参数的数量(有时称为自由度)很大时,模型往往更容易过拟合

    • 参数采用的值。当权重的取值范围较大时,模型可能更容易过拟合。

    • 训练样本的数量。即使你的模型很简单,也很容易过拟合只包含一两个样本的数据集。而过拟合一个有数百万个样本的数据集则需要一个极其灵活的模型。

    4.4.2. 模型选择

    通常在评估几个候选模型后选择最终的模型。 这个过程叫做模型选择。
    例如,训练多层感知机模型时,我们可能希望比较具有 不同数量的隐藏层、不同数量的隐藏单元以及不同的激活函数组合的模型。 为了确定候选模型中的最佳模型,我们通常会使用验证集。

    4.4.2.1. 验证集

    验证数据集(validation dataset), 也叫验证集(validation set)

    数据分成三份, 训练、验证和测试数据集之外

    4.4.2.2. 折交叉验证
    • 原始训练数据被分成K个不重叠的子集。
    • 然后执行K次模型训练和验证,每次在K-1 个子集上进行训练, 并在剩余的一个子集(在该轮中没有用于训练的子集)上进行验证。
    • 最后,通过对K次实验的结果取平均来估计训练和验证误

    4.4.3. 欠拟合还是过拟合

    • 欠拟合(underfitting)

    • 过拟合(overfitting)

    4.4.3.1. 模型复杂性

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7qpTSROE-1662646724842)(https://zh.d2l.ai/_images/capacity-vs-error.svg)]

    4.4.3.2. 数据集大小

    4.4.4. 多项式回归

    import math
    import numpy as np
    import torch
    from torch import nn
    from d2l import torch as d2l
    
    • 1
    • 2
    • 3
    • 4
    • 5
    4.4.4.1. 生成数据集
    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))
    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)
    
    
    # NumPy ndarray转换为tensor
    true_w, features, poly_features, labels = [torch.tensor(x, dtype=
        torch.float32) for x in [true_w, features, poly_features, labels]]
    
    features[:2], poly_features[:2, :], labels[:2]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    4.4.4.2. 对模型进行训练和测试
    def evaluate_loss(net, data_iter, loss):  #@save
        """评估给定数据集上模型的损失"""
        metric = d2l.Accumulator(2)  # 损失的总和,样本数量
        for X, y in data_iter:
            out = net(X)
            y = y.reshape(out.shape)
            l = loss(out, y)
            metric.add(l.sum(), l.numel())
        return metric[0] / metric[1]
        
        
    def train(train_features, test_features, train_labels, test_labels,
              num_epochs=400):
        loss = nn.MSELoss(reduction='none')
        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)
        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)
        animator = d2l.Animator(xlabel='epoch', ylabel='loss', yscale='log',
                                xlim=[1, num_epochs], ylim=[1e-3, 1e2],
                                legend=['train', 'test'])
        for epoch in range(num_epochs):
            d2l.train_epoch_ch3(net, train_iter, loss, trainer)
            if epoch == 0 or (epoch + 1) % 20 == 0:
                animator.add(epoch + 1, (evaluate_loss(net, train_iter, loss),
                                         evaluate_loss(net, test_iter, loss)))
        print('weight:', net[0].weight.data.numpy())
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    4.4.4.3. 三阶多项式函数拟合(正常)

    我们将首先使用三阶多项式函数,它与数据生成函数的阶数相同。 结果表明,该模型能有效降低训练损失和测试损失

    # 从多项式特征中选择前4个维度,即1,x,x^2/2!,x^3/3!
    train(poly_features[:n_train, :4], poly_features[n_train:, :4],
          labels[:n_train], labels[n_train:])
    
    • 1
    • 2
    • 3
    4.4.4.4. 线性函数拟合(欠拟合)

    让我们再看看线性函数拟合,减少该模型的训练损失相对困难。 在最后一个迭代周期完成后,训练损失仍然很高。 当用来拟合非线性模式(如这里的三阶多项式函数)时,线性模型容易欠拟合。

    # 从多项式特征中选择前2个维度,即1和x
    train(poly_features[:n_train, :2], poly_features[n_train:, :2],
          labels[:n_train], labels[n_train:])
    
    • 1
    • 2
    • 3
    4.4.4.5. 高阶多项式函数拟合(过拟合)

    现在,让我们尝试使用一个阶数过高的多项式来训练模型。 在这种情况下,没有足够的数据用于学到高阶系数应该具有接近于零的值。 因此,这个过于复杂的模型会轻易受到训练数据中噪声的影响。 虽然训练损失可以有效地降低,但测试损失仍然很高。 结果表明,复杂模型对数据造成了过拟合。

    # 从多项式特征中选取所有维度
    train(poly_features[:n_train, :], poly_features[n_train:, :],
          labels[:n_train], labels[n_train:], num_epochs=1500)
    
    • 1
    • 2
    • 3

    4.4.5. 小结¶

    • 欠拟合是指模型无法继续减少训练误差。过拟合是指训练误差远小于验证误差。

    • 由于不能基于训练误差来估计泛化误差,因 此简单地最小化训练误差并不一定意味着泛化误差的减小。机器学习模型需要注意防止过拟合,即防止泛化误差过大。

    • 验证集可以用于模型选择,但不能过于随意地使用它。

    • 我们应该选择一个复杂度适当的模型,避免使用数量不足的训练样本

  • 相关阅读:
    JAVA面向对象三大特征
    uniapp 学习笔记十七 vuex的模块化拆分,state和mutations结合实践
    Perl动态建立ZIP压缩文件
    Python中的进程池及进程池锁:multiprocessing.Pool及multiprocessing.Manager().Lock()
    轻松管理Web服务器:Linux Apache技巧与技术
    使用spring-boot-maven-plugin插件打包SpringBoot应用
    做私域,转化成交的秘诀!
    第145篇:js设计模式注册模式及相应实践
    Windows 事件日志监控
    《快速掌握QML》第一章 初识QML
  • 原文地址:https://blog.csdn.net/weixin_45063703/article/details/126773346