模型选择、欠配合、过配合【代码】
部署:如果模型在培训数据集上更准确,则在测试数据集上并不一定更准确。
1 .训练误差和泛化误差机器学习模型应关注泛化误差的降低
必须区分训练误差(training error )和泛化误差(generalization error )。 一般而言,前者指的是模型在训练数据集中表示的误差,而后者指的是模型在任何测试数据样本上表示的误差期望,往往由测试数据集中的误差近似。 训练误差和泛化误差的计算可以使用损失函数。
训练误差被认为是往年制作高考试题(训练试题)时的错误率,泛化误差可以近似为实际报考高考试题)时的答案错误率。 假设训练问题和测试问题是在基于同一考试纲的未知巨大试题库中随机抽样的。 如果让名为未学习中学知识的小学生解答的话,测试题和训练试题的解答错误率可能很接近。 但是,如果改变有名的反复练习训练题的高三备考答案,即使在训练问题上出错率为0,实际的考试成绩也不是那样的。
假设训练数据集(训练问题)和测试数据集(测试问题)的每个样本是从相同的概率分布中相互独立生成的。 基于此独立同分布假设,给出任意机器学习模型(包括参数),其训练误差期望和泛化误差相同。 例如,如果将模型参数设置为随机值(小学生),则训练误差和泛化误差非常接近。 模型的参数是通过在训练数据集上训练模型来学习的,参数选择来自最小化训练误差(名考生)。 因此,训练误差的期望是小于或等于泛化误差。 换句话说,通常,在培训数据集上学习的模型参数会影响模型在培训数据集上的性能,以及模型在优于或等于测试数据集上的性能。 因为不能从训练误差中推断泛化误差,所以语义上降低训练误差并不意味着泛化误差会降低。
2 .在模型选择机器学习中,通常需要评估几种候选模型的表示,并从中选择模型。 可选候选模型可以是具有不同超参数的同类模型。 以多层探测器为例,可以选择隐藏层的数量、每个隐藏层中隐藏单元的数量和激活函数。
严格来说,验证数据集仅在选择了所有超级参数和模型参数之后才可用。 请不要让调参等测试数据选择模型。 因为不能从训练误差中推断泛化误差,所以不能只依靠训练数据来选择模型。 因此,可以预约训练数据集和测试数据集以外的数据的一部分来进行模型选择。 本部分的数据为验证数据集,简称验证集(validation set )。 例如,可以从一个训练集中随机选择较小的部分作为验证集,而剩下的部分作为真正的训练集。
k交叉验证由于验证数据集不参与模型培训,因此在缺少培训数据的情况下保留大量验证数据是很奢侈的。 改进措施包括k-fold cross-validation (k-fold cross-validation )。 在k匝交叉验证中,将原始训练数据划分为k个不重叠的子数据集,进行了k次模型和验证。 每次都使用一个子数据集验证模型,并使用其他K-1子数据集训练模型。 在这次k次培训和验证中,用于验证模型的子数据集每次都不同。 最后,我们将这k次训练误差和验证误差分别平均。
3 .欠拟合和过拟合模型训练中常见的两个典型问题。 类是模型得不到低训练误差,我们把这种现象称为欠拟合(underfitting )。 替代是模型的训练误差远远小于测试数据集上的误差,这种现象称为过拟合(overfitting )。
为了说明模型复杂度模型的复杂度,以多项式函数拟合为例。 给定由标量数据特征x x x和对应的标量标签y y y组成的训练数据集,多项式函数意味着找到K K K次多项式函数
y ^=b k=
1 K x k w k (1) \hat{y}=b+\sum_{k=1}^{K} x^k w_k \tag 1 y^=b+k=1∑Kxkwk(1)
来近似 y y y。在上式中, w k w_k wk是模型的权重参数, b b b是偏差参数。与线性回归相同,多项式函数拟合也是用平方损失函数。
因为高阶多项式函数模型参数更多,模型函数的选择空间更⼤,所以高阶多项式函数比低阶多项式函数的复杂度更⾼。因此,高阶多项式函数⽐低阶多项式函数更容易在相同的训练数据集上得到更低的训练误差。给定训练数据集,模型复杂度和误差之间的关系通常如图所⽰。给定训练数据集,如果模型的复杂度过低,很容易出现欠拟合;如果模型复杂度过高,很容易出现过拟合。应对⽋拟合和过拟合的⼀个办法是针对数据集选择合适复杂度的模型。
训练数据集大小
影响⽋拟合和过拟合的另⼀个重要因素是训练数据集的⼤小。⼀般来说,如果训练数据集中样本数过少,特别是⽐模型参数数量(按元素计)更少时,过拟合更容易发⽣。此外,泛化误差不会随训练数据集⾥样本数量增加而增⼤。因此,在计算资源允许的范围之内,我们通常希望训练数据集大⼀些,特别是在模型复杂度较⾼时,例如层数较多的深度学习模型。
4. 多项式函数拟合实验 # 导包%matplotlib inlinefrom mxnet import autograd, gluon, ndfrom mxnet.gluon import data as gdata, loss as gloss, nn 生成数据集
我们将⽣成⼀个人工数据集。在训练数据集和测试数据集中,给定样本特征 x x x,我们使⽤如下的三阶多项式函数来⽣成该样本的标签:
y = 1.2 x − 3.4 x 2 + 5.6 x 3 + 5 + ε (2) y=1.2x-3.4x^2+5.6x^3+5+\varepsilon \tag 2 y=1.2x−3.4×2+5.6×3+5+ε(2)
其中噪声项 ε \varepsilon ε服从均值为0、标准差为0.1的正态分布。训练数据集和测试数据集的样本数都设为100。
n_train, n_test, true_w, true_b = 100, 100, [1.2, -3.4, 5.6], 5features = nd.random.normal(shape=(n_train + n_test, 1))poly_features = nd.concat(features, nd.power(features, 2), nd.power(features, 3))labels = (true_w[0] * poly_features[:,0] + true_w[1] * poly_features[:,1] + true_w[2] * poly_features[:,2] + true_b)labels += nd.random.normal(scale=0.1, shape=labels.shape)
生成的数据集的前两个样本。
features[:2], poly_features[:2], labels[:2] ( [[1.1630785] [0.4838046]] <NDArray 2×1 @cpu(0)>, [[1.1630785 1.3527517 1.5733565 ] [0.4838046 0.2340669 0.11324265]] <NDArray 2×3 @cpu(0)>, [10.534649 5.530093] <NDArray 2 @cpu(0)>) 定义、训练和测试模型
先定义作图函数semilogy,其中 y y y轴使用了对数尺度。
import matplotlib.pyplot as pltfrom utils import set_figsizedef semilogy(x_vals, y_vals, x_label, y_label, x2_vals=None, y2_vals=None, legend=None, figsize=(3.5, 2.5)): set_figsize(figsize) plt.xlabel(x_label) plt.ylabel(y_label) plt.semilogy(x_vals, y_vals) if x2_vals and y2_vals: plt.semilogy(x2_vals, y2_vals, linestyle=’:’) plt.legend(legend) num_epochs, loss = 100, gloss.L2Loss()def fit_and_plot(train_features, test_features, train_labels, test_labels): net = nn.Sequential() net.add(nn.Dense(1)) net.initialize() batch_size = min(10, train_labels.shape[0]) train_iter = gdata.DataLoader(gdata.ArrayDataset(train_features, train_labels), batch_size, shuffle=True) trainer = gluon.Trainer(net.collect_params(), ‘sgd’, {‘learning_rate’: 0.01}) train_ls, test_ls = [], [] for _ in range(num_epochs): for X, y in train_iter: with autograd.record(): l = loss(net(X), y) l.backward() trainer.step(batch_size) train_ls.append(loss(net(train_features), train_labels).mean().asscalar()) test_ls.append(loss(net(test_features), test_labels).mean().asscalar()) 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[0].weight.data().asnumpy(), ‘\nbias:’, net[0].bias.data().asnumpy()) 三阶多项式函数拟合(正常)
我们先使用与数据生成函数同阶的三阶多项式函数拟合。实验表明,这个模型的训练误差和在测试数据集的误差都较低。
fit_and_plot(poly_features[:n_train, :], poly_features[n_train:, :], labels[:n_train], labels[n_train:]) final epoch: train loss 0.00698169 test loss 0.0063500497weight: [[ 1.1729248 -3.3906946 5.604663 ]] bias: [4.985479]
线性函数拟合(欠拟合)
线性函数拟合。很明显,该模型的训练误差在迭代早期下降后便很难继续降低。在完
成最后⼀次迭代周期后,训练误差依旧很⾼。线性模型在⾮线性模型(如三阶多项式函数)⽣成的数据集上容易⽋拟合。
fit_and_plot(features[:n_train, :], features[n_train:, :], labels[:n_train], labels[n_train:]) final epoch: train loss 159.33257 test loss 102.91761weight: [[22.651974]] bias: [-0.65602565]
训练样本不足(过拟合)
即便使⽤与数据⽣成模型同阶的三阶多项式函数模型,如果训练样本不⾜,该模型依然
容易过拟合。让我们只使⽤两个样本来训练模型。显然,训练样本过少了,甚⾄少于模型参数的数量。这使模型显得过于复杂,以⾄于容易被训练数据中的噪声影响。在迭代过程中,尽管训练误差较低,但是测试数据集上的误差却很⾼。这是典型的过拟合现象。
fit_and_plot(poly_features[0:2,:], poly_features[n_train:,:], labels[0:2], labels[n_train:]) final epoch: train loss 0.47576833 test loss 133.27455weight: [[2.0588458 1.9273669 2.0477402]] bias: [2.482129]