《动手学——循环神经网络进阶、梯度消失、梯度爆炸以及Kaggle房价预测、过拟合、欠拟合及其解决方案》笔记

Tama ·
更新时间:2024-11-11
· 741 次阅读

《动手学——循环神经网络进阶》笔记

GRU
时间步数较大或者较小时,循环神经网络梯度较容易出现梯度衰减/梯度爆炸。
虽然裁剪梯度可以应对梯度爆炸,但没法解决梯度衰减问题。
所以提出⻔控循环神经⽹络GRU,来捕捉时间序列中时间步距离较⼤的依赖关系
RNN存在的问题:梯度较容易出现衰减或爆炸(BPTT)
⻔控循环神经⽹络:捕捉时间序列中时间步距离较⼤的依赖关系
RNN:

Ht=ϕ(XtWxh+Ht−1Whh+bh)
GRU:
Rt=σ(XtWxr+Ht−1Whr+br)
Zt=σ(XtWxz+Ht−1Whz+bz)
Ht=tanh(XtWxh+(Rt⊙Ht−1)Whh+bh)
Ht=Zt⊙Ht−1+(1−Zt)⊙Ht

• 重置⻔有助于捕捉时间序列⾥短期的依赖关系;
• 更新⻔有助于捕捉时间序列⾥⻓期的依赖关系。

初始化参数

num_inputs, num_hiddens, num_outputs = vocab_size, 256, vocab_size print('will use', device) def get_params(): def _one(shape): ts = torch.tensor(np.random.normal(0, 0.01, size=shape), device=device, dtype=torch.float32) #正态分布 return torch.nn.Parameter(ts, requires_grad=True) def _three(): return (_one((num_inputs, num_hiddens)), _one((num_hiddens, num_hiddens)), torch.nn.Parameter(torch.zeros(num_hiddens, device=device, dtype=torch.float32), requires_grad=True)) W_xz, W_hz, b_z = _three() # 更新门参数 W_xr, W_hr, b_r = _three() # 重置门参数 W_xh, W_hh, b_h = _three() # 候选隐藏状态参数 # W_x=x*h, W_h=h*h, b=h(x输入个数,h隐藏单元个数) # x=n*x(n批量大小,x输入个数) #(1*x)(x*h)+(1*h)(h*h)+h = h # 输出层参数 #输出到一个output,如果是分类器,q个类别,则输出到形状为q的tensor上 #Ht大小为h,output大小为q,则W_hq=h*q, b_q=q W_hq = _one((num_hiddens, num_outputs)) b_q = torch.nn.Parameter(torch.zeros(num_outputs, device=device, dtype=torch.float32), requires_grad=True) return nn.ParameterList([W_xz, W_hz, b_z, W_xr, W_hr, b_r, W_xh, W_hh, b_h, W_hq, b_q]) def init_gru_state(batch_size, num_hiddens, device): #隐藏状态H-1初始化 return (torch.zeros((batch_size, num_hiddens), device=device), ) #每个batch_size需要一个H-1,每个H-1都初始化为0 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: #对input的每一个样本X 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 #Y即需要的output outputs.append(Y) return outputs, (H,) #返回output和最后一个隐藏层H 训练模型 num_epochs, num_steps, batch_size, lr, clipping_theta = 160, 35, 32, 1e2, 1e-2 pred_period, pred_len, prefixes = 40, 50, ['分开', '不分开'] 1 2 d2l.train_and_predict_rnn(gru, get_params, init_gru_state, num_hiddens, vocab_size, device, corpus_indices, idx_to_char, char_to_idx, False, num_epochs, num_steps, lr, clipping_theta, batch_size, pred_period, pred_len, prefixes) #把第一个参数改成gru 简洁实现 num_hiddens=256 num_epochs, num_steps, batch_size, lr, clipping_theta = 160, 35, 32, 1e2, 1e-2 pred_period, pred_len, prefixes = 40, 50, ['分开', '不分开'] lr = 1e-2 # 注意调整学习率 gru_layer = nn.GRU(input_size=vocab_size, hidden_size=num_hiddens) model = d2l.RNNModel(gru_layer, vocab_size).to(device) d2l.train_and_predict_rnn_pytorch(model, num_hiddens, vocab_size, device, corpus_indices, idx_to_char, char_to_idx, num_epochs, num_steps, lr, clipping_theta, batch_size, pred_period, pred_len, prefixes)

