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

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

@


一、整体程序框架

如果要处理一个维度为【batch_size, length, input_dim】的输入,则需要的LSTM结构如图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程序,可以收敛。

pytorch自定义RNN结构(附代码)的更多相关文章

  1. Winform中实现向窗体中拖放照片并显示以及拖放文件夹显示树形结构(附代码下载)

    场景 向窗体中拖拽照片并显示效果 向窗体中拖拽文件夹并显示树形结构效果 注: 博客主页: https://blog.csdn.net/badao_liumang_qizhi 关注公众号 霸道的程序猿 ...

  2. 使用achartengine实现自定义折线图 ----附代码 调试OK

    achartengine作为android开发中最常用的实现图标的开源框架,使用比较方便,参考官方文档谢了如下Demo,实现了自定义折线图. package edu.ustb.chart; impor ...

  3. 分布式消息总线,基于.NET Socket Tcp的发布-订阅框架之离线支持,附代码下载

    一.分布式消息总线以及基于Socket的实现 在前面的分享一个分布式消息总线,基于.NET Socket Tcp的发布-订阅框架,附代码下载一文之中给大家分享和介绍了一个极其简单也非常容易上的基于.N ...

  4. 刚开始学python——数据结构——“自定义队列结构“

    自定义队列结构  (学习队列后,自己的码) 主要功能:用列表模拟队列结构,考虑了入队,出队,判断队列是否为空,是否已满以及改变队列大小等基本操作. 下面是封装的一个类,把代码保存在myQueue.py ...

  5. pytorch实现rnn并且对mnist进行分类

    1.RNN简介 rnn,相比很多人都已经听腻,但是真正用代码操练起来,其中还是有很多细节值得琢磨. 虽然大家都在说,我还是要强调一次,rnn实际上是处理的是序列问题,与之形成对比的是cnn,cnn不能 ...

  6. Spring Security教程(三):自定义表结构

    在上一篇博客中讲解了用Spring Security自带的默认数据库存储用户和权限的数据,但是Spring Security默认提供的表结构太过简单了,其实就算默认提供的表结构很复杂,也不一定能满足项 ...

  7. (转)Uri详解之——Uri结构与代码提取

    前言:依然没有前言…… 相关博客:1.<Uri详解之——Uri结构与代码提取>2.<Uri详解之二——通过自定义Uri外部启动APP与Notification启动> 上几篇给大 ...

  8. Uri详解之——Uri结构与代码提取

    目录(?)[+] 前言:依然没有前言…… 相关博客:1.<Uri详解之——Uri结构与代码提取>2.<Uri详解之二——通过自定义Uri外部启动APP与Notification启动& ...

  9. iOS开发 swift 3dTouch实现 附代码

    iOS开发 swift 3dTouch实现 附代码 一.What? 从iphone6s开始,苹果手机加入了3d touch技术,最简单的理解就是可以读取用户的点击屏幕力度大小,根据力度大小给予不同的反 ...

  10. 从实例一步一步入门学习SpringCloud的Eureka、Ribbon、Feign、熔断器、Zuul的简单使用(附代码下载)

    场景 SpringCloud -创建统一的依赖管理: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/102530574 Sprin ...

随机推荐

  1. 剑指 Offer 32 - I. 从上到下打印二叉树(java解题)

    目录 1. 题目 2. 解题思路 3. 数据类型功能函数总结 4. java代码 1. 题目 从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印.   例如: 给定二叉树: [3,9, ...

  2. Maven配置 阿里云镜像地址

    <?xml version="1.0" encoding="UTF-8"?> <settings> <mirrors> &l ...

  3. log4j 完美版配置

    百度的LOG4J配置记录一下 目前无时间阅读,反正能用就行了 # priority :debug<info<warn<error #you cannot specify every ...

  4. 无法将“obj\Debug\net5.0\xxx.dll”复制到“bin\Debug\net5.0\xxx.dll”。超出了重试计数 10。失败。

    解决办法 VS选中项目,右键清理解决方案,再次右键重新生成方案即可. 以上就是无法将"obj\Debug\net5.0\xxx.dll"复制到"bin\Debug\net ...

  5. 合肥光源纵向震荡数据源相关PV的增补

    合肥光源纵向震荡数据源相关PV 昨天发了上面那篇,对于那张二维纵向时间的图又做了些分析,因为要显示分析的结果,又增加了几个PV,说明如下: HLSII:BD:BCMSDS:Time:Spectrum  ...

  6. 推荐一款新的自动化测试框架:DrissionPage!

    今天给大家推荐一款基于Python的网页自动化工具:DrissionPage.这款工具既能控制浏览器,也能收发数据包,甚至能把两者合而为一,简单来说:集合了WEB浏览器自动化的便利性和 request ...

  7. Powershell获取当前文件夹内所有一级子文件夹大小

    需求:查看Windows某个文件夹所有一级子文件夹大小,并按照从大到小排序 解决方案:使用Powershell脚本 脚本内容如下 function filesize () {   [string]$f ...

  8. JZOJ 100019.A

    \(\text{Problem}\) \(\text{Solution}\) 把形如 \((a,ka)\) 的路径提出来 那么覆盖这些路径的路径为不合法路径 如果能不重不漏的找出这些路径,然后用总路径 ...

  9. 普冉PY32系列(六) 通过I2C接口驱动PCF8574扩展的1602LCD

    目录 普冉PY32系列(一) PY32F0系列32位Cortex M0+ MCU简介 普冉PY32系列(二) Ubuntu GCC Toolchain和VSCode开发环境 普冉PY32系列(三) P ...

  10. python 2.7中matplotlib的所有版本

    matplotlib versions: 0.86, 0.86.1, 0.86.2, 0.91.0, 0.91.1, 1.0.1, 1.1.0, 1.1.1, 1.2.0, 1.2.1, 1.3.0, ...