李沐《动手学深度学习》多层感知机 模型概念和代码实现

发布时间:2024年01月19日

系列文章

李沐《动手学深度学习》预备知识 张量操作及数据处理
李沐《动手学深度学习》预备知识 线性代数及微积分
李沐《动手学深度学习》线性神经网络 线性回归
李沐《动手学深度学习》线性神经网络 softmax回归


教材:李沐《动手学深度学习》

一、多层感知机

(一)隐藏层(提出原因、方式、多层感知机)

  1. 隐藏层的提出原因——线性模型可能会出错
    线性意味着单调假设,但是现实生活中存在许多违反单调性的情况:

    • 根据体温预测死亡率:对体温高于37摄氏度的人来说,温度越高风险越大;对体温低于37摄氏度的人来说,温度越高风险就越低。
    • 对猫和狗的图像进行分类:倒置图像后依然保留类别。
  2. 隐藏层的最简单方式:将许多全连接层堆叠在一起
    多层感知机架构:将许多全连接层堆叠在一起,每一层都输出到上面的层,直到生成最后的输出。

    • 4个输入,3个输出,隐藏层包含5个隐藏单元;
    • 输入层不涉及任何计算,只需要实现隐藏层和输出层的计算,因此层数为2;
    • 该MLP的两个层都是全连接的,每个输入都会影响隐藏层中的每个神经元, 而隐藏层中的每个神经元又会影响输出层中的每个神经元;
    • 多层感知机可以通过隐藏神经元,捕捉到输入之间复杂的相互作用。
      在这里插入图片描述
  3. 多层感知机在输出层和输入层之间增加一个或多个全连接隐藏层,并通过激活函数转换隐藏层的输出。

(二)激活函数(ReLU、sigmoid、tanh)

  1. 激活函数的概念
    • 作用:实现线性到非线性的飞跃;
    • 激活函数通过计算加权和并加上偏置来确定神经元是否应该被激活, 它们将输入信号转换为输出的可微运算;
    • 大多数激活函数都是非线性的。
  2. ReLU函数
    • 修正线性单元(Rectified linear unit,ReLU)
    • 给定元素 x x x,ReLU函数定义为该元素与0的最大值:
      R e L U ( x ) = m a x ( x , 0 ) ReLU(x)=max(x,0) ReLU(x)=max(x,0)
    • ReLU函数是分段线性的,仅保留正元素丢弃负元素。

ReLU函数:

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))

在这里插入图片描述
ReLU函数的导数: 输入为负时,导数为0;输入为正时,导数为1。

y.backward(torch.ones_like(x), retain_graph=True)
d2l.plot(x.detach(), x.grad, 'x', 'grad of relu', figsize=(5, 2.5))

在这里插入图片描述

ReLU求导后:要么让参数消失,要么让参数通过。 这使得优化表现得更好,并且ReLU减轻了困扰以往神经网络的梯度消失问题。

  1. sigmoid函数
    • sigmoid函数将一个定义域在R中的输入变换为区间(0,1)上的输出;
    • sigmoid也被称为挤压函数,将范围(-inf,inf)中的任意输入压缩到(0,1)中的某个值:
      s i g m o i d ( x ) = 1 1 + e x p ( ? x ) sigmoid(x)=\frac{1}{1+exp(-x)} sigmoid(x)=1+exp(?x)1?
    • sigmoid是一个平滑的、可微的阈值单元近似;
    • 将输出视作二元分类问题的概率时,sigmoid被广泛用作输出单元上的激活函数;
    • sigmoid在隐藏层中已经较少使用,在大部分时候被更简单、更容易训练的ReLU取代。

sigmoid函数:

y = torch.sigmoid(x)
d2l.plot(x.detach(), y.detach(), 'x', 'sigmoid(x)', figsize=(5, 2.5))