#epoch增加,困惑度减小

LSTM
** 长短期记忆long short-term memory **:
遗忘门:控制上一时间步的记忆细胞
输入门:控制当前时间步的输入
输出门:控制从记忆细胞到隐藏状态
记忆细胞:⼀种特殊的隐藏状态的信息的流动

It=σ(XtWxi+Ht−1Whi+bi)Ft=σ(XtWxf+Ht−1Whf+bf)Ot=σ(XtWxo+Ht−1Who+bo)C˜t=tanh(XtWxc+Ht−1Whc+bc)Ct=Ft⊙Ct−1+It⊙C˜tHt=Ot⊙tanh(Ct)I_t = σ(X_tW_{xi} + H_{t−1}W_{hi} + b_i) \F_t = σ(X_tW_{xf} + H_{t−1}W_{hf} + b_f)\O_t = σ(X_tW_{xo} + H_{t−1}W_{ho} + b_o)\\widetilde{C}t = tanh(X_tW{xc} + H_{t−1}W_{hc} + b_c)\C_t = F_t ⊙C_{t−1} + I_t ⊙\widetilde{C}_t\H_t = O_t⊙tanh(C_t)

记忆细胞:特殊的隐藏状态,形状和隐藏状态Ht-1相同

初始化参数

num_inputs, num_hiddens, num_outputs = vocab_size, 256, vocab_size print('will use', device) def get_params(): def _one(shape): ts = torch.tensor(np.random.normal(0, 0.01, size=shape), device=device, dtype=torch.float32) return torch.nn.Parameter(ts, requires_grad=True) def _three(): return (_one((num_inputs, num_hiddens)), _one((num_hiddens, num_hiddens)), torch.nn.Parameter(torch.zeros(num_hiddens, device=device, dtype=torch.float32), requires_grad=True)) W_xi, W_hi, b_i = _three() # 输入门参数 W_xf, W_hf, b_f = _three() # 遗忘门参数 W_xo, W_ho, b_o = _three() # 输出门参数 W_xc, W_hc, b_c = _three() # 候选记忆细胞参数 # 输出层参数 W_hq = _one((num_hiddens, num_outputs)) b_q = torch.nn.Parameter(torch.zeros(num_outputs, device=device, dtype=torch.float32), requires_grad=True) return nn.ParameterList([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]) def init_lstm_state(batch_size, num_hiddens, device): #H-1和C-1的初始化 return (torch.zeros((batch_size, num_hiddens), device=device), torch.zeros((batch_size, num_hiddens), device=device)) LSTM模型 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) #返回当前列表,当前H,当前C 训练模型 num_epochs, num_steps, batch_size, lr, clipping_theta = 160, 35, 32, 1e2, 1e-2 pred_period, pred_len, prefixes = 40, 50, ['分开', '不分开'] d2l.train_and_predict_rnn(lstm, get_params, init_lstm_state, num_hiddens, vocab_size, device, corpus_indices, idx_to_char, char_to_idx, False, num_epochs, num_steps, lr, clipping_theta, batch_size, pred_period, pred_len, prefixes) #把第一个参数改成lstm

9
简洁实现

num_hiddens=256 num_epochs, num_steps, batch_size, lr, clipping_theta = 160, 35, 32, 1e2, 1e-2 pred_period, pred_len, prefixes = 40, 50, ['分开', '不分开'] lr = 1e-2 # 注意调整学习率 lstm_layer = nn.LSTM(input_size=vocab_size, hidden_size=num_hiddens) model = d2l.RNNModel(lstm_layer, vocab_size) d2l.train_and_predict_rnn_pytorch(model, num_hiddens, vocab_size, device, corpus_indices, idx_to_char, char_to_idx, num_epochs, num_steps, lr, clipping_theta, batch_size, pred_period, pred_len, prefixes)

深度循环神经网络

