pytorch 实现多层神经网络MLP(Pytorch 05)

一 多层感知机

最简单的深度网络称为多层感知机。多层感知机由 多层神经元 组成,每一层与它的上一层相连,从中接收输入;同时每一层也与它的下一层相连,影响当前层的神经元。

softmax 实现了 如何处理数据,如何将 输出转换为有效的概率分布,并应用适当的 损失函数,根据 模型参数最小化损失

线性意味着单调假设:任何特征的增大都会导致模型输出的增大(如果对应的权重为正),或者导致模 型输出的减小(如果对应的权重为负)。

在网络中加入隐藏层,我们可以通过 在网络中加入一个或多个隐藏层来克服线性模型的限制,使其能处理更普遍的函数关系类型。 要做到这一点,最简单的方法是 将许多全连接层堆叠在一起。每一层都输出到上面的层,直到生成最后的输出。我们可以把前L−1层看作表示,把最后一层看作线性预测器。这种架构通常称为多层感知机(multilayer perceptron),通常缩写为 MLP。下面,我们以图的方式描述了多层感知机。

下面是一个单隐藏层的多层感知机,具有5个隐藏单元:

这个多层感知机有4个输入,3个输出,其隐藏层包含5个隐藏单元。输入层不涉及任何计算,因此使用此网络 产生输出 只需要实现隐藏层和输出层的计算。因此,这个多层感知机中的 层数为2。注意,这两个层都是全连 接的。每个输入都会影响隐藏层中的每个神经元,而隐藏层中的每个神经元又会影响输出层中的每个神经元。

线性模型公式:

 不加激活函数,参数可能会组到一起:

加上激活函数 σ 后公式:

为了发挥多层架构的潜力,我们还需要一个额外的关键要素:在仿射变换之后对每个隐藏单元应用非线性的 激活函数(activation function)σ。激活函数的输出(例如,σ(·))被称为活性值(activations)。一般来说, 有了激活函数,就不可能再将我们的多层感知机退化成线性模型
由于X中的每一行对应于小批量中的一个样本,出于记号习惯的考量,我们定义非线性函数σ也以按行的方 式作用于其输入,即一次计算一个样本。

但是 应用于隐藏层的激活函数通常不仅按行操作,也按元素操作。这意味着在计算每一层的线性部分之 后,我们可以 计算每个活性值,而不需要查看其他隐藏单元所取的值。对于大多数激活函数都是这样。 为了构建更通用的多层感知机,我们可以继续堆叠这样的隐藏层,一层叠一层,从而产生更有表达能力的模型。

而且,虽然一个单隐层网络能学习任何函数,但并不意味着我们应该尝试使用单隐藏层网络来解决所有问题。 事实上,通过 使用更深(而不是更广)的网络,我们可以更容易地逼近许多函数

二 激活函数

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

%matplotlib inline
import torch
from d2l import torch as d2l

2.1 ReLU函数

最受欢迎的激活函数是修正线性单元(Rectified linear unit,ReLU),因为它实现简单,同时在各种预测任务 中表现良好。ReLU提供了一种非常简单的非线性变换。给定元素x,ReLU函数被定义为该元素与0的最大值,ReLU函数通过将相应的活性值设为0,仅保留正元素并丢弃所有负元素

        ReLU(x) = max(x, 0)

x = torch.arange(-8.0, 8.0, 1, requires_grad=True)
print(x)
y = torch.relu(x)
d2l.plot(x.detach(), y.detach(), 'x', 'relu(x)', figsize=(5, 2.5))# tensor([-8., -7., -6., -5., -4., -3., -2., -1.,  0.,  1.,  2.,  3.,  4.,  5.,
#          6.,  7.], requires_grad=True)

当输入为负时,ReLU函数的导数为0,而当输入为正时,ReLU函数的导数为1。注意,当输入值精确等于0时, ReLU函数不可导。在此时,我们默认使用左侧的导数,即当输入为0时导数为0。

2.1.1 反向传播后查看X的梯度

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 减轻了困扰以往神经网络的梯度消失问题

2.2 sigmoid 函数

对于一个定义域在R中的输入,sigmoid函数将输入变换为区间(0, 1) 上的输出。

