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

【NLP】LSTM结构,原理,代码实现,序列池化

LSTM 长短期记忆网络

由于长序列的长期依赖问题,当序列很长,RNN难以学习并保持序列早期时间步的信息。

LSTM(Long Short-Term Memory) 引入了细胞状态的概念,通过门控机制,控制信息保留的程度。

LSTM结构

在这里插入图片描述

LSTM的关键是细胞状态c,也就是图中最上方的一条贯穿整个链式结构的水平线,每个隐藏状态的输出,由细胞中的几个门共同控制。同时,这几个门也作用于细胞状态的更新。

门控机制

  1. 遗忘门

遗忘门(Forget Gate) 决定了要保留多少上一个细胞状态的信息

在这里插入图片描述

通过输入当前时间步的x和上一隐藏状态,对其进行拼接,乘上对应的权重加上偏置,最后使用sigmoid进行激活,得到遗忘门的值

  1. 输出门

**输入门(Input Gate)**决定要从当前时间步得到多少信息来更新细胞状态

在这里插入图片描述

同遗忘门得到输入门的 i t i_t it,相同的公式,但是权重不同,可以得到不同的值

同时,对输入的 x t , h t − 1 x_t,h_{t-1} xt,ht1 计算得到细胞候选状态,通过输入门得到的保留程度,以此跟新细胞状态

  1. 细胞状态更新

C t = f t ∗ C t − 1 + i t ∗ C ~ t C_t = f_t * C_{t-1} + i_t * \tilde{C}_t Ct=ftCt1+itC~t

通过遗忘门和输入门得到对应的保存信息的权重$ f_t和i_t ,以及细胞候选状态 ,以及细胞候选状态 ,以及细胞候选状态\tilde{C}_t$,计算更新细胞状态

在这里插入图片描述

通过这样的细胞状态的更新传递,可以保留每个时间步的信息,也能记忆之前的时间步。每次的更新,得到不同的保留比例,从而实现长短期记忆

  1. 输出门

在特定的时间步的输出,同样的计算权重,而后使用当前细胞状态用tanh激活,乘上这个权重,得到当前时间步的输出,同时也是隐藏状态

在这里插入图片描述

总结

对比可知,所有的门控权重计算都相同,但是其输入的权重不同,所以可以保留不同的信息

在这里插入图片描述

注:这里最后的输出 h t h_t ht 应该是tanh而不是sigmoid

基于Numpy实现

import torch
import torch.nn as nn
import numpy as npclass LSTM(nn.Module):def __init__(self,input_size,hidden_size,output_size):super().__init__()""":pram input_size  输入的词向量的维度:param hidden_size  隐藏状态的维度  :pram output_size 输出的维度"""self.input_size = input_sizeself.hidden_size = hidden_sizeself.output_size = output_size# 初始化权重self.w_f = np.random.rand(hidden_size,input_size+hidden_size)self.b_f = np.random.rand(hidden_size)self.w_i = np.random.rand(hidden_size,input_size+hidden_size)self.b_i = np.random.rand(hidden_size)self.w_c = np.random.rand(hidden_size,input_size+hidden_size)self.b_c = np.random.rand(hidden_size)self.w_o = np.random.rand(hidden_size,input_size+hidden_size)self.b_o = np.random.rand(hidden_size)# 输出层self.w_y = np.random.rand(output_size, hidden_size)self.b_y = np.random.rand(output_size)# 激活函数def tanh(self,x):return  np.tanh(x)def sigmoid(self,x):return 1/(1+np.exp(-x))def forward(self,x):# 初始化隐藏状态h_t = np.zeros((self.hidden_size,))# cellc_t = np.zeros((self.hidden_size,))# 存储 时间步的隐藏和细胞状态h_state = []c_state = []for t in range(x.shape[0]):x_t = x[t] # 拿到当前时间步的输入# 垂直方向凭借  列不变 增加行x_t = np.concatenate([x_t,h_t])  # 拼接时间步和隐藏# 遗忘门f_t = self.sigmoid(np.dot(self.w_f,x_t)+self.b_f)# 候选细胞状态c_hat_t = self.tanh(np.dot(self.w_c,x_t)+self.b_c)# 输入门i_t = self.sigmoid(np.dot(self.w_i,x_t)+self.b_i)# 细胞状态更新c_t = f_t * c_t + i_t * c_hat_t# 输出门o_t = self.sigmoid(np.dot(self.w_o,x_t)+self.b_o)# 更新隐藏状态h_t = o_t * self.tanh(c_t)# 保存隐藏状态和细胞状态h_state.append(h_t)c_state.append(c_t)y_t = np.dot(self.w_y,h_t) + self.b_y# 转化为张量 dim 行的维度output = torch.softmax(torch.tensor(y_t),dim=0)return np.array(h_state),np.array(c_state),output
# 数据输入
x = np.random.rand(3,2) # 三个词 词向量维度2 
# 这里只是以输入一个数据为例,如果是多个批次,还需要对维度进行转换,以符合矩阵相乘
hidden_size = 5# 实例化模型
mdoel = LSTM(2,hidden_size,6)h_state,c_state,output = mdoel(x)print('class:' ,output)
print('hidden:' ,h_state)
print('hidden size :' ,h_state.shape)
print('cell:' ,c_state)
print('cell size:' ,c_state.shape)

