Task03 过拟合、欠拟合及其解决方案

1 模型选择、过拟合和欠拟合

阅前小思考:
在训练模型的时候,如果改变了实验中的模型结构和超参数的时候,会出现:当模型在训练数据集上更加准确时,它在测试数据集却不一定更加准确。这是为什么呢?

1.1 训练误差和泛化误差

在解释上述现象之前,我们需要区分训练误差(training error)和泛化误差(generalization error)。通俗来讲,前者指模型在训练数据集上表现出的误差,后者指模型在任意一个测试数据样本上表现出的误差的期望,并常常通过测试数据集上的误差来近似。计算训练误差和泛化误差可以使用之前介绍过的损失函数,例如线性回归用到的平方损失函数和softmax回归用到的交叉熵损失函数。

机器学习模型应关注降低泛化误差

1.2 模型选择

在机器学习中,我们通常需要评估若⼲候选模型的表现并从中选择模型。这⼀过程称为模型选择
(model selection)。可供选择的候选模型可以是有着不同超参数的同类模型。以多层感知机为例, 我们可以选择隐藏层的个数,以及每个隐藏层中隐藏单元个数和激活函数。为了得到有效的模 型,我们通常要在模型选择上花费⼀番功夫。下⾯,我们来描述模型选择中经常使⽤的验证数据 集(validation data set)。

1.2.1 验证数据集

从严格意义上讲,测试集只能在所有超参数和模型参数选定后使用一次。不可以使用测试数据选择模型,如调参。由于无法从训练误差估计泛化误差,因此也不应只依赖训练数据选择模型。鉴于此,我们可以预留一部分在训练数据集和测试数据集以外的数据来进行模型选择。这部分数据被称为验证数据集,简称验证集(validation set)。

1.2.2 K折交叉验证

由于验证数据集不参与模型训练,当训练数据不够用时,预留大量的验证数据显得太奢侈。一种改善的方法是K折交叉验证(K-fold cross-validation)。在K折交叉验证中,我们把原始训练数据集分割成K个不重合的子数据集,然后我们做K次模型训练和验证。每一次,我们使用一个子数据集验证模型,并使用其他K-1个子数据集来训练模型。在这K次训练和验证中,每次用来验证模型的子数据集都不同。最后,我们对这K次训练误差和验证误差分别求平均。

1.3 欠拟合和过拟合

模型训练中经常出现的两类典型问题:

一类是模型无法得到较低的训练误差,我们将这一现象称作欠拟合(underfitting);
另一类是模型的训练误差远小于它在测试数据集上的误差,我们称该现象为过拟合(overfitting)。

在实践中,我们要尽可能同时应对欠拟合和过拟合。
虽然有很多因素可能导致这两种拟合问题,在这里我们重点讨论两个因素:模型复杂度训练数据集大小

1.3.1 模型复杂度

模型的复杂度,以多项式函数拟合为例;
给定训练数据集,模型复杂度和误差之间的关系:

模型复杂度对⽋拟合和过拟合的影响

1.3.2 训练数据集大小

影响欠拟合和过拟合的另一个重要因素是训练数据集的大小。
一般来说,如果训练数据集中样本数过少,特别是比模型参数数量(按元素计)更少时,过拟合更容易发生。此外,泛化误差不会随训练数据集里样本数量增加而增大。因此,在计算资源允许的范围之内,我们通常希望训练数据集大一些,特别是在模型复杂度较高时,例如层数较多的深度学习模型。

不同模型以及训练数据集大小的几种拟合情况如下:
正常拟合:
3阶多项式函数拟合(正常)
欠拟合:
线性函数拟合(欠拟合)
过拟合:
训练样本不足(过拟合)
小结:应对过拟合和欠拟合的产生,应选择复杂度合适的模型以及避免使用过少的训练样本

2 应对过拟合的方法

由于欠拟合可以通过加大训练样本和训练次数以及通过设计较深的模型来解决,以下将详细讲过拟合的解决方法:
方法一,通过权重衰减来减轻过拟合
方法二,通过丢弃法来应对过拟合问题