sigmoid函数是一个自然的选择,因为它是一个 平滑的、可微 的阈值单元近似。当我们想要将输出视作二元分类问题的概率时,sigmoid仍然被广泛用作 输出单元上的激活函数(sigmoid可以视为softmax的特例)。sigmoid在隐藏层中已经较少使用,它在大部分时候被更简单、 更容易训练的ReLU 所取代。

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

sigmoid函数的导数图像如下所示。注意,当输入为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))

2.3 tanh函数

与sigmoid函数类似,tanh(双曲正切)函数 也能将其输入压缩转换到区间(‐1, 1)上

下面我们绘制tanh函数。注意,当输入在0附近时,tanh函数接近线性变换。函数的形状类似于sigmoid函数, 不同的是tanh函数 关于 坐标系原点中心对称

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

tanh函数的导数图像如下所示。当输入接近0时,tanh函数的导数接近最大值1。与我们在sigmoid函数图像 中看到的类似,输入在任一方向上越远离0点,导数越接近0

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

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

3.1 导入数据

Fashion‐MNIST中的每个图像由 28 × 28 = 784个灰度像素值组成。所有图像共分为10个类别

import torch
from torch import nn
from d2l import torch as d2lbatch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
len(train_iter), len(test_iter)# (235, 40)

3.2 初始化模型参数

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

num_inputs, num_outputs, num_hiddens = 784, 10, 256W1 = 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]
params

3.3 激活函数

使用 relu 函数:

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

3.4 定义模型

因为我们忽略了空间结构,所以我们使用 reshape将每个二维图像转换为一个长度为num_inputs的向量

def net(x):x = x.reshape((-1, num_inputs))h = relu(x@W1 + b1)return (h@W2 + b2)

3.5 定义损失函数

因此在这里我们直接使用高级API中的内置函数来 计 算softmax和交叉熵损失

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

3.6 执行训练

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)

3.7 执行预测

d2l.predict_ch3(net, test_iter)

 

四 直接调包实现 MLP

与softmax回归的简洁实现 相比,唯一的区别是我们 添加了2个全连接层(之前我们只添加了1个全 连接层)。第一层是隐藏层,它包含128个隐藏单元,并使用了ReLU激活函数。第二层是输出层。  (隐藏层单元个数可以改,保证两个全连接层输出和输入的层数要一致)

net = nn.Sequential(nn.Flatten(),nn.Linear(784, 128),nn.ReLU(),nn.Linear(128, 10))def init_weights(m):if type(m) == nn.Linear:nn.init.normal_(m.weight, std=0.01)
net.apply(init_weights)# Sequential(
#   (0): Flatten(start_dim=1, end_dim=-1)
#   (1): Linear(in_features=784, out_features=128, bias=True)
#   (2): ReLU()
#   (3): Linear(in_features=128, out_features=10, bias=True)
# )
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)

 

 4.1 模型拟合相关知识

将模型在 训练数据上拟合的比在潜在分布中更接近的现象称为过拟合(overfitting),用于对抗过拟合的技术 称为 正则化(regularization)。

我们需要了解训练误差和泛化误差。训练误差(training error)是指,模型在训 练数据集上计算得到的误差泛化误差(generalization error)是指,模型应用在 同样从原始样本的分布中 抽取的无限多数据样本时,模型误差的期望

为了评估模型,将我们的数据分成三份,除了训练和测试数据集之外,还增加一个 验证数据集(val‐ idation dataset),也叫验证集(validation set)。

训练误差和验证误差都很严重,但它们之间仅有一点差距。如果模型不能降低训练误差,这可能意味着模型过于简单(即 表达能力不足),无法捕获试图学习的模式。此外,由于我们的训练和验证误差之间的泛化误差很小,我们有 理由相信可以 用一个更复杂的模型 降低训练误差。这种现象被称为欠拟合(underfitting)。验证集可以用于模型选择,但不能过于随意地使用它

另一方面,当我们的训练误差明显低于验证误差时要小心,这表明严重的 过拟合(overfitting)。最终,我们 通常更关心验证误差,而不是训练误差和验证误差之间的差距

