LSTM在多分类中出现的loss不下降问题(pytorch实现)

最近在使用LSTM做基于THUCNews数据集的文本分类。之前用LSTM模型做10种新闻种类的分类时可以正常收敛,说明应该不是写错代码的原因,但是当我把新闻种类扩大到14种类别时,却出现了loss不下降的情况:
loss几乎没有变化
因为不知道是什么原因,所以希望能探究一下。

一、改变权重初始化方案

之前是让LSTM默认初始化,现在采用RNN常用的正交初始化,据说可以缓解梯度消失和梯度爆炸问题。

方法:

在初始化代码中加入:

        nn.init.orthogonal_(self.lstm.weight_ih_l0)
        nn.init.orthogonal_(self.lstm.weight_hh_l0)

结果

没有改善。

二、改变输入文本的最大长度

输入文本的长度直接影响到模型参数的多少,模型参数越多越难训练,可能会导致loss不下降的情况。

方法

之前分词后的文本截断到了500个词,此次把文本的长度截断到了50个。

结果

loss正常下降,且收敛速度很快,七个epoch之后在验证集上的准确率达到最高,为94.71%。
如果是用于短文本分类,减少文本的最大长度能够很好的改善loss不下降的问题,而且训练速度能够大大加快。
但是我最终要用LSTM去做长文本的分类,因此还需要找到其他的方法。

三、学习率的调整

一般来讲,学习率的调整是操作起来最简单,但是也可能是最麻烦的一件事情。我之前有过调了调学习率就让模型从不收敛到收敛的经历,但是这次并没有什么效果。

方法

调整学习率,在[0.0003,1]中不断尝试。学习率过大会直接导致loss飙升到200以上,在一定区间上调小学习率能降低loss,但是继续调小学习率loss无法继续下降了。折腾了半天,发现最佳的初始学习率是默认的0.001左右,但之后无论怎么调整都无法使loss按照正常状况下降了。

结果

没有改善。

四、文本长度的降维

我大概知道是因为模型的参数过多所以难以训练了,这样我们或许能通过对文本的长度进行降维来改善这种状况。降维我最先想到的是通过池化的方式,但是直接进行池化会丢失掉很多的信息,所以我在宽度为10的最大池化前面又加入了一层宽度为3的卷积层,这样增强了模型对局部特征的提取能力。

模型代码如下

class MergeNet2(nn.Module):
    def __init__(self,vocab_size,pkernel_size,embedding_dim,kernel_size,hidden_dim,layer_dim,output_dim):
        """
        :param vocab_size: 词典长度
        :param pkernel_size: 池化层kernel宽度
        :param embedding_dim: 词向量维度
        :param kernel_size: 卷积池kernel宽度
        :param hidden_dim: LSTM神经元的个数
        :param layer_dim: LSTM层数
        :param output_dim: 隐藏层的输出维度(分类的数量)
        """
        super(MergeNet2,self).__init__()
        ## 对文本进行词向量处理
        self.embedding = nn.Embedding(vocab_size,embedding_dim)
        ## 对文本长度进行降维
        self.conv = nn.Sequential(
                nn.Conv1d(in_channels=embedding_dim,
                          out_channels=embedding_dim,
                          kernel_size=kernel_size),
                nn.BatchNorm1d(embedding_dim),
                nn.ReLU(inplace=True),
                nn.MaxPool1d(kernel_size=(pkernel_size))
        )

        ## LSTM+全连接
        self.lstm = nn.LSTM(embedding_dim,hidden_dim,layer_dim,batch_first=True)
        self.fc1 = nn.Linear(hidden_dim,output_dim)

    def forward(self,x):
        embeds = self.embedding(x)
        ## embeds shape (batch,sent_long,embedding_dim)
        embeds = embeds.permute(0, 2, 1)
        ## embeds shape (batch,embedding_dim,sent_long)
        conved = self.conv(embeds)
        ## conved shape (batch,embedding_dim,(sent_long-kernel_size+1)/pkernel_size)
        conved = conved.permute(0, 2, 1)
        ## 这里lstmcell的输入维度要调整为embedding_dim,文本在时间上展开成一个个embedding向量,这样才是把文本作为序列信息处理
        r_out,(h_n,h_c) = self.lstm(conved,None)
        ## r_out shape shape(batch,time_step,output_size)
        ## h_n shape (n_layers,batch,hidden_size)
        ## h_c shape (n_layers,batch,hidden_size)
        ## self.lstm两个参数:词向量和隐藏层的初始值,None表示hidden state会零初始化
        ## 选取最后一个时间点的out输出
        out = self.fc1(r_out[:,-1,:])
        return out

结果

loss正常下降,九个epoch之后在验证集上的准确率达到最高,为94.95%。

总结

