将许多全连接层堆叠在一起。每一层都输出到上面的层,直到生成最后的输出。我们可以把前L−1层看作表示,把最后一层看作线性预测器。这种架构通常称为多层感知机通常缩写为MLP。
1 激活函数
激活函数(activation function)通过计算加权和并加上偏置来确定神经元是否应该被激活,它们将输入信号
转换为输出的可微运算。大多数激活函数都是非线性的。
1.1ReLU函数
给定元素𝑥,ReLU函数被定义为该元素与0的最大值:
通俗地说,ReLU函数通过将相应的活性值设为0,仅保留正元素并丢弃所有负元素。 为了直观感受一下,我们可以画出函数的曲线图。 正如从图中所看到,激活函数是分段线性的。
python">x = torch.arange(-8.0, 8.0, 0.1, requires_grad=True)
y = torch.relu(x)
d2l.plot(x.detach(), y.detach(), 'x', 'relu(x)', figsize=(5, 2.5))
sigmoid函数
[*对于一个定义域在ℝ中的输入, sigmoid函数将输入变换为区间(0, 1)上的输出]
python">y = torch.sigmoid(x)
d2l.plot(x.detach(), y.detach(), 'x', 'sigmoid(x)', figsize=(5, 2.5))
tanh函数
[tanh(双曲正切)函数也能将其输入压缩转换到区间(-1, 1)上]
python">y = torch.tanh(x)
d2l.plot(x.detach(), y.detach(), 'x', 'tanh(x)', figsize=(5, 2.5))
2 MLP实现
添加了2个全连接层, 第一层是[隐藏层],它(包含256个隐藏单元,并使用了ReLU激活函数)。 第二层是输出层。
python">import torch
from torch import nn
from d2l import torch as d2lnet = nn.Sequential(nn.Flatten(),nn.Linear(784, 256),nn.ReLU(),nn.Linear(256, 10))def init_weights(m):if type(m) == nn.Linear:nn.init.normal_(m.weight, std=0.01)net.apply(init_weights);batch_size, lr, num_epochs = 256, 0.1, 10
loss = nn.CrossEntropyLoss(reduction='none')
trainer = torch.optim.SGD(net.parameters(), lr=lr)train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)
3 权重衰减
权重衰减(weight decay)是最广泛使用的正则化的技术之一,它通常也被称为L2正则化。通过函数与零的距离来衡量函数的复杂度。将其范数作为惩罚项加到最小化损失的问题中。将原来的训练目标最小化训练标签上的预测损失,调整为最小化预测损失和惩罚项之和。
权重衰减为我们提供了一种连续的机制来调整函数的复杂度。较小的λ值对应较少约束的w,而较大的λ值对w的约束更大
在实例化优化器时直接通过weight_decay指定weight decay超参数。默认情况下,PyTorch同时衰减权重和偏移。这里我们只为权重设置了weight_decay,所以偏置参数b不会衰减。
python">def train_concise(wd):net = nn.Sequential(nn.Linear(num_inputs, 1))for param in net.parameters():param.data.normal_()loss = nn.MSELoss(reduction='none')num_epochs, lr = 100, 0.003# 偏置参数没有衰减trainer = torch.optim.SGD([{"params":net[0].weight,'weight_decay': wd},{"params":net[0].bias}], lr=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:trainer.zero_grad()l = loss(net(X), y)l.mean().backward()trainer.step()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范数:', net[0].weight.norm().item())
python">train_concise(0)
python">train_concise(3)
4 暂退法(Dropout)
在训练过程中,在计算后续层之前向网络的每一层注入噪声。因为当训练一个有多层的深层网络时,注入噪声只会在输入‐输出映射上增强平滑性。
对于深度学习框架的高级API,我们只需在每个全连接层之后添加一个Dropout层,将暂退概率作为唯一的参数传递给它的构造函数。在训练时,Dropout层将根据指定的暂退概率随机丢弃上一层的输出(相当于下一层的输入)。在测试时,Dropout层仅传递数据。
python">net = nn.Sequential(nn.Flatten(),nn.Linear(784, 256),nn.ReLU(),# 在第一个全连接层之后添加一个dropout层nn.Dropout(dropout1),nn.Linear(256, 256),nn.ReLU(),# 在第二个全连接层之后添加一个dropout层nn.Dropout(dropout2),nn.Linear(256, 10))def init_weights(m):if type(m) == nn.Linear:nn.init.normal_(m.weight, std=0.01)net.apply(init_weights);
python">trainer = torch.optim.SGD(net.parameters(), lr=lr)
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)
5 前向传播、反向传播和计算图
5.1 前向传播
按顺序(从输入层到输出层)计算和存储神经网络中每层的结果。
5.2 反向传播
根据微积分中的链式规则,按相反的顺序从输出层到输入层遍历网络。
在初始化模型参数后,我们交替使用前向传播和反向传播,利用反向传播给出的梯度来更新模型参数。注意,反向传播重复利用前向传播中存储的中间值,以避免重复计算。
6 数值稳定性和模型初始化
6.1梯度消失和梯度爆炸
梯度爆炸(gradient exploding)问题:参数更新过大,破坏了模型的稳定收敛;
梯度消失(gradient vanishing)问题:参数更新过小,在每次更新时几乎不会移动,导致模型无法学习。
解决(或至少减轻)上述问题的一种方法是进行参数初始化。