pytorch自定义RNN结构(附代码)

pytorch自定义LSTM结构(附代码)

有时我们可能会需要修改LSTM的结构,比如用分段线性函数替代非线性函数,这篇博客主要写如何用pytorch自定义一个LSTM结构,并在IMDB数据集上搭建了一个单层反向的LSTM网络,验证了自定义LSTM结构的功能。


一、整体程序框架

如果要处理一个维度为【batch_size, length, input_dim】的输入,则需要的LSTM结构如图1所示:
图1

layers表示LSTM的层数,batch_size表示批处理大小,length表示长度,input_dim表示每个输入的维度。

其中,每个LSTMcell执行的表达式如下所示:
在这里插入图片描述

二、LSTMcell

LSTMcell的计算函数如下所示;其中nn.Parameter表示该张量为模型可训练参数;

class LSTMCell(nn.Module):
    def __init__(self, input_size, hidden_size):
        super(LSTMCell, self).__init__()
        self.input_size = input_size
        self.hidden_size = hidden_size
        self.weight_cx = nn.Parameter(torch.Tensor(hidden_size, input_size)) #初始化8个权重矩阵
        self.weight_ch = nn.Parameter(torch.Tensor(hidden_size, hidden_size))
        self.weight_fx = nn.Parameter(torch.Tensor(hidden_size, input_size))
        self.weight_fh = nn.Parameter(torch.Tensor(hidden_size, hidden_size))
        self.weight_ix = nn.Parameter(torch.Tensor(hidden_size, input_size))
        self.weight_ih = nn.Parameter(torch.Tensor(hidden_size, hidden_size))
        self.weight_ox = nn.Parameter(torch.Tensor(hidden_size, input_size))
        self.weight_oh = nn.Parameter(torch.Tensor(hidden_size, hidden_size))
        self.bias_c = nn.Parameter(torch.Tensor(hidden_size)) #初始化4个偏置bias
        self.bias_f = nn.Parameter(torch.Tensor(hidden_size))
        self.bias_i = nn.Parameter(torch.Tensor(hidden_size))
        self.bias_o = nn.Parameter(torch.Tensor(hidden_size))
        self.reset_parameters() #初始化参数

    def reset_parameters(self):
        stdv = 1.0 / math.sqrt(self.hidden_size)
        for weight in self.parameters():
            weight.data.uniform_(-stdv, stdv)
    def forward(self, input, hc):
        h, c = hc
        i = F.linear(input, self.weight_ix, self.bias_i) + F.linear(h, self.weight_ih) #执行矩阵乘法运算
        f = F.linear(input, self.weight_fx, self.bias_f) + F.linear(h, self.weight_fh)
        g = F.linear(input, self.weight_cx, self.bias_c) + F.linear(h, self.weight_ch)
        o = F.linear(input, self.weight_ox, self.bias_o) + F.linear(h, self.weight_oh)
        i = F.sigmoid(i)   #激活函数
        f = F.sigmoid(f)
        g = F.tanh(g)
        o = F.sigmoid(o)
        c = f * c + i * g
        h = o * F.tanh(c)
        return h, c

三、LSTM整体程序

如图1所示,一个完整的LSTM是由很多LSTMcell操作组成的,LSTMcell的数量,取决于layers的大小;每个LSTMcell运行的次数取决于length的大小

1. 多层LSTMcell

需要的库函数:

import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.autograd import Variable
import math

假如我们设计的LSTM层数layers大于1,第一层的LSTM输入维度是input_dim,输出维度是hidden_dim,那么其他各层的输入维度和输出维度都是hidden_dim(下层的输出会成为上层的输入),因此,定义layers个LSTMcell的函数如下所示:

self.lay0 = LSTMCell(input_size,hidden_size) 
        if layers > 1:
            for i in range(1, layers):
                lay = LSTMCell(hidden_size,hidden_size)
                setattr(self, 'lay{}'.format(i), lay)      

其中setattr()函数的作用是,把lay变成self.lay ‘i’ ,如果layers = 3,那么这段程序就和下面这段程序是一样的

self.lay0 = LSTMCell(input_size,hidden_size) 
self.lay1 = LSTMCell(hidden_size,hidden_size) 
self.lay2 = LSTMCell(hidden_size,hidden_size) 

2. 多层LSTM处理不同长度的输入

每个LSTMcell都需要(h_t-1和c_t-1)作为状态信息输入,若没有指定初始状态,我们就自定义一个值为0的初始状态

        if initial_states is None:   
            zeros = Variable(torch.zeros(input.size(0), self.hidden_size))
            initial_states = [(zeros, zeros), ] * self.layers   #初始状态
        states = initial_states
        outputs = []
        length = input.size(1)
        for t in range(length):
            x = input[:, t, :]
            for l in range(self.layers):
                hc = getattr(self, 'lay{}'.format(l))(x, states[l])  
                states[l] = hc   #如图1所示,左面的输出(h,c)做右面的状态信息输入
                x = hc[0]   #如图1所示,下面的LSTMcell的输出h做上面的LSTMcell的输入
            outputs.append(hc) #将得到的最上层的输出存储起来

