31,32,33_过拟合、欠拟合的概念、L2正则化,Pytorch过拟合&欠拟合,交叉验证-Train-Val-Test划分,划分训练集和测试集,K-fold,Regularization

1.26.过拟合、欠拟合及其解决方案
1.26.1.过拟合、欠拟合的概念
1.26.1.1.训练误差和泛化误差
1.26.1.2.验证数据集与K-fold验证
1.26.1.3.过拟合和欠拟合
1.26.1.4.导致过拟合和欠拟合的关键因素
1.26.2.L2正则化
1.26.3.drop out
1.27.Pytorch过拟合&欠拟合
1.27.1.理想化情况
1.27.1.1.场景一:线性模型—房价预测
1.27.1.2.场景二:非线性模型—GPA
1.27.2.真实情况
1.27.3.模型学习能力
1.27.4.欠拟合与过拟合
1.28.Train-Val-Test划分
1.28.1.1.How to detect(如何检测)
1.28.1.2.Splitting(划分训练集和测试集)
1.28.1.3.for example
1.28.1.4.test while train
1.28.1.5.train test trade-off
1.28.1.6.for others judge
1.28.1.7.train-val-test
1.28.1.8.K-fold cross-validation
1.28.1.9.k-fold cross validation

1.26.过拟合、欠拟合及其解决方案

1.26.1.过拟合、欠拟合的概念

1.26.1.1.训练误差和泛化误差

前者指模型在训练数据集上表现出的误差,后者模型在任意一个测试数据样本上表现的误差的期望,并常常通过测试数据集上的误差来近似。

1.26.1.2.验证数据集与K-fold验证

预留一部分在训练数据集和测试数据集以外的数据来进行模型选择,这部分数据被称为验证数据集,简称验证集(validation set)。在K折交叉验证中,把原始训练数据集分割成K个不重合的子数据集,然后做K次模型训练和验证。每一次,使用一个子数据集验证模型,并使用其他K-1个子数据集来训练模型。在这K次训练和验证中,每次用来验证模型的子数据集都不同。最后,对这K次训练误差和验证误差分别求平均。

1.26.1.3.过拟合和欠拟合

欠拟合:模型无法得到较低的训练误差。
过拟合:模型的训练误差远小于它在测试数据集上的误差。

1.26.1.4.导致过拟合和欠拟合的关键因素

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

1.26.2.L2正则化

在这里插入图片描述

1.26.3.drop out

在这里插入图片描述

1.27.Pytorch过拟合&欠拟合

1.27.1.理想化情况

1.27.1.1.场景一:线性模型—房价预测

在这里插入图片描述

横坐标是房屋面积,纵坐标是房屋价格。可以看出这两者呈现线性关系。

1.27.1.2.场景二:非线性模型—GPA

在这里插入图片描述
老师给学生打分时,往往大部分学生一般的成绩,成绩差或成绩好的则占少数。因此总体上趋近于高斯模型,呈现非线性关系。

1.27.2.真实情况

在这里插入图片描述
从以上两个例子中,我们都已经提前知道了问题中的具体模型,不知道的只有模型的参数,如期望,方差。然而在现实生活中,大部分要解决的问题是不知道具体模型的。如手写数字识别,我们既不知道它的模型,也不知道里面需要包含什么参数。那么如何解决这个问题呢?
在这里插入图片描述
另外我们还要考虑另一个因素的影响:噪声。并不是所有的数据集都是严格按照模型的。如房价关系的线性模型,房价与面积在呈线性关系的同时往往有些许误差。我们可以认为误差服从高斯分布,因此为了尽量减少误差的影响,我们需要大量的数据集来训练模型。同时,如果数据集太少,可能被误认为不满足线性关系,而采用其他关系进行预测。

1.27.3.模型学习能力

在这里插入图片描述
我们对利用数据训练模型时,往往会选择不同的模型,可能一次方,二次方。。。到n次方,不同类型,不同次方的模型,次方越高,模型波动越大越复杂。
那么如何衡量不同模型的学习能力?
在这里插入图片描述
可以看出,只有一个参数的常数函数,学习能力最差,输出的是固定值,随着次方增加,参数越来越多,学习能力越来越强。
在这里插入图片描述
最近的深度学习网络层次越来越深,参数也越来越大,也从侧面反映出模型的model capacity越来越强。

1.27.4.欠拟合与过拟合

