Attention机制详解:从原理到中英翻译实战,让AI学会“划重点”

张开发
2026/4/16 22:07:15 15 分钟阅读

分享文章

Attention机制详解:从原理到中英翻译实战,让AI学会“划重点”
在自然语言处理领域机器翻译一直是个经典难题。早期的翻译模型像一个“死记硬背”的学生先把整篇中文课文背成一个固定长度的“小抄”再凭着这张小抄默写出英文。句子短的时候效果还行一旦句子变长小抄装不下了翻译就开始胡说八道。后来科学家发明了Attention机制注意力机制让模型学会了“翻书”——翻译每个词的时候都回头去原文里找最相关的地方看一眼。这一招彻底改变了NLP的格局。今天我们就用最通俗的语言配合完整的代码把Attention机制彻底讲清楚。从它的设计初衷到三种评分函数再到一个可以直接跑起来的中英翻译项目手把手带你拿下这个知识点。一、传统Seq2Seq模型的两大硬伤先复习一下没有Attention的老方法。经典的Seq2Seq模型序列到序列模型包含两个部分编码器Encoder读入源句子比如中文把整个句子的信息压缩成一个固定长度的向量通常叫做“上下文向量”或“语义向量”。解码器Decoder基于这个固定向量一步一步生成目标句子比如英文。这个过程就像你把一本小说浓缩成一张便利贴然后让别人根据这张便利贴还原整本小说——信息丢失是必然的。具体来说传统方法存在两个致命问题问题一信息压缩困难无论源句子多长哪怕是100个词的复杂长句最终都只能塞进一个固定大小的向量里。这导致长句的关键信息很容易被“挤丢”模型翻译到后面时前面的内容已经忘了。问题二缺乏动态感知解码器在生成每个目标词时用的是同一个静态向量。翻译“我”和翻译“散步”时参考的信息源一模一样。这就像你写作文时不允许回看原文只能凭印象写——开头还记得结尾就开始编了。为了解决这些问题研究者提出了Attention机制。二、Attention的核心思想动态查阅Attention机制的核心思想极其简单却威力巨大解码器在生成目标序列的每一步时不再依赖一个静态的上下文向量而是根据当前的解码状态动态地从编码器各时间步的隐藏状态中选取最相关的信息。换句话说解码器每生成一个词都会重新“扫一遍”原文用当前的“注意力”去加权提取原文中最有用的信息。这种机制让模型自动学会了对齐Alignment——它能判断出源句子中哪些位置与当前要生成的词关系最密切。举个具体例子翻译“我 爱 你” → “I love you”。当解码器要生成“love”时注意力权重会集中在“爱”这个字上生成“you”时注意力会转向“你”。模型不需要任何人教自己就学会了这种对应关系。三、工作原理一步一步拆解Attention计算Attention机制的实现可以分成四个步骤。下面我们用最常用的点积评分函数来讲解。3.1 计算注意力评分第一步我们需要衡量解码器当前隐藏状态与编码器每个时间步的隐藏状态之间的相关性。这个相关性分数就叫注意力评分。假设解码器当前步的隐藏状态是 hdechdec一个向量编码器有T个时间步的隐藏状态 h1,h2,...,hTh1,h2,...,hT。最简单的评分方法就是计算点积scoreihdec⋅hiscoreihdec⋅hi点积越大说明两个向量方向越一致相关性越强。如果你觉得“点积”不好理解可以把它想象成两个向量的相似度——越像分数越高。3.2 归一化得到注意力权重得到的分数大小不一直接使用不方便。我们用一个Softmax函数把它们转换成概率分布也就是注意力权重。Softmax的作用是让所有分数变成0~1之间的数并且加起来等于1。αiescorei∑j1Tescorejαi∑j1Tescorejescorei现在αiαi 就代表了模型应该对第i个源位置投入多少“注意力”。分数越高的位置权重越大。3.3 生成上下文向量有了注意力权重我们就可以对编码器的所有隐藏状态进行加权求和得到当前步的上下文向量context∑i1Tαi⋅hicontexti1∑Tαi⋅hi这个上下文向量就是模型从原文中提取的“重点笔记”——它融合了所有源位置的信息但更强调当前最相关的部分。3.4 解码信息融合最后一步解码器把当前步的GRU输出hdechdec和上下文向量拼接到一起组成一个更丰富的信息向量再通过一个线性层全连接层和Softmax预测下一个词的概率分布。combined[hdec;context]combined[hdec;context]outputsoftmax(W⋅combinedb)outputsoftmax(W⋅combinedb)这样解码器在预测每个词时既用到了自己当前的记忆hdechdec也用到了从原文动态提取的外部信息context翻译效果自然大幅提升。四、三种注意力评分函数各有千秋刚才我们用的是最简单的点积评分Dot。但实际应用中编码器和解码器的隐藏状态维度可能不一样或者任务复杂需要更强的表达能力。因此研究者提出了多种评分函数常见的有三种4.1 点积评分Dot通俗解释直接计算两个向量的内积值越大表示越相关。优点计算最快实现简单。缺点要求 hdechdec 和 henchenc 维度相同且表达能力有限。4.2 通用点积评分General通俗解释先对编码器状态做一个线性变换乘以矩阵W把它的维度变成和解码器一致然后再点积。优点可以处理维度不匹配的情况W是可学习的增加了模型灵活性。缺点比点积多了一些计算量。4.3 拼接评分Concat/ 加性注意力通俗解释把解码器状态和编码器状态拼成一个长向量扔进一个带激活函数的小型神经网络最后用另一个向量v投影成一个分数。优点表达能力最强因为引入了非线性变换tanh可以捕捉更复杂的交互关系。缺点计算量最大。实际工程中点积评分因为简单高效被广泛使用在Transformer中还使用了缩放点积注意力除以维度的平方根防止梯度消失。我们的代码示例就采用最基本的点积评分便于理解。五、完整代码实战中英翻译V2.0带Attention理论讲完我们上代码。下面是一个完整的中英翻译项目在传统Seq2Seq基础上引入了Attention机制。你可以按顺序创建文件然后运行训练和预测。项目结构translation_attention/ │ ├── data/ │ ├── raw/ # 存放原始数据 cmn.txt │ └── processed/ # 预处理后的数据 ├── models/ # 保存训练好的模型 ├── logs/ # TensorBoard日志 └── src/ ├── config.py ├── tokenizer.py ├── process.py ├── dataset.py ├── model.py ├── train.py ├── predict.py └── evaluate.py5.1 配置文件 config.py 配置文件集中管理所有超参数和路径 from pathlib import Path # 项目根目录src的父目录 BASE_DIR Path(__file__).parent.parent # 数据路径 RAW_DATA_DIR BASE_DIR / data / raw PROCESSED_DATA_DIR BASE_DIR / data / processed MODELS_DIR BASE_DIR / models LOGS_DIR BASE_DIR / logs # 模型参数 EMBEDDING_DIM 128 # 词向量维度 ENCODER_HIDDEN_DIM 512 # 编码器GRU隐藏维度 DECODER_HIDDEN_DIM 1024 # 解码器GRU隐藏维度是编码器的2倍因为编码器双向 ENCODER_LAYERS 1 # 编码器层数 # 训练参数 BATCH_SIZE 128 SEQ_LEN 30 # 输入/输出序列最大长度 LEARNING_RATE 1e-3 EPOCHS 305.2 分词器 tokenizer.py 分词器中文按字切分英文按词切分提供编码/解码/词表构建功能 from abc import abstractmethod from nltk import word_tokenize, TreebankWordDetokenizer from tqdm import tqdm class BaseTokenizer: 分词器基类 unk_token unk # 未知词 pad_token pad # 填充符 sos_token sos # 开始符 eos_token eos # 结束符 staticmethod abstractmethod def tokenize(sentence): pass abstractmethod def decode(self, indexes): pass classmethod def build_vocab(cls, sentences, vocab_file): 从句子列表构建词表并保存 unique_words set() for sentence in tqdm(sentences, desc构建词表): for word in cls.tokenize(sentence): unique_words.add(word) # 特殊标记放在最前面保证索引固定 vocab_list [cls.pad_token, cls.unk_token, cls.sos_token, cls.eos_token] list(unique_words) with open(vocab_file, w, encodingutf-8) as f: for w in vocab_list: f.write(w \n) def __init__(self, vocab_list): self.vocab_list vocab_list self.vocab_size len(vocab_list) self.word2index {w: i for i, w in enumerate(vocab_list)} self.index2word {i: w for i, w in enumerate(vocab_list)} self.unk_token_index self.word2index[self.unk_token] self.pad_token_index self.word2index[self.pad_token] self.sos_token_index self.word2index[self.sos_token] self.eos_token_index self.word2index[self.eos_token] classmethod def from_vocab(cls, vocab_file): with open(vocab_file, r, encodingutf-8) as f: vocab_list [line.strip() for line in f.readlines()] return cls(vocab_list) def encode(self, sentence, seq_len, add_sos_eosFalse): 将句子转为索引序列长度截断/补齐 tokens self.tokenize(sentence) indexes [self.word2index.get(t, self.unk_token_index) for t in tokens] if add_sos_eos: indexes indexes[:seq_len - 2] indexes [self.sos_token_index] indexes [self.eos_token_index] else: indexes indexes[:seq_len] if len(indexes) seq_len: indexes [self.pad_token_index] * (seq_len - len(indexes)) return indexes class ChineseTokenizer(BaseTokenizer): staticmethod def tokenize(sentence): return list(sentence) # 中文按字切分 def decode(self, indexes): return .join([self.index2word[i] for i in indexes]) class EnglishTokenizer(BaseTokenizer): staticmethod def tokenize(sentence): return word_tokenize(sentence) # 英文按词切分 def decode(self, indexes): tokens [self.index2word[i] for i in indexes] return TreebankWordDetokenizer().detokenize(tokens)5.3 数据预处理 process.py 预处理原始中英文对齐文件划分训练/测试集构建词表编码数据 import pandas as pd from sklearn.model_selection import train_test_split from tokenizer import ChineseTokenizer, EnglishTokenizer import config def process(): print(开始处理数据...) df pd.read_csv(config.RAW_DATA_DIR / cmn.txt, sep\t, headerNone, usecols[0,1], names[en,zh]) df df.dropna() df df[df[en].str.strip().ne() df[zh].str.strip().ne()] train_df, test_df train_test_split(df, test_size0.2, random_state42) # 构建词表 EnglishTokenizer.build_vocab(train_df[en].tolist(), config.PROCESSED_DATA_DIR / en_vocab.txt) ChineseTokenizer.build_vocab(train_df[zh].tolist(), config.PROCESSED_DATA_DIR / zh_vocab.txt) en_tokenizer EnglishTokenizer.from_vocab(config.PROCESSED_DATA_DIR / en_vocab.txt) zh_tokenizer ChineseTokenizer.from_vocab(config.PROCESSED_DATA_DIR / zh_vocab.txt) # 编码训练集英文加sos/eos中文不加 train_df[en] train_df[en].apply( lambda x: en_tokenizer.encode(x, config.SEQ_LEN, add_sos_eosTrue)) train_df[zh] train_df[zh].apply( lambda x: zh_tokenizer.encode(x, config.SEQ_LEN, add_sos_eosFalse)) train_df.to_json(config.PROCESSED_DATA_DIR / indexed_train.jsonl, orientrecords, linesTrue) # 编码测试集 test_df[en] test_df[en].apply( lambda x: en_tokenizer.encode(x, config.SEQ_LEN, add_sos_eosTrue)) test_df[zh] test_df[zh].apply( lambda x: zh_tokenizer.encode(x, config.SEQ_LEN, add_sos_eosFalse)) test_df.to_json(config.PROCESSED_DATA_DIR / indexed_test.jsonl, orientrecords, linesTrue) print(数据预处理完成) if __name__ __main__: process()5.4 数据集 dataset.py PyTorch Dataset和DataLoader封装 import pandas as pd import torch from torch.utils.data import Dataset, DataLoader import config class TranslationDataset(Dataset): def __init__(self, data_path): self.data pd.read_json(data_path, linesTrue).to_dict(orientrecords) def __len__(self): return len(self.data) def __getitem__(self, idx): src torch.tensor(self.data[idx][zh], dtypetorch.long) tgt torch.tensor(self.data[idx][en], dtypetorch.long) return src, tgt def get_dataloader(trainTrue): path config.PROCESSED_DATA_DIR / (indexed_train.jsonl if train else indexed_test.jsonl) ds TranslationDataset(path) return DataLoader(ds, batch_sizeconfig.BATCH_SIZE, shuffletrain)5.5 模型定义 model.py核心Attention模块 模型定义编码器(双向GRU) 注意力模块 解码器(单向GRUAttention) import torch import torch.nn as nn import config class Attention(nn.Module): 注意力机制模块点积评分 def forward(self, decoder_hidden, encoder_outputs): decoder_hidden: (1, batch, hidden_dim) 当前解码器隐藏状态 encoder_outputs: (batch, seq_len, hidden_dim*2) 编码器所有输出 返回 context: (batch, 1, hidden_dim) # 步骤1计算注意力分数 (batch, 1, seq_len) # 将 decoder_hidden 转置为 (batch, 1, hidden) # 将 encoder_outputs 转置为 (batch, hidden, seq_len) scores torch.bmm( decoder_hidden.transpose(0, 1), # (batch, 1, hidden) encoder_outputs.transpose(1, 2) # (batch, hidden, seq_len) ) # 步骤2Softmax归一化得到权重 weights torch.softmax(scores, dim2) # (batch, 1, seq_len) # 步骤3加权求和得到上下文向量 context torch.bmm(weights, encoder_outputs) # (batch, 1, hidden) return context class TranslationEncoder(nn.Module): 编码器双向GRU def __init__(self, vocab_size, padding_idx): super().__init__() self.embedding nn.Embedding(vocab_size, config.EMBEDDING_DIM, padding_idxpadding_idx) self.rnn nn.GRU(config.EMBEDDING_DIM, config.ENCODER_HIDDEN_DIM, num_layersconfig.ENCODER_LAYERS, batch_firstTrue, bidirectionalTrue) def forward(self, src): embedded self.embedding(src) # (batch, seq_len, embed_dim) outputs, hidden self.rnn(embedded) # outputs: (batch, seq_len, hidden_dim*2) # hidden: (2*num_layers, batch, hidden_dim) return outputs, hidden class TranslationDecoder(nn.Module): 解码器单向GRU Attention def __init__(self, vocab_size, padding_idx): super().__init__() self.embedding nn.Embedding(vocab_size, config.EMBEDDING_DIM, padding_idxpadding_idx) self.rnn nn.GRU(config.EMBEDDING_DIM, config.DECODER_HIDDEN_DIM, batch_firstTrue) self.attention Attention() self.linear nn.Linear(config.DECODER_HIDDEN_DIM * 2, vocab_size) def forward(self, tgt, hidden, encoder_outputs): tgt: (batch, 1) 当前输入token hidden: (1, batch, decoder_hidden_dim) 上一步隐藏状态 encoder_outputs: (batch, seq_len, encoder_hidden_dim*2) embedded self.embedding(tgt) # (batch, 1, embed_dim) output, new_hidden self.rnn(embedded, hidden) # output: (batch, 1, dec_hidden) context self.attention(new_hidden, encoder_outputs) # (batch, 1, dec_hidden) combined torch.cat((output, context), dim2) # (batch, 1, dec_hidden*2) logits self.linear(combined) # (batch, 1, vocab_size) return logits, new_hidden5.6 训练脚本 train.py 训练主程序包含一个epoch的训练函数和主循环 import time from itertools import chain import torch import torch.nn as nn from torch.utils.tensorboard import SummaryWriter from tqdm import tqdm from dataset import get_dataloader from tokenizer import ChineseTokenizer, EnglishTokenizer import config from model import TranslationEncoder, TranslationDecoder def train_one_epoch(dataloader, encoder, decoder, loss_fn, optimizer, device): encoder.train() decoder.train() total_loss 0 for src, tgt in tqdm(dataloader, desc训练): src src.to(device) # (batch, seq_len) tgt tgt.to(device) # (batch, seq_len) optimizer.zero_grad() # 编码器前向 encoder_outputs, encoder_hidden encoder(src) # encoder_hidden: (2, batch, enc_hidden) 因为双向 # 将双向最后一层的两个方向拼接作为解码器的初始隐藏状态 forward_hidden encoder_hidden[-2] # (batch, enc_hidden) backward_hidden encoder_hidden[-1] # (batch, enc_hidden) decoder_hidden torch.cat([forward_hidden, backward_hidden], dim1) # (batch, dec_hidden) decoder_hidden decoder_hidden.unsqueeze(0) # (1, batch, dec_hidden) # 解码器逐词预测 decoder_input tgt[:, 0:1] # 第一个输入是 sos decoder_outputs [] for step in range(1, config.SEQ_LEN): logits, decoder_hidden decoder(decoder_input, decoder_hidden, encoder_outputs) decoder_outputs.append(logits) decoder_input tgt[:, step:step1] # teacher forcing decoder_outputs torch.cat(decoder_outputs, dim1) # (batch, seq_len-1, vocab_size) targets tgt[:, 1:] # 去掉开头的sos loss loss_fn(decoder_outputs.reshape(-1, decoder_outputs.shape[-1]), targets.reshape(-1)) loss.backward() optimizer.step() total_loss loss.item() return total_loss / len(dataloader) def train(): device torch.device(cuda if torch.cuda.is_available() else cpu) print(f使用设备: {device}) train_loader get_dataloader(trainTrue) zh_tokenizer ChineseTokenizer.from_vocab(config.PROCESSED_DATA_DIR / zh_vocab.txt) en_tokenizer EnglishTokenizer.from_vocab(config.PROCESSED_DATA_DIR / en_vocab.txt) encoder TranslationEncoder(zh_tokenizer.vocab_size, zh_tokenizer.pad_token_index).to(device) decoder TranslationDecoder(en_tokenizer.vocab_size, en_tokenizer.pad_token_index).to(device) loss_fn nn.CrossEntropyLoss(ignore_indexen_tokenizer.pad_token_index) optimizer torch.optim.Adam(chain(encoder.parameters(), decoder.parameters()), lrconfig.LEARNING_RATE) writer SummaryWriter(log_dirconfig.LOGS_DIR / time.strftime(%Y-%m-%d_%H-%M-%S)) best_loss float(inf) for epoch in range(1, config.EPOCHS1): print(f\n Epoch {epoch} ) avg_loss train_one_epoch(train_loader, encoder, decoder, loss_fn, optimizer, device) print(f平均损失: {avg_loss:.4f}) writer.add_scalar(Loss, avg_loss, epoch) if avg_loss best_loss: best_loss avg_loss torch.save(encoder.state_dict(), config.MODELS_DIR / encoder.pt) torch.save(decoder.state_dict(), config.MODELS_DIR / decoder.pt) print(保存最佳模型) writer.close() if __name__ __main__: train()5.7 预测脚本 predict.py 交互式翻译预测加载训练好的模型对输入的中文句子进行翻译 import torch from tokenizer import ChineseTokenizer, EnglishTokenizer from model import TranslationEncoder, TranslationDecoder import config def predict_batch(input_tensor, encoder, decoder, en_tokenizer, device): encoder.eval() decoder.eval() with torch.no_grad(): encoder_outputs, encoder_hidden encoder(input_tensor) # 初始化解码器隐藏状态 forward_hidden encoder_hidden[-2] backward_hidden encoder_hidden[-1] decoder_hidden torch.cat([forward_hidden, backward_hidden], dim1).unsqueeze(0) batch_size input_tensor.size(0) decoder_input torch.full((batch_size, 1), en_tokenizer.sos_token_index, devicedevice) generated [[] for _ in range(batch_size)] finished [False] * batch_size for _ in range(1, config.SEQ_LEN): logits, decoder_hidden decoder(decoder_input, decoder_hidden, encoder_outputs) preds logits.argmax(dim-1) # (batch, 1) for i in range(batch_size): if finished[i]: continue token_id preds[i].item() if token_id en_tokenizer.eos_token_index: finished[i] True else: generated[i].append(token_id) if all(finished): break decoder_input preds return generated def predict_sentence(zh_sentence, encoder, decoder, zh_tokenizer, en_tokenizer, device): input_ids zh_tokenizer.encode(zh_sentence, config.SEQ_LEN, add_sos_eosFalse) input_tensor torch.tensor([input_ids], devicedevice) gen predict_batch(input_tensor, encoder, decoder, en_tokenizer, device) return en_tokenizer.decode(gen[0]) def run_predict(): device torch.device(cuda if torch.cuda.is_available() else cpu) zh_tokenizer ChineseTokenizer.from_vocab(config.PROCESSED_DATA_DIR / zh_vocab.txt) en_tokenizer EnglishTokenizer.from_vocab(config.PROCESSED_DATA_DIR / en_vocab.txt) encoder TranslationEncoder(zh_tokenizer.vocab_size, zh_tokenizer.pad_token_index).to(device) decoder TranslationDecoder(en_tokenizer.vocab_size, en_tokenizer.pad_token_index).to(device) encoder.load_state_dict(torch.load(config.MODELS_DIR / encoder.pt, map_locationdevice)) decoder.load_state_dict(torch.load(config.MODELS_DIR / decoder.pt, map_locationdevice)) print(翻译系统已启动输入中文句子输入 q 退出) while True: zh input(中文: ).strip() if zh in (q, quit): break if not zh: continue en predict_sentence(zh, encoder, decoder, zh_tokenizer, en_tokenizer, device) print(f英文: {en}) if __name__ __main__: run_predict()5.8 评估脚本 evaluate.py 使用BLEU分数评估模型性能 import torch from nltk.translate.bleu_score import corpus_bleu from tqdm import tqdm import config from tokenizer import ChineseTokenizer, EnglishTokenizer from model import TranslationEncoder, TranslationDecoder from dataset import get_dataloader from predict import predict_batch def evaluate(): device torch.device(cuda if torch.cuda.is_available() else cpu) zh_tokenizer ChineseTokenizer.from_vocab(config.PROCESSED_DATA_DIR / zh_vocab.txt) en_tokenizer EnglishTokenizer.from_vocab(config.PROCESSED_DATA_DIR / en_vocab.txt) encoder TranslationEncoder(zh_tokenizer.vocab_size, zh_tokenizer.pad_token_index).to(device) decoder TranslationDecoder(en_tokenizer.vocab_size, en_tokenizer.pad_token_index).to(device) encoder.load_state_dict(torch.load(config.MODELS_DIR / encoder.pt, map_locationdevice)) decoder.load_state_dict(torch.load(config.MODELS_DIR / decoder.pt, map_locationdevice)) test_loader get_dataloader(trainFalse) refs [] hyps [] specials {zh_tokenizer.pad_token_index, zh_tokenizer.sos_token_index, zh_tokenizer.eos_token_index} for src, tgt in tqdm(test_loader, desc评估): src src.to(device) pred_idxs predict_batch(src, encoder, decoder, en_tokenizer, device) hyps.extend(pred_idxs) for seq in tgt: filtered [idx.item() for idx in seq if idx.item() not in specials] refs.append([filtered]) bleu corpus_bleu(refs, hyps) print(f\nBLEU-4 分数: {bleu:.4f}) if __name__ __main__: evaluate()六、运行你的翻译模型下载数据集https://pan.baidu.com/s/1As2fpzjOn4HSQZPNeIHHDw?pwdes3y依次运行python src/process.py python src/train.py python src/predict.py训练大约30个epoch后模型会保存在 models/ 目录下。然后运行 predict.py 就可以交互式翻译了。完整代码下载https://pan.baidu.com/s/10pbHn5UWTWPqBdJNN7ouBw?pwd5zk6七、Attention机制的两大遗留问题尽管Attention机制极大地提升了Seq2Seq的能力但它仍然建立在RNN或GRU/LSTM的基础之上因此无法避免RNN固有的缺陷问题一计算过程无法并行RNN必须按时间步顺序执行计算第t步需要第t-1步的输出。这种强依赖关系导致GPU的并行能力无法充分发挥训练速度慢尤其对于长序列更是如此。问题二长期依赖问题仍未根除虽然Attention让解码器可以直接“看到”所有编码器状态但编码器内部仍然要靠RNN一步步传递信息。对于超长序列比如几百个词的句子RNN依然会面临梯度消失或梯度爆炸难以真正捕捉远距离依赖。正是为了解决这两个问题Google在2017年提出了Transformer架构——彻底抛弃RNN完全基于自注意力Self-Attention来建模。Transformer通过多头注意力和位置编码实现了全局并行计算和超长距离依赖建模成为了今天GPT、BERT等大模型的基石。八、总结与思考从最初“死记硬背”的固定向量到“动态查阅”的注意力机制再到彻底革命的自注意力Transformer机器翻译乃至整个NLP领域经历了一场深刻的思维变革。Attention机制的核心贡献在于它让模型学会了选择性关注不再平等对待所有输入信息而是根据当前任务需求动态分配计算资源。这种思想不仅适用于机器翻译在图像描述、语音识别、推荐系统等领域也同样大放异彩。通过本文的代码实战你可以亲手体验Attention的力量即使是一个简单的GRU模型加上注意力模块后翻译长句的流畅度和准确度都会有肉眼可见的提升。而如果你有兴趣还可以进一步尝试替换评分函数比如改成General或Concat或者实现Beam Search解码看看能带来多少改善。Attention机制不是终点但它无疑是通向现代深度学习的一扇重要大门。希望这篇文章能帮你把这扇门推开看到更广阔的风景。

更多文章