北京门户网站设计,wordpress类似股票行情,公司做网站要有服务器,常平哪里有招计算机网站开发的哈喽#xff0c;我是我不是小upper~
在深度学习领域#xff0c;序列数据处理一直是重要的研究方向#xff0c;从语音识别到自然语言处理#xff0c;从时间序列分析到机器翻译#xff0c;数据都以序列的形式存在。今天咱们就来聊聊从 RNN 到 Transformer 的演变历程我是我不是小upper~
在深度学习领域序列数据处理一直是重要的研究方向从语音识别到自然语言处理从时间序列分析到机器翻译数据都以序列的形式存在。今天咱们就来聊聊从 RNN 到 Transformer 的演变历程一起探究为什么 “Attention Is All You Need” 为什么一开始我们用 RNN
在深度学习发展早期面对序列数据传统神经网络难以捕捉数据间的时序依赖关系而 RNN循环神经网络的出现解决了这一难题。想象一下当咱们在看一部小说的时候每一页的内容都和前面发生的事情有关理解当前情节需要结合之前的故事脉络。RNN 就像一个人在读小说每读一句话就记住重点然后带着记忆去读下一句。它通过隐藏层的循环连接按顺序处理信息一步一步传递上下文信息。
具体来说RNN 在每个时间步都会接收当前输入和上一个时间步的隐藏状态通过特定的计算方式更新隐藏状态将之前积累的 “记忆” 与新信息融合。这种机制使得 RNN 能够处理变长序列数据在语音识别、文本生成等任务中崭露头角比如在语言模型中RNN 可以根据前文预测下一个可能出现的单词在机器翻译里能按顺序将源语言逐词转换为目标语言 。 RNN 的困境与局限
虽然 RNN 开创了序列数据处理的新方向但随着研究的深入和应用场景复杂度的提升它的问题逐渐暴露出来。
首先RNN 处理数据的速度太慢。由于它必须按顺序一个时间步一个时间步地处理就像你得一句一句等着看小说不能跳着看无法并行计算。在处理长文本、长语音序列等大规模数据时这种顺序处理方式会导致计算效率极低训练时间大幅增加难以满足实时性要求高的应用场景。
其次RNN 存在严重的 “记忆力不好” 问题也就是梯度消失问题。在反向传播过程中梯度需要从序列的最后一个时间步反向传递到第一个时间步来更新网络参数。随着序列长度增加梯度在传递过程中会不断衰减就像声音在长长的隧道里传播越传越弱导致网络难以学习到长距离的依赖关系前面发生的事容易被 “忘记” 。这使得 RNN 在处理长序列时对早期信息的利用能力很差比如在生成一篇长文章时可能会出现前后逻辑不一致、忘记前文设定等问题。
此外RNN 结构的局限性还体现在难以处理复杂的语义关系它对信息的整合方式较为单一无法有效捕捉序列中不同位置信息之间的复杂关联。 Transformer 出现前的 “革命”Attention 机制
在 Transformer 横空出世之前Attention注意力机制的出现已然是深度学习领域的一场 “革命”。它的灵感其实源于人类的认知习惯就好比咱们复习的时候会把小说的精彩段落划重点。当我们阅读文本时大脑并不会机械地逐字处理而是会根据当前内容灵活地回顾其他关键句子并决定 “我现在需要关注谁”。
从技术角度来讲Attention 是一种 “加权平均” 思想。在传统的 RNN 中每一步的输出依赖上一步的隐藏状态信息传递具有很强的顺序性而 Attention 机制打破了这种局限它不再只关注上一个时间步而是能够 “纵观全局”对所有的输入进行考量并根据相关性赋予不同的权重。
以自然语言处理中的指代消解为例当句子是 “张三去了公园。后来他……”当前输入是 “他”想要知道 “他” 指的是谁Attention 机制就会去 “翻阅” 前文通过计算语义的相关性发现 “张三” 和 “他” 最相关从而赋予 “张三” 更高的权重准确理解 “他” 的指代对象 。
用数学公式来描述 Attention 的基本原理假设我们有一个 “查询” 向量 Q以及多个 “键 - 值对”其中键组成矩阵 K值组成矩阵 V。Attention 的计算过程就是要从这些 “键 - 值对” 中找出与 “查询” 最相关的内容并进行加权求和。具体公式如下 其中 是键 K 的维度除以 这一步骤被称为缩放Scaling目的是避免在计算 时由于维度过高导致数值过大进而引发梯度爆炸的问题 。通过这个公式我们可以根据 “查询” 和 “键” 之间的相似度计算出每个 “值” 对应的权重再用这些权重对 “值” 进行加权求和得到最终的输出。 Transformer 的诞生与革新
为了解决 RNN 的诸多问题Transformer 应运而生。它直接放弃了 RNN 顺序处理的模式将 Attention 机制发挥到极致其核心思想正如论文标题所言 ——“Attention Is All You Need”即仅依靠注意力机制就能高效完成序列数据处理任务 。
Transformer 引入了 Self-Attention自注意力机制这种机制可以理解为在处理一个序列中的每个词时该词都能 “看到” 序列中的其他所有词包括自己并通过计算彼此之间的注意力权重来确定在生成当前词的表示时其他词的重要程度 。同时Transformer 丢掉了循环结构采用位置编码Positional Encoding来补充顺序信息弥补了没有循环结构可能丢失的位置信息。 Transformer 的核心构件 1. Self-Attention自注意力 假设输入是由词向量组成的矩阵 XTransformer 首先通过三个不同的线性变换将 X 分别映射为三个新的矩阵查询矩阵 Q、键矩阵 K和值矩阵 V即 其中、、是可学习的权重矩阵 。
得到 Q、K、V后就可以按照 Attention 的基本公式计算自注意力 经过这一系列计算最终输出依然是与输入矩阵 X 维度相同的矩阵只不过这个矩阵中的每个元素都融合了序列中各个位置的信息且根据相关性进行了加权 。 2. 多头注意力Multi-Head Attention
为了让模型能够从不同角度捕捉信息Transformer 并没有只用一次注意力计算而是采用了多头注意力机制。它并行地进行多次通常是 h 次自注意力计算也就是多个 “头”head 。每个 “头” 都有自己独立的参数对输入进行不同的变换和计算。
对于每个 “头” i其计算过程如下 其中、、是第 i个 “头” 对应的权重矩阵 。完成所有 “头” 的计算后将各个 “头” 的输出结果拼接起来再通过一个线性变换得到最终的多头注意力输出 其中 是用于线性变换的权重矩阵 。多头注意力机制使得模型能够捕捉到更丰富的语义信息和不同层面的依赖关系 。 3. Transformer Block 的结构
Transformer 的每一层由两部分组成
(1) 首先是多头自注意力层在计算多头自注意力后采用残差连接Residual Connection将输入直接加到多头自注意力的输出上这样可以有效缓解梯度消失问题帮助网络更好地训练。残差连接后再进行 LayerNorm层归一化操作LayerNorm 的作用是对每个样本的特征进行归一化使网络训练更加稳定 。
(2) 接着是前馈神经网络Feed Forward Network, FFN它由两个线性变换和一个非线性激活函数组成对多头自注意力的输出进一步处理 。同样在前馈神经网络输出后也会进行残差连接和 LayerNorm 。 4. 位置编码Positional Encoding
由于 Transformer 没有像 RNN 那样的循环结构来体现数据的顺序所以引入了位置编码来表示序列中元素的顺序信息。位置编码是一个与输入词向量维度相同的向量通过将其加到输入的词向量上就可以为模型提供位置信息 。常用的位置编码公式如下 其中pos 表示位置 表示维度索引 是模型的维度 。通过这种方式生成的位置编码能够让模型区分不同位置的元素并且不同位置的编码之间具有一定的数学关系便于模型学习 。 为什么 “Attention Is All You Need”
Transformer 之所以能够喊出 “Attention Is All You Need” 这句口号是因为它凭借强大的注意力机制实现了对传统序列处理模型的超越
结构简化丢掉了 RNN 复杂的循环结构不再受限于顺序处理极大地简化了模型架构降低了模型训练和优化的难度 。
高效的信息交互用注意力机制完成所有信息交互每个元素都能直接关注到序列中的其他元素能够更精准地捕捉长距离依赖关系和复杂的语义关联 。
并行计算支持并行计算不再像 RNN 那样一个时间步一个时间步地处理数据能够一次性处理整个序列大幅提升了训练和推理速度尤其在处理大规模数据时优势明显 。
长文本处理能力在处理长文本时不会出现像 RNN 那样的梯度消失和长距离依赖难以捕捉的问题对长文本的处理表现更加稳定和出色 。
从 RNN 到 Attention 机制再到 Transformer深度学习领域在序列数据处理方向的每一次突破都让我们离人工智能的 “智能” 本质更近一步。Transformer 凭借其创新性和高效性开启了人工智能技术发展的新篇章也为后续更多的研究和应用奠定了坚实的基础 。 完整案例
我们来构造一个简单的字符级序列任务输入是表示两位数字加法的字符序列如 1335输出是其结果如 48。该任务可以便于模拟 Seq2Seq 场景并能直观对比模型表现。
# comments: 数据集生成脚本
import randomdef generate_example():a random.randint(0, 99)b random.randint(0, 99)x f{a:02d}{b:02d} # 输入例如 0625y str(a b) # 输出 31return x, y# 生成数据集
dataset [generate_example() for _ in range(10000)]
train_set dataset[:8000]
valid_set dataset[8000:9000]
test_set dataset[9000:]
代码实现 Seq2Seq RNN 和 Transformer 模型~
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import Dataset, DataLoader# comments: 定义字符映射
CHARS [0,1,2,3,4,5,6,7,8,9,]
PAD_IDX 0
CHAR2IDX {c:i1 for i,c in enumerate(CHARS)}
IDX2CHAR {i:c for c,i in CHAR2IDX.items()}
VOCAB_SIZE len(CHAR2IDX) 1 # 包含 PADclass SeqAdditionDataset(Dataset):def __init__(self, data):self.data datadef __len__(self):return len(self.data)def __getitem__(self, idx):x, y self.data[idx]x_idx [CHAR2IDX[c] for c in x]y_idx [CHAR2IDX[c] for c in y]return torch.tensor(x_idx), torch.tensor(y_idx)# comments: collate_fn for padding
def collate_fn(batch):xs, ys zip(*batch)xs nn.utils.rnn.pad_sequence(xs, batch_firstTrue, padding_valuePAD_IDX)ys nn.utils.rnn.pad_sequence(ys, batch_firstTrue, padding_valuePAD_IDX)return xs, ys# RNN Encoder
class RNNEncoder(nn.Module):def __init__(self, input_dim, emb_dim, hid_dim, n_layers):super().__init__()self.embedding nn.Embedding(input_dim, emb_dim, padding_idxPAD_IDX)self.rnn nn.GRU(emb_dim, hid_dim, n_layers, batch_firstTrue)def forward(self, src):embedded self.embedding(src)outputs, hidden self.rnn(embedded)return outputs, hidden# RNN Decoder
class RNNDecoder(nn.Module):def __init__(self, output_dim, emb_dim, hid_dim, n_layers):super().__init__()self.embedding nn.Embedding(output_dim, emb_dim, padding_idxPAD_IDX)self.rnn nn.GRU(emb_dim, hid_dim, n_layers, batch_firstTrue)self.fc_out nn.Linear(hid_dim, output_dim)def forward(self, input, hidden):# input: [batch]input input.unsqueeze(1)embedded self.embedding(input)output, hidden self.rnn(embedded, hidden)prediction self.fc_out(output.squeeze(1))return prediction, hidden# comments: Seq2Seq 包装
class Seq2SeqRNN(nn.Module):def __init__(self, encoder, decoder, device):super().__init__()self.encoder encoderself.decoder decoderself.device devicedef forward(self, src, trg, teacher_forcing_ratio0.5):batch_size src.size(0)trg_len trg.size(1)trg_vocab_size self.decoder.fc_out.out_featuresoutputs torch.zeros(batch_size, trg_len, trg_vocab_size).to(self.device)enc_outputs, hidden self.encoder(src)input trg[:,0]for t in range(1, trg_len):output, hidden self.decoder(input, hidden)outputs[:,t] outputteacher_force random.random() teacher_forcing_ratiotop1 output.argmax(1)input trg[:,t] if teacher_force else top1return outputs
到这里大家可以完整体验从 RNN 到 Transformer 的设计理念并通过可视化体验 Attention 带来的优势。
大家在真实项目中可以进一步结合 BERT、GPT 等预训练模型实现更强大的性能提升噢。