另一个重要因素是数据集的大小。训练数据集中的样本越少,我们就越有可能过拟合。随着 训练数据量的增加,泛化误差通常会减小

我们应该选择一个复杂度适当的模型,避免使用数量不足的训练样本。

我们总是可以通过去收集更多的 训练数据来缓解过拟合。但这可能成本很高,耗时颇多,或者完全超出我们的控制,因而在短期内不可能做 到。假设我们已经拥有尽可能多的高质量数据,我们便可以将重点放在 正则化技术 上。

4.2 正则化相关

1 权重衰减(weight decay)是最广泛使用的正则化的技术之一,它通常也被 称为 L2正则化

2 暂退法在前向传播过程中,计算每一内部层的同时注入噪声,这已经成为 训练神经网络的常用技术。这种方法之所以被称为暂退法,因为我们从表面上看是在训练过程中丢弃(drop out)一些神经元。在整个训练过程的每一次迭代中,标准暂退法包括在计算下一层之前 将当前层中的一些节 点置零

当我们将暂退法应用到隐藏层,以p的概率 将隐藏单元置为零时,结果可以看作一个只包含原始神经元子集的网络。比如在 图4.6.1中,删除了h2和h5, 因此输出的计算不再依赖于h2或h5,并且它们各自的梯度在执行反向传播时也会消失。这样,输出层的计算 不能过度依赖于h1, . . . , h5的任何一个元素

  • 暂退法在前向传播过程中,计算每一内部层的同时丢弃一些神经元
  • 暂退法可以避免过拟合,它通常与控制权重向量的维数和大小结合使用的。
  • •暂退法仅在训练期间使用。

五 前向传播、反向传播和计算图

前向传播(forward propagation或forward pass)指的是:按顺序(从输入层到输出层)计算和存储神经网 络中每层的结果

反向传播(backward propagation或backpropagation)指的是计算神经网络参数梯度的方法

该方 法根据微积分中的链式规则,按相反的顺序从输出层到输入层遍历网络

在训练神经网络时,前向传播和反向传播相互依赖。对于前向传播,我们沿着依赖的方向遍历计算图并计算 其路径上的所有变量。然后将这些用于反向传播,其中计算顺序与计算图的相反。一方面,在前向传播期间计算正则项取决于 模型参数W(1)和 W(2)的当前值。它 们是由优化算法根据最近迭代的反向传播给出的。另一方面,反向传播期间参数的梯度计算,取决于 由前向传播给出的隐藏变量h的当前值

因此,在训练神经网络时,在初始化模型参数后,我们交替使用前向传播和反向传播,利用反向传播给出的 梯度来更新模型参数。注意,反向传播重复利用前向传播中存储的中间值,以避免重复计算。带来的影响之 一是我们 需要保留中间值,直到反向传播完成。这也是 训练比单纯的预测需要更多的内存(显存)的原因之 一。此外,这些中间值的大小与网络层的数量和批量的大小大致成正比。因此,使用更大的批量来训练更深 层次的网络更容易导致内存不足(out of memory)错误。

  • 前向传播在神经网络定义的计算图中按顺序计算和存储中间变量,它的顺序是从输入层到输出层。
  • 反向传播按相反的顺序(从输出层到输入层)计算和存储神经网络的中间变量和参数的梯度。
  • 在训练深度学习模型时,前向传播和反向传播是相互依赖的。
  • 训练比预测需要更多的内存。

5.1 参数初始化

初始化方案的选择在神经网络学习中起着举足轻重的作用,它对保持数值稳定性至关重要。此外, 这些初始化方案的选择可以与非线性激活函数的选择有趣的结合在一起。我们选择哪个函数以及如何初始化 参数可以决定优化算法收敛的速度有多快糟糕选择可能会导致我们在训练时遇到梯度爆炸或梯度消失

神经网络设计中的另一个问题是 其参数化所固有的对称性

默认初始化,我们使用正态分布来初始化权重值。如果我们不指定初始化方法,框架将 使用默认的随机初始化方法,对于中等难度的问题,这种方法通常很有效。

