持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第15天,点击查看活动详情
1.训练误差和泛化误差
在一些实验中,改变实验中的模型结构或者超参数,当模型在训练数据集上更准确时,它在测试数据集上不一定准确。 首先说的是训练误差:
训练误差一般情况下是指模型在训练数据集上表现出的误差,通俗的说相当于我们考研,训练数据集就相当于历年真题,训练误差就相当于做历年真题时的错误率。有很多一战二战三战甚至四站的考研选手,他们的历年真题的错误率非常低,甚至于说滚瓜烂熟了,但是并不代表着当年的考研试卷也能考成这样。
这就引出了另一个误差:泛化误差:
泛化误差通常指模型在任意一个测试数据样本上表现出的误差的期望,并常常通过测试数据集上的误差来近似,这也通俗的说是那些当年考研的人在考场考出的成绩,是驴子是马拉出来溜溜。这就反应出了有些是下功夫学习的,有些只不过是“考研气氛军”罢了。
在机器学习中,数据集中的每一个样本都是从同一个概率分布中相互独立地生成的。在正常情况下训练误差往往小于等于泛化误差。也就是说,由训练集学到的模型参数会使模型在训练数据集上的表现优于或等于在测试数据集上的表现。由于无法从训练误差估计泛化误差,一味地降低训练误差并不意味着泛化误差一定会降低。所以应关注如何降低泛化误差。
2.模型选择
所谓对症下药,我们在机器学习的过程中也需要评估若干候选模型的表现然后从中选择合适的模型。这个过程就叫做模型选择,这其中最常用的方法就是使用验证数据集!
2.1 验证数据集
先类比一下人类,我们在学习知识点的时候,通常要进行做题训练,对照答案写题显然对学习毫无帮助。
机器学习中也是,我们不可以使用测试数据来选择模型如调参等。鉴于此,我们的办法是预留一部分在训练数据集和测试数据集以外的数据来进行模型选择。这部分数据就被称为验证数据集。
2.2 K折交叉验证
还有一种情况,就是当训练数据不够用时,再预留验证数据就显得太过于奢侈,一种改善方法就是K折交叉验证。
在这种验证中,我们把原始训练数据集分隔成K个不重合的子数据集,然后我们做K次训练和验证。每一次,我们使用一个子数据集验证模型,并使用其他K-1个子数据集来训练模型。在这K次训练和验证中,每次用来验证模型的子数据集都不同。最后我们对这K次训练误差和验证误差分别求平均。
3. 欠拟合和过拟合
模型训练中经常出现两类典型问题:
一类是模型无法得到较低的训练误差,我们将这一现象称作欠拟合;另一类是模型的训练误差远小于它在测试数据集上的误差,我们称该现象为过拟合。
3.1模型复杂度
给定训练数据集,模型复杂度和误差之间的关系通常如图所示。在给定训练数据集,如果模型的复杂度过低,很容易出现欠拟合;如果模型的复杂度过高,很容易出现过拟合。应对欠拟合和过拟合的一个办法是针对数据集选择合适复杂度的模型。
3.2 训练数据集大小
影响欠拟合和过拟合的另一个重要因素是训练数据集的大小。一般来说,如果训练数据集中样本数过少,特别是比模型参数数量更少时,过拟合更容易发生。此外,泛化误差不会随训练数据集里样本数量增加而增大。因此,在允许范围内,我们还是希望训练数据集大一些,特别是在模型复杂度较高时,例如层数较多的深度学习模型。
4.案例实现
4.1 多项式函数拟合实验
4.1.1 环境配置和库包
import matplotlib_inline
import torch
import numpy as np
import sys
sys.path.append("E:/anaconda/envs/pytorch/Lib/d2lzh_pytorch")
import d2lzh_pytorch as d2l
这里跟上篇多层感知机的环境几乎相同,可直接引用。
4.1.2 生成数据集
这里就生成一个人工数据集给定样本特征,使用三阶多项式函数来生成样本的标签。 y=1.2x-3.4x²+5.6x³+5+の,其中の服从均值为0,标准差为0.01的正态分布。训练数据集和测试数据集的样本数都设为100。
n_train,n_test,true_w,true_b=100,100,[1.2,-3.4,5.6],5#训练数据集和测试数据集样本数为100,true_b为参数。true_b为常数项
features=torch.randn((n_train+n_test,1))#这里是随机生成100行1列的数据
poly_features=torch.cat((features,torch.pow(features,2),torch.pow(features,3)),1)#torch.cat(inputs, dimension=0) → Tensor进行连接操作
labels=(true_w[0]*poly_features[:,0]+true_w[1]*poly_features[:,1]+true_w[2]*poly_features[:,2]+true_b)#这两步就是实现那个多项式
labels+=torch.tensor(np.random.normal(0,0.01,size=labels.size()),dtype=torch.float)#正态分布
features[:2],poly_features[:2],labels[:2]#挑几个数字看一下
每一行都做了注释,可以仔细看一下功能和原因。
4.1.3 定义、训练和测试模型
这里定义作图函数semilogy,y轴使用对数尺度。
def semilogy(x_vals,y_vals,x_label,y_label,x2_vals=None,y2_vals=None,legend=None,figsize=(3.5,2.5)):
d2l.set_figsize(figsize)
d2l.plt.xlabel(x_label)
d2l.plt.ylabel(y_label)
d2l.plt.semilogy(x_vals,y_vals)
if x2_vals and y2_vals:
d2l.plt.semilogy(x2_vals,y2_vals,linestyle=':')
d2l.plt.legend(legend)
这里的多项式函数也是用平方损失函数
num_epochs,loss=100,torch.nn.MSELoss()
def fit_and_plot(train_features,test_features,train_labels,test_labels):
net=torch.nn.Linear(train_features.shape[-1],1)#通过Linear文档可知,pytorch已经将参数初始化了,所以这里不手动初始化了
batch_size=min(10,train_labels.shape[0])
dataset=torch.utils.data.TensorDataset(train_features,train_labels)
train_iter=torch.utils.data.DataLoader(dataset,batch_size,shuffle=True)
optimizer=torch.optim.SGD(net.parameters(),lr=0.01)
train_ls,test_ls=[ ],[ ]
for _ in range(num_epochs):
for X,y in train_iter:
l=loss(net(X),y.view(-1,1))
optimizer.zero_grad()
l.backward()
optimizer.step()
train_labels=train_labels.view(-1,1)
test_labels=test_labels.view(-1,1)
train_ls.append(loss(net(train_features),train_labels).item())
test_ls.append(loss(net(test_features),test_labels).item())
print('final epoch:train loss',train_ls[-1],'test loss',test_ls[-1])
semilogy(range(1,num_epochs + 1), train_ls,'epochs','loss',range(1,num_epochs+1),test_ls,['train','test'])
print('weight:',net.weight.data,'\nbias:',net.bias.data)
开始将模型进行训练。
4.2 三种拟合现象
4.2.1 正常拟合状态
fit_and_plot(poly_features[:n_train, :],poly_features[n_train:, :],labels[:n_train],labels[n_train:])
实验表明,这个模型的训练误差和在测试数据集的误差都较低。训练出的模型参数也接近真实值。
4.2.2 欠拟合状态
fit_and_plot(features[:n_train,:],features[n_train:,:],labels[:n_train],labels[n_train:])
很明显,该模型的训练误差在迭代早期下降后便很难继续降低,在完成最后一次迭代周期轴,训练误差依旧很高。线性模型在非线性模型生成的数据集容易欠拟合。
4.2.2 过拟合状态
过拟合就是训练样本不足的情况下。
fit_and_plot(poly_features[0:2,:],poly_features[n_train:,:],labels[0:2],labels[n_train:])
在迭代过程中,尽管训练误差较低,但是测试数据集上的误差却很高,这是典型的过拟合现象。