2.1 权重衰减

权重衰减等价于 L2 范数正则化(regularization)。正则化通过为模型损失函数添加惩罚项使得 学出的模型参数值较小,是应对过拟合的常⽤⼿段。

2.1.1 L2范数正则化(regularization)

L2 范数正则化在模型原损失函数基础上添加 L2 范数惩罚项,从而得到训练所需要最小化的函数。 L2 范数惩罚项指的是模型权重参数每个元素的平方和与一个正的常数的乘积。以线性回归中的线性回归损失函数为例

ℓ(w1,w2,b)=1n∑i=1n12(x(i)1w1+x(i)2w2+b−y(i))2

其中 w1,w2 是权重参数, b 是偏差参数,样本 i 的输入为 x(i)1,x(i)2 ,标签为 y(i) ,样本数为 n 。将权重参数用向量 w=[w1,w2] 表示,带有 L2 范数惩罚项的新损失函数为

ℓ(w1,w2,b)+λ2n|w|2,

其中超参数 λ>0 。当权重参数均为0时,惩罚项最小。当 λ 较大时,惩罚项在损失函数中的比重较大,这通常会使学到的权重参数的元素较接近0。当 λ 设为0时,惩罚项完全不起作用。上式中 L2 范数平方 |w|2 展开后得到 w21+w22 。 有了 L2 范数惩罚项后,在小批量随机梯度下降中,我们将线性回归一节中权重 w1 和 w2 的迭代方式更改为

w1w2←(1−ηλ|B|)w1−η|B|∑i∈Bx(i)1(x(i)1w1+x(i)2w2+b−y(i)),←(1−ηλ|B|)w2−η|B|∑i∈Bx(i)2(x(i)1w1+x(i)2w2+b−y(i)).

可见, L2 范数正则化令权重 w1 和 w2 先自乘小于1的数,再减去不含惩罚项的梯度。因此, L2 范数正则化又叫权重衰减。权重衰减通过惩罚绝对值较大的模型参数为需要学习的模型增加了限制,这可能对过拟合有效。

2.1.2 权重衰减的 pytorch实现

%matplotlib inline
import torch
import torch.nn as nn
import numpy as np
import sys
sys.path.append(".")
import d2lzh_pytorch as d2l

def fit_and_plot_pytorch(wd):
    # 对权重参数衰减。权重名称一般是以weight结尾
    net = nn.Linear(num_inputs, 1)
    nn.init.normal_(net.weight, mean=0, std=1)
    nn.init.normal_(net.bias, mean=0, std=1)
    optimizer_w = torch.optim.SGD(params=[net.weight], lr=lr, weight_decay=wd) # 对权重参数衰减
    optimizer_b = torch.optim.SGD(params=[net.bias], lr=lr)  # 不对偏差参数衰减
    
    train_ls, test_ls = [], []
    for _ in range(num_epochs):
        for X, y in train_iter:
            l = loss(net(X), y).mean()
            optimizer_w.zero_grad()
            optimizer_b.zero_grad()
            
            l.backward()
            
            # 对两个optimizer实例分别调用step函数,从而分别更新权重和偏差
            optimizer_w.step()
            optimizer_b.step()
        train_ls.append(loss(net(train_features), train_labels).mean().item())
        test_ls.append(loss(net(test_features), test_labels).mean().item())
    d2l.semilogy(range(1, num_epochs + 1), train_ls, 'epochs', 'loss',
                 range(1, num_epochs + 1), test_ls, ['train', 'test'])
    print('L2 norm of w:', net.weight.data.norm().item())
fit_and_plot_pytorch(0)

未加权重衰减:
结果为:L2 norm of w: 13.361410140991211

fit_and_plot_pytorch(3)

添加权重衰减结果:
L2 norm of w: 0.051789578050374985

2.2 丢弃法

多层感知机中神经网络图描述了一个单隐藏层的多层感知机。其中输入个数为4,隐藏单元个数为5,且隐藏单元 hi ( i=1,…,5 )的计算表达式为

