pytorch入门10--循环神经网络(RNN)

2023-05-16

补充:torch.randn()函数返回一个张量,包含了从正态分布(均值为0,方差为1)中抽取的一组随机数。张量的形状由参数决定,参数个数任意。
例如:torch.randn(3,4,5)返回一个shape为[3,4,5]即的张量,张量的元素满足标准正态分布。
在这里插入图片描述

一、简要介绍
RNN适用了处理序列性的数据,并利用权重共享机制(即一个RNN网络使用多次)。序列性数据用一个例子来理解,比如我们想要预知某一天是否会下雨,我们采集了大量天数的气象信息,气象信息包括温度、气压和降雨,我们分四天为一组,前三天(用x1,x2,x3表示)就是一个序列,第四天作为已知结果,通过第四天的已知结果与模型的估计值进行比较求损失、求梯度来优化模型参数。使用序列模型的意义在于数据前后之间有一定的联系或影响,即x2的数据依赖于x1,x3的数据依赖于x2,x4的数据依赖于x3,即前三天气象信息与是否降雨对第四天有一定的影响(天气的变化是比较缓和的,很少出现断崖式的变化,所以可以根据前些天的天气预知后一天的天气)。注意,这里的x1,x2,x3是一个序列,而x1,x2,x3三个张量又各自包含三个特征值,分别表示温度、气压和天气。
除了天气、股市等信息外,自然语言也是具有序列关系的信息。

二、RNN神经网络
RNN cell本质上是一个也是线性层(Linear Layer,进行wx+b的线性变换)。区别在于RNN cell的网络是共享的,即使用多次。
在这里插入图片描述
上图中的左边部分是RNN模型的一般书写模式,右边部分是其展开模式。,这里的X1,X2,X3,X4就是之前提到过的序列。h0可以是初始化全为0的张量。先将X1和h0(h表示hidden,即隐层)输入RNN cell(RNN 神经网络),输出结果为h1,再将x2和h1(即RNN cell的两个输出是一样的,都是hi)输入相同的RNN cell输出结果h2,依次类推,最终输出结果h4。可以看出h2的结果融合了x1与x2的信息,h3的结果融合了x1、x2与x3的信息,h4的结果融合了x1、x2、x3与x4的信息。
RNN网络的每一个隐藏层的具体过程:
在这里插入图片描述
input_size表示输入的维度,输入Xt先做一个线性变换,h(t-1)是上一层隐层的输出,也对其做一个线性变换,再将 两个线性变换的结果相加,上图中的Wih为的shape为[hidden_size, input_size],使得Wih
Xt+bih的结果为[hidden_size,1];Whh为的shape为[hidden_size, hidden_size]。最后使用激活函数tanh()使结果位于(-1,1)的区间内,ht为该隐层的输出。
实际上,两个线性运算可以写成一个线性运算:
在这里插入图片描述
RNN cell的公式:
在这里插入图片描述
三、代码实现
自定义一个RNN cell,只需输入参数input_size(即输入值的维度,即Xi有几个分量)和hidden_size(隐藏层的维度,即hi有几个分量)。

# 声明一个RNN cell
cell = torch.nn.RNNCell(input_size = input_size, hidden_size = hidden_size)
# 调用RNN cell,并把下列语句写下一个for循环里(实现序列多次调用RNN cell)
hidden = cell(input, hidden)

注意:input.shape为[batch, input_size],batch为批量的大小,即数据集的大小,即input.shape=batch.size*input_size的二维张量,hidden.shape为[batch, hidden_size]。输出的hidden.shape为[batch, hidden_size]。
实例:
(1)使用RNN cell
RNN的输入张量的shape为[batchSize, inputSize],输出张量(隐层)的维度为[batchSize, hiddenSize]。数据集(dataset)的shape定义为[seqLen,batchSize,inputSize],这里的seqLen即为序列长度。

import torch

batch_size = 1 # 批量大小
seq_len = 3
input_size = 4
hidden_size = 2

cell = torch.nn.RNNCell(input_size = input_size, hidden_size = hidden_size)

dataset = torch.randn(seq_len,batch_size,input_size)
hidden = torch.zeros(batch_size,hidden_size) # h0

# 对dataset进行遍历,每次取出序列中的一个元素,即先取x1,再去x2,最后取x3
for idx,input in enumerate(dataset):
    print('=' * 20,idx,'='*20)
    print('Input size:',input.shape)
    
    hidden = cell(input, hidden) # cell中的hidden为上一次输出的隐层,等号左边的hidden为这一次的隐层。
    
    print('outputs size',hidden.shape)
    print(hidden)