H(1)t=ϕ(XtW(1)xh+H(1)t−1W(1)hh+b(1)h)H(ℓ)t=ϕ(H(ℓ−1)tW(ℓ)xh+H(ℓ)t−1W(ℓ)hh+b(ℓ)h)Ot=H(L)tWhq+bq\boldsymbol{H}t^{(1)} = \phi(\boldsymbol{X}t \boldsymbol{W}{xh}^{(1)} + \boldsymbol{H}{t-1}^{(1)} \boldsymbol{W}{hh}^{(1)} + \boldsymbol{b}h{(1)})\\boldsymbol{H}_t{(\ell)} = \phi(\boldsymbol{H}t^{(\ell-1)} \boldsymbol{W}{xh}^{(\ell)} + \boldsymbol{H}{t-1}^{(\ell)} \boldsymbol{W}{hh}^{(\ell)} + \boldsymbol{b}_h^{(\ell)})\\boldsymbol{O}_t = \boldsymbol{H}t^{(L)} \boldsymbol{W}{hq} + \boldsymbol{b}_q

好处:可以抽取到更高层更抽象的信息

num_hiddens=256 num_epochs, num_steps, batch_size, lr, clipping_theta = 160, 35, 32, 1e2, 1e-2 pred_period, pred_len, prefixes = 40, 50, ['分开', '不分开'] lr = 1e-2 # 注意调整学习率 #区别,num_layers=2(前面不写的话默认=1) gru_layer = nn.LSTM(input_size=vocab_size, hidden_size=num_hiddens,num_layers=2) model = d2l.RNNModel(gru_layer, vocab_size).to(device) d2l.train_and_predict_rnn_pytorch(model, num_hiddens, vocab_size, device, corpus_indices, idx_to_char, char_to_idx, num_epochs, num_steps, lr, clipping_theta, batch_size, pred_period, pred_len, prefixes) gru_layer = nn.LSTM(input_size=vocab_size, hidden_size=num_hiddens,num_layers=6) model = d2l.RNNModel(gru_layer, vocab_size).to(device) d2l.train_and_predict_rnn_pytorch(model, num_hiddens, vocab_size, device, corpus_indices, idx_to_char, char_to_idx, num_epochs, num_steps, lr, clipping_theta, batch_size, pred_period, pred_len, prefixes)

#num_layers=2比=6的结果要好,说明深度循环神经网络不是越深越好,因为越深模型越复杂,对数据集要求也更高
#所以具体参数设置需要实验+经验结合

双向循环神经网络
在自然语言处理中较为常用

H−→tH←−t=ϕ(XtW(f)xh+H−→t−1W(f)hh+b(f)h)=ϕ(XtW(b)xh+H←−t+1W(b)hh+b(b)h)\begin{aligned} \overrightarrow{\boldsymbol{H}}t &= \phi(\boldsymbol{X}t \boldsymbol{W}{xh}^{(f)} + \overrightarrow{\boldsymbol{H}}{t-1} \boldsymbol{W}_{hh}^{(f)} + \boldsymbol{b}h^{(f)})\\overleftarrow{\boldsymbol{H}}t &= \phi(\boldsymbol{X}t \boldsymbol{W}{xh}^{(b)} + \overleftarrow{\boldsymbol{H}}{t+1} \boldsymbol{W}{hh}^{(b)} + \boldsymbol{b}_h^{(b)}) \end{aligned}

两个方向,另一个方向相当于,x1=xt’,xt=x1’。然后两个隐藏层concat拼接起来
好处,相当于同时考虑了前面的和后面的字对该字的影响

num_hiddens=128 num_epochs, num_steps, batch_size, lr, clipping_theta = 160, 35, 32, 1e-2, 1e-2 pred_period, pred_len, prefixes = 40, 50,

[‘分开’, ‘不分开’]

lr = 1e-2 # 注意调整学习率

#区别:加上bidirectional=True

gru_layer = nn.GRU(input_size=vocab_size, hidden_size=num_hiddens,bidirectional=True) model = d2l.RNNModel(gru_layer, vocab_size).to(device) d2l.train_and_predict_rnn_pytorch(model, num_hiddens, vocab_size, device, corpus_indices, idx_to_char, char_to_idx, num_epochs, num_steps, lr, clipping_theta, batch_size, pred_period, pred_len, prefixes) 动手学深度学习:梯度消失、梯度爆炸

