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
    评论
好的,关于多层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、付费专栏及课程。

余额充值