其中getattr()函数的作用是,获得括号内的字符串所代表的属性;若l = 3,则下面这两段代码等价:

 hc = getattr(self, 'lay{}'.format(l))(x, states[l]) 
 hc = self.lay3(x, states[3]) 

3. 整体程序

class LSTM(nn.Module): 
   def __init__(self, input_size, hidden_size, layers=1, sequences=True):
       super(LSTM, self).__init__()
       self.input_size = input_size
       self.hidden_size = hidden_size
       self.layers = layers
       self.sequences = sequences
       self.lay0 = LSTMCell(input_size,hidden_size)
       if layers > 1:
           for i in range(1, layers):
               lay = LSTMCell(hidden_size,hidden_size)
               setattr(self, 'lay{}'.format(i), lay)
   def forward(self, input, initial_states=None):
       if initial_states is None:
           zeros = Variable(torch.zeros(input.size(0), self.hidden_size))
           initial_states = [(zeros, zeros), ] * self.layers
       states = initial_states
       outputs = []
       length = input.size(1)
       for t in range(length):
           x = input[:, t, :]
           for l in range(self.layers):
               hc = getattr(self, 'lay{}'.format(l))(x, states[l])
               states[l] = hc
               x = hc[0]
           outputs.append(hc)
       if self.sequences: #是否需要图1最上层里从左到右所有的LSTMcell的输出
           hs, cs = zip(*outputs)
           h = torch.stack(hs).transpose(0, 1)
           c = torch.stack(cs).transpose(0, 1)
           output = (h, c)
       else:
           output = outputs[-1] # #只输出图1最右上角的LSTMcell的输出
       return output

三、反向LSTM

定义两个LSTM,然后将输入input1反向,作为input2,就可以了
代码如下所示:

import torch
input1 = torch.rand(2,3,4)
inp = input1.unbind(1)[::-1]  #从batch_size所在维度拆开,并倒序排列
input2 = inp[0]
for i in range(1, len(inp)): #倒序后的tensor连接起来
	input2 = torch.cat((input2, inp[i]), dim=1)
x, y, z = input1.size()   #两个输入同维度
input2 = input2.resize(x, y, z) 

在这里插入图片描述
OK,反向成功

四、实验

在IMDB上搭建一个单层,双向,LSTM结构,加一个FC层;

self.rnn1 = LSTM(embedding_dim, hidden_dim, layers = n_layers, sequences=False)
self.rnn2 = LSTM(embedding_dim, hidden_dim, layers = n_layers, sequences=False)
self.fc = nn.Linear(hidden_dim * 2, output_dim)

运行结果如图:
在这里插入图片描述
时间有限,只迭代了6次,实验证明,自定义的RNN程序,可以收敛。

五、项目下载:

该博客所用到的全部完整代码下载地址: 自定义LSTM文件及示例程序

下面是一个使用PyTorch定义RNN的示例代码: ```python import torch import torch.nn as nn # 定义RNN类 class RNN(nn.Module): def __init__(self, input_size, output_size, hidden_dim, n_layers): super(RNN, self).__init__() self.hidden_dim = hidden_dim self.n_layers = n_layers # 创建RNN层 self.rnn = nn.RNN(input_size, hidden_dim, n_layers, batch_first=True) # 最后的全连接层 self.fc = nn.Linear(hidden_dim, output_size) def forward(self, x): batch_size = x.size(0) # 初始化隐藏状态 hidden = self.init_hidden(batch_size) # 前向传播 out, hidden = self.rnn(x, hidden) # 提取最后一个时间步的输出 out = out[:, -1, :] # 通过全连接层得到最终输出 out = self.fc(out) return out def init_hidden(self, batch_size): # 初始化隐藏状态 hidden = torch.zeros(self.n_layers, batch_size, self.hidden_dim) return hidden # 创建RNN实例 input_size = 1 output_size = 1 hidden_dim = 32 n_layers = 1 rnn = RNN(input_size, output_size, hidden_dim, n_layers) # 打印RNN结构 print(rnn) ``` 这段代码首先定义了一个自定义RNN类,继承自`nn.Module`。在类的`__init__`方法中,定义了RNN结构,包括输入大小、隐藏状态的特征数、RNN的层数等。然后在`forward`方法中,进行了前向传播计算。最后,在`init_hidden`方法中,初始化了隐藏状态。 你可以根据自己的需求修改这段代码中的超参数,比如`input_size`、`output_size`、`hidden_dim`和`n_layers`等。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [【Pytorch】21. RNN代码分析](https://blog.csdn.net/qq_35272180/article/details/115765269)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [pytorch自定义RNN结构代码)](https://blog.csdn.net/kuan__/article/details/114652053)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

皮皮宽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值