当前位置: 首页 > news >正文

Pytorch深度学习实践(10)循环神经网络RNN

循环神经网络RNN

RNN看起来很复杂,但实际上就是线性层的一种复用

例如,对于一个下雨的数据集,收集了每天的温度、气压等信息,用来预测未来是否会下雨

  • 使用全连接层,将数据集中的温度、气压等信息作为下雨的的影响因素,做逻辑回归

    缺点:计算量较大

  • 由于这组数据带有序列性,因此可以考虑使用RNN循环神经网络

    RNN使用权重共享的特点,来减少模型的计算量

rnn中,对于每一步的输出,不仅取决于当前的变量,同时还受到前面数据的影响,适用于序列数据,即数据存在先序和后序关系

基本概念

RNN的本质是一个线性层

在这里插入图片描述

  • 将每一项的输入 x i x_i xi输入到RNN Cell中,同时,因为数据存在序列性,因此每一个的输出还要受到前面数据的影响,即将当前的数据与前面的输出的数据进行融合,得到 h i h_i hihidden
  • RNN Cell就是一个线性层,每一个线性层的权重相同,即权重参数共享

RNN网络的计算

每一步的计算不仅取决于当前的输入 x i x_i xi,还受到之前输出 h i − 1 h_{i-1} hi1的影响
h 1 = L i n e a r ( x 1 , h 0 ) h 2 = L i n e a r ( x 2 , h 1 ) ⋮ h n = L i n e a r ( x n , h n − 1 ) h_1 = Linear(x_1, h_0) \\ h_2 = Linear(x_2, h_1) \\ \vdots \\ h_n = Linear(x_n, h_{n-1}) h1=Linear(x1,h0)h2=Linear(x2,h1)hn=Linear(xn,hn1)
最后还要经过激活函数tanh进行非线性变换,得到最终的输出
h t = t a n h ( W i h x t + b i h + W h h h t − 1 + b h h ) h_t = tanh(W_{ih}x_t + b_{ih} + W_{hh}h_{t-1} + b_{hh}) ht=tanh(Wihxt+bih+Whhht1+bhh)
在这里插入图片描述

以这种循环的形式,把具有序列性的数据一个个输入进去,再把输出结果一个个循环一样的输出,这种计算方式就是循环神经网络

RNN网络的反向传播

由上,可以将RNN网络的数学模型写为如下格式:
O t = g ( V ⋅ h t ) h t = f ( U ⋅ x t + W ⋅ h t − 1 ) O_t = g(V\cdot h_t) \\ h_t = f(U\cdot x_t + W\cdot h_{t-1}) Ot=g(Vht)ht=f(Uxt+Wht1)
因此,再反向传播过程中,梯度可以初步写为:
∂ L t ∂ w = ∂ L t ∂ O t ⋅ ∂ O t ∂ h t ⋅ ∂ h t ∂ w \frac{\partial L_t}{\partial w} = \frac{\partial L_t}{\partial O_t} \cdot \frac{\partial O_t}{\partial h_t} \cdot \frac{\partial h_t}{\partial w} wLt=OtLthtOtwht
而又可以发现,由于权重共享,因此 h t h_t ht里还包含 h t − 1 h_{t-1} ht1,还需要加上对 h t − 1 h_{t-1} ht1对参数的求导
∂ L t ∂ w = ∂ L t ∂ O t ⋅ ∂ O t ∂ h t ⋅ ∂ h t ∂ w + ∂ L t ∂ O t ⋅ ∂ O t ∂ h t ⋅ ∂ h t ∂ h t − 1 ⋅ ∂ h t − 1 ∂ w \frac{\partial L_t}{\partial w} = \frac{\partial L_t}{\partial O_t} \cdot \frac{\partial O_t}{\partial h_t} \cdot \frac{\partial h_t}{\partial w} + \frac{\partial L_t}{\partial O_t} \cdot \frac{\partial O_t}{\partial h_t} \cdot \frac{\partial h_t}{\partial h_{t-1}} \cdot \frac{\partial h_{t-1}}{\partial w} wLt=OtLthtOtwht+OtLthtOtht1htwht1
h t − 1 h_{t-1} ht1里还包含 h t − 2 h_{t-2} ht2,不断往下,最终可以写为:
∂ L t ∂ w = ∑ i = 0 t ∂ L t ∂ O t ∂ O t ∂ h t ∂ h t ∂ h i ∂ h i ∂ w \frac{\partial L_t}{\partial w} = \sum _{i=0}^{t}\frac{\partial L_t}{\partial O_t}\frac{\partial O_t}{\partial h_t}\frac{\partial h_t}{\partial h_i}\frac{\partial h_i}{\partial w} wLt=i=0tOtLthtOthihtwhi
即,RNN神经网络中反向传播算法利用的是时间反向传播算法,需要求解所有时间步的梯度之后。利用多变量链式求导法则求解梯度

RNN中梯度消失与梯度爆炸

