动手学深度学习——多层感知机(原理解释+代码详解)

目录

  • 一、多层感知机
    • 1. 隐藏层
      • 1.1 线性模型可能会出错
      • 1.2 在网络中加入隐藏层
      • 1.3 从线性到非线性
      • 1.4 通用近似定理
    • 2. 激活函数
      • 2.1 ReLU函数
      • 2.2 sigmoid函数
      • 2.3 tanh函数
    • 3. 小结
  • 二、多层感知机的从零开始实现
    • 2.1 初始化模型参数
    • 2.2 激活函数
    • 2.3 模型
    • 2.4 损失函数
    • 2.5 训练
  • 三、多层感知机的简洁实现

一、多层感知机

1. 隐藏层

1.1 线性模型可能会出错

  1. 线性意味着单调假设: 特征的增大导致模型输出的增大(权重为正), 或者导致模型输出的减小(权重为负)。
  2. 数据通过一种表示,这种表示会考虑到特征之间的相关交互作用。

1.2 在网络中加入隐藏层

  1. 在网络中加入一个或多个隐藏层来克服线性模型的限制, 使其能处理更普遍的函数关系类型。
  2. 将许多全连接层堆叠在一起, 每一层都输出到上面的层,直到生成最后的输出。 前L-1层看作表示,把最后一层看作线性预测器。这种架构通常称为多层感知机(multilayer perceptron),通常缩写为MLP。动手学深度学习——多层感知机(原理解释+代码详解)_第1张图片
  3. 这个多层感知机有4个输入,3个输出,其隐藏层包含5个隐藏单元。输入层不涉及任何计算,因此使用此网络产生输出只需要实现隐藏层和输出层的计算。 因此,这个多层感知机中的层数为2。

1.3 从线性到非线性

  1. 对于h个隐藏单元的单隐藏层多层感知机, 用H表示隐藏层的输出, 称为隐藏表示。
  2. W(1)为隐藏层权重,W(2)为输出层权重,b(1)为隐藏层偏置,b(2)为输出层偏置。
    动手学深度学习——多层感知机(原理解释+代码详解)_第2张图片
  3. 此时模型依然是仿射函数,需要在仿射变换之后对每个隐藏单元应用非线性的激活函数,之后多层感知机不会退化成线性模型。动手学深度学习——多层感知机(原理解释+代码详解)_第3张图片

1.4 通用近似定理

  1. 多层感知机可以通过隐藏神经元,捕捉到输入之间复杂的相互作用, 这些神经元依赖于每个输入的值,可以很容易地设计隐藏节点来执行任意计算。
  2. 可以使用更深的网络来逼近许多函数。

2. 激活函数

  1. 激活函数通过计算加权和并加上偏置来确定神经元是否应该被激活, 它们将输入信号转换为输出的可微运算。
  2. 大多数激活函数都是非线性的,如ReLU函数,sigmoid函数和tanh函数。

2.1 ReLU函数

  1. ReLU函数是修正线性单元,提供了一种非常简单的非线性变换,它实现简单,同时在各种预测任务中表现良好。
  2. ReLU函数通过将相应的活性值设为0,仅保留正元素并丢弃所有负元素。在这里插入图片描述
  • detach():返回一个新的tensor,并且这个tensor是从当前的计算图中分离出来的。但是返回的tensor和原来的tensor是共享内存空间的。
  • figsize:Matplotlib库中的一个函数,用于设置图形的尺寸大小。
    figsize(width, height) :其中,width和height分别表示图像的宽度和高度,单位为英寸(inch)。
  • requires_grad =True:输出张量需要梯度。
%matplotlib inline
import torch
from d2l import torch as d2l

# 生成一个从-8.0到7.9的列表,以0.1为跳跃点
# 需要梯度
x = torch.arange(-8.0, 8.0, 0.1, requires_grad=True)

# 通过relu函数激活
y = torch.relu(x)
d2l.plot(x.detach(), y.detach(), 'x', 'relu(x)', figsize=(5, 2.5))

动手学深度学习——多层感知机(原理解释+代码详解)_第4张图片
ReLU函数的导数

  • torch.ones_like():返回一个与输入张量input形状相同的张量,所有元素都设置为1。
  • retain_graph=True:第一次计算完梯度后,计算图会被保留下来,再次计算梯度时,就可以重复使用这个计算图,从而避免重复构建计算图,提高计算效率。
y.backward(torch.ones_like(x), retain_graph=True)
d2l.plot(x.detach(), x.grad, 'x', 'grad of relu', figsize=(5, 2.5))

动手学深度学习——多层感知机(原理解释+代码详解)_第5张图片

  1. 当输入为负时,ReLU函数的导数为0,而当输入为正时,ReLU函数的导数为1(输入值精确等于0时,ReLU函数不可导,但输入不会为0,一般用左边值替代)。

2.2 sigmoid函数

  1. sigmoid函数将输入变换为区间(0, 1)上的输出,也叫挤压函数,它是一个平滑的、可微的阈值单元近似。。动手学深度学习——多层感知机(原理解释+代码详解)_第6张图片

  2. sigmoid在隐藏层中已经较少使用, 它在大部分时候被更简单、更容易训练的ReLU所取代。

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

