深度学习 | Pytorch深度学习实践 (Chapter 12 Basic RNN)

十二、Basic RNN —— 实际上就是对线性层的复用

         

使用RNN最重要的两点:

  1. 了解序列数据的维度;
  2. 循环过程所用的权重共享机制
    一般就是自己写个循环,权重层重复用就行了;


回顾:----------------------------------------------------------------------------------------------------------------------

        以前是用的神经网络也称为Dense、Deep(稠密层)的网络。

        

        但现在来考虑一下这个情况:

        

        若使用稠密的网络,需要把其拼成有九个维度的向量,

        将日期、温度和湿度拼成一个九维向量是为了让模型能够同时考虑所有三个特征,从而更好地进行预测。如果只使用三维的向量,可能会导致模型无法充分利用所有的特征信息,从而影响预测的准确性。此外,使用多维向量还有助于模型学习不同特征之间的关系,例如日期与温度的关系和日期与湿度的关系等。

        

        全连接层的权重要比卷积神经网络大很多,这是由于卷积神经网络虽然运算复杂但整个图像上的卷积核是共享的。

        但如果序列很长而且维度很高,拼接成高维向量 则权重数量太多到难以处理。

 

        —— 因此,RNN就是专门用来处理带有序列模式的数据,同时也要结合权重共享的思想来减少权重数量;不仅要考虑x1,x2之间的连接关系,还要考虑其时间上的先后序列关系(即x1和x2的数据依赖于x2/3)

        

         除了天气等一系列经典的时间顺序的数据以外,自然语言也是一种有着序列关系的数据;


12.1、RNN Cell

        RNN Cell:xt是指在序列中t时刻相应的数据,它可以把3维向量变为5维的向量。不难看出其本质为一个线性层。

         

         和普通的线性层相比,这里的RNN Cell是可以共享的。将上面的图像展开后就是下面这样:

         

12.1.1、和线性层的区别

在于:

        由于每一项的数据都和前一项相关,所以送进RNN Cell的数据不止其本身的数据,还要有上一项的数据(也就是图中的红色箭头。)

        而对于x1来说也需要一个相应的h0,表示先验知识。比如果要通过图像来生成文本,那么h0就设成一个CNN+Fc,如果没有的话就可以把它设成一个对应维度的0向量)。

        注意,这里所有的RNN Cell是同一个层

        

        若用公式来表示一下这个循环过程:

        


12.1.2、具体计算过程

        xt为输入,input_size为输入维度,hodden_size为隐层维度。

        

  • 这里是矩阵运算,x是一个维度为input_size的列向量也可以说是一个input_size*1的矩阵
  • 先将输入做一次线性变化,将其维度(input_size)转为隐藏层的维度(hidden_size)——Wih是一个hidden_size×input_size的矩阵;
  • 而上一个隐层的输入的权重矩阵就是hidden_size×hidden_size;
  • 然后将他们俩相加就完成了信息融合;
  • 接着要做一次激活,tanh被认为是一个效果较好的激活函数,得到的结果就是ht了;
  • 即 每个时间步的隐层状态 ht​ 是由当前时刻的输入 xt 和上一时刻的隐层状态 ht−1经过非线性变换得到的。

        实际上黄色阴影和红色阴影部分可以写到一起。见上面图上面部分的红色笔迹。

        在构建RNN Cell的时候,实际上的工作就是把x和h拼起来。

        最后的公式即为:

        

       


12.2、RNN

        那什么叫RNN呢?

—— 将RNN Cell以循环的形式把序列一个一个送进去,然后依次算出隐层的过程,就叫做循环神经网络,其中RNN Cell是其中的一个单元。

        在pytorch中有两种构造RNN的方式:

        1、做自己的RNN Cell,自己设计处理逻辑的循环。

        2、直接使用RNN。

12.2.1、RNN Cell in Pytorch

        

#要设定的参数主要是两个:输入维度和隐层的维度
cell = torch.nn.RNNCell(input_size=input_size, hidden_size=hidden_size)#调用方法
hidden = cell(input, hidden)# 本质上还是一个线性层。

        使用时注意维度的要求。

        

