多层感知机总结-PyTorch

动手学深度学习-多重感知机笔记

  • 多层感知机
    • 多层感知机的从零开始实现
    • 多层感知机的简洁实现
  • 范数与权重衰减(weight decay)
  • 暂退法(Dropout)
  • 前向传播和反向传播
  • 梯度消失和梯度爆炸
  • 模型验证方法
  • 过拟合和欠拟合


多层感知机

多层感知机(multilayer perceptron,简称MLP):由多层神经元组成,每一层都与下面一层和上面一层完全相连。
多层感知机总结-PyTorch_第1张图片

图1:含有一个隐藏层的多层感知机

图1的感知机模型层数为2,输入层不涉及计算,所以一般不算输入层。隐藏层和输出层是全连接的。每个输入都会影响隐藏层中的每个神经元,而隐藏层中的每个神经元又会影响输出层中的每个神经元。

我们通过矩阵 X ∈ R n × d X\in R^{n×d} XRn×d来表示n个样本的小批量,其中每个样本具有d个输入特征。对于具有h个隐藏单元的单隐藏层多层感知机,用 H ∈ R n × h H∈R^{n×h} HRn×h表示隐藏层的输出。隐藏层权重 W ( 1 ) ∈ R d × h W^{(1)}∈R^{d×h} W(1)Rd×h和隐藏层偏置 b ( 1 ) ∈ R 1 × h b^{(1)}∈R^{1×h} b(1)R1×h以及输出层权重 W ( 2 ) ∈ R h × q W^{(2)}∈R^{h×q} W(2)Rh×q 和输出层偏置 b ( 2 ) ∈ R 1 × q b^{(2)}∈R^{1×q} b(2)R1×q。形式上,我们按如下方式计算单隐藏层多层感知机的输出 O ∈ R n × q O∈R^{n×q} ORn×q:
H = σ ( X W ( 1 ) + b ( 1 ) ) O = H W ( 2 ) + b ( 2 ) H=\sigma (XW^{(1)}+b^{(1)})\\ O=HW^{(2)}+b^{(2)} H=σ(XW(1)+b(1))O=HW(2)+b(2)在仿射变换之后应用非线性激活函数 σ ( ⋅ ) \sigma(·) σ()防止多层感知机退化成线性模型。

(由于 X X X中的每一行对应于小批量中的一个样本,出于记号习惯的考量,我们定义非线性函数σ也以按行的方式作用于其输入,即一次计算一个样本。)

多层感知机在输出层和输入层之间增加一个或多个全连接隐藏层,并通过激活函数转换隐藏层的输出。

常用的激活函数包括ReLU函数、Sigmoid函数和Tanh函数。


多层感知机的从零开始实现

Fashion-MNIST中的每个图像由 28 × 28 = 784个灰度像素值组成。所有图像共分为10个类别。忽略像素之间的空间结构,我们可以将每个图像视为具有784个输入特征和10个类的简单分类数据集。首先,我们将实现一个具有单隐藏层的多层感知机,它包含256个隐藏单元。通常,我们选择2的若干次幂作为层的宽度。因为内存在硬件中的分配和寻址方式,这么做往往可以在计算上更高效。

import torch
from torch import nn
from d2l import torch as d2l

batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
num_inputs, num_outputs, num_hiddens = 784, 10, 256

# 初始化模型参数
W1 = nn.Parameter(torch.randn(num_inputs, num_hiddens, requires_grad=True) * 0.01)
b1 = nn.Parameter(torch.zeros(num_hiddens, requires_grad=True)) 
W2 = nn.Parameter(torch.randn(num_hiddens, num_outputs, requires_grad=True) * 0.01)
b2 = nn.Parameter(torch.zeros(num_outputs, requires_grad=True))
params = [W1, b1, W2, b2]

# 定义ReLU激活函数
def relu(X):
    a = torch.zeros_like(X) 
    return torch.max(X, a)

# 多重感知机
def net(X):
    X = X.reshape((-1, num_inputs))
    H = relu(X@W1 + b1) # 这里“@”代表矩阵乘法
    return (H@W2 + b2)
loss = nn.CrossEntropyLoss()

num_epochs, lr = 10, 0.1
updater = torch.optim.SGD(params, lr=lr)
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, updater)

d2l.predict_ch3(net, test_iter)

多层感知机总结-PyTorch_第2张图片

多层感知机的简洁实现

import torch
from torch import nn
from d2l import torch as d2l

net = 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()
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)

多层感知机总结-PyTorch_第3张图片

范数与权重衰减(weight decay)

权重衰减是正则化的技术之一,它通常也被称为L2正则化。

L1正则化线性回归通常被称为套索回归(lasso regression),L1 正则化会导致模型将权重集中在一小部分特征上, 而将其他权重清除为零,可用于特征选择。

L2正则化线性模型构成经典的岭回归(ridge regression)算法。L2范数对权重向量的大分量施加了巨大的惩罚,这使得我们的学习算法偏向于在大量特征上均匀分布权重的模型。

• 正则化是处理过拟合的常用方法:在训练集的损失函数中加入惩罚项,以降低学习到的模型的复杂度。

