LSTM变种模型

一、GRU

1.概念

GRU(门控循环单元,Gated Recurrent Unit)是一种循环神经网络(RNN)的变体,旨在解决标准 RNN 在处理长期依赖关系时遇到的梯度消失问题。GRU 通过引入门控机制简化了 LSTM(长短期记忆网络)的设计,使得模型更轻便,同时保留了 LSTM 的优点。

2.原理

2.1.两个重大改进

1.将输入门、遗忘门、输出门三个门变为更新门(Updata Gate)和重置门(Reset Gate)两个门。

2.将 (候选) 单元状态 与 隐藏状态 (输出) 合并,即只有 当前时刻候选隐藏状态 当前时刻隐藏状态

2.2模型结构

简化图:

内部结构:

GRU通过其门控机制能够有效地捕捉到序列数据中的时间动态,同时相较于LSTM来说,由于其结构更加简洁,通常参数更少,计算效率更高。

2.2.1 重置门

重置门决定在计算当前候选隐藏状态时,忽略多少过去的信息。

2.2.2 更新门

更新门决定了多少过去的信息将被保留。它使用前一时间步的隐藏状态 ( h_{t-1} ) 和当前输入 ( x_t ) 来计算得出。

2.2.3 候选隐藏状态

候选隐藏状态是当前时间步的建议更新,它包含了当前输入和过去的隐藏状态的信息。重置门的作用体现在它可以允许模型抛弃或保留之前的隐藏状态。

2.2.4 最终隐藏状态

最终隐藏状态是通过融合过去的隐藏状态和当前候选隐藏状态来计算得出的。更新门 ​控制了融合过去信息和当前信息的比例。

忘记传递下来的 中的某些信息,并加入当前节点输入的某些信息。这就是最终的记忆。

3. 代码实现

3.1 原生代码
复制代码
import numpy as np
​
class GRU:
    def __init__(self, input_size, hidden_size):
        self.input_size = input_size
        self.hidden_size = hidden_size
        
        # 初始化w和b 更新门
        self.W_z = np.random.rand(hidden_size, input_size + hidden_size)
        self.b_z = np.zeros(hidden_size)
        #重置门
        self.W_r = np.random.rand(hidden_size, input_size + hidden_size)
        self.b_r = np.zeros(hidden_size)
        #候选隐藏状态
        self.W_h = np.random.rand(hidden_size, input_size + hidden_size)
        self.b_h = np.zeros(hidden_size)
        
    def tanh(self, x):
        return np.tanh(x)
    
    def sigmoid(self, x):
        return 1 / (1 + np.exp(-x))
    def forward(self, x):
        #初始化隐藏状态
        h_prev=np.zeros((self.hidden_size,))
        concat_input=np.concatenate([x, h_prev],axis=0)
​
        z_t=self.sigmoid(np.dot(self.W_z,concat_input)+self.b_z)
        r_t=self.sigmoid(np.dot(self.W_r,concat_input)+self.b_r)
​
        concat_reset_input=np.concatenate([x,r_t*h_prev],axis=0)
        h_hat_t=self.tanh(np.dot(self.W_h,concat_reset_input)+self.b_h)
​
        h_t=(1-z_t)*h_prev+z_t*h_hat_t
​
        return h_t
​
# 测试数据
input_size=3
hidden_size=2
seq_len=4
​
x=np.random.randn(seq_len,input_size)
​
gru=GRU(input_size,hidden_size)
​
all_h=[]
for t in range(seq_len):
    h_t=gru.forward(x[t,:])
    all_h.append(h_t)
    
print(h_t.shape)
print(np.array(all_h).shape)
        
        
        
3.2 PyTorch
nn.GRUCell
复制代码
import torch
import torch.nn as nn
​
class GRUCell(nn.Module): 
    def __init__(self,input_size,hidden_size):
        super(GRUCell,self).__init__()
        self.input_size = input_size
        self.hidden_size = hidden_size
        
        self.gru_cell=nn.GRUCell(input_size,hidden_size)
        
    def forward(self,x):
        h_t=self.gru_cell(x)
        return h_t
    
