PyTorch 训练集、验证集、测试集、模型存档、正则化项

为什么要将数据集划分为三个部分?三个部分的作用?三个部分数据集的比例应如何设定?

另外一种常见的数据集划分方法是将数据集划分为两个部分(训练集和测试集),这种划分方法存在的问题在于,模型利用训练集数据进行训练,测试集数据进行模型泛化性测试。但我们不能利用测试集测试的bad case或者根据测试集的测试精度调整模型的参数。这是因为对于模型来说,数据集应该是只有训练集可见的,其他数据均不可见,如果利用测试集的结果调整模型相对于模型也"看到了"测试集的数据。将数据集划分为是独立同分布的三个部分就可以解决这个问题,将训练集数据用于模型训练,验证集(开发集)数据用于模型调参,测试集数据用于验证模型泛化性。

介绍

训练集

训练集(Training Dataset)是用来训练模型使用的。

验证集

当我们的模型训练好之后,我们并不知道他的表现如何。这个时候就可以使用验证集(Validation Dataset)来看看模型在新数据(验证集和测试集是不同的数据)上的表现如何。同时通过调整超参数,让模型处于最好的状态

验证集有2个主要的作用:

1、评估模型效果,为了调整超参数而服务

2、调整超参数,使得模型在验证集上的效果最好

说明:

1、验证集不像训练集和测试集,它是非必需的。如果不需要调整超参数,就可以不使用验证集,直接用测试集来评估效果。

2、验证集评估出来的效果并非模型的最终效果,主要是用来调整超参数的,模型最终效果以测试集的评估结果为准。

在机器学习中,训练集是用来训练模型的数据,而验证集通常是从原始数据集中划分出来的一个子集,用于在训练过程中检查模型的性能,是在过拟合或欠拟合的情况下对模型进行评估和调整的数据。

验证集的主要目的是为了找到一个最佳的模型及参数,使得模型在未知数据上的表现最好。

之前提到,训练集一般会占用60%或80%的比例,对应的验证集则一般会占用20%或10%的比例。划分比例的依据可以根据实际需求和数据集的大小来确定。

通常情况下,我们可以使用随机抽样的方法从原始数据集中划分验证集

验证集在整个模型训练的过程中起着关键的作用,我们从几个方面出发,聊聊其重要性。

1.调整模型的超参数

在机器学习模型中,有许多超参数需要我们参与设置,例如学习率、隐藏层神经元数量等。这些超参数的选择对模型的性能有很大影响。

为了找到最优的超参数组合,我们可以将训练过程分为多个阶段,每个阶段使用不同的超参数组合进行训练。然后,我们可以使用验证集来评估每个阶段模型的性能,从而选择出最优的超参数组合。

2.早停策略

在训练过程中,如果我们发现模型在验证集上的性能不再提高时,可以提前停止训练。

具体来说,我们可以设置一个小的阈值,当模型在连续多个迭代周期内,验证集上的误差没有降低到这个阈值以下时,我们就认为模型已经收敛,可以停止训练。

这样既可以节省训练时间,也可以降低不必要的成本。

3. 防止过拟合

过拟合是机器学习中的一个常见问题,指的是模型在训练集上表现很好,但在测试集上表现较差的现象。这是因为模型过于复杂,学习到了训练集中的一些噪声和异常数据。

为了解决这个问题,我们可以使用验证集来监控模型在训练过程中的性能。

如果发现模型在训练集上的表现越来越好,但在验证集上的表现越来越差,那么我们可以考虑减少模型的复杂度或者增加正则化项,以防止过拟合的发生。

在训练过程中可以得到验证集的Loss或者acc.的曲线,在曲线上就能大致判断发生over-fitting的点,选取在这个点之前的模型的参数作为学习到的参数,能让模型有较好的泛化能力。

可以记录下在每个时间戳(在验证集上测试时)验证集上的performance和模型参数,然后最后再去选取认为最好的模型。这个可以用checkpoint来做。

过拟合:随着模型复杂度和训练Epoch的增加,CNN模型在训练集上的误差降低,但是在测试集上的误差会降低后升高,如下图所示:

这是由于训练过程过于细致,以至于把样本的特有特征也当做识别特征一起训练了,所以在验证集上会表现得较差。

