【NLP】LSTM理解(Pytorch实现)

【参考:详解LSTM - 知乎
【参考:YJango的循环神经网络——实现LSTM - 知乎 强烈建议阅读
在这里插入图片描述

手动实现

  • 输入门i_t,
  • 遗忘门 f_t,
  • 输出门o_t
  • cell门g_t
    在这里插入图片描述
    小圆圈代表点乘,对应元素相乘

【参考:30、PyTorch LSTM和LSTMP的原理及其手写复现_哔哩哔哩_bilibili

单层单向

【参考:30 - LSTM,LSTMP手撸代码_取个名字真难呐的博客-CSDN博客

import torch
from torch import nn

torch.manual_seed(0)  # 设置随机种子,随机函数生成的结果会相同

batch_size = 2  # 批次大小
seq_len = 3  # 输入序列长度
input_size = 4  # 输入数据特征大小
hidden_size = 5  # 隐藏层特征大小
num_layers = 1  # 层数

# random init the input
input_one = torch.randn(batch_size, seq_len, input_size)  # bs,seq_len,input_size 随机初始化一个特征序列

# random init the init hidden state
h0 = torch.zeros(batch_size, hidden_size)  # 初始隐含状态h_0 (bs,hidden_size) pytorch默认初始化全0
# 本来应该是(1,batch_size,hidden_size) 这里为了简便传递参数和下面的计算 因为很多时候传递的参数都是二维
c0 = torch.zeros(batch_size, hidden_size)  # 初始值 不参与训练

# define the RNN layers
lstm_layer = nn.LSTM(input_size=input_size,
                     hidden_size=hidden_size,
                     num_layers=num_layers,
                     batch_first=True)

output_api, (h_n_api, c_n_api) = lstm_layer(input_one, (h0.unsqueeze(0), c0.unsqueeze(0)))
# h_0 对于输入的是批次数据时维度为 (D∗num_layers,bs,hidden_size) 看官方参数
# h_prev.unsqueeze(0) h_prev:(bs,hidden_size)->(1,bs,hidden_size) 这里D为1,num_layers也是1

print(f"output.shape={output_api.shape}")
print(f"h_n.shape={h_n_api.shape}")
print(f"c_n.shape={c_n_api.shape}")
print(f"output={output_api}")
print(f"h_n={h_n_api}")
print(f"c_n={c_n_api}")

# 获取模型的参数
for k, v in lstm_layer.named_parameters():
    print(k, v.shape)
weight_ih_l0 torch.Size([20, 4])
weight_hh_l0 torch.Size([20, 5])
bias_ih_l0 torch.Size([20])
bias_hh_l0 torch.Size([20])

