LSTM api的介绍

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


目标

1. 知道LSTM和GRU的使用方法及输入输出格式
2. 能够应用LSTM和GRU实现文本情感分类


提示:以下是本篇文章正文内容,下面案例可供参考

一、LSTM介绍

LSTM和GRU都是由torch.nn提供,通过观察文档,可知LSTM的参数:

torch.nn.LSTM(input_size,hidden_size,num_layers,batch_first,dropout,bidirectional)
  1. input_size:输入数据的形状,即embedding_dim
  2. hidden_size:隐藏层的数量,即每一层有多少个LSTM单元
  3. num_layer:即RNN中LSTM单元的层数
  4. batch_first:表示数据中的batch_size是否在第一个维度,默认值为False,输入的数据格式为[seq_len,batch_size,feature],如果为True,则为[batch_size,seq_len, feature]
  5. dropout:dropout的比例,默认值为0,表示不进行dropout。dropout是一种训练过程中让部分参数随机失活的一种方式,能够提高训练速度,同时能够解决过拟合的问题。这里是在LSTM的最后一层,对每个输出进行dropout。
  6. bidirectional:是否使用双向LSTM,默认是False

实例化LSTM对象之后,**不仅需要传入数据,还需要前一次的h_0(前一次的隐藏状态)和c_0(前一次memory)**如果没有传h_0和c_0,网络会自动帮我们生成一个全为0且符合结构的状态。
即:lstm(input,(h_0,c_0))
LSTM的默认输出为output,(h_n,c_n)

  • output:(seq_len,batch_size,num_directions * hidden_size)
  • h_n:(num_layers*num_directions,batch_size,hidden_size)
  • c_n:(num_layers*num_directions,batch_size,hidden_size)

二、LSTM使用实例

假设数据输入为input,形状是[10,20],假设embedding的形状是[100,30]。
代码如下(示例):

import torch.nn as nn
import torch

batch_size = 10     # 每次句子的数量
seq_len = 20        # 句子的长度
embedding_dim = 30  # 用长度为30的向量表示一个词语
vocab_size = 100    # 词典的大小
hidden_size = 18    # 隐层中lstm的个数
num_layer = 2       # 多少个隐藏层

# 准备数据
input = torch.randint(low=0,high=100,size=[batch_size,seq_len])

embedding = nn.Embedding(num_embeddings=vocab_size,embedding_dim=embedding_dim)
input_embedd = embedding(input)     # [batch_size,seq_len,embedding_dim]10,20,30

lstm = nn.LSTM(input_size=embedding_dim,hidden_size=hidden_size,num_layers=num_layer,batch_first=True)
output,(h_n,c_n) = lstm(input_embedd)
print(output.size())
print("*"*100)
print(h_n.size())
print("*"*100)
print(c_n.size())

GRU API

  • 也是torch.nn提供
  • GRU(参数同LSTM)
  • 输出为:output,h_n = gru(input,h_0)
  • 形状同LSTM

2.双向LSTM

如果需要使用双向LSTM,则在实例化LSTM的过程中,需要把LSTM的bidriectional 设为True,同时h_0和c_0使用num_layer*2

  • output的拼接顺序:正向的第一个拼接反向的最后一个,把最后一个维度进行拼接
    在这里插入图片描述
    双向LSTM中:
    output:按照正反计算的结果顺序把第2个维度进行拼接,正向第一个拼接反向的最后一个输出。
    hidden state:按照得到的结果在第0个维度进行拼接,正向第一个之后接着是反向第一个。[layers*num_direction,batch_size,hidden_size]
batch_size = 10     # 每次句子的数量
seq_len = 20        # 句子的长度
embedding_dim = 30  # 用长度为30的向量表示一个词语
vocab_size = 100    # 词典的大小
hidden_size = 18    # 隐层中lstm的个数
num_layer = 2       # 多少个隐藏层

# 准备数据
input = torch.randint(low=0,high=100,size=[batch_size,seq_len])
embedding = nn.Embedding(num_embeddings=vocab_size,embedding_dim=embedding_dim)
input_embedd = embedding(input)     # [batch_size,seq_len,embedding_dim]10,20,30
# 双向
bilstm = nn.LSTM(input_size=embedding_dim,hidden_size=hidden_size,num_layers=num_layer,batch_first=True,bidirectional=True)
h_0 = torch.rand(num_layer*2,batch_size,hidden_size)
c_0 = torch.rand(num_layer*2,batch_size,hidden_size)
output,(h_n,c_n) = bilstm(input_embedd,(h_0,c_0))
print(output)

