Pytorch深度学习实践 12 循环神经网络(基础篇)

本文详细介绍了PyTorch中的循环神经网络(RNN)、RNNCell、LSTM和GRU的基本概念、结构以及在实际项目中的使用示例,包括RNNCell的使用、RNN和LSTM的区别,以及如何结合Embedding和Linear层构建模型。
摘要由CSDN通过智能技术生成

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

最近在 B站 刘二大人 学习PyTorch ,上传一些学习用的代码,仅供参考和交流。

目录

1、循环神经网络RNN简介

(1)RNN Cell

(2)RNN

(3)编码器

(4)LSTM

(5)GRU

2、Example 1:Using RNNCell

3、Example 2:Using RNN

4、Example 3:Using embedding and linear layer


1、循环神经网络RNN简介

        循环神经网络(Recurrent Neural Network, RNN)是一类以序列数据为输入,在序列的演进方向进行递归且所有节点(循环单元)按链式连接的递归神经网络。常见的循环神经网络包括双向循环神经网络(Bidirectional RNN, Bi-RNN)和长短期记忆网络(Long Short-Term Memory networks,LSTM)。循环神经网络的真正形式是左边这种,但是也常表现为右边这种,一般隐藏层初始状态为0,当输入x1后,计算出下一个状态h1,当输入x2时,隐藏层状态输入变为h1,这时再输出一个新的状态h2,依次类推。

         RNN单元的内部如图所示,输入矩阵和状态矩阵经过线性变换后相加经过激活函数tanh即可输出新的状态,最终的输出也可能经过其他变换,如线性变换或者softmax等。

(1)RNN Cell

        RNNCell 是循环神经网络的基本构建块,负责处理序列中的单个时间步。它是一个单独的计算单元,通常实现了一个时间步的前向传播逻辑。

        当输入矩阵的input_size和隐藏层状态的hidden_size确定,就可以确定权重矩阵的维度,也是构建RNN Cell所需要的参数。

        定义:cell=torch.nn.RNNCell(input_size,hidden_size) 其中input_size:输入的大小;hidden_size:隐藏层的大小

        调用:hidden=cell(input,hidden) 其中input张量大小为(bacth_size,input_size);hidden张量大小为(batch,hidden_size);output的张量大小为(batch_size,hidden_size)

        输入dataset的张量大小为(seq_len,batch_size,input_size),其中seq_len为序列长度。

        所以预先定义的参数包括batch_size,seq_len,batch_size,input_size四个参数。

        另外,由于RNNCell只是一个循环神经网络单元,所以需要进行循环调用

        下面是一个例子的代码:

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)

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)

        由于隐藏层状态不同,即使是相同的输入,每次输出的结果也并不相同。

(2)RNN

        与RNNCell不同,RNN 是一个由多个 RNNCell 组成的循环神经网络。它通过将 RNNCell 在序列中按顺序堆叠来形成整个网络结构。RNN 的隐藏状态在序列中传递,每个时间步的输出都依赖于当前时间步的输入和前一个时间步的隐藏状态。

        定义:cell=torch.nn.RNN(input_size,hidden_size,num_layers) 其中input_size:输入的大小;hidden_size:隐藏层的大小;num_layers为RNN的层数

        调用:out,hidden=cell(inputs,hidden) 其中input张量大小为(seq_len,bacth_size,input_size);hidden张量大小为(num_layers,batch,hidden_size);out的张量大小为(num_layers,batch_size,hidden_size)。

        所以预先定义的参数包括batch_size,seq_len,batch_size,input_size,num_layers五个参数,与RNNCell相比增加了num_layers 。

        另外,由于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('Input size:',inputs.shape)
print('Input:',inputs)

print('Hidden size:',hidden.shape)
print('Hidden:',hidden)

print('Output size:',out.shape)
print('Out:',out)

        执行结果如下: 

(3)编码器

        编码器将每个输入元素映射为一个密集的低维向量,将高维离散的输入数据(如单词、字符等)转换为低维稠密的连续向量表示,从而提供更丰富的语义信息。   

        定义:emb=torch.nn.Embedding(input_size,embedding_size) 

        调用:output=emb(inputs),其中inputs是Long类型张量,output的张量大小为(*,embedding_size),*为inputs的张量大小。

(4)LSTM

       长短时记忆网络( Long Short-Term Memory,LSTM)是一种循环神经网络(RNN)的变体,通过引入专门的存储单元和门控机制来解决传统RNN中的长期依赖问题,使其能够更好地处理和记忆时间序列数据中的长期依赖关系。

        LSTM的主要组成部分包括:

        细胞状态(Cell State): 细胞状态是网络中负责传递和存储信息的核心部分。

        输入门(Input Gate): 输入门控制新输入的影响,决定将多少新信息添加到细胞状态中。它的操作包括对输入进行加权和筛选。

        遗忘门(Forget Gate): 遗忘门决定要从细胞状态中删除多少信息,选择性地忘记先前存储的信息。

        输出门(Output Gate): 输出门确定要传递到下一层的隐藏状态的哪些部分,根据细胞状态的当前状态和输入来计算。

        LSTM的前向传播过程可以总结如下:根据当前输入和上一时刻的隐藏状态,计算输入门、遗忘门和输出门的值。使用输入门的输出来更新细胞状态,通过遗忘门的输出来遗忘部分信息。根据更新后的细胞状态和输入门的输出,计算输出门的值,并得到当前时刻的隐藏状态。

        公式中it为输入门,ft为遗忘门,ot为输出门。 