"""
weight_ih_l0 是 W_ii,W_if,W_ig,W_io四个拼接起来的 (4*5,4)
weight_hh_l0,bias_ih_l0,bias_hh_l0同理
"""
# 单向单层custom_lstm_function
def custom_lstm_function(input, init_state, w_ih, w_hh, b_ih, b_hh):
    """
    :param input:
    :param init_state:
    :param w_ih: W_ii,W_if,W_ig,W_io四个拼接起来的 (4*hidden_size,input_size)
    :param w_hh: W_hi,W_hf,W_hg,W_ho四个拼接起来的 (4*hidden_size,hidden_size)
    :param b_ih: 四个拼接起来的
    :param b_hh: 四个拼接起来的
    :return:
    """

    # ho.shape=torch.Size([batch_size,hidden_size])
    # c0.shape=torch.Size([batch_size,hidden_size])
    h0, c0 = init_state
    h_prev = h0
    c_prev = c0

    batch_size, seq_len, input_size = input.shape
    hidden_size = w_ih.shape[0] // 4
    output = torch.zeros((batch_size, seq_len, hidden_size))
    # h_prev.shape = torch.Size([1,batch_size,hidden_size]) -> ([batch_size,hidden_size,1])
    # c_prev.shape = torch.Size([1,batch_size,hidden_size]) -> ([batch_size,hidden_size,1])

    # batch_w_ih.shape = (4*hidden_size,input_size) -> (batch_size,4*hidden_size,input_size)
    batch_w_ih = w_ih.unsqueeze(0).tile([batch_size, 1, 1])

    # batch_w_hh.shape=(4*hidden_size,hidden_size)->(batch_size,4*hidden_size,hidden_size)
    batch_w_hh = w_hh.unsqueeze(0).tile([batch_size, 1, 1])

    for t in range(seq_len):
        # input.shape = torch.Size([batch_size,seq_len,input_size])
        # x.shape = torch.Size([batch_size,input_size]) -> (batch_size,input_size,1)
        x = input[:, t, :].unsqueeze(-1)

        # w_ih.shape=torch.Size([4*hidden_size,input_size])
        # w_ih_times_x.shape=torch.Size([batch_size,4*hidden_size,1]) -> ([batch_size,4*hidden_size])
        w_ih_times_x = torch.bmm(batch_w_ih, x).squeeze(-1)

        # h_prev = (batch_size,hidden_size) -> (batch_size,hidden_size,1)
        # batch_w_hh.shape=torch.Size([batch_size,4*hidden_size,hidden_size])
        # w_hh_times_prev_h.shape = torch.Size([batch_size,4*hidden_size,1]) -> ([batch_size,4*hidden_size])
        w_hh_times_prev_h = torch.bmm(batch_w_hh, h_prev.unsqueeze(-1)).squeeze(-1)

        # define the 输入门i_t,遗忘门 f_t,cell门g_t,输出门o_t,c_t,h_t

        #  i_t.shape = torch.Size([batch_size,hidden_size])
        i_t = torch.sigmoid(w_ih_times_x[:, :hidden_size]
                            + b_ih[:hidden_size]
                            + w_hh_times_prev_h[:, :hidden_size]
                            + b_hh[:hidden_size])
        # f_t.shape = torch.Size([batch_size,hidden_size])
        f_t = torch.sigmoid(w_ih_times_x[:, hidden_size:2 * hidden_size]
                            + b_ih[hidden_size:2 * hidden_size]
                            + w_hh_times_prev_h[:, hidden_size:2 * hidden_size]
                            + b_hh[hidden_size:2 * hidden_size])
        # (batch_size,hidden_size)
        g_t = torch.tanh(w_ih_times_x[:, 2 * hidden_size:3 * hidden_size]
                         + b_ih[2 * hidden_size:3 * hidden_size]
                         + w_hh_times_prev_h[:, 2 * hidden_size:3 * hidden_size]
                         + b_hh[2 * hidden_size:3 * hidden_size])
        # (batch_size,hidden_size)
        o_t = torch.sigmoid(w_ih_times_x[:, 3 * hidden_size:]
                            + b_ih[3 * hidden_size:]
                            + w_hh_times_prev_h[:, 3 * hidden_size:]
                            + b_hh[3 * hidden_size:])

        # c_prev.shape = ([batch_size,hidden_size,1]) ->(batch_size,hidden_size)
        c_prev = f_t * c_prev.squeeze(-1) + i_t * g_t # c_t

        # h_prev.shape = # (batch_size,hidden_size)
        h_prev = o_t * torch.tanh(c_prev) # h_t

        # output[:,t,:].shape = torch.Size([batch_size,hidden_size])
        output[:, t, :] = h_prev

    return output, (h_prev.unsqueeze(0), c_prev.unsqueeze(0))

cu_input = input_one
cu_init_state = (h0,c0)
cu_weight_ih_l0 = lstm_layer.weight_ih_l0
cu_weight_hh_l0 = lstm_layer.weight_hh_l0
cu_bias_ih_l0 = lstm_layer.bias_ih_l0
cu_bias_hh_l0 = lstm_layer.bias_hh_l0

custom_output,(custom_hn,custom_cn) = custom_lstm_function(cu_input,cu_init_state,cu_weight_ih_l0,cu_weight_hh_l0,cu_bias_ih_l0,cu_bias_hh_l0)



print(f"custom_output.shape={custom_output.shape}")
print(f"custom_hn.shape={custom_hn.shape}")
print(f"custom_cn.shape={custom_cn.shape}")
print(f"custom_output={custom_output}")
print(f"custom_hn={custom_hn}")
print(f"custom_cn={custom_cn}")

