欢迎访问 生活随笔!

生活随笔

当前位置: 首页 >

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

发布时间:2024/9/27 52 豆豆

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, transformsbatch_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 = modelself.weight_decay = weight_decayself.p = pself.weight_list = self.get_weight(model)self.weight_info(self.weight_list)def to(self, device):'''指定运行模式:param device: cuda or cpu:return:'''self.device = devicesuper().to(device)return selfdef 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_listdef regularization_loss(self, weight_list, weight_decay, p=2):'''计算张量范数:param weight_list::param weight_decay::param p: 范数计算中的幂指数值,默认求2范数:return:'''reg_loss = 0for name, w in weight_list:l2_reg = torch.norm(w, p=p)reg_loss = reg_loss + l2_regreg_loss = weight_decay * reg_lossreturn reg_lossdef 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.01model = 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正则化。

总结

以上是生活随笔为你收集整理的31,32,33_过拟合、欠拟合的概念、L2正则化,Pytorch过拟合欠拟合,交叉验证-Train-Val-Test划分,划分训练集和测试集,K-fold,Regularization的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。