RNN反向传播过程可以看出, ∂ h t ∂ h i \frac{\partial h_t}{\partial h_i} hiht的计算量比较大,是一个连乘递归的过程

  • ∂ h t ∂ h i \frac{\partial h_t}{\partial h_i} hiht每一项都比较小( < 1 <1 <1)时,连乘起来的结果趋近于0,因此导致梯度趋近于0,发生梯度消失的问题,且随着 t t t越往前,梯度消失的现象越明显(因为越往前,递归项越多,梯度消失越多)
  • ∂ h t ∂ h i \frac{\partial h_t}{\partial h_i} hiht都比较大时,连乘起来结果非常大,因此导致梯度非常大,计算复杂,发生梯度爆炸的问题,且随着 t t t越往前,梯度爆炸的现象越明显(因为越往前,递归项越多,梯度爆炸越多)

Pytorch代码实现

手写循环实现

定义RNN Cell

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

hidden层的输出

hidden = cell(input, hidden)

关键:需要仔细考虑好维度的问题

  • 输入的维度: ( b a t c h , i n p u t _ s i z e ) = b × x (batch, input\_size)=b×x (batch,input_size)=b×x
  • hidden的维度: ( b a t c h , h i d d e n _ s i z e ) = b × h (batch, hidden\_size) = b×h (batch,hidden_size)=b×h
  • 输出的维度: ( b a t c h , h i d d e n _ s i z e ) = b × h (batch, hidden\_size) = b×h (batch,hidden_size)=b×h
  • 整个序列需要构造的维度: ( s e q L e n , b a t c h , i n p u t _ s i z e ) (seqLen, batch, input\_size) (seqLen,batch,input_size)(序列长度,批量大小,输入维度)
import torch########### 初始化参数 ##########
batch_size = 1
seq_len = 3
input_size = 4
hidden_size = 2########## 构造cell ##########
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)########## 循环计算 ##########
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)

使用torch.nn.RNN

构造网络

cell = torch.nn.RNN(input_size = input_size,hidden_size = hidden_size,num_layers = num_layers)  # 网络层数

输出结果

out, hidden = cell(inputs, hidden)
  • 参数中的hidden代表 h 0 h_0 h0,维度为 ( n u m _ l a y e r s , b a t c h _ s i z e , h i d d e n _ s i z e ) (num\_layers, batch\_size,hidden\_size) (num_layers,batch_size,hidden_size)
  • 输出结果中的hidden代表最后的输出 h n h_n hn,维度为 ( n u m _ l a y e r s , b a t c h _ s i z e , h i d d e n _ s i z e ) (num\_layers, batch\_size,hidden\_size) (num_layers,batch_size,hidden_size)
  • 输出结果中的out代表 h 1 − h n h_1 - h_n h1hn,维度为 ( s e q L e n , b a t c h , i n p u t _ s i z e ) (seqLen, batch, input\_size) (seqLen,batch,input_size)

在这里插入图片描述

layer设置多层RNN

在这里插入图片描述

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.randn(seq_len, batch_size, input_size)
hidden = torch.zeros(num_layers, batch_size, hidden_size)########## 模型计算 ##########
out, hidden = cell(inputs, hidden)print('Outputs size:', out.shape)
print('Output:', out)
print('Hidden size:', hidden.shape)
print("Hidden:", hidden)

简单实战

hello → \to ohlol

在这里插入图片描述

数据预处理

首先要对输入的数据进项变化

由于我们输入的数据是字符串,而RNN神经网络需要的数据是矩阵向量,因此需要先对字符串中每个字符进行编码,写到一个字典里:

characterindex
e e e0
h h h1
l l l2
o o o3

从而将hello转化为编码10223

然后再使用独热编码,即one-hot,将其转化为矩阵格式,得到独热向量:
0 1 0 0 1 0 0 0 0 0 1 0 0 0 1 0 0 0 0 1 \begin{matrix} 0 \ &1 \ & 0 \ & 0 \\ 1 \ &0 \ & 0 \ & 0 \\ 0 \ &0 \ & 1 \ & 0 \\ 0 \ &0 \ & 1 \ & 0 \\ 0 \ &0 \ & 0 \ & 1 \\ \end{matrix} 0 1 0 0 0 1 0 0 0 0 0 0 1 1 0 00001
因此就确定了input_size为4

one-hot

0 → \to 10000 10000 10000(第一位为1)

1 → \to 01000 01000 01000(第二位为1)

整体过程如下所示:
在这里插入图片描述

输出数据的处理

最终输出的是类别概率,即四个字符h、e、l、o四个类别的概率,即最终转变为一个分类问题

因此最终的输出还要接一个softmax函数和交叉熵损失

代码实现