# 测试数据
input_size=3
hidden_size=2
seq_len=4
​
gru_model=GRUCell(input_size,hidden_size)
​
x=torch.randn(seq_len,input_size)
​
for t in range(seq_len):
    h_t=gru_model(x[t])
    
print(h_t)
    
​
​
nn.GRU
复制代码
import torch
import torch.nn as nn
​
class GRU(nn.Module):
    def __init__(self,input_size,hidden_size):
        super(GRU,self).__init__()
        self.input_size = input_size
        self.hidden_size = hidden_size
        self.gru=nn.GRU(input_size,hidden_size)
        
    def forward(self,x):
        out,_=self.gru(x)
        return out
    
# 测试数据
input_size=3
hidden_size=2
seq_len=4
batch_size=5
x=torch.randn(seq_len,batch_size,input_size)
gru_mosel=GRU(input_size,hidden_size)
​
out=gru_mosel(x)
print(out)
print(out.shape)
​

二、BiLSTM

1.概述

双向长短期记忆网络(BiLSTM)是长短期记忆网络(LSTM)的扩展,旨在同时考虑序列数据中的过去和未来信息。BiLSTM 通过引入两个独立的 LSTM 层,一个正向处理输入序列,另一个逆向处理,使得每个时间步的输出包含了该时间步前后的信息。这种双向结构能够更有效地捕捉序列中的上下文关系,从而提高模型对语义的理解能力。

  • 正向传递: 输入序列按照时间顺序被输入到第一个LSTM层。每个时间步的输出都会被计算并保留下来。

  • 反向传递: 输入序列按照时间的逆序(即先输入最后一个元素)被输入到第二个LSTM层。与正向传递类似,每个时间步的输出都会被计算并保留下来。

  • 合并输出: 在每个时间步,将两个LSTM层的输出通过某种方式合并(如拼接或加和)以得到最终的输出。

2. BILSTM模型应用背景

命名体识别

标注集

BMES标注集

分词的标注集并非只有一种,举例中文分词的情况,汉子作为词语开始Begin,结束End,中间Middle,单字Single,这四种情况就可以囊括所有的分词情况。于是就有了BMES标注集,这样的标注集在命名实体识别任务中也非常常见。

词性标注

在序列标注问题中单词序列就是x,词性序列就是y,当前词词性的判定需要综合考虑前后单词的词性。而标注集最著名的就是863标注集和北大标注集。

3. 代码实现

原生代码

复制代码
import numpy as np
import torch
​
class BiLSTM():
    def __init__(self, input_size, hidden_size,output_size):
        self.input_size = input_size
        self.hidden_size = hidden_size
        self.output_size = output_size
        
        #正向
        self.lstm_forward = LSTM(input_size, hidden_size,output_size)
        
        #反向
        self.lstm_backward = LSTM(input_size, hidden_size,output_size)
        
    def forward(self,x):
        # 正向LSTM
        output,_,_=self.lstm_forward.forward(x)
        # 反向LSTM,np.flip()是将数组进行翻转
        output_backward,_,_=self.lstm_backward.forward(np.flip(x,1))
        #合并两层的隐藏状态
        combine_output=[np.concatenate((x,y),axis=0) for x,y in zip(output,output_backward)]
        return combine_output
        
class LSTM:
    
    def __init__(self, input_size, hidden_size,output_size):
        """
        :param input_size: 词向量大小
        :param hidden_size: 隐藏层大小
        :param output_size: 输出类别
        """
        self.input_size = input_size
        self.hidden_size = hidden_size
        self.output_size = output_size
​
        # 初始化权重和偏置 我们把结构图上的W U 拼接在了一起 所以参数是 input_size+hidden_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,)) # 初始隐藏状态
        c_t = np.zeros((self.hidden_size,)) # 初始细胞状态