针对过拟合的情况,可以从两个方面来解决(1)模型训练方式,(2)数据集。

1、模型训练方式

  • 正则化(Regulation)
  • Dropout:随机失活
  • 提前停止训练

2、数据集

合理的从给定的数据集中拆分出训练集和验证集,将大大减低模型过拟合的可能,常用的验证集划分的方法有:

  • 留出法(Hold-out):按一定比例直接将训练集划分为两部分。
  • K折交叉验证(K-flod Cross Validation):将训练集划分为K份,将其中K-1份作为训练集,剩下一份作为验证集,循环K次训练。
  • 自助采样(BootStrap):通过有放回的采样方式得到新的训练集和验证集,这样每次的训练集和验证集都是有区别的。这对小数据量的训练较为适用。

4. 对比不同模型结构

我们可以通过对比不同模型结构在验证集上的性能,选择最适合任务的模型结构。

这有助于避免选择过于简单或过于复杂的模型,从而提高模型的实际效果。

比如,比较卷积神经网络(CNN) 和循环神经网络(RNN) 在情感分析任务上的性能。通过观察它们的表现,选择更适合处理文本数据的模型结构。

测试集

当我们调好超参数后,就要开始「最终考试」了。我们通过测试集(Test Dataset)来做最终的评估。

通过测试集的评估,我们会得到一些最终的评估指标,例如:准确率、精确率、召回率、F1等。

扩展阅读:分类模型评估指标:Accuracy、Precision、Recall、F1、ROC曲线、AUC、PR曲线

图解

训练集&测试集 模式

需要注意的是这里C点所在的测试集一定只能用来测试,不能用来训练

训练集&验证集&测试集 模式

Training set & Validation set & Test set

既然有多种模型可选,且每种模型的参数可调。那么我们如何选择最好的模型呢?

------上一节我们提到测试集永远只能用来测试,因此我们将训练集拆成两个部分------训练集 -> 训练集+验证集

在训练集训练模型得到模型参数后,验证集就要对这个在各种超参数下得到的模型进行评估,找到一组最优的超参数。然后将超参数固定,再拿到整个训练集上重新训练模型,反复迭代比较,获取到基本的模型。最后,由测试集评估最终模型的性能

如何合理的划分数据集?

下面的数据集划分方式主要针对「留出法」的验证方式,除此之外还有其他的交叉验证法,详情见下文 --- --- 交叉验证法。

数据划分的方法并没有明确的规定,不过可以参考3个原则:

1、对于小规模样本集(几万量级),常用的分配比例是 60% 训练集、20% 验证集、20% 测试集。

2、对于大规模样本集(百万级以上),只要验证集和测试集的数量足够即可,例如有 100w 条数据,那么留 1w 验证集,1w 测试集即可。1000w 的数据,同样留 1w 验证集和 1w 测试集。

3、超参数越少,或者超参数很容易调整,那么可以减少验证集的比例,更多的分配给训练集。

交叉验证法

为什么要用交叉验证法?

假如我们教小朋友学加法:1个苹果+1个苹果=2个苹果

当我们再测试的时候,会问:1个香蕉+1个香蕉=几个香蕉?

如果小朋友知道「2个香蕉」,并且换成其他东西也没有问题,那么我们认为小朋友学习会了「1+1=2」这个知识点。

如果小朋友只知道「1个苹果+1个苹果=2个苹果」,但是换成其他东西就不会了,那么我们就不能说小朋友学会了「1+1=2」这个知识点。

评估模型是否学会了「某项技能」时,也需要用新的数据来评估,而不是用训练集里的数据来评估。这种「训练集」和「测试集」完全不同的验证方法就是交叉验证法。

3 种主流的交叉验证法


留出法(Holdout cross validation)

上文提到的,按照固定比例将数据集静态的划分为训练集、验证集、测试集的方式就是留出法。

留一法(Leave one out cross validation)
每次的测试集都只有一个样本,要进行 m 次训练和预测。 这个方法用于训练的数据只比整体数据集少了一个样本,因此最接近原始样本的分布。但是训练复杂度增加了,因为模型的数量与原始数据样本数量相同。 一般在数据缺乏时使用。

k 折交叉验证(k-fold cross validation)
静态的「留出法」对数据的划分方式比较敏感,有可能不同的划分方式得到了不同的模型 。「k 折交叉验证」是一种动态验证 的方式,这种方式可以降低数据划分带来的影响 。具体步骤如下:

1、将数据集分为训练集和测试集,将测试集放在一边

2、将训练集分为 k 份

3、每次使用 k 份中的 1 份作为验证集,其他全部作为训练集。

4、通过 k 次训练后,我们得到了 k 个不同的模型。

5、评估 k 个模型的效果,从中挑选效果最好的超参数

6、使用最优的超参数,然后将 k 份数据全部作为训练集重新训练模型,得到最终模型。

k 一般取 10。数据量小的时候,k 可以设大一点,这样训练集占整体比例就比较大,不过同时训练的模型个数也增多。 数据量大的时候,k 可以设小一点。

PyTorch中的 训练和验证

首先应该设置模型的状态:如果是训练状态,那么模型的参数应该支持反向传播的修改;如果是验证/测试状态,则不应该修改模型参数。在PyTorch中,模型的状态设置非常简便,如下的两个操作二选一即可:

python 复制代码
model.train()   # 训练状态
model.eval()   # 验证/测试状态
python 复制代码
# 构造训练集
train_loader = torch.utils.data.DataLoader(
	train_dataset,
	batch_size=10,
	shuffle=True,
	num_workers=10, )

# 构造验证集
val_loader = torch.utils.data.DataLoader(
	val_dataset,
	batch_size=10,
	shuffle=False,
	num_workers=10, )

model = SVHN_Model1()  # 加载模型
criterion = nn.CrossEntropyLoss (size_average=False) # 损失函数
optimizer = torch.optim.Adam(model.parameters(), 0.001) # 优化器
best_loss = 1000.0 # 最优损失

for epoch in range(20):
	print('Epoch: ', epoch)
	train(train_loader, model, criterion, optimizer, epoch)
	val_loss = validate(val_loader, model, criterion)

	# 记录下验证集精度
	if val_loss < best_loss:
		best_loss = val_loss
		torch.save(model.state_dict(), './model.pt')
python 复制代码
def train(train_loader, model, criterion, optimizer, epoch):
	# 切换模型为训练模式
	model.train()
	for i, (input, target) in enumerate(train_loader):
		c0, c1, c2, c3, c4, c5 = model(data[0])
		loss = criterion(c0, data[1][:, 0]) + \
				criterion(c1, data[1][:, 1]) + \
				criterion(c2, data[1][:, 2]) + \
				criterion(c3, data[1][:, 3]) + \
				criterion(c4, data[1][:, 4]) + \
				criterion(c5, data[1][:, 5])
	loss /= 6
	optimizer.zero_grad()
	loss.backward()
	optimizer.step()

验证/测试的流程基本与训练过程一致,不同点在于:

  • 需要预先设置torch.no_grad,以及将model调至eval模式
  • 不需要将优化器的梯度置零
  • 不需要将loss反向回传到网络
  • 不需要更新optimizer
python 复制代码
def validate(val_loader, model, criterion):
	# 切换模型为预测模型
	model.eval()
	val_loss = []
	# 不记录模型梯度信息
	with torch.no_grad():
			for i, (input, target) in enumerate(val_loader):
				c0, c1, c2, c3, c4, c5 = model(data[0])
				loss = criterion(c0, data[1][:, 0]) + \
						criterion(c1, data[1][:, 1]) + \
						criterion(c2, data[1][:, 2]) + \
						criterion(c3, data[1][:, 3]) + \
						criterion(c4, data[1][:, 4]) + \
						criterion(c5, data[1][:, 5])
				loss /= 6
				val_loss.append(loss.item())
	return np.mean(val_loss)

保存模型

python 复制代码
# 模型保存到 ./model.pt文件下
torch.save(model_object.state_dict(), 'model.pt')



# 加载模型 ./model.pt
model.load_state_dict(torch.load(' model.pt'))
# 或
checkpoint = torch.load( 'best_model.pt' ) 
model.load_state_dict(checkpoint)

模型训练通常需要花很久的时间,有时候会遇到被断电或各种中止程式的状况,所以随时存档是件重要的事,这样就能接续训练。但如果要接续训练,存档就不能只存模型,要把优化器一起储存 ,因为它会记录一些训练资讯(像是动量之类的值)。要同时存模型和优化器,可以用「字典」的方式存,简单来讲就是用大括号:{ }。范例如下:

python 复制代码
torch.save({ 'model_state_dict' : model.state_dict(), 
           'optimizer_state_dict' : optimizer.state_dict() 
           }, 'best_model.pt' )

可以看到模型参数model.state_dict()存在'model_state_dict'标签下;优化器参数optimizer.state_dict()存在'optimizer_state_dict'标签下,标签名称可以随便设定没关系,要存多一点资讯进去也行,像是epoch、训练误差...等。

要读取模型和优化器,可以执行下面的程式:

python 复制代码
checkpoint = torch.load( 'best_model.pt' ) 
model.load_state_dict(checkpoint[ 'model_state_dict' ]) 
optimizer.load_state_dict(checkpoint[ 'optimizer_state_dict' ])

回到刚刚的问题,储存最佳模型。我会先设定一个变数best_loss,用来记录最低的验证误差,我会把值设定的很大(如:999999),如果计算出来的误差比这个值更低,去把误差值取代,并存下模型,然后一直重复。这样存下来的模型就是验证误差最小的模型。程式码如下:

python 复制代码
if val_loss < best_loss: 
    best_loss = val_loss 
    torch.save({ 'epoch' : epoch, 
                'model_state_dict' : model.state_dict(), 
               'optimizer_state_dict' : optimizer.state_dict() 
               }, 'best_model.pt' )

PyTorch中使用正则化项

L2 Regularization

若使用L2正则化项:

只要直接在训练前为optimizer设置正则化项的λ \lambdaλ参数(这里不叫Regularization而是用了Weight Decay这个叫法):

python 复制代码
optimizer = optim.SGD(net.parameters(), lr=learning_rate, weight_decay=0.01)

正则化项目是用来克服over-fitting的,如果网络本身就没有发生over-fitting,那么设置了正则化项势必会导致网络的表达能力不足,引起网络的performance变差。

L1 Regularization

若使用L1正则化项,即对所有参数绝对值求和再乘以一个系数:

在PyTorch中还没有直接设置L1范数的方法,可以在训练时Loss做BP之前(也就是.backward()之前)手动为Loss加上L1范数:

python 复制代码
		# 为Loss添加L1正则化项
		L1_reg = 0
		for param in net.parameters():
			L1_reg += torch.sum(torch.abs(param))
		loss += 0.001 * L1_reg  # lambda=0.001

Reference

【小萌五分钟】机器学习 | 数据集的划分(一): 训练集及测试集
一文看懂 AI 数据集:训练集、验证集、测试集(附:分割方法+交叉验证)
Pytorch实现模型训练与验证
深入浅出PyTorch
训练、验证、存档

相关推荐
余生H14 分钟前
transformer.js(三):底层架构及性能优化指南
javascript·深度学习·架构·transformer
果冻人工智能33 分钟前
2025 年将颠覆商业的 8 大 AI 应用场景
人工智能·ai员工
代码不行的搬运工35 分钟前
神经网络12-Time-Series Transformer (TST)模型
人工智能·神经网络·transformer
石小石Orz37 分钟前
Three.js + AI:AI 算法生成 3D 萤火虫飞舞效果~
javascript·人工智能·算法
罗小罗同学43 分钟前
医工交叉入门书籍分享:Transformer模型在机器学习领域的应用|个人观点·24-11-22
深度学习·机器学习·transformer
孤独且没人爱的纸鹤1 小时前
【深度学习】:从人工神经网络的基础原理到循环神经网络的先进技术,跨越智能算法的关键发展阶段及其未来趋势,探索技术进步与应用挑战
人工智能·python·深度学习·机器学习·ai
阿_旭1 小时前
TensorFlow构建CNN卷积神经网络模型的基本步骤:数据处理、模型构建、模型训练
人工智能·深度学习·cnn·tensorflow
羊小猪~~1 小时前
tensorflow案例7--数据增强与测试集, 训练集, 验证集的构建
人工智能·python·深度学习·机器学习·cnn·tensorflow·neo4j
极客代码1 小时前
【Python TensorFlow】进阶指南(续篇三)
开发语言·人工智能·python·深度学习·tensorflow
zhangfeng11331 小时前
pytorch 的交叉熵函数,多分类,二分类
人工智能·pytorch·分类