举例:----------------------------------------------------------------------------------------------------------------------

先假设我们的参数是下面这样的:

  • 𝑏𝑎𝑡𝑐ℎ𝑆𝑖𝑧𝑒 = 1 (N 批量)
  • 𝑠𝑒𝑞𝐿𝑒𝑛 = 3 (有多少个x)
  • 𝑖𝑛𝑝𝑢𝑡𝑆𝑖𝑧𝑒 = 4 (每个x的维度)
  • ℎ𝑖𝑑𝑑𝑒𝑛𝑆𝑖𝑧𝑒 = 2
  • 那么RNNCell的输入以及输出就是:
  • input.shape = (batchSize,inputSize)
  • output.shape = (batchSize,hiddenSize) —— 在h0的时候也要满足这个要求。
  • 在处理RNN时,我们会将整个序列的维度构造成这样:
  • dataset.shape = (seqLen,batchSize,inputSize)
    • 注意这里seqLen放第一位,这是为了在循环的时候,每次拿出当前循环时刻 t 的张量,也就是后两项参数,显得自然一些;

 代码:

import torch# 1、初始化参数
batch_size = 1
seq_len = 3
input_size = 4
hidden_size = 2# 2、构造RNNCell
cell = torch.nn.RNNCell(input_size=input_size, hidden_size=hidden_size)# 3、设置一个简单的序列数据 (seq, batch, faetures)
dataset = torch.randn(seq_len, batch_size, input_size)
# 4、构造隐层,这里设置全零 h0
hidden = torch.zeros(batch_size, hidden_size)# 5、训练的循环
for idx, input in enumerate(dataset):print('=' * 20, idx, '=' * 20)print('input size:',input.shape)hidden = cell(input, hidden)print('outputs size:',hidden.shape)print(hidden)

 结果:

        


12.2.2、RNN in Pytorch

# 仅多了个指明层数的参数num_layers
cell = torch.nn.RNN(input_size=input_size, hidden_size=hidden_size, num_layers = num_layers)
# 输出的out是指h1,...,hN,而hidden则是特指最后一个hN
out, hidden = cell(inputs, hidden)
# 这样一来,就不用自己写循环了
# inputs为整个输入的序列

         

        out,hidden = cell(inputs,hidden)

        这里的inputs实际上就是整个的输入序列,hidden就是h0。

        将来输出两个张量,第一个张量out就是h1一直到hn,第二个张量hidden就是hn

        所以我们用RNN就不用自己写循环了。

        也要注意维度!

        

        要确认的数多了一个numlayer:

  • 𝑏𝑎𝑡𝑐ℎ𝑆𝑖𝑧𝑒
  • 𝑠𝑒𝑞𝐿𝑒𝑛
  • 𝑖𝑛𝑝𝑢𝑡𝑆𝑖𝑧𝑒, ℎ𝑖𝑑𝑑𝑒𝑛𝑆𝑖𝑧𝑒,
  • 𝑛𝑢𝑚𝐿𝑎𝑦𝑒𝑟s

        输入以及h_0的维度要求:

  • input.shape = (seqLen, batchSize, inputSize)
  • h_0.shape = (numLayers, batchSize, hiddenSize)

        输出以及h_n的维度要求:

  • output.shape = (seqLen, batchSize, hiddenSize)
  • h_n.shape = (numLayers, batchSize, hiddenSize)

        numLayers的结构:

        

        每个xi结合多个Hij后得到输出的hi;

        这里同样颜色的RNN Cell全都是一个层;
 

举例:---------------------------------------------------------------------------------------------------------------------- 

 代码:

import torch# 1、设置模型参数
batch_size = 1 #仅在构造h0时才会用到
seq_len = 3
input_size = 4
hidden_size = 2
num_layers = 1# 2、构造RNN
cell = torch.nn.RNN(input_size=input_size,hidden_size=hidden_size,num_layers=num_layers)# 3、自定义一个测试的inputs、h0
inputs = torch.randn(seq_len,batch_size,input_size)
hidden = torch.zeros(num_layers,batch_size,hidden_size)out,hidden = cell(inputs,hidden)print('Output size:',out.shape)
print('Output:', out)
print('Hidden size:', hidden.shape)
print('Hidden:',hidden)

 结果:

 