在这里插入图片描述
sigmoid函数的导数:
d d x s i g m o i d ( x ) = e x p ( ? x ) ( 1 + e x p ( ? x ) ) 2 = s i g m o i d ( x ) ( 1 ? s i g m o i d ( x ) ) \frac{d}{dx}sigmoid(x)=\frac{exp(-x)}{({{1+exp(-x)}})^2}=sigmoid(x)(1-sigmoid(x)) dxd?sigmoid(x)=(1+exp(?x))2exp(?x)?=sigmoid(x)(1?sigmoid(x))
当输入为0时,sigmoid函数的导数达到最大值0.25,输入在任一方向越远离0点时,导数越接近0。

# 清除以前的梯度
x.grad.data.zero_()
y.backward(torch.ones_like(x),retain_graph=True)
d2l.plot(x.detach(), x.grad, 'x', 'grad of sigmoid', figsize=(5, 2.5))

在这里插入图片描述

  1. tanh函数
    • 与sigmoid函数类似, tanh(双曲正切)函数也能将其输入压缩转换到区间(-1, 1)上:
      t a n h ( x ) = 1 ? e x p ( ? 2 x ) 1 + e x p ( ? 2 x ) tanh(x)=\frac{1-exp(-2x)}{1+exp(-2x)} tanh(x)=1+exp(?2x)1?exp(?2x)?
    • 输入为0附近时,tanh函数接近线性变换;
    • tanh函数形状类似于sigmoid函数,不同的是tanh函数关于坐标系原点中心对称。

tanh函数:

y = torch.tanh(x)
d2l.plot(x.detach(), y.detach(), 'x', 'tanh(x)', figsize=(5, 2.5))

在这里插入图片描述
tanh函数的导数:
d d x t a n h ( x ) = 1 ? t a n h 2 ( x ) \frac{d}{dx}tanh(x)=1-tanh^2(x) dxd?tanh(x)=1?tanh2(x)
当输入接近0时,tanh函数的导数接近最大值1,输入在任一方向越远离0点时,导数越接近0。

# 清除以前的梯度
x.grad.data.zero_()
y.backward(torch.ones_like(x),retain_graph=True)
d2l.plot(x.detach(), x.grad, 'x', 'grad of tanh', figsize=(5, 2.5))

在这里插入图片描述

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

(一)导入数据和相关库

导入相关库,设置批量大小为256,调用load_data_fashion_mnist函数获取数据集

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]

(三)模型构建

  1. ReLU激活函数
def relu(X):
    a = torch.zeros_like(X)
    return torch.max(X, a)
  1. 模型实现

隐藏层数目和隐藏单元数视为超参数,一般选择2的若干次幂为层的宽度, 因为内存在硬件中的分配和寻址方式,这么做往往可以在计算上更高效。

  • 输入:将28x28=784的灰度像素图像展平
  • 中间层:选择实现一个具有单隐藏层的多层感知机,包含256个隐藏单元
  • 输出:10个类别
def net(X):
    X = X.reshape((-1, num_inputs))
    H = relu(X@W1 + b1)  # 这里“@”代表矩阵乘法
    return (H@W2 + b2)

(四)损失函数

李沐《动手学深度学习》线性神经网络 softmax回归中实现了交叉熵损失函数,因此这里直接使用高级API中的内置函数计算softmax和交叉熵损失。

loss = nn.CrossEntropyLoss(reduction='none')

(五)训练

多层感知机的训练过程与softmax回归的训练过程完全相同。 可以直接调用d2l包的train_ch3函数, 将迭代周期数设置为10,并将学习率设置为0.1:

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)

在这里插入图片描述为了对学习到的模型进行评估,在测试数据上应用这个模型:
在这里插入图片描述

三、多层感知机的简洁实现

  1. 导入相关库
import torch
from torch import nn
from d2l import torch as d2l
  1. 模型构建
  • 展平层:调整网络输入的形状
  • 全连接层/线性层:输入784,输出256
  • ReLU激活
  • 全连接层/线性层:输入256,输出10
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);
  1. 训练
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)

在这里插入图片描述

文章来源:https://blog.csdn.net/weixin_47748259/article/details/135685359
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。