动手学深度学习pytorch(ModernRNN,过拟合欠拟合及其解决方案,梯度消失、梯度爆炸)
- ModernRNN
- RNN:
RNN(Recurrent Neural Network)是一类用于处理序列数据的神经网络。
RNN存在一些问题梯度较容易出现衰减或爆炸(BPTT)
- 梯度爆炸,梯度消失
首先举一个例子:
如果有一个层输为的多层感知机的第层的参数为,输出层的权重参数为。当我们不考虑偏差参数,同时设所有隐藏层的**函数为恒等映射(identity mapping)。对于已经给定的输入,多层感知机的第层的输出。
此时,如果层数较大,的计算可能会出现衰减或爆炸。举个例子,假设输入和所有层的权重参数都是标量,如权重参数为0.2和5,多层感知机的第30层输出为输入分别与(消失)和(爆炸)的乘积。当层数较多时,梯度的计算也容易出现消失或爆炸。
这就说明了当神经网络的层数较多时,模型的数值稳定性容易变差。
- GRU
• 重置⻔有助于捕捉时间序列⾥短期的依赖关系;
• 更新⻔有助于捕捉时间序列⾥⻓期的依赖关系。
- GRU模型
def gru(inputs, state, params):
W_xz, W_hz, b_z, W_xr, W_hr, b_r, W_xh, W_hh, b_h, W_hq, b_q = params
H, = state
outputs = []
for X in inputs:
Z = torch.sigmoid(torch.matmul(X, W_xz) + torch.matmul(H, W_hz) + b_z)
R = torch.sigmoid(torch.matmul(X, W_xr) + torch.matmul(H, W_hr) + b_r)
H_tilda = torch.tanh(torch.matmul(X, W_xh) + R * torch.matmul(H, W_hh) + b_h)
H = Z * H + (1 - Z) * H_tilda
Y = torch.matmul(H, W_hq) + b_q
outputs.append(Y)
return outputs, (H,)
- LSTM
首先还是先看图
长短期记忆long short-term memory :
遗忘门:控制上一时间步的记忆细胞 输入门:控制当前时间步的输入
输出门:控制从记忆细胞到隐藏状态
记忆细胞:⼀种特殊的隐藏状态的信息的流动
def lstm(inputs, state, params):
[W_xi, W_hi, b_i, W_xf, W_hf, b_f, W_xo, W_ho, b_o, W_xc, W_hc, b_c, W_hq, b_q] = params
(H, C) = state
outputs = []
for X in inputs:
I = torch.sigmoid(torch.matmul(X, W_xi) + torch.matmul(H, W_hi) + b_i)
F = torch.sigmoid(torch.matmul(X, W_xf) + torch.matmul(H, W_hf) + b_f)
O = torch.sigmoid(torch.matmul(X, W_xo) + torch.matmul(H, W_ho) + b_o)
C_tilda = torch.tanh(torch.matmul(X, W_xc) + torch.matmul(H, W_hc) + b_c)
C = F * C + I * C_tilda
H = O * C.tanh()
Y = torch.matmul(H, W_hq) + b_q
outputs.append(Y)
return outputs, (H, C)
- 深度循环神经网络
- 双向循环神经网络
- 过拟合、欠拟合及其解决方案
在深度学习中,我们要找到一个要找到一个训练误差,而且是一个比较低的训练误差。这样就存在两种情况:
- 我们得到的训练误差比较大,这种我们叫欠拟合
- 我们得到的训练误差非常小,甚至趋向于0,我们叫过拟合
为什么这两种情况不能被我们接受
首先,解释欠拟合,我们要训练一组数据,用这组数据得到的权重来预测一组新的数据,如果欠拟合情况发生,意味着,我们预测得到的结果会偏离实际的结果
其次,如果是过拟合的情况发生,那么简单的说,输入等于输出,也就是说,我们建立的模型,完全没有了预测的功能,只是这个模型训练误差小,但是泛化误差大。
会导致这两种情况产生的,在这里我们只讨论两个因素模型复杂度和训练数据集大小。
- 模型复杂度
以多项式拟合为例。
给定一个由标量数据特征和对应的标量标签组成的训练数据集,多项式函数拟合的目标是找一个阶多项式函数来近似。
该式中是模型的权重参数,是偏差参。在这里我们运用平方损失函数。
给定训练数据集,模型复杂度和误差之间的关系:
- 训练数据集大小
影响欠拟合和过拟合的另一个重要因素是训练数据集的大小。一般来说,如果训练数据集中样本数过少,特别是比模型参数数量(按元素计)更少时,过拟合更容易发生。此外,泛化误差不会随训练数据集里样本数量增加而增大。因此,在计算资源允许的范围之内,我们通常希望训练数据集大一些,特别是在模型复杂度较高时,例如层数较多的深度学习模型。
- 三阶多项式函数拟合(正常)
fit_and_plot(poly_features[:n_train, :], poly_features[n_train:, :], labels[:n_train], labels[n_train:])
- 线性函数拟合(欠拟合)
fit_and_plot(features[:n_train, :], features[n_train:, :], labels[:n_train], labels[n_train:])
- 线性函数拟合(欠拟合)
fit_and_plot(poly_features[0:2, :], poly_features[n_train:, :], labels[0:2], labels[n_train:])
所以当产生这些情况时,我们有几个方法去解决它们
- 权重衰减
所谓权重衰减,就是范数正则化(regularization)
范数正则化在模型原损失函数基础上添加范数惩罚项,从而得到训练所需要最小化的函数。范数惩罚项指的是模型权重参数每个元素的平方和与一个正的常数的乘积。以线性回归中的线性回归损失函数为例
将权重参数用向量表示,带有范数惩罚项的新损失函数为
有了范数惩罚项后,在小批量随机梯度下降中,权重和的迭代方式为
分析这些式子,我们可以发现正则化使得权重和自乘小于1的数,将权重衰减。这样对过拟合的情况是比较有效的。
- 定义范数惩罚项
def l2_penalty(w):
return (w**2).sum() / 2
我们来观察以下,使用和不使用权重衰减的情况
- 定义训练和测试
batch_size, num_epochs, lr = 1, 100, 0.003
net, loss = d2l.linreg, d2l.squared_loss
dataset = torch.utils.data.TensorDataset(train_features, train_labels)
train_iter = torch.utils.data.DataLoader(dataset, batch_size, shuffle=True)
def fit_and_plot(lambd):
w, b = init_params()
train_ls, test_ls = [], []
for _ in range(num_epochs):
for X, y in train_iter:
# 添加了L2范数惩罚项
l = loss(net(X, w, b), y) + lambd * l2_penalty(w)
l = l.sum()
if w.grad is not None:
w.grad.data.zero_()
b.grad.data.zero_()
l.backward()
d2l.sgd([w, b], lr, batch_size)
train_ls.append(loss(net(train_features, w, b), train_labels).mean().item())
test_ls.append(loss(net(test_features, w, b), test_labels).mean().item())
d2l.semilogy(range(1, num_epochs + 1), train_ls, ‘epochs’, ‘loss’,
range(1, num_epochs + 1), test_ls, [‘train’, ‘test’])
print(‘L2 norm of w:’, w.norm().item())
未使用权重衰减
fit_and_plot(lambd=0)
使用权重衰减
fit_and_plot(lambd=3)
- 丢弃法
当我们对一个有着单隐藏层的多层感知机的隐藏层进行丢弃法的时候,该单元的隐藏层是有一定概率被丢弃掉。
用数学解释:假设丢弃的概率为,那么不被丢弃的概率就是,所以当我们计算新的隐藏单元
在数学中,这就是一个两点分布所以我们容易得到
所以
这就告诉我们,丢弃法不改变输入的期望值
与此同时,因为丢弃是随机的,输出层无法过度依赖中任何一个,这就可以在训练中起到正则化的作用,就可以应对过拟合
- 实际操作可能的问题
在处理kaggle房价预测问题前。我们先了解一些问题
- 随机初始化模型参数
假设输出层只保留一个输出单元(删去和以及指向它们的箭头),且隐藏层使用相同的**函数。如果将每个隐藏单元的参数都初始化为相等的值,那么在正向传播时每个隐藏单元将根据相同的输入计算出相同的值,并传递至输出层。在反向传播中,每个隐藏单元的参数梯度值相等。因此,这些参数在使用基于梯度的优化算法迭代后值依然相等。之后的迭代也是如此。在这种情况下,无论隐藏单元有多少,隐藏层本质上只有1个隐藏单元在发挥作用。所以我们要随机初始化模型参数。
- 考虑环境因素
在这里我们要考虑三个偏移因素
- 协变量偏移
简单的说,当我们进行猫狗分类的问题时,如果我们训练的全是猫和狗的真实照片,但是当我们将这个分类器进行实际作用时,如果提供的全是猫和狗的卡通图片,显然,这个分类器是不能作用的。
专业的来说:
统计学家称这种协变量变化是因为问题的根源在于特征分布的变化(即协变量的变化)。数学上,我们可以说P(x)改变了,但P(y∣x)保持不变。尽管它的有用性并不局限于此,当我们认为x导致y时,协变量移位通常是正确的假设。
- 标签偏移
当我们认为导致偏移的是标签P(y)上的边缘分布的变化,但类条件分布是不变的P(x∣y)时,就会出现相反的问题。当我们认为y导致x时,标签偏移是一个合理的假设。例如,通常我们希望根据其表现来预测诊断结果。在这种情况下,我们认为诊断引起的表现,即疾病引起的症状。有时标签偏移和协变量移位假设可以同时成立。例如,当真正的标签函数是确定的和不变的,那么协变量偏移将始终保持,包括如果标签偏移也保持。有趣的是,当我们期望标签偏移和协变量偏移保持时,使用来自标签偏移假设的方法通常是有利的。这是因为这些方法倾向于操作看起来像标签的对象,这(在深度学习中)与处理看起来像输入的对象(在深度学习中)相比相对容易一些。
病因(要预测的诊断结果)导致 症状(观察到的结果)。
训练数据集,数据很少只包含流感p(y)的样本。
而测试数据集有流感p(y)和流感q(y),其中不变的是流感症状p(x|y)。
- 概念偏移
另一个相关的问题出现在概念转换中,即标签本身的定义发生变化的情况。这听起来很奇怪,毕竟猫就是猫。的确,猫的定义可能不会改变,但我们能不能对软饮料也这么说呢?事实证明,如果我们周游美国,按地理位置转移数据来源,我们会发现,即使是如图所示的这个简单术语的定义也会发生相当大的概念转变。
如果我们要建立一个机器翻译系统,分布P(y∣x)可能因我们的位置而异。这个问题很难发现。另一个可取之处是P(y∣x)通常只是逐渐变化。