hi=ϕ(x1w1i+x2w2i+x3w3i+x4w4i+bi)

这里 ϕ 是激活函数, x1,…,x4 是输入,隐藏单元 i 的权重参数为 w1i,…,w4i ,偏差参数为 bi 。当对该隐藏层使用丢弃法时,该层的隐藏单元将有一定概率被丢弃掉。设丢弃概率为 p ,那么有 p 的概率 hi 会被清零,有 1−p 的概率 hi 会除以 1−p 做拉伸。丢弃概率是丢弃法的超参数。具体来说,设随机变量 ξi 为0和1的概率分别为 p 和 1−p 。使用丢弃法时我们计算新的隐藏单元 h′i
h′i=ξi1−phi

由于 E(ξi)=1−p ,因此

E(h′i)=E(ξi)1−phi=hi

即丢弃法不改变其输入的期望值。让我们对之前多层感知机的神经网络中的隐藏层使用丢弃法,一种可能的结果如图所示,其中 h2 和 h5 被清零。这时输出值的计算不再依赖 h2 和 h5 ,在反向传播时,与这两个隐藏单元相关的权重的梯度均为0。由于在训练中隐藏层神经元的丢弃是随机的,即 h1,…,h5 都有可能被清零,输出层的计算无法过度依赖 h1,…,h5 中的任一个,从而在训练模型时起到正则化的作用,并可以用来应对过拟合。在测试模型时,我们为了拿到更加确定性的结果,一般不使用丢弃法
隐藏层使用了丢弃法的感知机

2.2.1 丢弃法的pytorch实现

%matplotlib inline
import torch
import torch.nn as nn
import numpy as np
import sys
sys.path.append("/home/kesci/input")
import d2lzh1981 as d2l

print(torch.__version__)
        d2l.FlattenLayer(),
        nn.Linear(num_inputs, num_hiddens1),
        nn.ReLU(),
        nn.Dropout(drop_prob1),
        nn.Linear(num_hiddens1, num_hiddens2), 
        nn.ReLU(),
        nn.Dropout(drop_prob2),
        nn.Linear(num_hiddens2, 10)
        )

for param in net.parameters():
    nn.init.normal_(param, mean=0, std=0.01)
optimizer = torch.optim.SGD(net.parameters(), lr=0.5)
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, batch_size, None, None, optimizer)

结果:
epoch 1, loss 0.0046, train acc 0.553, test acc 0.736
epoch 2, loss 0.0023, train acc 0.785, test acc 0.803
epoch 3, loss 0.0019, train acc 0.818, test acc 0.756
epoch 4, loss 0.0018, train acc 0.835, test acc 0.829
epoch 5, loss 0.0016, train acc 0.848, test acc 0.851

train_ch3 代码


def train_ch3(net,train_iter,test_iter,loss,num_epochs,batch_size,params=None,lr=None,optimizer =None):
    for epoch in range(num_epochs):
        train_l_sum,train_acc_sum,n = 0.0,0.0,0
        for x,y in train_iter:
            y_hat = net(x)
            l = loss(y_hat,y).sum()

            #梯度清零
            if optimizer is not None:
                optimizer.zero_grad()
            elif params is not None and params[0].grad is not None:
                for param in params:
                    param.grad.data.zero_()

            l.backward()
            #梯度优化
            if optimizer is None:
                sgd(params,lr,batch_size)
            else:
                optimizer.step()

            train_l_sum += l.item()
            train_acc_sum += (y_hat.argmax(dim=1)==y).sum().item()
            n += y.shape[0]

        test_acc = evaluate_accuracy(test_iter,net)
        print('epoch:{} ,loss:{:.4f}, train_acc:{:.3f}, test_acc:{:.3f}'.format(epoch+1,train_l_sum/n , train_acc_sum/n,test_acc))

总结

欠拟合现象:模型无法达到一个较低的误差
过拟合现象:训练误差较低但是泛化误差依然较高,二者相差较大

过拟合的的应对方法:丢弃法和权重衰减
欠拟合的应对方法:增加模型复杂度,增加训练次数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值