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

news2024/11/18 23:41:45

一 多层感知机

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

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 d2l

batch_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, 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]
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.coloradmin.cn/o/1541416.html

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

相关文章

【Godot4.2】基础知识 - Godot中的2D向量

概述 在Godot中,乃至一切游戏编程中,你应该都躲不开向量。这是每一个初学者都应该知道和掌握的内容,否则你将很难理解和实现某些其实原理非常简单的东西。 估计很多刚入坑Godot的小伙伴和我一样,不一定是计算机专业或编程相关专…

WSL使用

WSL使用 WSL安装和使用 Termianl和Ubuntu的安装 打开Hype-V虚拟化配置Microsoft Store中搜索Window Terminal并安装Microsoft Store中搜索Ubuntu, 选择安装Ubuntu 22.04.3 LTS版本打开Window Terminal选择Ubuntu标签栏, 进入命令行 中文输入法安装 查看是否安装了fcitx框架…

2023第13届上海生物发酵展8月7-9日举办

2024第13届国际生物发酵产品与技术装备展(上海展) 2024年8月7-9日|上海新国际博览中心 主办单位: 中国生物发酵产业协会 承办单位: 上海信世展览服务有限公司 院校支持: 北京工商大学 大连工业大学 华东理工大…

FakeLocation报虚拟位置服务连接失败,请重启设备再试

虚拟位置服务连接失败,请重启设备再试 最近遇到一个手机软件报的bug“虚拟位置服务连接失败,请重启设备再试” 因为我的实体“虚拟机”已经root,按道理是不可能报这个错的 折腾了2天,终于解决了 原来是这样,安装最新…

React腳手架已經創建好了,想使用Vite作為開發依賴

使用Vite作為開發依賴 安裝VITE配置VITE配置文件簡單的VITE配置項更改package.json中的scripts在根目錄中添加index.html現在可以瀏覽你的頁面了 安裝VITE 首先,在現有的React項目中安裝VITE npm install vite --save-dev || yarn add vite --dev配置VITE配置文件 …

【MySQL】复合查询——基本单表查询、多表查询、自连接、子查询、使用from进行子查询、合并查询

文章目录 MySQL复合查询1. 基本单表查询2. 多表查询3. 自连接4. 子查询4.1 单行子查询4.2 多行子查询4.3 多列子查询4.4 使用from进行子查询 5. 合并查询5.1 union5.2 union all MySQL 复合查询 数据库的复合查询是指在一个查询中结合使用多个查询条件或查询子句,以…

常见技术难点及方案

1. 分布式锁 1.1 难点 1.1.1 锁延期 同一时间内不允许多个客户端同时获得锁; 1.1.2 防止死锁 需要确保在任何故障场景下,都不会出现死锁; 1.2.3 可重入 特殊的锁机制,它允许同一个线程多次获取同一个锁而不会被阻塞。 1.2…

五、分布式锁-redission

源码仓库地址:gitgitee.com:chuangchuang-liu/hm-dingping.git 1、redission介绍 目前基于redis的setnx特性实现的自定义分布式锁仍存在的问题: 问题描述重入问题同一个线程无法多次获取统一把锁。当方法A成功获取锁后,调用方法B&#xff0…

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

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

19.删除链表的倒数第N个结点 92.反转链表II

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。 示例 1: 输入:head [1,2,3,4,5], n 2 输出:[1,2,3,5]示例 2: 输入:head [1], n 1 输出:[]示例 3: …

模拟-算法

文章目录 替换所有的问号提莫攻击Z字形变换外观数列数青蛙 替换所有的问号 算法思路: 从前往后遍历整个字符串,找到问号之后,就遍历 a ~ z 去尝试替换即可。 class Solution {public String modifyString(String s) {char[] ss s.toCharA…

删除字符串--给你一个字符串S,要求你将字符串中出现的所有“gzu“子串删除,输出删除之后的S。

输入描述: 输入一行字符串S&#xff0c;长度不超过100。 输出描述: 输出进行删除操作之后的S。 #include <stdio.h> #include <stdlib.h> #include <string.h>//结合了串的模式匹配算法思路int main(){char s[100];char a[3]{g,z,u};gets(s);int nstrlen…

数据库语言一些基本操作

1&#xff0c;消除取值重复的行。 例如&#xff1a;查成绩不及格的学号&#xff1a;SELECT DISTINCT sno FROM SC WHERE grade<60. 这里使用DISTINCT表示取消取值重复的行。 2&#xff0c;比较。 例如&#xff1a;查计算机系全体学生的姓名&#xff1a;SELECT Sname FROM…

C++一维数组练习oj(3)

为什么C的一维数组练习要出要做那么多的题目&#xff1f;因为我们是竞赛学生&#xff01;想要将每个知识点灵活运用的话就必须刷大量的题目来锻炼思维。 我使用的是jsswoj.com这个刷题网站&#xff0c;当然要钱... C一维数组练习oj(2)-CSDN博客这是上一次的题目讲解 这道题有…

java每日一题——买啤酒(递归经典问题)

前言&#xff1a; 非常喜欢的一道题&#xff0c;经典中的经典。打好基础&#xff0c;daydayup!!!啤酒问题&#xff1a;一瓶啤酒2元&#xff0c;4个盖子可以换一瓶&#xff0c;2个空瓶可以换一瓶&#xff0c;请问10元可以喝几瓶 题目如下&#xff1a; 啤酒问题&#xff1a;一瓶…

[Halcon学习笔记]在Qt上实现Halcon窗口的字体设置颜色设置等功能

1、 Halcon字体大小设置在Qt上的实现 在之前介绍过Halcon窗口显示文字字体的尺寸和样式&#xff0c;具体详细介绍可回看 &#xff08;一&#xff09;Halcon窗口界面上显示文字的字体尺寸、样式修改 当时介绍的设定方法 //Win下QString Font_win "-Arial-10-*-1-*-*-1-&q…

传输层——UDP协议

端口号(Port) 端口号标识了一个主机上进行通信的不同的应用程序&#xff0c;准确来说&#xff0c;端口号标识了主机上唯一的一个进程。 在TCP/IP协议中, 用 "源IP", "源端口号", "目的IP", "目的端口号", "协议号" 这样一个…

罗德与施瓦茨联合广和通全面验证RedCap模组FG132系列先进性能

近日&#xff0c;罗德与施瓦茨联合广和通完成Redcap(Reduce Capability)功能和性能验证。本次测试使用R&SCMX500 OBT(One Box Tester)无线通信测试仪&#xff0c;主要验证广和通RedCap模组FG132系列射频性能以及IP层吞吐量&#xff0c;包括RedCap上下行吞吐量和射频指标如矢…

Java 自定义线程池实现

自定义线程池 简介任务图示阻塞队列 BlockingQueue<T>ReentrantLock代码 线程池 ThreadPool工作线程类 Worker 拒绝策略接口代码测试类 TestThreadPool为什么需要j i&#xff1f;&#xff08;lambad表达式相关&#xff09; 测试结果拒绝策略&#xff1a;让调用者自己执行…

c++常考基础知识(2)

二.c关键字 关键字汇总 c中共有63个关键字&#xff0c;其中包括int&#xff0c;char&#xff0c;double等类型关键字&#xff0c;if&#xff0c;else&#xff0c;while&#xff0c;do&#xff0c;等语法关键字&#xff0c;还有sizeof等函数关键字。 三.数据结构 1.数组&#x…