12.2.3、RNN的其他参数

        batch_first:

        若设为True,那么batchSize和seqLen两个参数的位置就会互换,输入时要把batch_size作为第一个参数;


12.2.4、一个例子 seq → seq

  • 例子:把模型训练成一种序列到序列的转换模型,比如将"hello"转化为"ohlol”。
  • 对应的流程图如下所示:

  • 第一步,将输入的字符向量化,因为RNN Cell的输入得是向量: 
  • 此时,InputSize就是4(矩阵的宽)

        一般都是根据字符来构造词典,并分配索引,再通过独热向量来进行查询字符。

        宽度就是词的种类数量; 

  • 这样一来,就可以化为一个多分类问题,也就是把输入经过RNN来分类到对应的输出上去。可以接一个softmax 把这个变成一个分布,就可以拿去训练了;


        整个代码流程如下:

 代码:

1)RNN Cell实现:

import torch# 模型参数
batch_size = 1 #仅在构造h0时才会用到
input_size = 4
hidden_size = 4# 1、数据准备:将hello输出为ohlol
idx2char = ['e','h','l','o']
x_data = [1,0,2,2,3]
y_data = [3,1,2,3,2]one_hot_lookup = [[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]
x_one_hot = [one_hot_lookup[x] for x in x_data] #seq x input_size,构造独热向量inputs = torch.Tensor(x_one_hot).view(-1,batch_size,input_size) #(segLen , batchSize, inputSize)
labels = torch.LongTensor(y_data).view(-1,1) #变为seq x 1# 2、定义模型
class Model(torch.nn.Module):def __init__(self,input_size,hidden_size,batch_size):super(Model,self).__init__()#self.num_layers = num_layersself.batch_size = batch_sizeself.input_size = input_sizeself.hidden_size = hidden_sizeself.rnncell = torch.nn.RNNCell(input_size=self.input_size,hidden_size=self.hidden_size)def forward(self,input,hidden):hidden = self.rnncell(input,hidden)return hiddendef init_hidden(self):return torch.zeros(self.batch_size,self.hidden_size)net = Model(input_size,hidden_size,batch_size)# 3、选择优化器
criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(net.parameters(),lr=0.1)# 4、训练
for epoch in range(15):loss = 0optimizer.zero_grad() # 优化器梯度归零hidden = net.init_hidden() # 初始化h0print('Predicted string:',end='')for input,label in zip(inputs,labels):# inputs [seq,batch,input_size]# input [batch,input_size]# labels [seq,1]# label [1]hidden = net(input,hidden)loss += criterion(hidden,label) # loss需要累加计算图#输出预测值_,idx = hidden.max(dim=1)print(idx2char[idx.item()],end='')loss.backward() # 反向传播optimizer.step() # 优化更新print(',Epoch [%d/15] loss=%.4f' % (epoch+1,loss.item()))

 结果:

        

2)若使用RNN

