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. Python标准库typing

    Python标准库typing https://docs.python.org/zh-cn/3/library/typing.html 简介 python3.5+才有 Python 运行时不强制执行函 ...

  2. win10使用python自动化安装mysql8.0.11

    流程概要 下载mysql-8.0.11-winx64压缩包 解压 编辑配置文件my.ini 管理员权限cmd安装(注意初始化时设置默认密码为空) pymysql连接,执行sql操作. 代码实现 依赖: ...

  3. element-UI el-table动态显示隐藏列造成固定一侧的列(fixed=“left/right“)错误显示

    问题原因:多个tabs共用一个实体,动态显示隐藏列 出现了固定在右侧的列(fixed="right")错位 [解决方案] 表格的重新布局,只要table数据发生变化的时候就重新渲染 ...

  4. 记录一次vue部署docker步骤

    vue部署docker 拉取nginx镜像 docker pull nginx 有Dockerfile文件步骤: 1.打包vue npm run build 2.写一个default.conf 文件, ...

  5. Vue3中的响应式api

    一.setup文件的认识 特点1:script 中间的内容就是一个对象 特点2:script 在第一层 定义的方法 或者 变量 => 就是这个对象 属性  => 顶层的绑定回被暴露给模板( ...

  6. 【KAWAKO】soundtoch-使用可执行文件对音频进行变调或变速

    目录 下载 单次使用 使用python脚本批量处理 下载 从官网下载可执行文件. 单次使用 在终端中直接运行,会出现使用方法和可选参数. 变速就设置tempo,变调就设置pitch,都变就都设置.变速 ...

  7. Docker安装配置Oracle详细教程(以作持久化处理)

    Docker安装Oracle 1,拉取Oracle镜像,拉取成功后查看  docker pull registry.cn-hangzhou.aliyuncs.com/helowin/oracle_11 ...

  8. Windows必装软件,一个比一个好用

    前言 每次重装完系统,或者新换了电脑之后,你第一时间会安装哪些软件呢?那这次小卷就为大家带来18款电脑必备软件,它们共同的特点是绿色小巧无广告,而且在我看来,每一个同类型软件中最出类拔萃的,我就这么说 ...

  9. django我学习的第一个web框架

    第一个APP----------联通后台管理系统 第二个APP----------baby商场

  10. 前端js下载excel

    // 1.文件流下载文件: export function axiosPostExport(url, data, fileName, suffix = '.xlsx') {     url = get ...