(5)GRU

        门控循环单元(Gated Recurrent Unit,GRU) 。GRU 是一种循环神经网络(RNN)的变体,类似于LSTM。GRU 简化了 LSTM 的结构,减少了参数数量,并在一些任务上表现优于 LSTM。        

        GRU 和 LSTM 一样,具有门控机制,但 GRU 只有两个门:更新门(Update Gate)和重置门(Reset Gate)。这两个门控制着信息的流动,允许 GRU 在处理时间序列数据时更好地捕捉长期依赖关系。

        GRU 的主要组成部分包括:

        更新门(Update Gate): 决定要从先前的隐藏状态中保留多少信息。

        重置门(Reset Gate): 决定要丢弃多少先前的信息,以便更好地适应当前输入。

2、Example 1:Using RNNCell

        任务:使用RNN,实现输入序列“hello”,输出序列“ohlol”

        

        (1)字母不能直接作为RNN的输入,需要先进行One-hot编码

        (2)最终输出相当于进行分类,损失函数使用交叉熵函数。

         完整代码:

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]

inputs=torch.Tensor(x_one_hot).view(-1,batch_size,input_size)#seq_len,batch_size,input_size5,1,4
labels=torch.LongTensor(y_data).view(-1,1)#将 y_data 转换为 PyTorch 的 LongTensor,并将其形状改为只有一列

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

    def init_hidden(self):
        return torch.zeros(self.batch_size,self.hidden_size)

net=Model(input_size,hidden_size,batch_size)

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

for epoch in range(15):
    loss=0
    optimizer.zero_grad()#梯度清零
    hidden=net.init_hidden()#隐藏状态初始化
    print('Predicted string:',end=' ')
    for input,label in zip(inputs,labels):
        hidden=net(input,hidden)
        # print()
        loss+=criterion(hidden,label)# 使用损失函数 (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()))

        输出结果:

3、Example 2:Using RNN

        与Example 1相同,代码采用RNN,稍微有改动,参考刘老师的视频。

        完整代码如下:

import torch

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

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)#seq_len,batch_size,input_size5,1,4
labels=torch.LongTensor(y_data)#将 y_data 转换为 PyTorch 的 LongTensor
class Model(torch.nn.Module):
    def __init__(self,input_size,hidden_size,batch_size,num_layers=1):
        super(Model,self).__init__()
        self.num_layers=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,self.hidden_size)
        # print('input:',input.shape)
        # print('hidden:',hidden.shape)
        out,_=self.rnn(input,hidden)
        # print('out',out.shape)
        # print(h.shape)
        return out.view(-1,self.hidden_size)
    #最后,返回的out张量被重塑(或展平)为一个二维张量,其中第一个维度是-1,意味着它会自动计算以保持元素总数不变。第二个维度是隐藏层的大小(self.hidden_size)。

net=Model(input_size,hidden_size,batch_size,num_layers)

#定义损失函数
criterion=torch.nn.CrossEntropyLoss()#PyTorch 的 CrossEntropyLoss 内部会执行 softmax 操作,将预测分数转换为概率分布,然后计算交叉熵损失
#定义优化器
optimizer=torch.optim.Adam(net.parameters(),lr=0.1)#Adam优化器
for epoch in range(15):
    optimizer.zero_grad()#梯度清零
    outputs=net(inputs)
    # print(outputs.shape)
    loss=criterion(outputs,labels)
    loss.backward()
    optimizer.step()

    _,idx=outputs.max(dim=1)
    idx=idx.data.numpy()#将张量转换为numpy array
    print('Predicted:',''.join([idx2char[x] for x in idx]),end='')#将预测的索引转换为字符,并打印出来
    print(',Epoch[%d/15] loss=%.4f'%(epoch+1,loss.item()))

输出结果:

4、Example 3:Using embedding and linear layer

        网络架构:编码器->RNN单元->线性层

        完整代码如下: 

import torch

num_class=4
input_size=4
hidden_size=8
num_layers=2
batch_size=1
seq_len=5
embedding_size=10

idx2char_1=['e','h','l','o']
idx2char_2=['h','l','o']
x_data=[[1,0,2,2,3]]#hello
y_data=[3,1,2,3,2]#ohlol

#inputs维度为(batchsize,seqlen)
inputs=torch.LongTensor(x_data)#seq_len,batch_size,input_size5,1,4
#labels维度为(batchsize*seqlen)
labels=torch.LongTensor(y_data)#将 y_data 转换为 PyTorch 的 LongTensor,并将其形状改为只有一列
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) # 进行embedding处理,把输入的长整型张量转变成嵌入层的稠密型张量
        x,_=self.rnn(x,hidden)
        x=self.fc(x)
        return x.view(-1,num_class)

net=Model()

#定义损失函数
criterion=torch.nn.CrossEntropyLoss()#PyTorch 的 CrossEntropyLoss 内部会执行 softmax 操作,将预测分数转换为概率分布,然后计算交叉熵损失
#定义优化器
optimizer=torch.optim.Adam(net.parameters(),lr=0.1)#Adam优化器

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 array
    print('Predicted:',''.join([idx2char_1[x] for x in idx]),end='')
    print(',Epoch[%d/15] loss=%.4f'%(epoch+1,loss.item()))

        输出结果为:

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值