(2)使用RNN
在这里插入图片描述

# num_layers表示隐层的层数(后面图解)
cell = torch.nn.RNN(input_size = input_size, hidden_size = hidden_size, num_layers= num_layers)
# 调用
out, hidden = cell(inputs,hidden) #input为输入的序列[x1,x2,x3,..,xN],input.shape为[seqLen, batch, input_size]
# cell中的hidden表示h0,out表示[h1,h1,...,hN),等号左边的hidden为hN。hidden.shape为[numLayers, batch, hidden_size]
# output.shape为[seqLen, batch, hiddden_size]

在这里插入图片描述
上图的numLayers即为3,表示三层隐藏层,同一种颜色的RNN cell为同一层(其实同一种颜色是同一个RNN cell)。

import torch

batch_size = 1 # 批量大小
seq_len = 3
input_size = 4
hidden_size = 2
num_layers = 1

cell = torch.nn.RNN(input_size = input_size, hidden_size = hidden_size,num_layers=num_layers)

inputs = torch.zeros(seq_len,batch_size,input_size)
hidden = torch.zeros(num_layers,batch_size,hidden_size)

out, hidden = cell(inputs,hidden)

print('Output size:', output.size)
print('Output:', output)
print('Hidden size:', hidden.size)
print('Hidden:', hidden) 

四、实例
在这里插入图片描述
训练目标,将输入的“hello"经过RNN网络根据每一个隐藏层的结果输出为“ohlol”。
1.将单词转化为张量
使用独热(one-hot)编码:
在这里插入图片描述
对于hello的每一个字符都对应一个索引值,将hello转化为索引值得数组,再根据独热编码得方法转化为等长得向量,可以用一个一维含四个特征值得张量表示。注意每一次RNNcell输出的hi是一个含有四个元素的张量,通过softmax函数决定是输出结果是e,h,l,o中的哪一类:
在这里插入图片描述

import torch

input_size = 4
hidden_size = 4
batch_size = 1

#准备数据
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] # 根据x_data的索引序列生成每一位字符的独热编码(后面详解)

inputs = torch.Tensor(x_one_hot).view(-1,batch_size,input_size)
labels = torch.LongTensor(y_data).view(-1,1) # torch.LongTensor()用于创建长整型的张量,参数是多维数组。
# torch.view()和torch.reshape()都是用来重塑tensor的shape的,view()只适用于满足连续性条件的tensor进行操作,而reshape同时还可以对不满足连续性条件的tensor进行操作。

# 定义模型
class Model(torch.nn.Module):
    def __init__(self,input_size,hidden_size,batch_size):
        super(Model,self).__init__()
        self.batch_size = batch_size
        self.input_size = input_size
        self.hidden_size = hidden_size
        self.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 hidden
    
    # 生成初始的h0
    def init_hidden(self):
        return torch.zeros(self.batch_size,self.hidden_size)
    
    
net = Model(input_size,hidden_size,batch_size)

# 损失函数
criterion = torch.nn.CrossEntryLoss()
# 优化器
optimizer = torch.optim.Adam(net.parameters(),lr=0.1)

# 训练模型
for epoch in range(15):
    loss = 0
    optimizer.zero_grad()
    hidden = net.init_hidden()
    print("Predicted stringL:",end='')
    for input,label in zip(inputs,labels):
        hidden = net(input,hidden)
        loss += criterion(hidden,label)
        _,idx = hiddden.max(dim=1)
        print(idx2char[idx.item()],end='')
    loss.backward()
    optimizer.step()
    print(',Epoch[%d/15] loss=%.4f' % (epoch+1,loss.item()))

补充:(1)torch.Tensor()与torch.tensor()的区别
在这里插入图片描述
(2)使用for循环来生成多维数组
下图中x_data中的每一个x作为下标值找到对应one_hot_lookup中的元素,最后生成一个多维数组。
在这里插入图片描述

使用RNN的书写形式:

# 定义模型
class Model(torch.nn.Module):
    def __init__(self,input_size,hidden_size,batch_size,numlayers=1):
        super(Model,self).__init__()
        self.numlayers = num_layers
        self.batch_size = batch_size
        self.input_size = input_size
        self.hidden_size = hidden_size
        self.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,slef.hidden_size)
        out,_=self.rnn(input,hidden)
        return out.view(-1,self.hidden_size) # out.shape为[seqLen*batchSize,hiddenSize]
    
    # 生成初始的h0
    def init_hidden(self):
        return torch.zeros(self.batch_size,self.hidden_size)
    