目录:

梯度消失和梯度爆炸的基本概念
考虑到环境因素的其他问题
Kaggle房价预测# 梯度消失、梯度爆炸以及Kaggle房价预测
1、梯度消失和梯度爆炸的基本概念
1.1 梯度消失和梯度爆炸
深度模型有关数值稳定性的典型问题是消失(vanishing)和爆炸(explosion)。当神经网络的层数较多时,模型的数值稳定性容易变差。
假设一个层数为LLL的多层感知机的第lll层H(l)\boldsymbol{H}^{(l)}H
(l)
的权重参数为W(l)\boldsymbol{W}^{(l)}W
(l)
,输出层H(L)\boldsymbol{H}^{(L)}H
(L)
的权重参数为W(L)\boldsymbol{W}^{(L)}W
(L)
。为了便于讨论,不考虑偏差参数,且设所有隐藏层的激活函数为恒等映射(identity mapping)ϕ(x)=x\phi(x) = xϕ(x)=x。给定输入X\boldsymbol{X}X,多层感知机的第lll层的输出H(l)=XW(1)W(2)…W(l)\boldsymbol{H}^{(l)} = \boldsymbol{X} \boldsymbol{W}^{(1)} \boldsymbol{W}^{(2)} \ldots \boldsymbol{W}^{(l)}H
。此时,如果层数lll较大,H(l)\boldsymbol{H}^{(l)}H
(l)
的计算可能会出现衰减或爆炸。举个例子,假设输入和所有层的权重参数都是标量,如权重参数为0.2和5,多层感知机的第30层输出为输入X\boldsymbol{X}X分别与0.230≈1×10−210.2^{30} \approx 1 \times 10^{-21}0.2 30 ≈1×10 −21 (消失)和530≈9×10205^{30} \approx 9 \times 10^{20}5 30 ≈9×10 20 (爆炸)的乘积。当层数较多时,梯度的计算也容易出现消失或爆炸。

1.2 随机初始化模型参数
在神经网络中,通常需要随机初始化模型参数。下面我们来解释这样做的原因。为了方便解释,假设输出层只保留一个输出单元

以及指向它们的箭头),且隐藏层使用相同的激活函数。如果将每个隐藏单元的参数都初始化为相等的值,那么在正向传播时每个隐藏单元将根据相同的输入计算出相同的值,并传递至输出层。在反向传播中,每个隐藏单元的参数梯度值相等。因此,这些参数在使用基于梯度的优化算法迭代后值依然相等。之后的迭代也是如此。在这种情况下,无论隐藏单元有多少,隐藏层本质上只有1个隐藏单元在发挥作用。因此,正如在前面的实验中所做的那样,我们通常将神经网络的模型参数,特别是权重参数,进行随机初始化。

1.2 PyTorch的默认随机初始化
随机初始化模型参数的方法有很多。在线性回归的简洁实现中,我们使用torch.nn.init.normal_()使模型net的权重参数采用正态分布的随机初始化方式。不过,PyTorch中nn.Module的模块参数都采取了较为合理的初始化策略(不同类型的layer具体采样的哪一种初始化方法的可参考源代码),因此一般不用我们考虑。

1.3 Xavier随机初始化
假设某全连接层的输入个数为aaa,输出个数为bbb,Xavier随机初始化将使该层中权重参数的每个元素都随机采样于均匀分布

U(−6a+b−−−√,6a+b−−−√).U\left(-\sqrt{\frac{6}{a+b}}, \sqrt{\frac{6}{a+b}}\right).

它的设计主要考虑到,模型参数初始化后,每层输出的方差不该受该层输入个数影响,且每层梯度的方差也不该受该层输出个数影响。

2、 考虑环境因素
2.1 协变量偏移
这里我们假设,虽然输入的分布可能随时间而改变,但是标记函数,即条件分布P(y∣x)不会改变。虽然这个问题容易理解,但在实践中也容易忽视。

想想区分猫和狗的一个例子。我们的训练数据使用的是猫和狗的真实的照片,但是在测试时,我们被要求对猫和狗的卡通图片进行分类。