import torch
import matplotlib.pyplot as plt########## 初始化具体参数 ##########
input_size = 4
hidden_size = 4
num_layers = 1
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]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]inputs = torch.Tensor(x_one_hot).view(seq_len, batch_size, input_size)
labels = torch.LongTensor(y_data)########### 定义模型 ##########
class Model(torch.nn.Module):def __init__(self, input_size, hidden_size, batch_size, num_layers=1):super(Model, self).__init__()## 初始化参数self.input_size = input_sizeself.hidden_size = hidden_sizeself.batch_size = batch_sizeself.num_layers = num_layers## 定义RNN模型self.rnn = torch.nn.RNN(input_size=self.input_size,hidden_size=self.hidden_size,num_layers=self.num_layers)def forward(self, x):## 初始化 h0hidden = torch.zeros(self.num_layers,self.batch_size,self.hidden_size)## 模型计算out, _ = self.rnn(x, hidden)return out.view(-1, self.hidden_size)model = Model(input_size, hidden_size, batch_size, num_layers)########## 定义损失函数和优化器 ##########
criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.05)########## 模型训练 ##########
epoch_history = []
loss_history = []
for epoch in range(15):optimizer.zero_grad()# forwardoutputs = model(inputs)loss = criterion(outputs, labels)# forwardloss.backward()# updataoptimizer.step()epoch_history.append(epoch)loss_history.append(loss.item())_, 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()))plt.plot(epoch_history, loss_history)
plt.xlabel('epoch')
plt.ylabel('loss')
plt.show()

最终输出结果:

在这里插入图片描述

损失函数曲线:

在这里插入图片描述

相关文章:

  • 昇思25天学习打卡营第24天|RNN实现情感分类
  • 黑马JavaWeb企业级开发(知识清单)03——HTML实现正文:排版(音视频、换行、段落)、布局标签(div、span)、盒子模型
  • .mp4格式的视频为何不能通过video标签在chrome浏览器中播放?
  • Typescript学习笔记(2.0)
  • Pytorch使用教学4-张量的索引
  • 88个python的基本语法知识【二】
  • 找工作准备刷题Day10 回溯算法 (卡尔41期训练营 7.24)
  • 心跳机制详解
  • 【python】python基于 Q-learning 算法的迷宫游戏(源码+论文)【独一无二】
  • 个性化音频生成GPT-SoVits部署使用和API调用
  • Java正则表达式判断有无特殊字符
  • 数据结构—红黑树
  • 记一次折腾后台nodejs服务的经历
  • shopee虾皮 java后端 一面面经 整体感觉不难
  • Android TabLayout的简单用法
  • 【159天】尚学堂高琪Java300集视频精华笔记(128)
  • 【附node操作实例】redis简明入门系列—字符串类型
  • 【刷算法】从上往下打印二叉树
  • 5、React组件事件详解
  • Elasticsearch 参考指南(升级前重新索引)
  • iOS小技巧之UIImagePickerController实现头像选择
  • JAVA多线程机制解析-volatilesynchronized
  • Python中eval与exec的使用及区别
  • React Transition Group -- Transition 组件
  • 聊聊spring cloud的LoadBalancerAutoConfiguration
  • 人脸识别最新开发经验demo
  • 阿里云移动端播放器高级功能介绍
  • ​LeetCode解法汇总2696. 删除子串后的字符串最小长度
  • # 移动硬盘误操作制作为启动盘数据恢复问题
  • #QT项目实战(天气预报)
  • (编译到47%失败)to be deleted
  • (分享)一个图片添加水印的小demo的页面,可自定义样式
  • (南京观海微电子)——示波器使用介绍
  • (算法)Travel Information Center
  • .class文件转换.java_从一个class文件深入理解Java字节码结构
  • .jks文件(JAVA KeyStore)
  • .NET 4.0中的泛型协变和反变
  • .NET MAUI学习笔记——2.构建第一个程序_初级篇
  • .NET MVC之AOP
  • .NET/C# 推荐一个我设计的缓存类型(适合缓存反射等耗性能的操作,附用法)
  • @GlobalLock注解作用与原理解析
  • @RequestBody的使用
  • [ C++ ] STL priority_queue(优先级队列)使用及其底层模拟实现,容器适配器,deque(双端队列)原理了解
  • [2016.7 test.5] T1
  • [Algorithm][综合训练][kotori和气球][体操队形][二叉树中的最大路径和]详细讲解
  • [android] 练习PopupWindow实现对话框
  • [AutoSar]工程中的cpuload陷阱(三)测试
  • [BZOJ5125]小Q的书架(决策单调性+分治DP+树状数组)
  • [C++][ProtoBuf][初识ProtoBuf]详细讲解
  • [Erlang 0129] Erlang 杂记 VI 2014年10月28日
  • [E单调栈] lc2487. 从链表中移除节点(单调栈+递归+反转链表+多思路)
  • [Firefly-Linux] RK3568 pca9555芯片驱动详解
  • [LeetCode] 19. 删除链表的倒数第 N 个结点
  • [leetcode]56. Merge Intervals归并区间
  • [Linux系统编程] 静态库与动态库