学习参考:
- 动手学深度学习2.0
- Deep-Learning-with-TensorFlow-book
- pytorchlightning
①如有冒犯、请联系侵删。
②已写完的笔记文章会不定时一直修订修改(删、改、增),以达到集多方教程的精华于一文的目的。
③非常推荐上面(学习参考)的前两个教程,在网上是开源免费的,写的很棒,不管是开始学还是复习巩固都很不错的。
深度学习回顾,专栏内容来源多个书籍笔记、在线笔记、以及自己的感想、想法,佛系更新。争取内容全面而不失重点。完结时间到了也会一直更新下去,已写完的笔记文章会不定时一直修订修改(删、改、增),以达到集多方教程的精华于一文的目的。所有文章涉及的教程都会写在开头、一起学习一起进步。
一、模型选择、欠拟合和过拟合
模型选择是指在机器学习中选择最合适的模型来解决特定问题的过程。在解决机器学习问题时,通常会考虑多种不同的模型,每种模型有不同的复杂度、特性和适用范围。模型选择的目标是找到一个在训练数据上表现良好且能够泛化到新数据的模型。
模型选择是指在机器学习中选择最合适的模型来解决特定问题的过程。在解决机器学习问题时,通常会考虑多种不同的模型,每种模型有不同的复杂度、特性和适用范围。模型选择的目标是找到一个在训练数据上表现良好且能够泛化到新数据的模型。
模型选择通常包括以下几个步骤:
- 选择模型类型:首先确定要使用的模型类型,例如线性模型、决策树、神经网络等。选择模型类型时需要考虑问题的特性和数据的特点。
- 定义模型结构:对于选择的模型类型,需要定义其具体的结构,包括网络层数、每层的节点数、激活函数等参数。
- 训练模型:使用训练数据对模型进行训练,调整模型参数使其能够更好地拟合训练数据。
- 评估模型:使用验证集或交叉验证等方法对训练好的模型进行评估,评估模型在新数据上的泛化能力。
- 调整模型:根据评估结果对模型进行调整,可以尝试不同的模型结构、超参数或特征工程方法,以提高模型的性能。
- 选择最佳模型:根据评估结果选择表现最好的模型作为最终模型,用于解决实际问题。
欠拟合(Underfitting) 是指模型对训练数据学习不足,不能很好地捕捉数据中的模式和规律的现象。在训练过程中,模型没有很好地适应训练数据,导致模型在训练集上表现较差,同时在测试集上也表现不佳。欠拟合通常是由于模型过于简单或者学习能力不足导致的。例如,对于非常复杂的数据模式,使用线性模型进行建模可能会导致欠拟合。欠拟合的模型往往在训练数据上表现不佳,其预测能力较差,无法很好地泛化到新的数据上。
解决欠拟合问题的方法包括:
- 增加模型复杂度:可以尝试使用更复杂的模型,例如增加神经网络的层数或节点数,以增加模型的表达能力。
- 添加更多特征:可以尝试添加更多有意义的特征,以帮助模型更好地捕捉数据中的模式。
- 减少正则化:如果模型使用了正则化项,可以尝试减少正则化的程度,以降低模型对参数的约束,提高模型的灵活性。
- 增加训练数据:增加训练数据量可以帮助模型更好地学习数据中的模式,从而减少欠拟合的问题。
- 调整超参数:可以调整模型的超参数,例如学习率、批次大小等,以改善模型的性能。
过拟合(Overfitting) 是指机器学习模型在训练数据上表现很好,但在测试数据或新数据上表现较差的现象。过拟合通常是由于模型过于复杂或者训练数据过少导致的,模型在训练数据上学习到了数据中的噪声和随机变化,导致模型过度拟合了训练数据的特性,无法很好地泛化到新的数据上。
过拟合的特点包括:
- 在训练集上表现很好,但在测试集上表现较差。
- 模型的泛化能力较差,不能很好地适应新的数据。
- 模型的复杂度较高,包括参数数量较多或者模型结构过于复杂等。
解决过拟合问题的方法包括:
- 简化模型:可以尝试使用更简单的模型,减少模型的复杂度,例如减少神经网络的层数或节点数。
- 正则化:可以使用正则化技术,例如 L1 正则化或 L2 正则化,来约束模型的复杂度,防止模型过拟合训练数据。
- 增加训练数据:增加训练数据量可以帮助模型更好地学习数据中的模式,减少过拟合的可能性。
- 提前停止训练:可以通过监控模型在验证集上的表现,在模型性能开始下降之前停止训练,避免模型过度拟合训练数据。
- 特征选择:可以选择最重要的特征,去除无关的特征,减少模型学习的复杂度。
1.训练误差和泛化误差
训练误差(training error) 是指, 模型在训练数据集上计算得到的误差。
泛化误差(generalization error) 是指, 模型应用在同样从原始样本的分布中抽取的无限多数据样本时,模型误差的期望。
问题是,我们永远不能准确地计算出泛化误差。 这是因为无限多的数据样本是一个虚构的对象。 在实际中,我们只能通过将模型应用于一个独立的测试集来估计泛化误差, 该测试集由随机选取的、未曾在训练集中出现的数据样本构成。
简单的模型和大量的数据,会期望泛化误差与训练误差相近。 当有更复杂的模型和更少的样本时,预计训练误差会下降,但泛化误差会增大。 模型复杂性由什么构成是一个复杂的问题。 一个模型是否能很好地泛化取决于很多因素。
通常对于神经网络,认为需要更多训练迭代的模型比较复杂, 而需要早停(early stopping)的模型(即较少训练迭代周期)就不那么复杂。
2.模型选择
通常在评估几个候选模型后选择最终的模型。 这个过程叫做模型选择。 有时,需要进行比较的模型在本质上是完全不同的(比如,决策树与线性模型)。 又有时,需要比较不同的超参数设置下的同一类模型。
2.1训练集、测试集、验证集
训练集、测试集和验证集是在机器学习中常用的数据集划分方式,用于模型训练、评估和调优。
将数据分成三份, 除了训练和测试数据集之外,还增加一个验证数据集(validation dataset), 也叫验证集(validation set)。 但现实是验证数据和测试数据之间的边界模糊得令人担忧。
(1)训练集(Training Set):训练集是用来训练模型的数据集,模型通过训练集学习数据中的模式和特征。训练集通常包含大量的样本数据,用于模型参数的学习和优化。
(2)测试集(Test Set):测试集是用来评估模型性能的数据集,模型在测试集上进行测试,评估模型在新数据上的泛化能力。测试集通常是从原始数据中独立抽取的,确保测试集和训练集没有重叠的样本。
(3)验证集(Validation Set):验证集是用来调整模型超参数和选择模型的数据集,通过在验证集上评估不同参数或模型的性能,选择最佳的模型和参数组合。验证集通常是从训练集中独立抽取的一部分数据,与测试集、训练集没有交集。
区别:
- 训练集用于训练模型,测试集用于评估模型性能,验证集用于调整模型参数和选择模型。
- 训练集和验证集通常用于模型的训练和调优过程,测试集用于最终评估模型的泛化能力。
- 训练集和验证集是在模型开发过程中使用的,测试集是在模型开发完成后使用的。
- 测试集是不能用来更新模型和调整超参数的,验证集才可以。
2.2 K折交叉验证
K折交叉验证的主要目的包括:
- 评估模型性能:通过多次训练和验证,可以更准确地评估模型在不同数据子集(测试集)上的性能,避免因为单次划分数据而引入偶然性。
- 选择最佳超参数:在交叉验证过程中,可以尝试不同的超参数组合,并选择在验证集上性能最好的模型作为最终模型,从而提高模型泛化能力。
- 减少过拟合风险:通过K折交叉验证,可以更好地评估模型在未见数据(测试集) 上的泛化能力,减少过拟合的风险。
注意:很多文章在交叉验证中有的把除训练集之外的另一个数据集称为测试集、有的称为验证集。难有统一的称呼。在这里统一唤作验证集即可。
具体来说,K折交叉验证的步骤如下:
- 将原始数据集分成K个子集,其中一个子集作为验证集,剩下的K-1个子集作为训练集。
- 对于每个子集,使用K-1个子集进行训练,并在剩下的一个验证集上评估模型的性能。
- 重复上述步骤K次,每次选择不同的验证集。
- 最终将K次验证的结果进行平均,得到最终的评估结果。
3.欠拟合、过拟合
欠拟合(Underfitting)和过拟合(Overfitting)是机器学习中常见的两种模型训练问题,它们分别表示模型在训练数据上表现不佳或过度学习训练数据的特征,而无法泛化到新数据上。
训练误差和验证误差都很严重, 但它们之间仅有一点差距。 如果模型不能降低训练误差,这可能意味着模型过于简单(即表达能力不足), 无法捕获试图学习的模式。此外,由于训练和验证误差之间的泛化误差很小, 有理由相信可以用一个更复杂的模型降低训练误差。 这种现象被称为欠拟合(underfitting)。
欠拟合(Underfitting):
- 指模型无法在训练数据上学习到数据的基本模式和规律,导致模型在训练集和测试集上表现均不佳。
- 原因可能是模型过于简单,无法捕捉数据中的复杂关系,或者数据特征过于多样化,模型无法很好地拟合。
- 解决方法包括增加模型复杂度、添加更多特征、减少正则化等。
训练误差明显低于验证误差时要小心, 这表明严重的过拟合(overfitting)。过拟合并不总是一件坏事。最好的预测模型在训练数据上的表现往往比在保留(验证)数据上好得多。 最终,通常更关心验证误差,而不是训练误差和验证误差之间的差距。
过拟合(Overfitting):
- 指模型在训练数据上表现良好,但在未见数据(如测试集)上表现不佳,因为模型过度学习了训练数据的噪声和细节。
- 过拟合可能是因为模型过于复杂,训练数据量太少,或者模型没有合适的正则化。
- 解决方法包括减少模型复杂度、增加训练数据、使用正则化技术(如L1正则化、L2正则化)等。
欠拟合是指模型无法继续减少训练误差。过拟合是指训练误差远小于验证误差。
4.多项式回归来对比欠拟合、过拟合
import math
import numpy as np
import tensorflow as tf
from d2l import tensorflow as d2l
4.1生成数据集
使用以下三阶多项式来生成训练和测试数据的标签:
噪声项 𝜖服从均值为0且标准差为0.1的正态分布。 在优化的过程中,我们通常希望避免非常大的梯度值或损失值。
poly_features是存放 三阶多项式函数 生成的数据。注意,这里的数据集总共只有200个样本。
max_degree = 20 # 多项式的最大阶数
n_train, n_test = 100, 100 # 训练和测试数据集大小
true_w = np.zeros(max_degree) # 分配大量的空间
true_w[0:4] = np.array([5, 1.2, -3.4, 5.6])# 生成数据
features = np.random.normal(size=(n_train + n_test, 1))
# 打乱数据
np.random.shuffle(features)
poly_features = np.power(features, np.arange(max_degree).reshape(1, -1))for i in range(max_degree):poly_features[:, i] /= math.gamma(i + 1) # gamma(n)=(n-1)!
# labels的维度:(n_train+n_test,)
labels = np.dot(poly_features, true_w)
labels += np.random.normal(scale=0.1, size=labels.shape)
转换为张量
# NumPy ndarray转换为tensor
true_w, features, poly_features, labels = [tf.constant(x, dtype=tf.float32) for x in [true_w, features, poly_features, labels]]
4.2对模型进行训练和测试
def evaluate_loss(net, data_iter, loss): #@save"""评估给定数据集上模型的损失"""metric = d2l.Accumulator(2) # 损失的总和,样本数量for X, y in data_iter:l = loss(net(X), y)metric.add(tf.reduce_sum(l), d2l.size(l))return metric[0] / metric[1]
定义训练函数:
def train(train_features, test_features, train_labels, test_labels,num_epochs=400):loss = tf.losses.MeanSquaredError()input_shape = train_features.shape[-1]# 不设置偏置,因为我们已经在多项式中实现了它net = tf.keras.Sequential()# 网络只有一层net.add(tf.keras.layers.Dense(1, use_bias=False))batch_size = min(10, train_labels.shape[0])train_iter = d2l.load_array((train_features, train_labels), batch_size)test_iter = d2l.load_array((test_features, test_labels), batch_size,is_train=False)trainer = tf.keras.optimizers.SGD(learning_rate=.01)animator = d2l.Animator(xlabel='epoch', ylabel='loss', yscale='log',xlim=[1, num_epochs], ylim=[1e-3, 1e2],legend=['train', 'test'])for epoch in range(num_epochs):d2l.train_epoch_ch3(net, train_iter, loss, trainer)if epoch == 0 or (epoch + 1) % 20 == 0:animator.add(epoch + 1, (evaluate_loss(net, train_iter, loss),evaluate_loss(net, test_iter, loss)))print('weight:', net.get_weights()[0].T)
4.3三阶多项式函数拟合(正常)
首先使用三阶多项式函数数据(即多项式的前4项),它与数据生成函数的阶数相同。 结果表明,该模型能有效降低训练损失和测试损失。 学习到的模型参数也接近真实值 𝑤=[5,1.2,−3.4,5.6]
。
# 从多项式特征中选择前4个维度,即1,x,x^2/2!,x^3/3!
train(poly_features[:n_train, :4], poly_features[n_train:, :4],labels[:n_train], labels[n_train:])
weight: [[ 5.0062456 1.1831801 -3.3876977 5.622506 ]]
4.4线性函数拟合(欠拟合)
使用线性函数的数据(即多项式的前两项)进行拟合,减少该模型的训练损失相对困难。 在最后一个迭代周期完成后,训练损失仍然很高。 当用来拟合非线性模式(如这里的三阶多项式函数)时,线性模型容易欠拟合。
# 从多项式特征中选择前2个维度,即1和x
train(poly_features[:n_train, :2], poly_features[n_train:, :2],labels[:n_train], labels[n_train:])
weight: [[3.1562712 5.449455 ]]
4.5高阶多项式函数拟合(过拟合)
尝试使用一个阶数过高的多项式生成的数据来训练模型。 在这种情况下,没有足够的数据用于学到高阶系数应该具有接近于零的值。 因此,这个过于复杂的模型会轻易受到训练数据中噪声的影响。 虽然训练损失可以有效地降低,但测试损失仍然很高。 结果表明,复杂模型对数据造成了过拟合。
# 从多项式特征中选取所有维度
train(poly_features[:n_train, :], poly_features[n_train:, :],labels[:n_train], labels[n_train:], num_epochs=1500)
weight: [[ 5.017295 1.2858739 -3.4337099 5.131178 -0.10286681 1.06592680.77857715 0.13716547 0.14918433 0.46685842 0.4061673 -0.03210513-0.519065 -0.49761721 0.32880613 0.44722757 -0.20228322 0.237536010.08765773 0.1151495 ]]
可见,在总共只有200个样本的数据中,数据维度太高会导致过拟合。
如果将训练数据提高到300个,那么不会出现这种过拟合情况:
5.权重衰减(正则化)
保持模型简单的一个特别的选择是使用 𝐿2惩罚的权重衰减。这会导致学习算法更新步骤中的权重衰减。正则化是处理过拟合的常用方法:在训练集的损失函数中加入惩罚项,以降低学习到的模型的复杂度。
5.1范数与权重衰减
在训练参数化机器学习模型时, 权重衰减(weight decay)是最广泛使用的正则化的技术之一。范数(Norm)是一个数学概念,用来衡量向量的大小。在机器学习中,常用的范数包括L1范数和L2范数。权重衰减(Weight Decay)是一种正则化技术,通过在损失函数中添加权重的范数来限制模型的复杂度,防止过拟合。
关于范数与权重衰减的关系:
L2范数和权重衰减:
- 在权重衰减中,通常会在模型的损失函数中添加权重参数的L2范数(平方范数)作为正则化项,以约束模型的权重参数。
- L2范数正则化会使得模型训练过程中的权重参数趋向于较小的值,有助于防止过拟合,提高模型的泛化能力。
L1范数和权重衰减:
- 类似地,L1范数正则化也可以用于权重衰减。在损失函数中添加权重参数的L1范数(绝对值范数)作为正则化项,可以促使模型的权重参数变得稀疏。
- L1范数正则化有助于特征选择,即使得模型更加稀疏,去除不重要的特征,从而提高模型的解释性和泛化能力。
权重衰减的作用:
- 权重衰减通过正则化项控制模型的复杂度,防止模型过拟合训练数据。它在优化过程中会使得模型更倾向于选择简单的参数组合,而不是过度拟合训练数据。
- 在神经网络训练中,通过调节权重衰减的系数,可以平衡模型的拟合能力和泛化能力,从而提高模型在未见数据上的表现。
范数(如L1范数和L2范数)与权重衰减之间有密切关系,通过在损失函数中添加权重参数的范数作为正则化项,可以有效控制模型的复杂度,防止过拟合,并提高模型的泛化能力。
𝐿2正则化线性模型构成经典的岭回归(ridge regression)算法, 𝐿1正则化线性回归是统计学中类似的基本模型, 通常被称为套索回归(lasso regression)。 使用𝐿2范数的一个原因是它对权重向量的大分量施加了巨大的惩罚。 这使得我们的学习算法偏向于在大量特征上均匀分布权重的模型。 在实践中,这可能使它们对单个变量中的观测误差更为稳定。 相比之下,𝐿1惩罚会导致模型将权重集中在一小部分特征上, 而将其他权重清除为零。 这称为特征选择(feature selection),这可能是其他场景下需要的。
如何使用范数进行权重衰减呢?
一种简单的方法是通过线性函数 𝑓(𝐱)=𝐰⊤𝐱 中的权重向量的某个范数来度量其复杂性, 例如 ‖𝐰‖²。 要保证权重向量比较小, 最常用方法是将其范数作为惩罚项加到最小化损失的问题中。 将原来的训练目标最小化训练标签上的预测损失, 调整为最小化预测损失和惩罚项之和。
为了惩罚权重向量的大小, 必须以某种方式在损失函数中添加 ‖𝐰‖² , 但是模型应该如何平衡这个新的额外惩罚的损失? 实际上,通过正则化常数 𝜆来描述这种权衡, 这是一个非负超参数,我们使用验证数据拟合:
𝐿2正则化回归的小批量随机梯度下降更新如下式:
5.2生成实验用的数据集
选择标签是关于输入的线性函数。 标签同时被均值为0,标准差为0.01高斯噪声破坏。 为了使过拟合的效果更加明显,可以将问题的维数增加到 𝑑=200 , 并使用一个只包含20个样本的小训练集。生成数据分布的公式如下:
%matplotlib inline
import tensorflow as tf
from d2l import tensorflow as d2ln_train, n_test, num_inputs, batch_size = 20, 100, 200, 5
true_w, true_b = tf.ones((num_inputs, 1)) * 0.01, 0.05
train_data = d2l.synthetic_data(true_w, true_b, n_train)
train_iter = d2l.load_array(train_data, batch_size)
test_data = d2l.synthetic_data(true_w, true_b, n_test)
test_iter = d2l.load_array(test_data, batch_size, is_train=False)
5.3从0实现权重衰减
从0开始实现权重衰减,只需将 𝐿2的平方惩罚添加到原始目标函数中。
(1)初始化模型参数w,b
def init_params():w = tf.Variable(tf.random.normal(mean=1, shape=(num_inputs, 1)))b = tf.Variable(tf.zeros(shape=(1, )))return [w, b]
(2)定义 𝐿2范数惩罚
实现这一惩罚最方便的方法是对所有项求平方后并将它们求和。
def l2_penalty(w):return tf.reduce_sum(tf.pow(w, 2)) / 2
(3)定义训练代码
将模型拟合训练数据集,并在测试数据集上进行评估。通过d2l.linreg
和d2l.squared_loss
导入线性网络和平方损失。 唯一的变化是损失现在包括了惩罚项。
def train(lambd):w, b = init_params()net, loss = lambda X: d2l.linreg(X, w, b), d2l.squared_lossnum_epochs, lr = 100, 0.003animator = d2l.Animator(xlabel='epochs', ylabel='loss', yscale='log',xlim=[5, num_epochs], legend=['train', 'test'])for epoch in range(num_epochs):for X, y in train_iter:with tf.GradientTape() as tape:# 增加了L2范数惩罚项,# 广播机制使l2_penalty(w)成为一个长度为batch_size的向量l = loss(net(X), y) + lambd * l2_penalty(w)grads = tape.gradient(l, [w, b])d2l.sgd([w, b], grads, lr, batch_size)if (epoch + 1) % 5 == 0:animator.add(epoch + 1, (d2l.evaluate_loss(net, train_iter, loss),d2l.evaluate_loss(net, test_iter, loss)))print('w的L2范数是:', tf.norm(w).numpy())
(4)不使用正则化直接训练(会过拟合)
用lambd = 0
禁用权重衰减后运行这个代码。 注意,这里训练误差有了减少,但测试误差没有减少, 这意味着出现了严重的过拟合。
train(lambd=0)
(5)使用正则化进行训练(权重衰减)
使用权重衰减来运行代码。 注意,在这里训练误差增大,但测试误差减小。期望从正则化中得到的效果。
train(lambd=3)
5.4 简洁实现权重衰减
由于权重衰减在神经网络优化中很常用, 深度学习框架为了便于使用权重衰减, 将权重衰减集成到优化算法中,以便与任何损失函数结合使用。 此外,这种集成还有计算上的好处, 允许在不增加任何额外的计算开销的情况下向算法中添加权重衰减。 由于更新的权重衰减部分仅依赖于每个参数的当前值, 因此优化器必须至少接触每个参数一次。
使用权重衰减超参数wd
创建一个 𝐿2正则化器, 并通过kernel_regularizer
参数将其应用于网络层
def train_concise(wd):net = tf.keras.models.Sequential()net.add(tf.keras.layers.Dense(1, kernel_regularizer=tf.keras.regularizers.l2(wd)))net.build(input_shape=(1, num_inputs))w, b = net.trainable_variablesloss = tf.keras.losses.MeanSquaredError()num_epochs, lr = 100, 0.003trainer = tf.keras.optimizers.SGD(learning_rate=lr)animator = d2l.Animator(xlabel='epochs', ylabel='loss', yscale='log',xlim=[5, num_epochs], legend=['train', 'test'])for epoch in range(num_epochs):for X, y in train_iter:with tf.GradientTape() as tape:# tf.keras需要为自定义训练代码手动添加损失。l = loss(net(X), y) + net.lossesgrads = tape.gradient(l, net.trainable_variables)trainer.apply_gradients(zip(grads, net.trainable_variables))if (epoch + 1) % 5 == 0:animator.add(epoch + 1, (d2l.evaluate_loss(net, train_iter, loss),d2l.evaluate_loss(net, test_iter, loss)))print('w的L2范数:', tf.norm(net.get_weights()[0]).numpy())
(1)不使用正则化(会过拟合)
train_concise(0)
(2)使用正则化训练(权重衰减)
train_concise(3)
6.暂退法Dropout(正则化)
泛化性和灵活性之间的这种基本权衡被描述为偏差-方差权衡(bias-variance tradeoff)。 线性模型有很高的偏差:它们只能表示一小类函数。深度神经网络位于偏差-方差谱的另一端。 与线性模型不同,神经网络并不局限于单独查看每个特征,而是学习特征之间的交互。 例如,神经网络可能推断“尼日利亚”和“西联汇款”一起出现在电子邮件中表示垃圾邮件, 但单独出现则不表示垃圾邮件。
6.1暂退法
暂退法在前向传播过程中,计算每一内部层的同时注入噪声,这已经成为训练神经网络的常用技术。 这种方法之所以被称为暂退法,因为我们从表面上看是在训练过程中丢弃(drop out)一些神经元。 在整个训练过程的每一次迭代中,标准暂退法包括在计算下一层之前将当前层中的一些节点置零。
神经网络过拟合与每一层都依赖于前一层激活值相关,称这种情况为“共适应性”。 暂退法会破坏共适应性,就像有性生殖会破坏共适应的基因一样。如何注入这种噪声。 一种想法是以一种无偏向(unbiased)的方式注入噪声。 这样在固定住其他层时,每一层的期望值等于没有噪音时的值。
将高斯噪声添加到线性模型的输入中。 在每次训练迭代中,他将从均值为零的分布 𝜖∼N(0,𝜎²)
采样噪声添加到输入 𝐱 , 从而产生扰动点 𝐱′=𝐱+𝜖 , 预期是 𝐸[𝐱′]=𝐱 。
在标准暂退法正则化中,通过按保留(未丢弃)的节点的分数进行规范化来消除每一层的偏差。 换言之,每个中间活性值 ℎ以暂退概率 𝑝由随机变量 ℎ′ 替换,如下所示:
当将暂退法应用到隐藏层,以 𝑝的概率将隐藏单元置为零时, 结果可以看作一个只包含原始神经元子集的网络。 删除了 ℎ2 和 ℎ5 , 因此输出的计算不再依赖于 ℎ2 或 ℎ5 ,并且它们各自的梯度在执行反向传播时也会消失。 这样,输出层的计算不能过度依赖于 ℎ1,…,ℎ5 的任何一个元素。
Dropout 是一种正则化技术,通过在训练过程中随机地让一部分神经元失活,即将它们的输出值设为零,从而减少神经元之间的协作,降低模型的复杂度,防止过拟合。这种暂时关闭神经元的操作有助于提高模型的泛化能力,使得模型更具有鲁棒性。因此,可以说 Dropout 是一种暂退法,它在训练过程中暂时地禁用神经元,以促进模型的泛化能力和稳健性。
总结:
- 暂退法在前向传播过程中,计算每一内部层的同时丢弃一些神经元。
- 暂退法可以避免过拟合,它通常与控制权重向量的维数和大小结合使用的。
- 暂退法将活性值 ℎ 替换为具有期望值 ℎ 的随机变量。
- 暂退法仅在训练期间使用。
6.2从零开始实现暂退法
要实现单层的暂退法函数, 从均匀分布 𝑈[0,1] 中抽取样本,样本数与这层神经网络的维度一致。 然后保留那些对应样本大于 𝑝的节点,把剩下的丢弃。
实现 dropout_layer
函数, 该函数以dropout的概率丢弃张量输入X中的元素,重新缩放剩余部分:将剩余部分除以1.0-dropout
。
import tensorflow as tf
from d2l import tensorflow as d2ldef dropout_layer(X, dropout):assert 0 <= dropout <= 1# 在本情况中,所有元素都被丢弃if dropout == 1:return tf.zeros_like(X)# 在本情况中,所有元素都被保留if dropout == 0:return Xmask = tf.random.uniform(shape=tf.shape(X), minval=0, maxval=1) < 1 - dropoutreturn tf.cast(mask, dtype=tf.float32) * X / (1.0 - dropout)
测试dropout_layer函数。 将输入X通过暂退法操作,暂退概率分别为0、0.5和1。
定义模型参数
引入的Fashion-MNIST数据集。 定义具有两个隐藏层的多层感知机,每个隐藏层包含256个单元。
num_outputs, num_hiddens1, num_hiddens2 = 10, 256, 256
定义模型
将暂退法应用于每个隐藏层的输出(在激活函数之后), 并且可以为每一层分别设置暂退概率: 常见的技巧是在靠近输入层的地方设置较低的暂退概率。 下面的模型将第一个和第二个隐藏层的暂退概率分别设置为0.2和0.5, 并且暂退法只在训练期间有效。
dropout1, dropout2 = 0.2, 0.5class Net(tf.keras.Model):def __init__(self, num_outputs, num_hiddens1, num_hiddens2):super().__init__()self.input_layer = tf.keras.layers.Flatten()self.hidden1 = tf.keras.layers.Dense(num_hiddens1, activation='relu')self.hidden2 = tf.keras.layers.Dense(num_hiddens2, activation='relu')self.output_layer = tf.keras.layers.Dense(num_outputs)def call(self, inputs, training=None):x = self.input_layer(inputs)x = self.hidden1(x)# 只有在训练模型时才使用dropoutif training:# 在第一个全连接层之后添加一个dropout层x = dropout_layer(x, dropout1)x = self.hidden2(x)if training:# 在第二个全连接层之后添加一个dropout层x = dropout_layer(x, dropout2)x = self.output_layer(x)return xnet = Net(num_outputs, num_hiddens1, num_hiddens2)
训练和测试
num_epochs, lr, batch_size = 10, 0.5, 256
loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
trainer = tf.keras.optimizers.SGD(learning_rate=lr)
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)
6.3简洁实现
对于深度学习框架的高级API,只需在每个全连接层之后添加一个Dropout层, 将暂退概率作为唯一的参数传递给它的构造函数。 在训练时,Dropout层将根据指定的暂退概率随机丢弃上一层的输出(相当于下一层的输入)。 在测试时,Dropout层仅传递数据。
net = tf.keras.models.Sequential([tf.keras.layers.Flatten(),tf.keras.layers.Dense(256, activation=tf.nn.relu),# 在第一个全连接层之后添加一个dropout层tf.keras.layers.Dropout(dropout1),tf.keras.layers.Dense(256, activation=tf.nn.relu),# 在第二个全连接层之后添加一个dropout层tf.keras.layers.Dropout(dropout2),tf.keras.layers.Dense(10),
])
训练模型和评估测试集
trainer = tf.keras.optimizers.SGD(learning_rate=lr)
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)