import torch# 模型参数
batch_size = 1 #仅在构造h0时才会用到
input_size = 4
hidden_size = 4
num_layers = 1
seq_len = 5# 1、数据准备:将hello输出为ohlol
idx2char = ['e','h','l','o']
x_data = [1,0,2,2,3]
y_data = [3,1,2,3,2]one_hot_lookup = [[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]
x_one_hot = [one_hot_lookup[x] for x in x_data] #seq x input_size,构造独热向量inputs = torch.Tensor(x_one_hot).view(-1,batch_size,input_size) #(segLen , batchSize, inputSize)
labels = torch.LongTensor(y_data).view(y_data) #################变为seq x batch , 1# 2、定义模型
class Model(torch.nn.Module):def __init__(self,input_size,hidden_size,batch_size):super(Model,self).__init__()self.num_layers = num_layers #########################self.batch_size = batch_sizeself.input_size = input_sizeself.hidden_size = hidden_sizeself.rnn = torch.nn.RNN(input_size=self.input_size,hidden_size=self.hidden_size,num_layers= num_layers)def forward(self, input):hidden = torch.zeros(self.num_layers, self.batch_size, self.hidden_size)out, _ = self.rnn(input, hidden)return out.view(-1, self.hidden_size) #################变为seq x batch , hiddennet = Model(input_size,hidden_size,batch_size)# 3、选择优化器
criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(net.parameters(),lr=0.05)# 4、训练
for epoch in range(15):optimizer.zero_grad() # 优化器梯度归零outputs = net(inputs)loss = criterion(outputs,labels)loss.backward() # 反向传播optimizer.step() # 优化更新_, idx = outputs.max(dim=1)print('Predicted string:', ''.join([idx2char[x] for x in idx]), end='')print(', Epoch[%d/15] loss=%.4f' % (epoch + 1, loss.item()))

        


12.3、独热编码与嵌入层

        独热向量的缺点:

  • 维度太高;
  • 向量过于稀疏;
  • 是硬编码的,即每个词对应的向量是早就编码好的;

        目标就是要变成:

  • 低维;
  • 稠密;
  • 学习的;
    ——>常用的方式叫嵌入层Embeding:把高维,稀疏的样本映射到低维,稠密的空间当中去
  • (即降维,其实可以高低互转,看情况);

        降维示意图

        

         通过嵌入层,就可以直接学习一个单词,而不是一个个字母;

         

        这样一来,把网络变成下面这样的结构:

                

经过Embed把独热向量变成稠密表示,最上面的线性层用于调整维度

  • Embedding类,但是常用的就前两个参数

class torch.nn.Embedding(num_embeddings, embedding_dim, padding_idx=None, max_norm=None, norm_type=2.0, scale_grad_by_freq=False, sparse=False, _weight=None, device=None, dtype=None)

         

框起来的第一个参数代表input的维度;第二个参数代表了矩阵的宽和高

        
        *就是inputsize,就是seq×batch

         

线性层的参数和输入输出

其实就是把维度相同的input和output进行转换;

         

交叉熵的参数和输入输出

这里的输入可以加入维度di,反正最终的结果就是每个维度上的交叉熵求和;

 代码:

import torch# embedding层
class Model(torch.nn.Module):def __init__(self):super(Model, self).__init__()self.emb = torch.nn.Embedding(input_size, embedding_size)self.rnn = torch.nn.RNN(input_size=embedding_size,hidden_size=hidden_size,num_layers=num_layers,batch_first=True)self.fc = torch.nn.Linear(hidden_size, num_class)def forward(self, x):hidden = torch.zeros(num_layers, x.size(0), hidden_size)x = self.emb(x) #这样就可以变成稠密的向量x, _ = self.rnn(x, hidden)x = self.fc(x)return x.view(-1,num_class)#一堆参数
num_class = 4
input_size = 4
hidden_size = 8
embedding_size = 10
num_layers = 2
batch_size = 1
seq_len = 5#准备数据
idx2char = ['e', 'h', 'l', 'o']
x_data = [[1, 0, 2, 2, 3]] # (batch, seq_len)
y_data = [3, 1, 2, 3, 2] # (batch * seq_len)inputs = torch.LongTensor(x_data).view(batch_size, seq_len)
labels = torch.LongTensor(y_data)#实例化模型并构造损失函数和优化器
net = Model()criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(net.parameters(), lr=0.05)#训练
for epoch in range(15):optimizer.zero_grad()outputs = net(inputs)loss = criterion(outputs, labels)loss.backward()optimizer.step()_, idx = outputs.max(dim=1)idx = idx.data.numpy()print('Predicted string:', ''.join([idx2char[x] for x in idx]), end='')print(', Epoch[%d/15] loss=%.4f' % (epoch+1, loss.item()))

 结果:

         

        可以看到:能够更早的获得ohlol,这是因为模型的学习能力比之前要强


Homework:

1、LSTM

         

        遗忘门,前面来的数据乘上(0,1)的参数,从而使其少了一部分 

         

        简单来说就是运用上一层的数据来构建下一层的,有点像迭代算法。但是这种方法可以减小梯度消失的情况;

         

        σ函数就是

        这样一来就多了一条c t+1的通路,方便反向传播。

        一般来说,LSTM的效果要比RNN好得多,但是其运算较为复杂。所以时间复杂度也就高,那么就采用折中的方法:

        GRU,就是下面这堆公式

        

         W就是对应的权重。在构造cell时只需要拿来做上面相应的运算就可以了;

作业2:用下GRU来训练模型;


部分参考于

Pytorch:十二、RNN(基础) - 简书

讲解视频:

13.循环神经网络(高级篇)_哔哩哔哩_bilibili

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

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

相关文章

破局创新,天翼云HBlock如何以小见大、以柔克刚?

引言:另辟蹊径开拓创新 不走传统存储厂商的“寻常路” 【全球存储观察 | 科技热点关注】 在分布式块存储领域,大部分厂商的安装软件套件大小都在GB级。然而,天翼云破天荒地将存储资源盘活系统HBlock的软件安装包浓缩到了170MB&a…

linux中proc与sys的区别

在Linux系统中,/sys目录和/proc目录都是特殊的虚拟文件系统,用于提供对系统内核和设备信息的访问。 虽然它们的作用有一些重叠,但它们在功能和用途上有一些区别。 功能: /sys目录主要用于提供对设备和驱动程序的信息和配置的访…

Python-乒乓球小游戏【附完整源码】

乒乓球小游戏 乒乓球小游戏是一个简单而有趣的2D页面交互式游戏,玩家可以通过键盘输入来控制球拍上下移动来接球,从而体验乒乓球的乐趣。该游戏有单人和双人两种模式 运行效果: 一:主程序: import sys import cfg …

Jupyter Notebook: 交互式数据科学和编程工具

Jupyter Notebook: 功能强大的交互式编程和数据科学工具 简介 Jupyter Notebook是一个开源的Web应用程序,广泛用于数据分析、科学计算、可视化以及机器学习等领域。它允许创建和共享包含实时代码、方程式、可视化和解释性文本的文档。总而言之,我认为它…

3D Font

在游戏中使用3D文本 只需添加预制件并立即生成您的文本。 特点: *真实3D字母,可用作游戏对象*移动友好低聚 *VR兼容 *WebGL兼容 *30种以上不同字体 *材料和颜色可定制 WebGL演示 https://indiechest.itch.io/3d-font-engine 下载: ​​Unity资源商店链…

【tips】base64编码怎么反显出图片

格式 <img width"400" height"300" src"data:image/jpeg;base64,这里存放base64的图片内容/>实际的数据展示是这样的 然后把以上的文件内容放置到html文件中 最终展示样例 点击这个 展示出来是这样的

opencv 十五 红外图像中虹膜的提取

一、算法需求 在医疗检测中&#xff0c;需要使用红外相机拍摄眼睛照片&#xff0c;然后提取出虹膜的区域。在拍摄过程瞳孔需要进行运动&#xff0c;其通常不在正前方&#xff0c;无法形成圆形&#xff0c;不能使用常规的霍夫圆检测进行提取定位。且在在红外图像中&#xff0c;…

将输入的字符串反向输出(c语言)

#include<stdio.h> #include<string.h> int main() {int i, j, k;char s[50], temp;gets(s);//输入k strlen(s);//计算字符的长度//反向输出for (i 0, j k - 1;i < k / 2;i, j--){temp s[i];s[i] s[j];s[j] temp;}puts(s);//输出 }

最近面试了一位5年的测试,一问三不知,还反怼我...

最近看了很多简历&#xff0c;很多候选人年限不小&#xff0c;但是做的都是一些非常传统的项目&#xff0c;想着也不能通过简历就直接否定一个人&#xff0c;何况现在大环境越来 越难&#xff0c;大家找工作也不容易&#xff0c;于是就打算见一见。 在沟通中发现&#xff0c;由…

GB28181学习(十八)——图像抓拍

前言 本文主要介绍图像抓拍功能&#xff0c;通过自研的sip库&#xff08;mysipsdk.dll&#xff09;对接真实设备&#xff0c;使用http方式实现图像数据传输&#xff0c;最终达到图像抓拍与保存的目的。 基本要求 图像格式宜使用JPEG&#xff1b;图像分辨率宜采用与主码流相同…

linux ksm实现与代码简述

KSM 全称是 Kernel Samepage Merging&#xff0c;表示相同的物理页只映射一份拷贝。 原理 在ksm初始化时&#xff08;ksm_init&#xff09;&#xff0c;注册了一个ksm_scan_thread线程&#xff0c;这个线程的核心入口是ksm_do_scan。当对一个进程第一次通过madvice(MADV_MERGE…

Linux高级管理-基于域名的虚拟Web主机搭建

客服机限制地址 通过 Require 配置项&#xff0c;可以根据主机的主机名或P地址来决定是否允许客户端访问。在httpd服 务器的主配置文件的<Location>&#xff0c;<Directory>、<Files>、<Limit>配置段中均可以使用Require 配置 项来控制客户端的访问。使…

Java基础:如何创建多层文件夹

一、单层多个 代码实现如下&#xff1a; public class Main {public static void main(String[] args) {//在D盘中创建File file new File("D:"File.separator"docum");file.mkdir();//在D盘中的docum目录中创建file new File("D:\\docum" Fi…

kafka 详细介绍

目录 前言 分布式架构&#xff1a; 消息发布-订阅模型&#xff1a; 持久性存储&#xff1a; 分区和副本&#xff1a; 水平扩展&#xff1a; 高性能&#xff1a; 生态系统&#xff1a; 我的其他博客 前言 Kafka 是由 Apache 软件基金会开发的一种开源流处理平台&#xf…

微信小程序自定义提示框组件并使用插槽 tooltip

创建tooltip组件引用 创建一个自定义组件&#xff0c;例如命名为 tooltip tooltip.wxml&#xff1a;用于定义组件的结构&#xff1b; <!--components/tooltip/tooltip.wxml--> <view class"tooltip-wrapper" hidden"{{hidden}}" style"lef…

纺织辅料小程序商城制作全攻略

随着互联网的普及和移动支付的便捷性&#xff0c;越来越多的消费者喜欢在线购物&#xff0c;尤其是购买纺织辅料这类产品。为了满足消费者的需求&#xff0c;纺织辅料企业或商家需要制作一个专业的小程序商城&#xff0c;以便更好地展示和销售自己的产品。本文将详细介绍如何制…

Fine-Grained Semantically Aligned Vision-Language Pre-Training细粒度语义对齐的视觉语言预训练

abstract 大规模的视觉语言预训练在广泛的下游任务中显示出令人印象深刻的进展。现有方法主要通过图像和文本的全局表示的相似性或对图像和文本特征的高级跨模态关注来模拟跨模态对齐。然而&#xff0c;他们未能明确学习视觉区域和文本短语之间的细粒度语义对齐&#xff0c;因为…

Java开发环境简介(JDK、JRE、JVM)

目录 1、Java开发环境 2、JDK和JRE 3、JDK下载和安装 3.1 下载 3.2 安装 3.3 配置path环境变量 JDK8配置方案1&#xff1a;只配置path ⭐JDK8配置方案2&#xff1a;配置JAVA_HOMEpath&#xff08;推荐&#xff09; path配置小结 JDK17配置方案&#xff1a;自动配置 …

redis-学习笔记(Jedis 前置知识)

自定义的 Redis 客户端 咱们可以实现编写出一个自定义的 Redis 客户端 因为 Redis 公开了自己使用的自定义协议 ---- RESP 协议清楚了, 那么通信数据格式就清除了, 就能完成各层次之间的数据传输, 就能开发服务器和客户端 RESP — Redis 的 序列化 协议 特点: 简单好实现快读进…

AC修炼计划(AtCoder Beginner Contest 332)

传送门&#xff1a;AtCoder Beginner Contest 332 - AtCoder a,b,c都还是很基础了。d题是一个bfs的纯暴力问题。 E - Lucky bag 看看范围&#xff0c;n15&#xff0c;第一个想法是dfs纯暴力&#xff0c;但所有的情况太大&#xff0c;各种决策层出不穷&#xff0c;会t。所以转…