2.2 标签偏移
当我们认为导致偏移的是标签P(y)上的边缘分布的变化,但类条件分布是不变的P(x∣y)时,就会出现相反的问题。当我们认为y导致x时,标签偏移是一个合理的假设。例如,通常我们希望根据其表现来预测诊断结果。在这种情况下,我们认为诊断引起的表现,即疾病引起的症状。
有时标签偏移和协变量移位假设可以同时成立。
例如,当真正的标签函数是确定的和不变的,那么协变量偏移将始终保持,包括如果标签偏移也保持。有趣的是,当我们期望标签偏移和协变量偏移保持时,使用来自标签偏移假设的方法通常是有利的。这是因为这些方法倾向于操作看起来像标签的对象,这(在深度学习中)与处理看起来像输入的对象(在深度学习中)相比相对容易一些。病因(要预测的诊断结果)导致 症状(观察到的结果)。 训练数据集,数据很少只包含流感p(y)的样本。 而测试数据集有流感p(y)和流感q(y),其中不变的是流感症状p(x|y)。

2.3 概念偏移
另一个相关的问题出现在概念转换中,即标签本身的定义发生变化的情况。这听起来很奇怪,毕竟猫就是猫。的确,猫的定义可能不会改变,但我们能不能对软饮料也这么说呢?事实证明,如果我们周游美国,按地理位置转移数据来源,我们会发现,即使是这个简单术语的定义也会发生相当大的概念转变。

如果我们要建立一个机器翻译系统,分布P(y∣x)可能因我们的位置而异。这个问题很难发现。另一个可取之处是P(y∣x)通常只是逐渐变化。

动手学深度学习:过拟合、欠拟合及其解决方案

目录:

相关的基本概念
权重衰减
代码实现
丢弃法
代码实现
1、相关的基本概念
训练误差:
模型在训练数据集上表现出的误差。

泛化误差:
模型在任意一个测试数据样本上表现出的误差的期望,并常常通过测试数据集上的误差来近似。

欠拟合(underfitting):
模型无法得到较低的训练误差。

过拟合(overfitting):
模型的训练误差远小于它在测试数据集上的误差。

注:在实践中,我们要尽可能同时应对欠拟合和过拟合。虽然有很多因素可能导致这两种拟合问题,重点讨论两个因素:模型复杂度和训练数据集大小。

模型复杂度
为了解释模型复杂度,我们以多项式函数拟合为例。给定一个由标量数据特征xxx和对应的标量标签yyy组成的训练数据集,多项式函数拟合的目标是找一个KKK阶多项式函数

yˆ=b+∑Kk=1xkwk\hat{y} = b + \sum_{k=1}^K x^k w_k

来近似 yyy。在上式中,wkw_kw 是模型的权重参数,bbb是偏差参数。与线性回归相同,多项式函数拟合也使用平方损失函数。特别地,一阶多项式函数拟合又叫线性函数拟合。

给定训练数据集,模型复杂度和误差之间的关系:

训练数据集大小
影响欠拟合和过拟合的另一个重要因素是训练数据集的大小。一般来说,如果训练数据集中样本数过少,特别是比模型参数数量(按元素计)更少时,过拟合更容易发生。此外,泛化误差不会随训练数据集里样本数量增加而增大。因此,在计算资源允许的范围之内,我们通常希望训练数据集大一些,特别是在模型复杂度较高时,例如层数较多的深度学习模型。
1)正常拟合

2)过拟合

3)欠拟合

2、 权重衰减
2.1、方法
权重衰减等价于 L2L_2L 范数正则化(regularization)。正则化通过为模型损失函数添加惩罚项使学出的模型参数值较小,是应对过拟合的常用手段。
2.2、L2 范数正则化(regularization)
L2L_2L
2 范数正则化在模型原损失函数基础上添加L2L_2L
2 范数惩罚项,从而得到训练所需要最小化的函数。L2L_2L
2 范数惩罚项指的是模型权重参数每个元素的平方和与一个正的常数的乘积。以线性回归中的线性回归损失函数为例

