一、GRU
1、什么是GRU
门控循环单元(GRU)是一种循环神经网络(RNN)的变体,它通过引入门控机制来控制信息的流动,从而有效地解决了传统RNN中的梯度消失问题。GRU由Cho等人在2014年提出,它简化了LSTM的结构,将遗忘门和输入门合并为一个更新门,并增加了一个重置门,同时合并了单元状态和隐藏状态,使得模型更加简洁,训练速度更快,且在性能上与LSTM相当。
2、GRU的核心
核心在于两个门:更新门(update gate)和重置门(reset gate)。更新门控制着从前一时刻的状态信息中保留多少到当前状态,而重置门决定着前一状态有多少信息被写入到当前的候选集中。这种结构使得GRU在处理长序列数据时能够更好地捕捉长期依赖关系,同时减少了模型参数,提高了计算效率。
3、GRU的应用
应用非常广泛,包括但不限于自然语言处理(NLP)、语音识别、图像处理等领域。在NLP领域,GRU可以用于语言建模、情感分析、机器翻译等任务;在语音识别领域,GRU可以用于语音信号的特征提取和识别;在图像处理领域,GRU可以用于图像分类、目标检测等任务。GRU的简洁性和效率使其在处理大规模序列数据时具有优势。
在选择GRU和LSTM时,通常考虑的因素包括任务的复杂性、数据集的大小以及训练资源。由于GRU参数更少,收敛速度更快,因此在需要快速迭代和实验时,GRU通常是首选。然而,在某些需要对复杂序列依赖关系进行建模的任务中,LSTM可能会表现得更好。
总的来说,GRU是一种强大的循环神经网络架构,它通过引入门控机制来控制信息流,有效地解决了传统RNN的梯度消失问题。GRU的简洁性和效率使其在多种序列建模任务中表现出色,成为了深度学习中处理时序数据的重要工具之一。
4、GRU的工作原理
5、手写代码实现
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.randn(self.hidden_size, self.input_size + self.hidden_size)
self.b_z = np.zeros(self.hidden_size)
# 重置门
self.W_r = np.random.randn(self.hidden_size, self.input_size + self.hidden_size)
self.b_r = np.zeros(self.hidden_size)
# 候选隐藏状态
self.W_h = np.random.randn(self.hidden_size, self.input_size + self.hidden_size)
self.b_h = np.zeros(self.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
二、BiLSTM
1、什么是BiSTM
BiSTM,即双向门控循环单元(Bidirectional Gated Recurrent Unit),是一种循环神经网络(RNN)的变体。它结合了前向和后向的GRU,能够同时处理过去和未来的信息,从而更好地捕捉序列数据中的上下文关系。
在BiSTM中,数据通过两个GRU网络进行处理:一个从左到右(前向),另一个从右到左(后向)。这两个网络的输出然后被拼接或相加,形成最终的特征表示,这个特征表示包含了序列的双向信息。这种结构特别适合于需要理解序列中前后文信息的任务,如文本分类、语音识别、命名实体识别(NER)等。
2、BiSTM的关键特点包括:
-
双向信息捕捉:BiSTM能够同时考虑序列中每个元素之前的和之后的上下文信息,这使得它在处理像文本这样的序列数据时非常有效,因为文本中词汇的含义往往受到其前后词汇的影响。
-
门控机制:BiSTM继承了GRU的门控机制,包括更新门和重置门,这些门控单元可以控制信息的流动,从而减少无效或噪声信息的干扰,并增强模型对重要信息的记忆能力。
-
应用广泛:BiSTM因其强大的序列处理能力而被广泛应用于各种领域,包括自然语言处理(NLP)、语音识别、时间序列分析等。
-
模型性能:在某些任务中,BiSTM能够提供比单向GRU或LSTM更好的性能,尤其是在需要捕捉长期依赖关系的任务中。
-
模型复杂度:由于BiSTM包含两个GRU网络,其模型参数和计算复杂度相对于单向GRU或LSTM会有所增加,但在很多情况下,这种增加是值得的,因为它能带来更准确的预测结果
3、手写BiLSTM代码
import torch
import torch.nn as nn
from torch.nn.utils.rnn import pack_padded_sequence, pad_packed_sequence
class LSTM(nn.Module):
def __init__(self, vocab_size, target_size, input_size=512, hidden_size=512):
super(LSTM, self).__init__()
self.hidden_size = hidden_size
self.embedding = nn.Embedding(vocab_size, input_size)
self.mlp = nn.Sequential(
nn.Linear(input_size, hidden_size),
nn.GELU(),
nn.Linear(hidden_size, hidden_size)
)
self.lstm = nn.LSTM(hidden_size, hidden_size * 2, num_layers=3, batch_first=True, dropout=0.5)
self.avg_lstm = nn.AdaptiveAvgPool1d(1)
self.avg_linear = nn.AdaptiveAvgPool1d(1)
self.out_linear = nn.Sequential(
nn.Linear(hidden_size * 2 + hidden_size, hidden_size),
nn.GELU(),
nn.LayerNorm(hidden_size),
nn.Linear(hidden_size, target_size)
)
self.norm = nn.LayerNorm(hidden_size * 2)
def forward(self, x, lengths):
x = self.embedding(x)
mlp = self.mlp(x)
pached_embed = pack_padded_sequence(mlp, lengths, batch_first=True, enforce_sorted=False)
lstm_out, _ = self.lstm(pached_embed)
lstm_out, _ = pad_packed_sequence(lstm_out, batch_first=True)
lstm_out = self.norm(lstm_out)
avg_lstm = self.avg_lstm(lstm_out.permute(0, 2, 1)).squeeze(-1)
avg_linear = self.avg_linear(mlp.permute(0, 2, 1)).squeeze(-1)
out = torch.cat([avg_lstm, avg_linear], dim=-1)
return self.out_linear(out)
class BiLSTM(nn.Module):
def __init__(self, input_size=512, hidden_size=512, output_size=512):
super(BiLSTM, self).__init__()
self.hidden_size = hidden_size
self.lstm_forward = nn.LSTM(input_size, hidden_size, num_layers=1, batch_first=True)
self.lstm_backward = nn.LSTM(input_size, hidden_size, num_layers=1, batch_first=True)
def forward(self, x):
out_forward, _ = self.lstm_forward(x)
out_backward, _ = self.lstm_backward(torch.flip(x, dims=[1]))
out_backward = torch.flip(out_backward, dims=[1])
combined_output = torch.cat([out_forward, out_backward], dim=-1)
return combined_output