改进后模型显然已经不是一个纯粹的LSTM模型了,而是一个卷积和LSTM相结合的模型。对于长文本的输入,无论有多么长(我最多试过3000个词,这个时候的池化层宽度为20),只要合理调整池化层和卷积层的参数都能够使loss正常下降。比起单一的LSTM模型,改进后的模型对文本长度的限制更小,从而能更全面地提取长文本的特征,理论上比单一LSTM模型具有更好的准确率。
当然,本文的初衷并不是做模型的融合来提高准确率,或许在LSTM之前采用多层卷积并联或串联的方式能够得到更好的模型,但也要小心过拟合的问题。

  • 14
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
【资源介绍】 基于Pytorch框架的多头选择方法实现文命名实体识别python源码+使用说明+模型+数据集.zip 在多头选择的基础上,设计了一个额外的任务:判断是否存在某类型的实体,即一个多标签分类的任务。目前可运行的是cner数据,其它数据可参照模板进行修改,具体步骤如下: - 1、在raw_data下是原始数据,通过process.py获得mid_data下的数据。 - 2、通过preprocess_mhs.py获得final_data下的数据。 - 3、运行main.py进行训练、验证、测试和预测。 需要注意的是要保持每一步骤各参数的一致性。数据和模型下载地址:链接:https://pan.baidu.com/s/1vpoDVZ875E27aLHjXK7yQA?pwd=4mcs 提取码:4mcs # 依赖 ``` pytorch==1.6.0 tensorboasX seqeval pytorch-crf==0.7.2 transformers==4.4.0 ``` # 运行 ```python python main.py \ --bert_dir="model_hub/chinese-bert-wwm-ext/" \ --data_dir="./data/cner/" \ --log_dir="./logs/" \ --output_dir="./checkpoints/" \ --pair_tags=9 \ --entity_tags=8 \ --seed=123 \ --gpu_ids="0" \ --max_seq_len=150 \ --lr=3e-5 \ --crf_lr=3e-2 \ --other_lr=3e-4 \ --train_batch_size=32 \ --train_epochs=3 \ --eval_batch_size=16 \ --max_grad_norm=1 \ --warmup_proportion=0.1 \ --adam_epsilon=1e-8 \ --weight_decay=0.01 \ --lstm_hidden=128 \ --num_layers=1 \ --use_lstm='True' \ --dropout_prob=0.3 \ --dropout=0.3 \ ``` ### 结果 ```python [eval] loss:0.0000 precision=0.9410 recall=0.8931 f1_score=0.9164 Saving model checkpoint to ./checkpoints/bert_bilstm Load ckpt from ./checkpoints/bert_bilstm/model.pt Use single gpu in: ['0'] precision recall f1-score support CONT 0.91 0.91 0.91 33 EDU 0.92 0.87 0.90 109 LOC 0.00 0.00 0.00 2 NAME 0.97 0.96 0.97 110 ORG 0.93 0.91 0.92 535 PRO 0.89 0.84 0.86 19 RACE 0.67 0.80 0.73 15 TITLE 0.95 0.88 0.91 748 micro avg 0.94 0.89 0.92 1571 macro avg 0.78 0.77 0.77 【备注】 该项目是个人毕设/课设/大作业项目,代码都经过本地调试测试,功能ok才上传,高分作品,可快速上手运行!欢迎下载使用,可用于小白学习、进阶。 该资源主要针对计算机、通信、人工智能、自动化等相关专业的学生、老师或从业者下载使用,亦可作为期末课程设计、课程大作业、毕业设计等。 项目整体具有较高的学习借鉴价值!基础能力强的可以在此基础上修改调整,以实现不同的功能。 欢迎下载使用,也欢迎交流学习!
好的,关于多层LSTMPyTorch实现,可以按照以下步骤进行: 1. 导入必要的库和模块: ```python import torch import torch.nn as nn ``` 2. 定义LSTM模型类: ```python class LSTMModel(nn.Module): def __init__(self, input_size, hidden_size, num_layers, output_size): super(LSTMModel, self).__init__() self.hidden_size = hidden_size self.num_layers = num_layers self.lstm = nn.LSTM(input_size, hidden_size, num_layers, batch_first=True) self.fc = nn.Linear(hidden_size, output_size) def forward(self, x): h0 = torch.zeros(self.num_layers, x.size(0), self.hidden_size).to(x.device) c0 = torch.zeros(self.num_layers, x.size(0), self.hidden_size).to(x.device) out, _ = self.lstm(x, (h0, c0)) out = self.fc(out[:, -1, :]) return out ``` 其,`input_size`表示输入数据的特征数,`hidden_size`表示LSTM隐藏层的大小,`num_layers`表示LSTM的层数,`output_size`表示输出数据的大小。 3. 实例化模型: ```python model = LSTMModel(input_size=10, hidden_size=20, num_layers=2, output_size=1) ``` 其,`input_size`为10,表示输入数据有10个特征;`hidden_size`为20,表示LSTM隐藏层有20个神经元;`num_layers`为2,表示LSTM有2层;`output_size`为1,表示输出数据的大小为1。 4. 定义损失函数和优化器: ```python criterion = nn.MSELoss() optimizer = torch.optim.Adam(model.parameters(), lr=0.01) ``` 其,使用均方误差(MSE)作为损失函数,使用Adam优化器进行优化。 5. 训练模型: ```python for epoch in range(num_epochs): for i, (inputs, labels) in enumerate(train_loader): inputs = inputs.to(device) labels = labels.to(device) outputs = model(inputs) loss = criterion(outputs, labels) optimizer.zero_grad() loss.backward() optimizer.step() if (i+1) % 10 == 0: print('Epoch [{}/{}], Step [{}/{}], Loss: {:.4f}' .format(epoch+1, num_epochs, i+1, total_step, loss.item())) ``` 其,`train_loader`是训练数据的数据加载器,`num_epochs`表示训练的轮数,`total_step`表示每轮训练的总步数。 6. 测试模型: ```python with torch.no_grad(): correct = 0 total = 0 for inputs, labels in test_loader: inputs = inputs.to(device) labels = labels.to(device) outputs = model(inputs) _, predicted = torch.max(outputs.data, 1) total += labels.size(0) correct += (predicted == labels).sum().item() print('Test Accuracy of the model on the {} test images: {} %'.format(total, 100 * correct / total)) ``` 其,`test_loader`是测试数据的数据加载器。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值