print("output is equal ?")
print(torch.isclose(custom_output, output_api))
print("h_n is equal ?")
print(torch.isclose(custom_hn, h_n_api))
print("c_n is equal ?")
print(torch.isclose(custom_cn, c_n_api))
...
output is equal ?
tensor([[[True, True, True, True, True],
         [True, True, True, True, True],
         [True, True, True, True, True]],

        [[True, True, True, True, True],
         [True, True, True, True, True],
         [True, True, True, True, True]]])
h_n is equal ?
tensor([[[True, True, True, True, True],
         [True, True, True, True, True]]])
c_n is equal ?
tensor([[[True, True, True, True, True],
         [True, True, True, True, True]]])
  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: LSTM Attention是一种基于LSTM模型和注意力机制的深度学习模型,用于处理序列数据。在PyTorch中,可以使用torch.nn模块中的LSTM和Attention层来实现LSTM Attention模型。LSTM层用于处理序列数据的长期依赖关系,而Attention层则用于对序列中的不同部分进行加权,以便更好地捕捉序列中的重要信息。LSTM Attention模型在自然语言处理、语音识别等领域中得到了广泛应用。 ### 回答2: LSTM Attention是一种基于循环神经网络(RNN)和注意力机制的深度学习模型,主要用于序列数据的建模和预测。该模型包含一个长短时记忆(LSTM)单元,它能够对输入序列进行建模并捕捉其长期依赖性。而同时,其通过引入注意力机制,能够聚焦于特定部分的输入,在模型性能上取得了较好的提升。 具体来说,该模型中的注意力机制是通过对输入序列进行权重分配来实现的。在原始LSTM中,每个时间步的输入都被平等地处理,而在LSTM Attention中,则添加了一个权重系数向量,能够为每个时间步的输入赋予不同的重要性。该权重系数向量是通过对潜在的注意力机制建模得到的,从而能够选择并聚焦于序列中最相关的部分。 在PyTorch实现LSTM Attention模型相对简单。首先,需要定义LSTM模型的输入和输出维度,以及注意力机制的参数,比如权重分配函数和激活函数。接下来,可以使用PyTorchLSTM模块来创建LSTM层,并将其与注意力层组合在一起,以构建完整的LSTM Attention模型。最后,可以使用PyTorch的优化器和损失函数训练模型,以便在给定的数据集上完成分类或回归任务。 总体而言,LSTM Attention模型在序列建模和预测方面表现出色,其能够有效地处理时间序列数据中的长期依赖性和重要性,并在应用领域中取得广泛的成功,比如自然语言处理、计算机视觉和推荐系统等。 ### 回答3: LSTM(长短期记忆网络)是一种适用于序列数据的深度学习模型,而Attention机制则是解决序列数据分类和回归问题时的一种非常有用的技术。而PyTorch则是一个开源的机器学习框架,它能够方便地构建、训练和部署深度学习模型。 LSTM模型是一种能够处理序列数据的神经网络模型,它通过特殊的门控机制来解决序列数据的长期依赖问题。我们在设计LSTM模型时往往需要考虑的是如何利用前面的历史信息,来预测后面的序列数据,因此这种模型非常适用于时间序列数据的预测。 而Attention机制则是一种能够为序列数据中每个时间步骤分配不同权重的技术,它可以通过对不同时间步骤的特定信息进行聚焦,从而提高序列数据分类的准确性。在使用Attention机制时,我们需要根据当前的输入序列的状态信息,来计算出新的权重系数,然后使用这些系数来计算加权和,从而得到最终的分类或回归结果。 在PyTorch中,我们可以很方便地使用LSTM和Attention机制来实现序列数据的分类和回归问题。在PyTorch中,我们需要对模型的输入数据进行预处理,然后使用PyTorch提供的LSTM和Attention模块来构建模型。然后通过训练模型,我们可以不断调整模型的参数,提高模型的分类和回归准确率。 总之,LSTM和Attention机制是一种非常有用的深度学习技术,而PyTorch则是一个方便易用的机器学习框架,能够帮助我们实现序列数据的分类和回归问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值