模型的欠拟合和过拟合

x.1 前言

网上关于欠拟合和过拟合的解释很多,例如:

模型欠拟合,如你用一次函数拟合二次函数就是欠拟合。

模型过拟合,如你使用三次函数拟合二次函数就是过拟合。

两种情况均会导致模型泛化能力较差。

但是为了方便记忆,本作者是如此记的:

  1. 过拟合的研究远大于欠拟合。
  2. 欠拟合常常表现的是train loss和validation loss都很糟糕(如你的数据没有pair好,就是常说的这网络学不明白这数据啊),过拟合表现的是训练误差大于验证误差的情况。
  3. 轻微过拟合是绝对存在。

过拟合的程度通过训练误差和验证误差的差值来体现,通过经验得知:较小的过拟合表明现有数据可以支持一个更复杂的模型;较大的过拟合意味着模型相较于数据过于复杂。

x.2 当数据复杂度大于模型复杂度时引起的过拟合

通过增加模型层数等实现(是深度,而非广度)。

x.3 当模型复杂度大于数据复杂度时引起的过拟合

  • 正则化技术,如L2正则化又称暂退法,在loss中增加参数损失实现
  • 暂退法dropout
  • 增加数据集,可能由于你的现有数据并不能体现所有的特征;再如K折交叉验证
  • 增加BN层,原理是均值方差的引入类比噪音引入,通过数据增强/协变量偏移减少了过拟合

x.4 d2l 代码实现

代码如下:


'''

使用MLP进行多项式回归

我们能够发现由于 MLP 引入了 非线性的原因, 它能够拟合更多的表达式,例如次数大于1的幂函数等。

在下面我们将通过 引入参数次数多少来体现`过拟合`和`欠拟合`。

欠拟合的表现是training loss和validation loss都很糟糕;
过拟合的表现是training loss很好但是validation loss很糟糕且会越来越糟糕。(稍微过拟合并不一定是糟糕的,因为你能发现training loss总是很小于validation loss)

在数据量方面,数据量过少会引起过拟合,数据量过多会引起欠拟合。
过拟合:当数据量太少时,模型无法完成充分的训练,模型过度拟合用于训练的少量数据的信息,对测试数据效果不好,泛化能力差;
欠拟合:数据量很多,但是模型太简单没有充分利用数据信息模型不够准确。
'''

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


"""
(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]]

print(features[:2], poly_features[:2, :], labels[:2])


"""
(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]


"""
(3) 定义训练函数

"""
from animator import Animator
from test4_1 import train_epoch_ch3
from torch.utils import data

def load_array(data_arrays, batch_size, is_train=True):
    """Construct a PyTorch data iterator.

    Defined in :numref:`sec_linear_concise`"""
    dataset = data.TensorDataset(*data_arrays)
    return data.DataLoader(dataset, batch_size, shuffle=is_train)

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 = Animator(xlabel='epoch', ylabel='loss', yscale='log',
        xlim=[1, num_epochs], ylim=[1e-3, 1e2],
        legend=['train', 'test'])
    for epoch in range(num_epochs):
        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())


# 三阶多项式: 正常拟合
# 从多项式特征中选择前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:])


# 一阶多项式: 欠拟合
# 从多项式特征中选择前2个维度,即1和x
train(poly_features[:n_train, :2], poly_features[n_train:, :2],
    labels[:n_train], labels[n_train:])


# ⾼阶多项式函数拟合(过拟合)
# 从多项式特征中选取所有维度
train(poly_features[:n_train, :], poly_features[n_train:, :],
    labels[:n_train], labels[n_train:], num_epochs=1500)
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值