【PyTorch学习笔记】14:划分训练-验证-测试集,使用正则化项

划分训练-验证-测试集

简述

测试集一般都是客户用来验收模型的,是拿不到的,在Kaggle比赛里就是用来计算选手模型的score的集合。而训练集拿到以后要划分成训练集和验证集,只用训练集来训练,验证集用来以一定的频率对模型的performance做验证,也就是用来防止over-fitting的,在训练过程中可以得到验证集的Loss或者acc.的曲线,在曲线上就能大致判断发生over-fitting的点,选取在这个点之前的模型的参数作为学习到的参数,能让模型有较好的泛化能力。

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

即便拿不到测试集,如果总是能知道样本在测试集上的performance,那么如果用这个performance来调整模型,实际上这时的测试集起到的就是验证集的作用了,这是不对的,让测试集失去意义了,这样训练出的模型根本不知道用哪些样本来判断模型的泛化能力如何了,因此Kaggle比赛总是有提交限制的,就是尽量在避免这种手段刷榜。

留出法(hold-out)

也就是直接划分,如将60k的训练集划分出10k来做验证集。

import torch
from torchvision import datasets, transforms

batch_size = 200

"""读取训练集和测试集"""
train_db = datasets.MNIST('../data', train=True, download=True,
						  transform=transforms.Compose([
							  transforms.ToTensor(),
							  transforms.Normalize((0.1307,), (0.3081,))
						  ]))

test_db = datasets.MNIST('../data', train=False,
						 transform=transforms.Compose([
							 transforms.ToTensor(),
							 transforms.Normalize((0.1307,), (0.3081,))
						 ]))

"""刚读取进来的"""
print('train:', len(train_db), 'test:', len(test_db))

"""将训练集划分为训练集和验证集"""
train_db, val_db = torch.utils.data.random_split(train_db, [50000, 10000])
print('train:', len(train_db), 'validation:', len(val_db))

"""
这一步是将torch.utils.data.dataset.Subset
转换成torch.utils.data.dataloader.DataLoader
根据batch_size让它多出了一个"批编号"的维度
"""
# 训练集
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)
# 测试集
test_loader = torch.utils.data.DataLoader(
	test_db,
	batch_size=batch_size, shuffle=True)

运行结果:

train: 60000 test: 10000
train: 50000 validation: 10000

在这种划分方式下,只有最终50k的训练数据做了backward来调整模型参数,剩下的10k的验证数据和10k的测试数据都没用来做backward。测试数据就不用说了肯定是不能动的,验证数据却也仅仅是用来比较模型的performance,然后用来选取训练停止的点,在那个点上checkpoint保存模型了。

K-fold cross validation

也就是K折交叉验证。这可以将验证集充分利用起来,比如在每个epoch重新划分这60k的数据,拿出其中的50k作为训练数据,其中的10k作为验证集。好处是这60k数据中每个都有可能是用来做train的,同时每个数据都有可能是做validation的,也同样防止了用train的来做validation出现的记样本的问题。

K-fold cross-validation划分成K份,每次取K-1份用来作为训练数据,1份用来做验证。

使用正则化项

在PyTorch中使用正则化项。

L2 Regularization

若使用 L 2 L2 L2正则化项:
1 2 λ ∣ ∣ W ∣ ∣ 2 = 1 2 λ ∑ j w j 2 \frac{1}{2}\lambda||W||^2=\frac{1}{2}\lambda \sum_j w_j^2 21λW2=21λjwj2
只要直接在训练前为optimizer设置正则化项的 λ \lambda λ参数(这里不叫Regularization而是用了Weight Decay这个叫法):

optimizer = optim.SGD(net.parameters(), lr=learning_rate, weight_decay=0.01)

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

L1 Regularization

若使用 L 1 L1 L1正则化项,即对所有参数绝对值求和再乘以一个系数:
λ ∑ j ∣ θ j ∣ \lambda\sum_j |\theta_j| λjθj
在PyTorch中还没有直接设置 L 1 L1 L1范数的方法,可以在训练时Loss做BP之前(也就是.backward()之前)手动为 L o s s Loss Loss加上 L 1 L1 L1范数:

		# 为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

用上节的代码试了一下,使用 L 1 L1 L1正则化项时如果指定和使用 L 2 L2 L2正则化项时相同的 λ = 0.01 \lambda=0.01 λ=0.01会发生under-fitting,似乎如果要用 L 1 L1 L1正则化的话要把其系数设置的小一点,所以这里用了0.001。

下面是跑到8个epoch时的情况,感觉performance还不错:
【PyTorch学习笔记】14:划分训练-验证-测试集,使用正则化项_第1张图片

你可能感兴趣的:(#,PyTorch)