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

pytorch学习-12循环神经网络(基础篇)

12.循环神经网络(基础篇)_哔哩哔哩_bilibili

12.1 RNN Cell

        RNN  Cell 本质为一个线性层,与普通线性层区别为,该线性层输入x_{t}时,同时需要x_{t-1}经过RNN Cell计算得到的h_{t-1}参与运算。如下图所示:

        RNN Cell 内部运算如下图所示: 

        具体运算过程:

  1. 输入x_{t}h_{t-1},均做线性变换 
  2. 将其线性变换的结果求和并代入tanh()函数计算

        注意:

       1. x_{t}的维度是input_size, h_{t-1}的维度是hidden_size,输出h_{t}的维度是hidden_size,我们需要先把x_{t}的维度变成hidden_size,所以W_{ih}应该是一个 input_size*hidden_size的矩阵,x_{t}W_{ih}(矩阵运算时一般x在W前,与公式中不同) 得到一个 1*hidden_size的矩阵(就是维度为hidden_size的向量),b_{ih }是偏置。输入权重矩阵W_{hh}是一个hidden_size* hidden_size的矩阵。(i代表input_size,h代表hidden_size)
        2. W_{hh}h_{t-1}+b_{hh}W_{ih}x_{t}+b_{ih}都是维度为hidden_size的向量,两个向量相加,就把信息融合起来了,融合之后用tanh做激活,循环神经网络的激活函数用的是tanh,因为tanh的取值在-1到+1之间,算出结果得到隐藏层输出h_{t}

        把RNN Cell以循环的方式把序列(x1,x2,…)一个一个送进去,然后依次算出隐藏层(h1,h2…)的过程,每一次算出来的h会作为下一个RNN Cell的输入,这就叫循环神经网络

12.2 构建RNN Cell方式构造RNN层

        

注意:

        1.torch.nn.RNNCell 中输入 input_size和hidden_size,会自动确定线性层的W_{ih}b_{ih }

        2.hidden为求每一个x_{t}所对应的h_{t},其输入为x_{t}h_{t-1},所以需要多次循环求所有h。  

      

  • batchSize表示批量
  • seqLen=3表示每一个样本都有x1,x2,x3这些特征
  • inputSize=4表示每一个特征都是4维的
  • hoddenSize=2表示每一个隐藏层是2维

        代码演示:

import torchbatch_size=1    #表示每次处理的数据样本数量。这里设置为1,意味着每次只处理一个样本
seq_len=3       #表示每个样本的序列长度。这里设置为3,意味着每个样本包含3个特征向量
input_size=4    #表示每个特征值的维度。这里设置为4
hidden_size=2   #表示隐藏层的维度。这里设置为2RNNCell=torch.nn.RNNCell(input_size,hidden_size)
# 这里创建了一个RNN单元,该单元的输入维度为4,输出(隐藏状态)维度为2。这个RNN单元将用于处理序列数据。
# W权重矩阵为input_size*hidden_size(4*2),bias为hidden_size维度。dataset=torch.randn(seq_len,batch_size,input_size)
# 这里随机创建了一个数据集,包含3个序列,每个序列包含1个样本,每个样本包含4个特征向量。
print('dataset:', dataset)
print('dataset.size():', dataset.size())hidden=torch.zeros(batch_size,hidden_size)
# 这里创建了一个初始的隐藏状态,维度为(batch_size,hidden_size)(1*2)for index,input in enumerate(dataset):print('='*20,index,'='*20)print("input_size:",input.size())hidden=RNNCell(input,hidden)    # 这里将输入数据输入到RNN单元中,并得到输出的隐藏状态。print("output_size:",hidden.size())print(hidden)

运行结果: 

12.3 直接用RNN类方式构造RNN层

  • inputs:所有的x
  • hidden:h_{0}

        用RNN不用自己写循环,它自动循环,所以输入的时候要把所有的序列都送进去,然后给定h_{0},然后我们就会得到所有的隐层输出(out)以及最后一层的输出(hidden)

         当有多层RNN时,可调整函数中num_layer参数,多层RNN如下图所示:

        batch_first设置为TRUE,需要进行下图的调整:

        代码演示:

import torchbatch_size=1
input_size=3
hidden_size=2
seq_len=4
num_layers=1RNN=torch.nn.RNN(input_size,hidden_size,num_layers,batch_first=True)
#batch_first=True表示输入的第一个维度是batch_size,第二个维度是序列长度dataset=torch.randn(batch_size,seq_len,input_size)
#由于batch_first=True,所以输入的第一个维度是batch_size,第二个维度是序列长度hidden=torch.zeros(num_layers,batch_size,hidden_size)output,hidden=RNN(dataset,hidden)print("ouput shape: ",output.shape)
print("output: ",output)
print("hidden shape: ",hidden.shape)
print("hidden: ",hidden)

        运行结果: 

12.4 使用RNN Cell训练hello转换到ohlol 

        12.4.1 准备数据集

        由于hello为字符串不具备特征向量,所以先将其转换为字典形式,再生成相应的特征向量。

如下图所示:

         因为输入有四个字符(e,h,l,o),input_size=4,这相当于一个多分类问题,输出就是一个4维的向量,每一维代表是某一个字符的概率,接交叉熵就能输出概率了

        one-hot(独热编码):独热编码通常用于处理类别间不具有大小关系的特征。 采用独热编码后,会把具有n个特征的变量变成有一个n维的稀疏向量

        代码演示:

import torchbatch_size=1
input_size=4
hidden_size=4# 准备数据集
idx2char=['e','h','l','o']
x_data=[1,0,2,2,3]  #hello
y_data=[3,1,2,3,2]  #ohlol# one-hot编码
one_hot_lookup=[[1,0,0,0],  #e[0,1,0,0],  #h[0,0,1,0],  #l[0,0,0,1]]  #ox_one_hot=[one_hot_lookup[x] for x in x_data]inputs=torch.Tensor(x_one_hot).view(-1,batch_size,input_size) # (seq_len,batch_size,input_size)
labels=torch.LongTensor(y_data).view(-1,1)  # (seq_len,1)#2.定义网络
class RNN(torch.nn.Module):def __init__(self,input_size,hidden_size,batch_size):super(RNN,self).__init__()self.input_size=input_sizeself.hidden_size=hidden_sizeself.batch_size=batch_sizeself.RNN_cell=torch.nn.RNNCell(input_size,hidden_size)  #定义RNNCelldef forward(self,inputs,hidden):#求hidden的输出hthidden=self.RNN_cell(input,hidden)return hiddendef init_hidden(self):#初始化隐状态return torch.zeros(self.batch_size,self.hidden_size)net=RNN(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()  # 初始化隐状态print('Predicted string: ', end='')for input, label in zip(inputs, labels):#RNNCell的训练每次只能求一个hidden,所以需要多次循环求所有的hiddenhidden = net(input, hidden)         # 前向传播loss += criterion(hidden, label)    # 计算损失_, idx = hidden.max(dim=1)          # 预测字符print(idx2char[idx.item()], end='')  # 打印预测字符loss.backward()     # 反向传播optimizer.step()    # 更新参数print(', Epoch [%d/15] loss=%.4f' % (epoch+1, loss.item()))

        运行结果:

12.5 使用用RNN 训练hello转换到ohlol

        代码演示:

        

import torchbatch_size=1
input_size=4
hidden_size=4
num_layers=1
seq_len=5# 准备数据集
idx2char=['e','h','l','o']
x_data=[1,0,2,2,3]  #hello
y_data=[3,1,2,3,2]  #ohlol# one-hot编码
one_hot_lookup=[[1,0,0,0],  #e[0,1,0,0],  #h[0,0,1,0],  #l[0,0,0,1]]  #ox_one_hot=[one_hot_lookup[x] for x in x_data]inputs=torch.Tensor(x_one_hot).view(seq_len,batch_size,input_size) # (seq_len,batch_size,input_size)
labels=torch.LongTensor(y_data)  # (seq_len,1)#2.定义网络
class RNN(torch.nn.Module):def __init__(self,input_size,hidden_size,batch_size,num_layers):super(RNN,self).__init__()self.input_size=input_sizeself.hidden_size=hidden_sizeself.batch_size=batch_sizeself.num_layers=num_layersself.RNN=torch.nn.RNN(input_size=input_size,hidden_size=hidden_size,num_layers=num_layers)  #定义RNNCelldef forward(self,inputs):   #求hidden的输出所有hhidden = torch.zeros(self.num_layers, self.batch_size, self.hidden_size)out,_=self.RNN(inputs,hidden)return out.view(-1,self.hidden_size)net=RNN(input_size,hidden_size,batch_size,num_layers)#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)          #预测结果  idx = idx.data.numpy()               #转化为numpy格式print('Predicted: ', ''.join([idx2char[x] for x in idx]), end='')   #打印预测结果print(', Epoch [%d/15] loss = %.3f' % (epoch + 1, loss.item()))

        运行结果:

12.6 Embedding(嵌入层)编码方式 

        Embedding把一个高维的稀疏的样本映射到一个稠密的低维的空间里面,也就是数据的降维。降维的原理就是矩阵乘法。此外,还可以对低维的数据进行升维,把一些其他特征给放大了,或者把笼统的特征给分开了。

  • one-hot表示的缺点:
    1、维度太高(字符级:字符集ASIIC 128维 单词级:几万维)
    2、过于稀疏
    3、硬编码的,这个词向量并不是学习出来的

        嵌入层降维的方式:输入一个索引,通过查表来找到对应的向量,找的方法是通过将lookup表与一个one-hot向量进行相乘,然后得出最后的嵌入向量。以下图为例

        若想取出第三行数据(下标为2),可通过先将原矩阵(4x5)转置(5x4),再乘相应的(4x1)的矩阵即可。如下所示

        \begin{bmatrix} -1.1195 & 0.9164 & \mathbf{ 0.3896} & 2.3852 \\ -0.3138 & 1.2496 & \boldsymbol{1.6515} & 1.6643 \\ -1.4441 & 0.2717 & \boldsymbol{-0.9640} & -1.0360 \\ -0.4976 & -0.8645 & \boldsymbol{\mathbf{} 1.2836} & -1.3234 \\ 1.1591 & 1.0408 & \boldsymbol{ 1.2430} & -1.3370 \end{bmatrix}\begin{bmatrix} 0\\ 0\\ 1\\ 0 \end{bmatrix} 

         有时候的输出的隐藏层h的维度与类别的维度o不一致,所以可以添加Linear Layer,将h的维度映射为o的维度

  •         num_embeddings:嵌入层的输入空间的大小
  •         embedding_dim:为嵌入向量的维度

        假设我们有一个词汇表,其中包含10000个不同的单词。我们希望将这些单词映射到一个低维的连续向量空间中,以便在深度学习模型中使用这些词嵌入。

在这种情况下:

"num_embeddings" 将是词汇表的大小,即 10000。
"embedding_dim" 可以是词嵌入的维度,例如 300。

        线性层

        输入中*:代表添加的维度

        输出中的*:代表处理最后一个维度,其他维度都与输入维度相同

   

     交叉熵

        N:batch一组样本的个数

        C:class输入的维度大小

        中间可增加维度。

        代码演示:

import torch#参数初始化
num_class=4     #分类数,e,h,l,o
input_size=4    #每个特征的维度
hidden_size=8   #隐藏层大小
embedding_size=10   #Embedding层输出维度
num_layers=2    #RNN层数
batch_size=1
seq_len=5       #一个样本的特征数#1.准备数据集,输入输出
idx2char=['e','h','l','o']
x_data=[[1,0,2,2,3]]  #hello (batch,seq_len)
y_data=[3,1,2,3,2] #ohlol   (batch,seq_len)inputs=torch.LongTensor(x_data) #inputs为longtensor类型
labels=torch.LongTensor(y_data) #labels为longtensor类型#2.定义RNN网络
class RNN_Embedding_Linear_Net(torch.nn.Module):def __init__(self):super(RNN_Embedding_Linear_Net, self).__init__()self.Embedding=torch.nn.Embedding(input_size,embedding_size)     #定义Embedding层#参数为 input_size,embedding_size#作用是将输入的数字转换为向量形式,embedding_size为输出的向量维度,之后传给RNN层self.Rnn=torch.nn.RNN(input_size=embedding_size,hidden_size=hidden_size,num_layers=num_layers,batch_first=True)   #定义RNN层#参数为 embedding_size,hidden_size,num_layers,batch_first=True#经过Embedding层后,输入为(batch,seq_len,embedding_size)#input_size=embedding_size,因为Embedding层的输出维度为embedding_sizeself.Linear=torch.nn.Linear(hidden_size,num_class)   #定义线性层#参数为 hidden_size,num_class#作用是将RNN的输出映射到num_class维度的空间中def forward(self,inputs):hidden=torch.zeros(num_layers,inputs.size(0),hidden_size)   #初始化隐藏状态embedding_output=self.Embedding(inputs)   #将输入通过Embedding层转换为向量形式#输出为(batch,seq_len,embedding_size)Rnn_output,hidden=self.Rnn(embedding_output,hidden)   #将向量输入到RNN层中#输出为(batch,seq_len,hidden_size)output=self.Linear(Rnn_output)       #将RNN的输出通过线性层映射到num_class维度的空间中#输出为(batch,seq_len,num_class)return output.view(-1,num_class)   #将输出reshape为(batch*seq_len,num_class)model=RNN_Embedding_Linear_Net()   #实例化模型#3.定义损失函数和优化器
criterion=torch.nn.CrossEntropyLoss()   #定义损失函数为交叉熵
optimizer=torch.optim.Adam(model.parameters(),lr=0.05)   #定义优化器为Adam#4.训练模型
for epoch in range(15):optimizer.zero_grad()   #梯度清零outputs=model(inputs)   #模型前向传播loss=criterion(outputs,labels)   #计算损失loss.backward()   #反向传播optimizer.step()   #更新参数_,idx=outputs.max(dim=1)   #获取预测结果idx=idx.data.numpy()     #转换为numpy数组print("predict: ",''.join([idx2char[i] for i in idx]),end=' ')   #预测结果print(',epoch: %d/15 loss= %.4f'%(epoch+1,loss.item()))   #打印损失和epoch数

        运行结果:
 


12.7参考

跟着刘二大人学pytorch(第---12---节课之RNN基础篇)_刘二pytorch课件-CSDN博客

一文读懂Embedding的概念,以及它和深度学习的关系 - 知乎

神经网络嵌入层中的“embedding_dim“和“num_embeddings“分别是什么_embedding dim-CSDN博客

http://www.dtcms.com/a/273007.html

相关文章:

  • 机器视觉之激光码检测系统
  • 【世纪龙科技】学测-汽车信息化综合实训考核平台(机电方向)
  • 数字孪生系统如何助力汽车零部件企业实现虚拟智控
  • RedisJSON 内存占用剖析与调优
  • Lua嵌入式爬虫实现步骤
  • 【Linux系统】冯诺依曼体系结构 | 初识操作系统
  • 生产者、消费者问题(C语言、POSIX)
  • 测试覆盖标准-条件覆盖-短路求值
  • 全新开源AI知识库系统!PandaWiki一键构建智能文档,支持AI问答、创作与搜索!
  • [特殊字符] 05_Jenkins 部署前端项目实现自动化部署
  • rv1106使用笔记
  • 【RL-VLM-F】算法框架图绘图学习笔记
  • ubuntu server配置静态IP
  • ​​​​​​​微软PowerBI PL-300认证考试报名入口及费用
  • 【PTA数据结构 | C语言版】顺序队列的3个操作
  • 完美卸载 Ubuntu 双系统:从规划到实施的完整指南
  • 乐鑫代理商飞睿科技,ESP32模组重塑AIoT体验的四大技术支柱
  • C++类型萃取(Type Traits):深入解析std::enable_if与std::is_same
  • git fetch的使用
  • 【第五章-基础】Python 函数---以一个初学者来理解函数
  • 第十六天,7月10日,八股
  • 【网络安全】利用 Cookie Sandwich 窃取 HttpOnly Cookie
  • vue中token的使用与统计实践
  • android闪光灯源码分析
  • Android 插件化实现原理详解
  • 【读书笔记】如何画好架构图:架构思维的三大底层逻辑
  • 遥感影像图像分割-地物提取模型训练与大图直接推理流程
  • 突破传统局限:60G 3D毫米波雷达如何实现精准人体全状态检测?
  • Vue3基础知识
  • 论文笔记(LLM distillation):Distilling Step-by-Step!