需要用启发式的初始化方法来确保初始梯度既不太大也不太小,ReLU激活函数缓解了梯度消失问题,这样可以加速收敛。随机初始化是保证在进行优化前打破对称性的关键

Xavier初始化,,Xavier初始化从均值为零,方差  的高斯分布中采样权重。Xavier初始化表明,对于每一层,输出的方差不受输入数量的影响,任何梯度的方差不受输出数量的影 响。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/767952.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Qt 写一个邮件发送程序

最近在完成一个邮箱代替的告警功能&#xff0c;写了一个邮件发送的demo 以下为代码&#xff1a; #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> #include<QTcpSocket> namespace Ui { class MainWindow; }class MainWindow : public QMainWin…

第四十五周:文献阅读

目录 摘要 Abstract 文献阅读&#xff1a;基于注意力的双向LSTM和编码器-解码器的水质预测 现有问题 提出方法 创新点 方法论 1、EMD&#xff08;经验模态分解&#xff09; 2、VMD&#xff08;变分模态分解&#xff09; 3、VBAED模型 研究实验 数据集 数据预处理 …

力扣15. 三数之和

思路&#xff1a;先对数组排序&#xff0c;然后确定第一个数nums[i]&#xff0c;再新建左右双指针&#xff1b; 寻找的3元组&#xff0c;a,b,c,即是 nums[i], nums[letf], nums[right] 数组1&#xff1a;-1,-1,-1,0,1,2; 前面3个-1&#xff0c;只有一个-1是有用的&#xff0c;需…

Android开发系列全套课程

教程介绍 本系列课程面向有java基础&#xff0c;想进入企业从事android开发的计算机专业者。学习搭配实战案例&#xff0c;高效掌握岗位知识。 学习地址 链接&#xff1a;https://pan.baidu.com/s/10p2NGYLM3NcCZwYjJZzfzw?pwdk4p0 提取码&#xff1a;k4p0

【智能算法】JAYA算法原理及实现

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.结果展示4.参考文献 1.背景 2016年&#xff0c; Rao等人受到趋利避害性质启发&#xff0c;提出了JAYA算法&#xff08;JAYA梵文意思即胜利&#xff09;。 2.算法原理 2.1算法思想 JAYA基于趋利避害性质&#xff0c;表达式十…

Markdown的基本撰写和格式语法

基本撰写和格式语法 使用简单的语法在 GitHub 上为您的散文和代码创建复杂的格式。 本文内容 标题 文本样式 引用文本 引用代码 支持的颜色模型 链接 章节链接 relative links (相对链接) 图像 列表 任务列表 提及人员和团队 引用议题和拉取请求 引用外部资源 上传资产 使用表情…

15 UART回环

UART 串口简介 常用的通信方式可分为为串行通信&#xff08;serial communication&#xff09;和并行通信&#xff08;parallel communication&#xff09;两种。并行通信是多比特数据同时通过并行线进行传送&#xff08;一般以字或字节为单位并行进行传输&#xff09;&#x…

什么是Linux?它与其他操作系统有何区别?

什么是Linux&#xff1f;它与其他操作系统有何区别&#xff1f; 什么是Linux&#xff1f;它与其他操作系统有何区别&#xff1f;摘要引言正文内容了解LinuxLinux与其他操作系统的区别开放性多样性安全性 &#x1f914; QA环节小结 参考资料表格总结总结未来展望 博主 默语带您 …

DBO优化GRNN回归预测(matlab代码)

DBO-GRNN回归预测matlab代码 蜣螂优化算法(Dung Beetle Optimizer, DBO)是一种新型的群智能优化算法&#xff0c;在2022年底提出&#xff0c;主要是受蜣螂的的滚球、跳舞、觅食、偷窃和繁殖行为的启发。 数据为Excel股票预测数据。 数据集划分为训练集、验证集、测试集,比例…

如何使用OpenCV扫描图像、查找表和时间测量

返回&#xff1a;OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇&#xff1a;OpenCV4.9.0开源计算机视觉库核心功能&#xff08;核心模块&#xff09; ​ 编辑 目标 我们将寻求以下问题的答案&#xff1a; 如何浏览图像的每个像素&#xff1f;OpenCV 矩…