L2正则化线性模型损失函数:
L ( w , b ) = 1 n ∑ i = 1 n l ( i ) ( w , b ) + λ 2 ∣ ∣ w ∣ ∣ 2 L(\pmb w,b)=\frac{1}{n}\sum_{i=1}^nl^{(i)}(\pmb w,b)+\frac{\lambda}{2}||\pmb w||^2 L(www,b)=n1i=1nl(i)(www,b)+2λwww2
L2正则化回归的小批量随机梯度下降更新如下式:
w ← ( 1 − η λ ) w − η ∣ B ∣ ∑ i ∈ B ∂ w l ( i ) ( w , b ) \pmb w\gets{\color{Red}(1-\eta\lambda)\pmb w}-\frac{\eta}{|\mathcal B|}\sum_{i\in\mathcal B}{\partial}_{\pmb w}l^{(i)}(\pmb w,b) www(1ηλ)wwwBηiBwwwl(i)(www,b)
根据估计值与观测值之间的差异来更新 w \pmb w www,同时也在试图将 w \pmb w www的大小缩小到零。这就是为什么这种方法有时被称为权重衰减。较小的λ值对应较少约束的 w \pmb w www,而较大的λ值对 w \pmb w www的约束更大。

%matplotlib inline
import torch
from torch import nn
from d2l import torch as d2l

n_train, n_test, num_inputs, batch_size = 20, 100, 200, 5
true_w, true_b = torch.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)

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.sum().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())
# train_concise(0)
train_concise(3)

多层感知机总结-PyTorch_第4张图片

• 正则化是处理过拟合的常用方法:在训练集的损失函数中加入惩罚项,以降低学习到的模型的复杂度。

• 保持模型简单的一个特别的选择是使用L2惩罚的权重衰减。这会导致学习算法更新步骤中的权重衰减。

• 权重衰减功能在深度学习框架的优化器中提供。

暂退法(Dropout)

如图有1个隐藏层和5个隐藏单元的多层感知机。将暂退法应用到隐藏层,以p的概率将隐藏单元置为零时,结果可以看作是一个只包含原始神经元子集的网络。比如删除了 h 2 h_2 h2 h 5 h_5 h5, 因此输出的计算不再依赖于 h 2 h_2 h2 h 5 h_5 h5 ,并且它们各自的梯度在执行反向传播时也会消失。这样,输出层的计算不能过度依赖于 h 1 , . . . , h 5 h_1, . . . , h_5 h1,...,h5的任何一个元素。
多层感知机总结-PyTorch_第5张图片
可以将暂退法应用于每个隐藏层的输出(在激活函数之后),并且可以为每一层分别设置暂退概率:常⻅的技巧是在靠近输入层的地方设置较低的暂退概率。一般暂退法只在训练期间有效。在测试时,Dropout层仅传递数据。

dropout1, dropout2 = 0.2, 0.5
num_epochs, lr, batch_size = 10, 0.5, 256

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)

        
loss = nn.CrossEntropyLoss()
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
net.apply(init_weights)
trainer = torch.optim.SGD(net.parameters(), lr=lr)
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)

前向传播和反向传播

训练神经网络时,交替使用前向传播和反向传播,利用反向传播给出的梯度来更新模型参数。

注意,反向传播重复利用前向传播中存储的中间值,以避免重复计算。带来的影响之一是我们需要保留中间值,直到反向传播完成。这也是训练比单纯的预测需要更多的内存(显存)的原因之一。此外,这些中间值的大小与网络层的数量和批量的大小大致成正比。

梯度消失和梯度爆炸

梯度爆炸(gradient exploding)问题:参数更新过大,破坏了模型的稳定收敛;

梯度消 失(gradient vanishing)问题:参数更新过小,在每次更新时几乎不会移动,导致模型无法学习。

模型评估指标可以看这个(模型验证方法、欠拟合和过拟合等)

模型验证方法

Holdout检验:将原始的样本集合随机划分成训练集和验证集两部分,常见的是70%的样本为训练集;30% 的样本为验证集。

k折交叉验证:将数据集划分为k个相等的子集,每次取一个子集作为验证集,其余k-1个作为训练集,最后将k次结果取平均,实际中,k常取10。

自助法(Bootstrap):当数据集规模比较小时再划分训练集和测试集会影响模型训练效果。对于总数为n的样本集合,进行n次有放回的随机抽样,得到大小为n的训练集。n次采样过程中,有的样本会被重复采样,有的样本没有被抽出过,将这些没有被抽出的样本作为验证集,进行模型验证,这就是自助法的验证过程。

过拟合和欠拟合

过拟合:指模型对于训练数据拟合过当,导致模型在训练集上的表现很好,但在测试集和新数据上的表现较差。

欠拟合:指模型在训练和预测时表现都不好的情况。

过拟合说明模型过于复杂,把噪声数据的特征也学习到模型中,导致模型泛化能力下降。降低“过拟合”,可以通过以下方法:

1)使用更多的训练数据让模型学习到更多更有效的特征,减小噪声的影响。

2)降低模型复杂度以避免模型拟合过多的采样噪声。

3)正则化,如L1正则化 L = L 0 + λ ∣ ∣ w ∣ ∣ 1 L=L_0+\lambda||w||_1 L=L0+λw1,L2正则化 L = L 0 + λ 2 ∣ ∣ w ∣ ∣ 2 2 L=L_0+\frac{\lambda}{2}||w||^2_2 L=L0+2λw22

4)集成学习,如基于Bagging的算法和基于Boosting的算法。

降低“过拟合”,可以通过以下方法:

1)添加新特征,当特征不足或者现有特征与样本标签的相关性不强时,模型容易出现欠拟合。

2)增加模型复杂度。

3)减小正则化系数。

你可能感兴趣的:(深度学习知识点,pytorch,深度学习,机器学习,神经网络,回归)