动手学深度学习——多层感知机(原理解释+代码详解)_第7张图片
sigmoid函数的导数

# 清除以前的梯度
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))

动手学深度学习——多层感知机(原理解释+代码详解)_第8张图片

3.当输入为0时,sigmoid函数的导数达到最大值0.25; 而输入在任一方向上越远离0点时,导数越接近0。

2.3 tanh函数

  1. tanh(双曲正切)函数也能将其输入压缩转换到区间(-1, 1)上。动手学深度学习——多层感知机(原理解释+代码详解)_第9张图片
y = torch.tanh(x)
d2l.plot(x.detach(), y.detach(), 'x', 'tanh(x)', figsize=(5, 2.5))

动手学深度学习——多层感知机(原理解释+代码详解)_第10张图片
tanh函数的导数

# 清除以前的梯度
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))

动手学深度学习——多层感知机(原理解释+代码详解)_第11张图片

  1. 当输入接近0时,tanh函数的导数接近最大值1。 输入在任一方向上越远离0点,导数越接近0。

3. 小结

  1. 多层感知机在输出层和输入层之间增加一个或多个全连接隐藏层,并通过激活函数转换隐藏层的输出。
  2. 常用的激活函数包括ReLU函数、sigmoid函数和tanh函数。

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

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

2.1 初始化模型参数

  1. Fashion-MNIST中的每个图像由28×28=784个灰度像素值组成, 所有图像共分为10个类别。
  2. 将每个图像视为具有784个输入特征 和10个类的简单分类数据集。
  3. 实现一个具有单隐藏层的多层感知机, 它包含256个隐藏单元。
  • torch.zeros()函数:返回一个形状为为size,类型为torch.dtype,里面的每一个值都是0的tensor。
  • torch.randn:用来生成随机数字的tensor,这些随机数字满足标准正态(0~1)。
num_inputs, num_outputs, num_hiddens = 784, 10, 256

# 定义参数w1,w2,b1,b2
# w1,w2为满足标准正态分布的随机数字,b1,b2为0
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]

2.2 激活函数

自己实现ReLU激活函数。

def relu(X):
    a = torch.zeros_like(X)
    return torch.max(X, a)

2.3 模型

使用reshape将每个二维图像转换为一个长度为num_inputs的向量。

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

2.4 损失函数

  1. 直接使用高级API中的内置函数来计算softmax和交叉熵损失。
  • reduction = none:表示直接返回n分样本的loss。
loss = nn.CrossEntropyLoss(reduction='none')

2.5 训练

直接调用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)

动手学深度学习——多层感知机(原理解释+代码详解)_第12张图片
在测试数据集上进行预测

d2l.predict_ch3(net, test_iter)

动手学深度学习——多层感知机(原理解释+代码详解)_第13张图片

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

  1. 通过高级API更简洁地实现多层感知机。
# 导入d2l包
import torch
from torch import nn
from d2l import torch as d2l
  1. 添加2个全连接层, 第一层是隐藏层,它包含256个隐藏单元,并使用了ReLU激活函数,第二层是输出层。
  • nn.Sequential():一个序列容器,用于搭建神经网络的模块被按照被传入构造器的顺序添加到nn.Sequential()容器中。把多个模块封装成一个模块。
  • nn.Flatten():将连续的维度范围展平为张量。 经常在nn.Sequential()中出现,一般写在某个神经网络模型之后,用于对神经网络模型的输出进行处理,得到tensor类型的数据。
  • nn.Linear():定义一个神经网络的线性层:
    torch.nn.Linear(in_features, # 输入的神经元个数
    out_features, # 输出神经元个数
    bias=True # 是否包含偏置
    )
  • torch.nn.init.normal_(tensor, mean=0.0, std=1.0):tensor为一个n维torch.Tensor,mean为正态分布的平均值,std为正态分布的标准差。
  • init_weights:初始化网络的权重。它还将神经网络的权重设置为非零值,这对神经网络来说是有帮助的,因为神经网络往往会陷入局部最小值,所以给它们许多不同的起始值是个好主意。
  • apply(fn):该方法会将fn递归的应用于模块的每一个子模块(.children()的结果)及其自身。
net = nn.Sequential(nn.Flatten(), # 展平为张量
					
					# 定义784输入神经单元,256个输出神经单元的线性层
                    nn.Linear(784, 256), 
                    
					# ReLU激活函数
                    nn.ReLU(),

					# 定义256输入神经单元,10个输出神经单元的线性层
                    nn.Linear(256, 10))

# 初始化网络权重
def init_weights(m):
    if type(m) == nn.Linear:
        nn.init.normal_(m.weight, std=0.01) #初始化权重:标准差为0.01

# 每层都循环一下,最后对整个Sequential也进行操作,递归调用
net.apply(init_weights);

# 批量大小:256,学习率:10,迭代轮数:10
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)

动手学深度学习——多层感知机(原理解释+代码详解)_第14张图片

你可能感兴趣的:(深度学习,深度学习,人工智能)