其中超参数λ>0\lambda > 0λ>0。当权重参数均为0时,惩罚项最小。当λ\lambdaλ较大时,惩罚项在损失函数中的比重较大,这通常会使学到的权重参数的元素较接近0。当λ\lambdaλ设为0时,惩罚项完全不起作用。上式中L2L_2L
2

范数正则化又叫权重衰减。权重衰减通过惩罚绝对值较大的模型参数为需要学习的模型增加了限制,这可能对过拟合有效。
————————————————

原文链接:https://blog.csdn.net/blexsantos/article/details/104332638

3、代码实现

%matplotlib inline import torch import numpy as np import sys sys.path.append("/home/kesci/input") import d2lzh1981 as d2l print(torch.__version__) #初始化模型参数 n_train, n_test, true_w, true_b = 100, 100, [1.2, -3.4, 5.6], 5 features = torch.randn((n_train + n_test, 1)) poly_features = torch.cat((features, torch.pow(features, 2), torch.pow(features, 3)), 1) 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] #定义、训练和测试模型 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()) # 将训练损失保存到train_ls中 test_ls.append(loss(net(test_features), test_labels).item()) # 将测试损失保存到test_ls中 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)

#三阶多项式函数拟合(正常)
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:])

4、高维线性回归实验从零开始的实现
下面,我们以高维线性回归为例来引入一个过拟合问题,并使用权重衰减来应对过拟合。设数据样本特征的维度为ppp。对于训练数据集和测试数据集中特征为x1,x2,…,xpx_1, x_2, \ldots, x_px

的任一样本,我们使用如下的线性函数来生成该样本的标签:

y=0.05+∑pi=10.01xi+ϵy = 0.05 + \sum_{i = 1}^p 0.01x_i + \epsilon
y=0.05+
i=1

其中噪声项ϵ\epsilonϵ服从均值为0、标准差为0.01的正态分布。为了较容易地观察过拟合,我们考虑高维线性回归问题,如设维度p=200p=200p=200;同时,我们特意把训练数据集的样本数设低,如20。

4.1、代码实现

%matplotlib inline import torch import torch.nn as nn import numpy as np import sys sys.path.append("/home/kesci/input") import d2lzh1981 as d2l print(torch.__version__) #初始化模型参数 n_train, n_test, num_inputs = 20, 100, 200 true_w, true_b = torch.ones(num_inputs, 1) * 0.01, 0.05 features = torch.randn((n_train + n_test, num_inputs)) labels = torch.matmul(features, true_w) + true_b labels += torch.tensor(np.random.normal(0, 0.01, size=labels.size()), dtype=torch.float) train_features, test_features = features[:n_train, :], features[n_train:, :] train_labels, test_labels = labels[:n_train], labels[n_train:] 定义参数初始化函数,初始化模型参数并且附上梯度 def init_params(): w = torch.randn((num_inputs, 1), requires_grad=True) b = torch.zeros(1, requires_grad=True) return [w, b] #定义L2范数惩罚项 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)

#简洁实现
def fit_and_plot_pytorch(wd):
# 对权重参数衰减。权重名称一般是以weight结尾
net = nn.Linear(num_inputs, 1)
nn.init.normal_(net.weight, mean=0, std=1)
nn.init.normal_(net.bias, mean=0, std=1)
optimizer_w = torch.optim.SGD(params=[net.weight], lr=lr, weight_decay=wd) # 对权重参数衰减
optimizer_b = torch.optim.SGD(params=[net.bias], lr=lr) # 不对偏差参数衰减

train_ls, test_ls = [], [] for _ in range(num_epochs): for X, y in train_iter: l = loss(net(X), y).mean() optimizer_w.zero_grad() optimizer_b.zero_grad() l.backward() # 对两个optimizer实例分别调用step函数,从而分别更新权重和偏差 optimizer_w.step() optimizer_b.step() train_ls.append(loss(net(train_features), train_labels).mean().item()) test_ls.append(loss(net(test_features), 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:', net.weight.data.norm().item())

fit_and_plot_pytorch(0)

fit_and_plot_pytorch(3)

5、丢弃法
多层感知机中神经网络图描述了一个单隐藏层的多层感知机。其中输入个数为4,隐藏单元个数为5,且隐藏单元hih_ih
i

(i=1,…,5i=1, \ldots, 5i=1,…,5)的计算表达式为