​
        h_states = [] # 存储每个时间步的隐藏状态
        c_states = [] # 存储每个时间步的细胞状态
​
        for t in range(x.shape[0]):
            x_t = x[t] # 当前时间步的输入
            # concatenate 将x_t和h_t拼接 垂直方向
            x_t = np.concatenate([x_t,h_t])
​
            # 遗忘门
            f_t = self.sigmoid(np.dot(self.w_f,x_t)+self.b_f)
​
            # 输入门
            i_t = self.sigmoid(np.dot(self.w_i,x_t)+self.b_i)
            # 候选细胞状态
            c_hat_t = self.tanh(np.dot(self.w_c,x_t)+self.b_c)
​
            # 更新细胞状态
            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_states.append(h_t)
            c_states.append(c_t)
​
        # 输出层 对最后一个时间步的隐藏状态进行预测,分类类别
        y_t = np.dot(self.w_y,h_t)+self.b_y
        # 转成张量形式 dim 0 表示行的维度
        output = torch.softmax(torch.tensor(y_t),dim=0)
​
        return np.array(h_states), np.array(c_states), output
    
​
# 测试数据
input_size=3
hidden_size=8
output_size=5
seq_len=4
​
x=np.random.randn(seq_len,input_size)
​
bilstm=BiLSTM(input_size,hidden_size,output_size)
outputs=bilstm.forward(x)        
print(outputs)
print(np.array(outputs).shape)
复制代码
# ---------------------------------------------------------------------------
import numpy as np
​
# 创建一个包含两个二维数组的列表
inputs = [np.array([[0.1], [0.2], [0.3]]), np.array([[0.4], [0.5], [0.6]])]
​
# 使用 numpy 库中的 np.stack 函数。这会将输入的二维数组堆叠在一起,从而形成一个新的三维数组
inputs_3d = np.stack(inputs)
​
# 将三维数组转换为列表
list_from_3d_array = inputs_3d.tolist()
​
print(list_from_3d_array)

Pytorch

复制代码
import torch
import torch.nn as nn
​
class BiLSTM(nn.Module):
    def __init__(self, input_size, hidden_size,output_size):
        super(BiLSTM, self).__init__()
        #定义双向LSTM
        self.lstm=nn.LSTM(input_size,hidden_size,bidirectional=True)
        #输出层 因为双向LSTM的输出是双向的,所以第一个参数是隐藏层*2
        self.linear=nn.Linear(hidden_size*2,output_size)
​
    def forward(self,x):
        out,_=self.lstm(x)
        linear_out=self.linear(out)
        return linear_out
    
# 测试数据
input_size=3
hidden_size=8
output_size=5
seq_len=4
batch_size=6
​
x=torch.randn(seq_len,batch_size,input_size)   
model=BiLSTM(input_size,hidden_size,output_size)
outputs=model(x)
print(outputs)
print(outputs.shape)
复制代码
相关推荐
陈广亮10 分钟前
构建具有长期记忆的 AI Agent:从设计模式到生产实践
人工智能
会写代码的柯基犬19 分钟前
DeepSeek vs Kimi vs Qwen —— AI 生成俄罗斯方块代码效果横评
人工智能·llm
Mintopia1 小时前
OpenClaw 是什么?为什么节后热度如此之高?
人工智能
爱可生开源社区1 小时前
DBA 的未来?八位行业先锋的年度圆桌讨论
人工智能·dba
叁两4 小时前
用opencode打造全自动公众号写作流水线,AI 代笔太香了!
前端·人工智能·agent
前端付豪4 小时前
LangChain记忆:通过Memory记住上次的对话细节
人工智能·python·langchain
strayCat232554 小时前
Clawdbot 源码解读 7: 扩展机制
人工智能·开源
王鑫星4 小时前
SWE-bench 首次突破 80%:Claude Opus 4.5 发布,Anthropic 的野心不止于写代码
人工智能
lnix4 小时前
当“大龙虾”养在本地:我们离“反SaaS”的AI未来还有多远?
人工智能·aigc