【C++】如何用一个哈希表同时封装出unordered_set与unordered_map

&#x1f440;樊梓慕&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C》《Linux》《算法》 &#x1f31d;每一个不曾起舞的日子&#xff0c;都是对生命的辜负 目录 前言 1.哈希桶源码 2.哈希…

分割数组的两种解法:动态规划、二分法

410. 分割数组的最大值 1. 动态规划 「将数组分割为 m 段&#xff0c;求……」是动态规划题目常见的问法 理清状态转移方程比较难&#xff0c;因此不推荐用动态规划解题。 2. 贪心 二分法 「使……最大值尽可能小」是二分搜索题目常见的问法。 本题中&#xff0c;我们注意…

数据库管理开发工具Navicat for MySQL Mac版下载

Navicat for MySQL&#xff08;Mac版&#xff09;是一款强大的数据库管理开发工具&#xff0c;专为MySQL设计。它提供直观的用户界面&#xff0c;支持数据建模、查询构建、数据传输等功能&#xff0c;帮助用户轻松管理数据库。其特点包括高效的数据处理能力、安全的数据传输机制…

算法---前缀和练习-1(除自身以外数组的乘积)

除自身以外数组的乘积 1. 题目解析2. 讲解算法原理3. 编写代码 1. 题目解析 题目地址&#xff1a;点这里 2. 讲解算法原理 首先&#xff0c;创建两个辅助数组 f 和 g&#xff0c;它们的长度与 nums 相同。数组 f 用于存储每个元素左侧所有元素的乘积&#xff0c;数组 g 用于存…

SpringCloud-Nacos注册中心

服务注册到nacos Nacos是SpringCloudAlibaba的组件&#xff0c;而SpringCloudAlibaba也遵循SpringCloud中定义的服务注册、服务发现规范。因此使用Nacos和使用Eureka对于微服务来说&#xff0c;并没有太大区别。 主要差异在于&#xff1a; 依赖不同服务地址不同 1. 引入依赖…

XML Data – Semi-Structured Data XML 数据 - 半结构化数据

Outline • Structured, Semistructured, and Unstructured Data • XML Hierarchical (Tree) Data Model • Extracting XML Documents from Relational Databases • XML Documents, DTD, and XML Schema • XML Languages 结构化、半结构化和非结构化数据 - XML 层次&#x…

$.when.apply($, deferreds).done(function() {}) 用法

$.when.apply($, deferreds).done(function() {}) 这行代码是 jQuery 中用于处理多个异步操作的一种模式。让我们逐步解释其用法&#xff1a; $.when(): 这是 jQuery 中的一个方法&#xff0c;用于创建一个新的 Deferred&#xff08;延迟&#xff09;对象。Deferred 对象用于管…

如何设计循环队列(两种方法)

文章目录 前言一、方法一:数组法二、方法二.链表法总结 前言 前面有提到过队列的知识&#xff0c;这次来说一下怎么设计一个循环队列 一.循环队列&#xff08;力扣&#xff09; . - 力扣&#xff08;LeetCode&#xff09;. - 备战技术面试&#xff1f;力扣提供海量技术面试资…

【算法每日一练]-动态规划(保姆级教程 篇17 状态压缩)#POJ1185:炮兵阵地 #互不侵犯

目录 今日知识点&#xff1a; 把状态压缩成j,dp每行i的布置状态&#xff0c;从i-1和i-2行进行不断转移 把状态压缩成j,dp每行i的布置状态&#xff0c;从i-1行进行状态匹配&#xff0c;然后枚举国王数转移 POJ1185&#xff1a;炮兵阵地 思路&#xff1a; 题目&#xff1a;互…

代码随想录算法训练营第二十八天|● 93.复原IP地址 ● 78.子集 ● 90.子集II (JS写法)

93 复原IP地址 题目链接/文章讲解&#xff1a;https://programmercarl.com/0093.%E5%A4%8D%E5%8E%9FIP%E5%9C%B0%E5%9D%80.html 视频讲解&#xff1a;https://www.bilibili.com/video/BV1XP4y1U73i/ 思路&#xff1a; /*** param {string} s* return {string[]}*/ var resto…