hi=ϕ(x1w1i+x2w2i+x3w3i+x4w4i+bi)h_i = \phi\left(x_1 w_{1i} + x_2 w_{2i} + x_3 w_{3i} + x_4 w_{4i} + b_i\right)
h

,偏差参数为bib_ib
i

即丢弃法不改变其输入的期望值。让我们对之前多层感知机的神经网络中的隐藏层使用丢弃法,一种可能的结果如图所示,其中h2h_2h

5.1、丢弃法从零开始的实现
%matplotlib inline
import torch
import torch.nn as nn
import numpy as np
import sys
sys.path.append("/home/kesci/input")
import d2lzh1981 as d2l

print(torch.version)

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

X = torch.arange(16).view(2, 8)
dropout(X, 0)

dropout(X, 0.5)

dropout(X, 1.0)

参数的初始化

num_inputs, num_outputs, num_hiddens1, num_hiddens2 = 784, 10, 256, 256

W1 = torch.tensor(np.random.normal(0, 0.01, size=(num_inputs, num_hiddens1)), dtype=torch.float, requires_grad=True)
b1 = torch.zeros(num_hiddens1, requires_grad=True)
W2 = torch.tensor(np.random.normal(0, 0.01, size=(num_hiddens1, num_hiddens2)), dtype=torch.float, requires_grad=True)
b2 = torch.zeros(num_hiddens2, requires_grad=True)
W3 = torch.tensor(np.random.normal(0, 0.01, size=(num_hiddens2, num_outputs)), dtype=torch.float, requires_grad=True)
b3 = torch.zeros(num_outputs, requires_grad=True)

params = [W1, b1, W2, b2, W3, b3]
drop_prob1, drop_prob2 = 0.2, 0.5

def net(X, is_training=True):
X = X.view(-1, num_inputs)
H1 = (torch.matmul(X, W1) + b1).relu()
if is_training: # 只在训练模型时使用丢弃法
H1 = dropout(H1, drop_prob1) # 在第一层全连接后添加丢弃层
H2 = (torch.matmul(H1, W2) + b2).relu()
if is_training:
H2 = dropout(H2, drop_prob2) # 在第二层全连接后添加丢弃层
return torch.matmul(H2, W3) + b3
def evaluate_accuracy(data_iter, net):
acc_sum, n = 0.0, 0
for X, y in data_iter:
if isinstance(net, torch.nn.Module):
net.eval() # 评估模式, 这会关闭dropout
acc_sum += (net(X).argmax(dim=1) == y).float().sum().item()
net.train() # 改回训练模式
else: # 自定义的模型
if(‘is_training’ in net.code.co_varnames): # 如果有is_training这个参数
# 将is_training设置成False
acc_sum += (net(X, is_training=False).argmax(dim=1) == y).float().sum().item()
else:
acc_sum += (net(X).argmax(dim=1) == y).float().sum().item()
n += y.shape[0]
return acc_sum / n

num_epochs, lr, batch_size = 5, 100.0, 256 # 这里的学习率设置的很大,原因与之前相同。
loss = torch.nn.CrossEntropyLoss()
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size, root=’/home/kesci/input/FashionMNIST2065’)
d2l.train_ch3(
net,
train_iter,
test_iter,
loss,
num_epochs,
batch_size,
params,
lr)

5.2、简洁实现
net = nn.Sequential(
d2l.FlattenLayer(),
nn.Linear(num_inputs, num_hiddens1),
nn.ReLU(),
nn.Dropout(drop_prob1),
nn.Linear(num_hiddens1, num_hiddens2),
nn.ReLU(),
nn.Dropout(drop_prob2),
nn.Linear(num_hiddens2, 10)
)

for param in net.parameters():
nn.init.normal_(param, mean=0, std=0.01)

optimizer = torch.optim.SGD(net.parameters(), lr=0.5)
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, batch_size, None, None, optimizer)


作者:loveheart123



动手学 kaggle 过拟合 欠拟合 循环神经网络 循环 梯度 神经网络 解决方案

需要 登录 后方可回复, 如果你还没有账号请 注册新账号
相关文章
Tricia 2020-10-13
661