一.欠拟合和过拟合
1.1训练误差与泛化误差
在解释上述现象之前,我们需要区分训练误差(training error)和泛化误差(generalization error)。通俗来讲,前者指模型在训练数据集上表现出的误差,后者指模型在任意一个测试数据样本上表现出的误差的期望,并常常通过测试数据集上的误差来近似。
1.2 模型选择
1.2.1 验证数据集
从严格意义上讲,测试集只能在所有超参数和模型参数选定后使用一次。不可以使用测试数据选择模型,如调参。由于无法从训练误差估计泛化误差,因此也不应只依赖训练数据选择模型。鉴于此,我们可以预留一部分在训练数据集和测试数据集以外的数据来进行模型选择。这部分数据被称为验证数据集,简称验证集(validation set)。
1.2.2 K折交叉验证
由于验证数据集不参与模型训练,当训练数据不够用时,预留大量的验证数据显得太奢侈。一种改善的方法是K折交叉验证(K-fold cross-validation)。在K折交叉验证中,我们把原始训练数据集分割成K个不重合的子数据集,然后我们做K次模型训练和验证。每一次,我们使用一个子数据集验证模型,并使用其他K-1个子数据集来训练模型。在这K次训练和验证中,每次用来验证模型的子数据集都不同。最后,我们对这K次训练误差和验证误差分别求平均。
def get_k_fold_data(k, i , x, y):
assert k>1
fold_size = x.shape[0]/k
x_train,y_train = None, None
for j in range(k):
idx = slice(j * fold_size , (j+1) * fold_size)
x_part, y_part = x[idx , :], y[idx]
if j == i:
x_valid, y_valid = x_part, y_part
elif x_train is None:
x_train,y_train = x_part , y_part
else:
x_train = torch.cat((x_train, x_part), dim=0)
y_train = torch.cat((y_train, y_part), dim=0)
return x_train, y_train, x_valid, y_valid
1.3 过拟合和欠拟合
一类是模型无法得到较低的训练误差,我们将这一现象称作欠拟合(underfitting);
另一类是模型的训练误差远小于它在测试数据集上的误差,我们称该现象为过拟合(overfitting)。
1.4 模型复杂度
给定训练数据集,模型复杂度和误差之间的关系:
1.5 训练数据集大小
一般来说,如果训练数据集中样本数过少,特别是比模型参数数量(按元素计)更少时,过拟合更容易发生。此外,泛化误差不会随训练数据集里样本数量增加而增大。
1.6 L2 范数正则化
正则化通过为模型损失函数添加惩罚项使学出的模型参数值较小,是应对过拟合的常用手段。
将权重参数用向量w=[w1,w2]表示,带有L2范数惩罚项的新损失函数为
其中超参数λ>0。当权重参数均为0时,惩罚项最小。当λ较大时,惩罚项在损失函数中的比重较大,这通常会使学到的权重参数的元素较接近0。当λ设为0时,惩罚项完全不起作用。在小批量随机梯度下降中,我们将线性回归一节中权重w1和w2的迭代方式更改为
def l2_penalty(w):
return (w**2).sum()/2
1.7 丢弃法
设丢弃概率为p,那么有p的概率hi会被清零,有1−p的概率hi会除以1−p做拉伸。丢弃概率是丢弃法的超参数。具体来说,设随机变量ξi为0和1的概率分别为p和1−p。使用丢弃法时我们计算新的隐藏单元hi′
即丢弃法不改变其输入的期望值。由于在训练中隐藏层神经元的丢弃是随机的,即h1,…,h5都有可能被清零,输出层的计算无法过度依赖h1,…,h5中的任一个,从而在训练模型时起到正则化的作用,并可以用来应对过拟合。在测试模型时,我们为了拿到更加确定性的结果,一般不使用丢弃法
def dropout(x, drop_prob):
x = x.float()
assert 0<= drop_prob <=1
keep_prob = 1- drop_prob
if keep_prob ==0:
return torch.zeros_like(x)
mask = (torch.rand(x.shape)< keep_prob).float()
return mask * x/keep_prob