基于PyTorch API实现

import torch
import torch.nn as nnclass LSTM(nn.Module):def __init__(self,input_size,hidden_size,output_size):""":param input_size:  词向量:param hidden_size:  隐藏层:param output_size:  输出类别"""super().__init__()self.input_size = input_sizeself.hidden_size = hidden_sizeself.output_size = output_sizeself.lstm = nn.LSTM(input_size,hidden_size)self.fc = nn.Linear(hidden_size,output_size)def forward(self,x):# 初始化隐藏状态和细胞状态h0 = torch.zeros(1,x.size()[1],self.hidden_size)c0 = torch.zeros(1,x.size()[1],self.hidden_size)# 前向传播out,state = self.lstm(x,(c0,h0)) # out所有时间步的输出  state最后一个时间步的状态  隐藏状态和细胞状态out = self.fc(out[-1,:,:]) # 取最后一个时间步的输出return out# 定义参数
seq_size,batch_size,input_size = 5,4,2 # 5批次 4个词  向量维度2
hidden_size,output_size = 6,7x = torch.randn(seq_size,batch_size,input_size )
model = LSTM(input_size,hidden_size,output_size)out = model(x)
print(out)
print(out.shape)

序列池化

序列池化,把变长序列转换为固定长度的表示方法。如一个序列有多个词向量组成,每个向量的求和平均,合并为一个向量

主要有最大池化(突出序列特定的最大激活值)平均池化(保留序列总体的信息)注意力池化(注意力机制池化,对每个时间步分配权重)

import torch
import torch.nn as nninput_data = torch.randn(2,3,4) # 批次  词数, 词向量维度avg_pool = nn.AdaptiveAvgPool1d(1)  
# max_pool = nn.AdaptiveMaxPool1d(1)# 调整维度
input_data = input_data.permute(0,2,1) # b.s.d >> b.d.s #output = avg_pool(input_data) # 转化为句向量
# output = max_pool(input_data) # 转化为句向量print(output.shape) # torch.Size([2, 4, 1])

http://www.mrgr.cn/news/37142.html

相关文章:

  • SQL_create_view
  • 解释一下Java中的多线程。如何创建一个新的线程?
  • 力扣随机一题——所有元音按顺序排序的最长字符串
  • SRE的必修课:学会看账单
  • LeetCode(Python)-贪心算法
  • STL之vector篇(下)(手撕底层代码,从零实现vector的常用指令,深度剖析并优化其核心代码)
  • c++292类模板
  • 828华为云征文|Flexus云服务器X实例实践:安装SimpleMindMap思维导图工具
  • 【刷题日记】最大不重叠区间的数量 leetcode 435
  • Dockerfile如何使用
  • 【如何学习Python编程?】
  • USB 3.1 Micro-A 与 Micro-B 插头,Micro-AB 与 Micro-B 插座,及其引脚定义
  • 一窥AI大模型奥秘:技术前沿与产业应用双轮驱动
  • Studying-图论包含的算法总结
  • 【VUE】axios组件
  • 绝了,自从用了它,我每天能多摸鱼2小时!
  • 滑动窗口 -- 限制窗口内某元素的数量/种类
  • 深度学习—神经网络基本概念
  • 数据结构——初始树和二叉树
  • Linux(含麒麟操作系统)如何实现多显示器屏幕采集录制