3. LSTM和GRU的使用注意点

  1. 在第一次调用之前,需要初始化隐藏状态,如果不初始化,默认创建全为0的隐藏状态
  2. 往往会使用LSTM或GRU输出的最后一维的结果,来代表LSTM、GRU对文本处理的结果,其形状为[batch,num_directions*hidden_size]
    - 并不是所有模型都会使用最后一维的结果
    - 如果实例化LSTM的过程中,batch_first=False,则output[-1]output[-1,:,:]可以获得最后一维
    - 如果实例化LSTM的过程中,batch_first=True,则output[:,-1,:]可以获得最后一维
  3. 如果结果是(seq_len,batch_size,num_directions*hidden_size),需要把它转化为(batch_size,seq_len,num_directions*hidden_size)的形状,不能够不是view等变形的方法,需要使用output.permute(1,0,2),即交换0和1轴,实现上述效果
  4. 使用双向LSTM的时候,往往会分别使用每个方向最后一次的output,作为当前数据经过双向LSTM的结果
    - 即:torch.cat([h_1[-2,:,:],h_1[-1,:,:]],dim=-1)
    - 最后表示的size是[batch_size,hidden_size*2]
  5. 上述内容在GRU中同理

4. 使用LSTM完成文本情感分类

在前面,我们使用了word embedding去实现了toy级别的文本情感分类,那么现在我们在这个模型中加上LSTM层,观察分类结果。
为了达到更好的效果,对之前的模型做了如下修改:

  1. max_len = 200
  2. 构建dataset的过程,把数据转化为2分类的问题,pos为1,neg为0,否则25000个样本完成10个类别的划分,数据量是不够的
  3. 在实例化LSTM的时候,使用dropout=0.5,在model.eval()的过程中,dropout自动会为0.
class MyModel(nn.Module):

    def __init__(self):
        super().__init__()
        super(MyModel,self).__init__()
        self.embedding = nn.Embedding(len(lib.ws),100)  # 参数为[词的数量,词的维度]
        self.lstm = nn.LSTM(input_size=100,hidden_size=lib.hidden_size,num_layers=lib.num_layers,
                            batch_first=True,bidirectional=lib.bidirectional,dropout=lib.dropout)
        self.fc = nn.Linear(lib.hidden_size*2,2)          # 参数为[input_feature,output_feature]

    def forward(self, input):
        """
        模型训练时,不需要使用forward,只要在实例化一个对象中传入对应的参数就可以自动调用 forward 函数
        :param input:[batch_size,max_len]
        :return:
        """
        x = self.embedding(input)   # 进行embedding操作,形状为:[batch_size,max_len]
        # 输出中的x:[batch_size,max_len,2*hidden_size] ,h_n:[2*2,batch_size,hidden_size]
        x,(h_n,c_n) = self.lstm(x)
        # 获得两个方向的output,进行一次concat
        output_fw = h_n[-2,:,:] # 正向最后一次的输出
        output_bw = h_n[-1,:,:] # 反向最后一次的输出
        output = torch.cat([output_fw,output_bw],dim=-1)   # [batch_size,hidden_size*2]
        out = self.fc(output)
        return F.log_softmax(out,dim=-1)# 一般loss为负是在loss采用交叉熵的情况下:可以将softmax 改为 log_softmax

model = MyModel().to(lib.device)
optimizer = Adam(model.parameters(),0.001)
if os.path.exists("./model/model.pkl"):
    model.load_state_dict(torch.load("./model/model.pkl"))
    optimizer.load_state_dict(torch.load("./model/optimizer.pkl"))

def train(epoch):
    for idx,(input,target) in enumerate(get_dataloader(train=True)):
        input.to(lib.device)
        target.to(lib.device)
        # 梯度归0
        optimizer.zero_grad()
        output = model(input)
        loss = F.nll_loss(output,target)
        loss.backward()
        optimizer.step()
        print(epoch,idx,loss.item())

        if idx%100 == 0:
            torch.save(model.state_dict(),"./model/model.pkl")
            torch.save(optimizer.state_dict(), "./model/model.pkl")

if __name__ == '__main__':
    for i in range(1):
        train(i)

模型评估:

def eval():
    loss_list = []
    acc_list = []
    data_loader = get_dataloader(train=False)
    for idx, (input, target) in tqdm(enumerate(data_loader),total=len(data_loader)):
        input = input.to(lib.device)
        target = target.to(lib.device)
        with torch.no_grad():
            output = model(input)
            # 计算损失
            cur_loss = F.nll_loss(output,target)
            loss_list.append(cur_loss)
            # 计算准确度
            pred = output.max(dim=-1)[-1]
            cur_acc = pred.eq(target).float().mean()
            acc_list.append(cur_acc)
    print("total loss acc :",np.mean(loss_list),np.mean(acc_list))
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值