net = Model(input_size,hidden_size,batch_size)
# 损失函数
criterion = torch.nn.CrossEntryLoss()
# 优化器
optimizer = torch.optim.Adam(net.parameters(),lr=0.1)

# 训练模型
for epoch in range(15):
    optimizer.zero_grad()
    outputs = net(inputs)
    loss = criterion(outputs,labels)
    loss.backwrd()
    optimizer.step()
    _,idx= outputs.max(dim=1)
    idx = idx.data.numpy()
   

2.使用嵌入层代替独热编码(embedding)
独热编码有许多缺点,维度大、张量稀疏、是硬编码(不是通过提取特征得到的)。因此想到使用低维的(lower-dimension),稠密的(dense),学习到的张量(learn from data)。
embedding即把一个高位的、稀疏的样本映射到一个低维的、稠密的空间(即数据降维):
在这里插入图片描述
加入嵌入层后:
在这里插入图片描述

加入嵌入层的网络结构:

# 参数
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]
y_data = [3,1,2,3,2]

inputs = torch.LongTensor(x_data)
labels = torch.LongTensor(y_data).view(-1,1)

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=self.hidden_size,
                                       num_layers = num_layers,
                               batch_first=True)# batch_first=True设为true时后面输入的数据的张量的input.shape为[batchSize,seqLen,input_size],即把训练维度与样本序列进行交换,这里的input_size转变为embedding_size。
                                               # output.shape为[batchSize,seqLen,hiddenSize] 
        self.fc = torch.nn.Linear(hidden_size,num_class) # 完成输出类别的变换
        
    def forward(self,x):
        hidden = torch.zeros(num_layers,x.size(0),slef.hidden_size)
        x = self.emb(x)
        x,_=self.rnn(x,hidden)
        x =self.fc(x)
        return x.view(-1,num_class) # out.shape为[seqLen*batchSize,hiddenSize]
    
    # 生成初始的h0
    def init_hidden(self):
        return torch.zeros(self.batch_size,self.hidden_size)
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:',''.join([idx2char[x] for x in idx]},end='')
    print(',Epoch[%d/15] loss=%.3f' % (epoch +1,loss.item())

对于嵌入层可以通过下面的例子来理解:
在这里插入图片描述

嵌入层可以增大张量的维度,是通过查询的方式。比如上图中的x_data是一个shape为[5]的张量,通过对每一个元素(1,0,2,2,3)来找到一个对应的张量(张量的元素个数由embedding_size决定,为10),这样x_data就转化为了inputs,其shape为[5,10]。
注意:同一个嵌入层对象多次输入一个张量对应唯一的一个输出结果,同一个张量输入不同的嵌入层对象结果不同。
在这里插入图片描述
在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

pytorch入门10--循环神经网络(RNN) 的相关文章

随机推荐

  • 正则表达式语法

    量词 通过量词可以设置一个内容出现的次数量词只对前一个内容起作用 n 表示整好出现n次 m n 表示出现m到n次 m xff0c 表示出现m次以上 43 表示至少一个 xff0c 相当于 1 xff0c 例如 a 43 表示0个或多个 xf
  • 51单片机之——串口通信(含实现部分)

    目录 一 串口通信简介 二 前置知识 二 前置准备 三 实现单片机通过串口向电脑发送数据 四 实现单片机通过串口接收电脑发送数据 一 串口通信简介 本篇文章将实现两个部分 xff0c 第一部分为单片机通过串口向电脑发送数据 xff1b 第二
  • Java web速成之jsp

    一 JSP简介 1 1 jsp运行和环境搭建 1 知识目标 xff08 1 xff09 能够叙述Java web的发展历程 xff1b xff08 2 xff09 能够描述JSP引擎在JSP中的作用 xff1b xff08 3 xff09
  • 单片机学习总结

    51单片机的学习总结 经过一个学期的51单片机的学习 xff0c 对于51单片机有了很大的理解 由于我学的是汇编语言 xff0c 实验也是用的汇编语言 首先对于51单片机要了解他的内部结构 一个8位的CPU xff0c 一个片内振荡器及时钟
  • 【计算机三级网络技术】 快速求出IP地址块经聚合后的IP地址

    计算机三级 网络技术之快速求出IP地址块经聚合后的IP地址 相信大家在备考计算机三级网络技术都会遇到求多个IP地址聚合后的地址是什么的题目 xff0c 以下是一个小技巧 xff08 具体原理自己把十进制的IP地址和子网掩码换算成二进制理解一
  • Ubuntu虚拟机使用NAT连接 然后无法上网的情况

    问题描述 xff1a 以下为我的虚拟机的网络适配器配置 xff0c 使用的是NAT模式 正常连接上网络的时候虚拟机右上角会如下图所示 当网络down了以后 xff0c 就不会显示第一个 xff0c 那么这个时候就可以使用以下方法来解决 仅作
  • 【计算机网路】(3)网络层:IP地址组成、分类,子网划分

    目录 IP地址 xff08 32位 xff09 1 1 组成 1 2 分类 1 2 1 A B C三类IP地址的组成 1 2 2 私有地址 记 1 3 子网掩码 1 3 1 示例 求IP地址为192 168 1 0的网络号 1 4 DHCP
  • 【知识图谱】知识图谱入门详细介绍

    知识图谱 入门与介绍 文章目录 知识图谱 入门与介绍1 引言2 概念3 分类4 发展历程5 作用5 1 搜索5 2 问答5 3 辅助大数据分析 6 知识图谱的构建6 1 数据获取6 2 信息 xff08 知识 xff09 抽取6 3 知识融
  • vnc服务器配置—linux

    VNC服务器配置 vnc服务器是个远程控制服务器 需求 xff1a 在linux中装服务器端 xff0c 在windows中装客户端 xff0c 使得可以远程控制linux 首先下载vnc服务器 yum install tigervnc s
  • 18715 出栈序列

    18715 出栈序列 时间限制 1000MS 代码长度限制 10KB 提交次数 0 通过次数 0 题型 编程题 语言 不限定 Description 一种简洁的栈定义方法如下 int st 1000 top 61 0 以top作为栈顶指针
  • Ubuntu 打不开终端

    因为我在做python时做了应该是删除了软连接 xff0c 然后终端就打不开了 在网上寻找多个方法 xff0c 终于找到适合自己的 请注意 xff0c 这里可能会出现其他问题 xff0c 使用时请注意 因为进不去终端 xff0c 所以按ct
  • unity实现切割图片

    我们在找资源时有时候会遇到把一堆图片放进一张图片的情况 xff0c 往往需要将一块块小图片切割出来使用 xff1a 步骤 xff1a xff08 1 xff09 将Sprite Mode设置为Multiple 2 xff09 点击Sprit
  • 使用mybatis实现数据库插入数据

    xff08 1 xff09 在Dao层的实体类接口中添加相应函数 xff08 2 xff09 在相应的 xml文件中加入一个mapper标记 xff08 3 xff09 修改main 函数中的语句 xff0c 关键是修改了sqlId 修改指
  • 计算机图形学(三维观察投影线、视图变换、投影变换)

    一 三维观察流水线 概念 xff1a 将建立的三维场景显示在二维视口的过程称为三维观察流水线 在三维观察流水线中 xff0c 也就是在将三维场景显示在二维视口的过程中 xff0c 需要在不同坐标系下进行不同的操作 xff0c 这些坐标系包括
  • javascript:html嵌入javascript代码的三种方式

    一 概念 javascript是运行在浏览器中的脚本语言 xff0c 运行在浏览器的内存当中 xff0c 不需要程序员手动编译 xff0c 编写玩源代码之后 xff0c 浏览器直接打开解释执行 xff0c 简称JS 二 html嵌入java
  • idea中新建和写一个简单的vue项目

    一 使用ida创建一个vue项目 xff08 默认node js环境已配好 xff0c idea已加vue js插件 xff09 xff08 1 xff09 新建项目 xff08 2 xff09 观察项目目录结构 node modules文
  • css的相对定位和绝对定位

    css标签的相对定位和绝对定位是通过position属性来控制的 xff0c 相对定位和绝对定位不改变元素的大小形状 xff0c 只改变元素的位置 一 position属性的值有以下几种 xff1a static 默认值 xff0c 没有定
  • 个人博客系统(Vue实现)的主页布局设计

    源码地址 xff1a https gitee com cheng xuyuan blogWeb git xff08 请忽略这句 xff09 一 整体布局 上下划分 xff0c 再左右划分 主体代码 xff1a lt el container
  • 绘制Vue主页的列表结构(包括增、删、改、查功能)

    源码地址 xff1a https gitee com cheng xuyuan blogWeb git xff08 请忽略这句 xff09 一 面包屑导航区域设计 xff08 下面划红线的部分 xff09 说明 xff1a el bread
  • pytorch入门10--循环神经网络(RNN)

    补充 xff1a torch randn 函数返回一个张量 xff0c 包含了从正态分布 xff08 均值为0 xff0c 方差为1 xff09 中抽取的一组随机数 张量的形状由参数决定 xff0c 参数个数任意 例如 xff1a torc