接下来我们观察两种情况:
情况一:欠拟合
在这里插入图片描述
我们用的模型复杂度小于真实数据模型的复杂度,就好像利用线性模型去预测非线性模型的GPA函数。这种情况会造成模型的表达能力不够,无法表达真实数据模型,这种情况叫做欠拟合。
在这里插入图片描述
再举一个WGAN的例子,早期的WGAN对模型加了许多约束,导致生成的数据表达能力不够强,上面三幅图无法像下面三幅图一样表达许多精美的图片。
那么欠拟合如何体现出来,我们如何发现模型出现欠拟合?
在这里插入图片描述

训练集准确度低
测试集准确度低

情景二:过拟合
在这里插入图片描述
我们训练出的模型为了拟合所有数据变得过分复杂,为了降低loss,模型趋近于每一个点,训练模型复杂度大于真实数据集复杂度,这样会导致在训练集上的效果非常好,但是在测试集上的效果不好,这种情况叫做过拟合。

那么过拟合如何体现出来,我们如何发现模型出现过拟合?
在这里插入图片描述
在这里插入图片描述
上面的图分别表示:欠拟合,适配良好,过拟合

1.28.Train-Val-Test划分

1.28.1.1.How to detect(如何检测)

在这里插入图片描述

1.28.1.2.Splitting(划分训练集和测试集)

合理的 Train/Test 集划分会有效地减少 under-fitting 和 over-fitting 现象
以数字识别为例,正常一个数据集我们要划分出来训练部分和测试部分,如下图所示:

左侧橘色部分作为训练部分,神经网路在该区域内不停地学习,将特征转入到函数中,学习好后得到一个函数模型。随后将上图右面白色区域的测试部分导入到该模型中,进行accuracy和loss的验证
在这里插入图片描述
通过不断地测试,查看模型是否调整到了一个最佳的参数,及结果是否发生了over-fitting现象。

1.28.1.3.for example

在这里插入图片描述

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms


batch_size=200
learning_rate=0.01
epochs=10


# 使用 train=True 或 train=False来进行数据集的划分
# train=True时为训练集,相反即为测试集
train_db = datasets.MNIST('../data', train=True, download=True,
                   transform=transforms.Compose([
                       transforms.ToTensor(),
                       transforms.Normalize((0.1307,), (0.3081,))
                   ]))

# 训练-测试代码写法
# 一般使用DataLoader函数来让机器学习或测试
train_loader = torch.utils.data.DataLoader(
    train_db,
    batch_size=batch_size, shuffle=True)

test_db = datasets.MNIST('../data', train=False, transform=transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.1307,), (0.3081,))
]))
test_loader = torch.utils.data.DataLoader(test_db,
    batch_size=batch_size, shuffle=True)
1.28.1.4.test while train

在这里插入图片描述

1.28.1.5.train test trade-off

在这里插入图片描述

1.28.1.6.for others judge

其实正常情况下除了Train Set和挑选最佳参数的Test Set外,一般还要有Validation Set。
Val Set代替Test Set的功能,而Test Set则要交给客户,进行实际验证,正常情况下Test Set是不加入到测试中的
在这里插入图片描述
说个很具体的场景,就比方说 Kaggle 竞赛中,比赛的主办方给你训练的数据集,一般我们拿来都会自己分成 Train Set 和 Val Set 两部分,用 Train Set 训练,Val Set 挑选最佳参数,训练好以后提交模型,此时主办方会用它自己的数据集,即 Test Set 去测试你的模型,得到一个 Score。

从上面的过程能看出,Val Set可以理解为是从Train Set找拆出来的一部分,而与Test Set没有关系

# 这里注意,正常情况下数据集是要有validation(验证)集的,若没有设置,即将test和val集合并为一个了
# 这里注意,正常情况下数据集是要有validation(验证)集的,若没有设置,即将test和val集合并为一个了
print('train:', len(train_db), 'test:', len(test_db))
# 首先先查看train和test数据集的数量,看看是否满足预订的分配目标
# 随机分配法将数据分为50k和10k的数量
train_db, val_db = torch.utils.data.random_split(train_db, [50000, 10000])
print('db1:', len(train_db), 'db2:', len(val_db))
train_loader = torch.utils.data.DataLoader(
    train_db,
    batch_size=batch_size, shuffle=True)
val_loader = torch.utils.data.DataLoader(
    val_db,
    batch_size=batch_size, shuffle=True)
1.28.1.7.train-val-test

在这里插入图片描述

1.28.1.8.K-fold cross-validation

在这里插入图片描述

1.28.1.9.k-fold cross validation

