当前位置:首页 > 娱乐 > pytorch lstm 注意力机制 正文

pytorch lstm 注意力机制

时间:2026-10-17 05:06:21 来源:力不从心网

PyTorch里直接用nn.LSTM,意力默认只给你最后一个时间步的机制hidden state。做文本分类时这通常够用,意力但遇到长句子就麻烦了——"这家餐厅的机制菜还行,但服务态度差到让人想立刻走人",意力最后一个hidden state可能只记住了"走人"的机制情绪,前面的意力"还行"早被冲淡了。注意力机制就是机制来解决这个遗忘问题的。

我拿IMDB影评数据试过。意力不加注意力,机制LSTM二分类准确率卡在86%左右。意力加了一层简单的机制加性注意力,跑到89.2%。意力差别在哪?机制注意力让模型在每个时间步都输出一个权重,最后把所有hidden state加权求和,意力而不是只取最后一个。

实现上没多复杂。PyTorch的LSTM如果设batch_first=True,输入形状是(batch, seq_len, embed_dim)。前向传播时,lstm_out, (h_n, c_n) = self.lstm(x),这里的lstm_out形状是(batch, seq_len, hidden_dim),包含了每个时间步的output。注意力层要做的就是给这seq_len个向量各算一个分数。

最直接的做法叫加性注意力(Bahdanau那套)。拿一个可学习的参数向量v,对每个时间步的h_t做v^T * tanh(W * h_t),得到一个标量分数。所有分数过softmax归一化,再加权求和。代码大概长这样:

class Attention(nn.Module): def __init__(self, hidden_dim): super().__init__() self.w = nn.Linear(hidden_dim, hidden_dim) self.v = nn.Linear(hidden_dim, 1, bias=False) def forward(self, lstm_out): # lstm_out: (batch, seq_len, hidden_dim) score = self.v(torch.tanh(self.w(lstm_out))) # (batch, seq_len, 1) weight = torch.softmax(score, dim=1) context = torch.sum(weight * lstm_out, dim=1) # (batch, hidden_dim) return context, weight 

注意dim=1,不是dim=-1。这个坑我踩过——softmax维度搞错,权重全变成1,注意力直接失效,训练loss跟没加一样。

还有个细节:LSTM默认输出是(seq_len, batch, hidden_dim),除非你设了batch_first=True。我习惯在DataLoader里就把batch维度放前面,省得后面transpose来transpose去,容易搞混。

实际跑的时候,注意力权重可以可视化。拿一条影评出来,看哪些词被赋予高权重。有次我输入"The movie starts slow but the ending is absolutely brilliant",注意力在"brilliant"上给了0.31的权重,"slow"只拿到0.04。模型确实学会了抓关键情感词。但也有翻车的时候——反讽句"Great, another two hours of my life wasted",注意力死盯"Great",分类结果判成正面。这说明注意力不是万能药,它只是让模型多了一个选择权,选得对不对还得看训练数据。

计算开销方面,加性注意力每个时间步要做一次hidden_dim × hidden_dim的矩阵乘。seq_len=200、hidden_dim=128时,比纯LSTM大概多15%的训练时间。可以接受。如果嫌慢,换成点积注意力(Luong那套),直接h_t^T * h_s,省掉参数矩阵,但效果在短文本上差不多,长文本略差一点。

最后说个工程上的观察:注意力层的学习率通常要比LSTM主体小一个量级。我一般给LSTM设1e-3,注意力层设1e-4。不然注意力权重震荡厉害,前几个epoch几乎在随机跳。这个没有理论保证,纯属调参经验,但在我手头三个数据集上都管用。

推荐内容