hold-out(留出法)
直接将数据集划分为两个互斥的集合
但是这种方法,只有新划分的训练集会参与到backward过程中

K-fold cross-validation(K拆交叉验证)
即把数据分成K份,每次拿出一份作为验证集,剩下k-1份作为训练集,重新K次。最后平均K次的结果,作为误差评估的结果。

这种方法可以将验证集充分利用起来,从而使得每个数据集都可以参与backward过程中。
不过这种方法对模型的提升有限,毕竟数据集并没有增加。
在这里插入图片描述

1.30.Regularization

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
为了解决torch.optim优化器只能实现L2正则化以及惩罚网络中的所有参数的缺陷,这里实现类似于TensorFlow正则化的方法。

1.30.1.自定义正则化Regularization类

这里封装成一个实现正则化的Regularization类,各个方法都给出了注释。

# -*- coding: UTF-8 -*-

import torch

# 检查GPU是否可用
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# device='cuda'
print("--------device:{}".format(device))
print("--------Pytorch version:{}".format(torch.__version__))


class Regularization(torch.nn.Module):
    def __init__(self, model, weight_decay, p=2):
        '''
        :param model 模型
        :param weight_decay: 正则化参数
        :param p: 范数计算中的幂指数值,默认求2范数,当p=0为L2正则化,p=1为L1正则化
        '''
        super(Regularization, self).__init__()
        if weight_decay <= 0:
            print("param weight_decay can not <=0")
            exit(0)
        self.model = model
        self.weight_decay = weight_decay
        self.p = p
        self.weight_list = self.get_weight(model)
        self.weight_info(self.weight_list)

    def to(self, device):
        '''
        指定运行模式
        :param device: cuda or cpu
        :return:
        '''
        self.device = device
        super().to(device)
        return self

    def get_weight(self, model):
        '''
        获得模型的权重列表
        :param model:
        :return:
        '''
        weight_list = []
        for name, param in model.named_parameters():
            if 'weight' in name:
                weight = (name, param)
                weight_list.append(weight)
        return weight_list

    def regularization_loss(self, weight_list, weight_decay, p=2):
        '''
        计算张量范数
        :param weight_list:
        :param weight_decay:
        :param p: 范数计算中的幂指数值,默认求2范数
        :return:
        '''
        reg_loss = 0
        for name, w in weight_list:
            l2_reg = torch.norm(w, p=p)
            reg_loss = reg_loss + l2_reg

        reg_loss = weight_decay * reg_loss
        return reg_loss

    def weight_info(self, weight_list):
        '''
        打印权重列表信息
        :param weight_list:
        :return:
        '''
        print("-------regularization weight--------")
        for name, w in weight_list:
            print(name)
        print("------------------------------------")

    def forward(self, model):
        # 获得最新的权重
        self.weight_list = self.get_weight(model)
        reg_loss = self.regularization_loss(self.weight_list, self.weight_decay, p=self.p)
        return reg_loss

1.30.2.Regularization使用方法

使用方法很简单,就当一个普通Pytorch模块来使用,例如:

# 检查GPU是否可用
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

print("------device:{}".format(device))
print("------Pytorch version:{}".format(torch.__version__))

weight_decay = 100.0  #正则化参数
learning_rate=0.01

model = Regularization().to(device)
# 初始化正则化
if weight_decay > 0:
    reg_loss = Regularization(model, weight_decay, p=2).to(device)
else:
    print("no regularization")


# CrossEntropyLoss=softmax+cross entropy
criterion = nn.CrossEntropyLoss().to(device)
# 不需要指定参数weight_decay
optimizer = optim.Adam(model.parameters(), lr=learning_rate)


# train
batch_train_data = ...
batch_train_label = ...

out = model(batch_train_data)

# loss and regularization
loss = criterion(input=out, target=batch_train_label)
if weight_decay > 0:
    loss = loss + reg_loss(model)
total_loss = loss.item()

# backprop
# 清除当前所有的累积梯度
optimizer.zero_grad()
total_loss.backward()
optimizer.step()

训练时输出的 loss和Accuracy信息:

(1)当weight_decay=0.0时,未使用正则化
(2)当weight_decay=10.0时,使用正则化
(3)当weight_decay=10000.0时,使用正则化

对比torch.optim优化器的实现L2正则化方法,这种Regularization类的方法也同样达到正则化的效果,并且与TensorFlow类似,loss把正则化的损失也计算了。

此外更改参数p,如当p=0表示L2正则化,p=1表示L1正则化。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

涂作权的博客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值