Transformer 从0到1:长时依赖问题的本质——梯度消失与爆炸
# Transformer 从0到1长时依赖问题的本质——梯度消失与爆炸## 引言序列模型的困境在自然语言处理、语音识别、时间序列分析等领域处理序列数据是核心任务。一个理想的序列模型不仅需要捕捉局部的语法结构如主语和动词的搭配更需要具备建模**长时依赖**的能力。所谓长时依赖指的是序列中相距较远的元素之间存在逻辑或语义上的关联。例如在句子“我出生在法国虽然我后来移居了多个国家但我仍然能说一口流利的______”中空白处的答案“法语”依赖于句子开头出现的“法国”。这两个词之间的距离可能长达数十个甚至上百个单词。在 Transformer 架构问世之前循环神经网络及其变种LSTM、GRU是处理序列数据的事实标准。RNN 的设计理念是优雅的它通过一个循环的“隐藏状态”来维护一个记忆单元理论上能够将信息从序列的起点传递到终点。然而在实际应用中RNN 在捕捉长时依赖时表现得力不从心。这背后的根本原因正是深度学习训练过程中臭名昭著的 **梯度消失** 与 **梯度爆炸** 问题。本文将深入浅出地探讨这一问题的数学本质分析传统 RNN 为何难以应对并最终揭示 Transformer 是如何通过其革命性的自注意力机制和架构设计从根本上绕开这一困境从而实现对长时依赖的高效建模。我们将从理论推导出发结合代码示例一步步构建起对 Transformer 的深刻理解。---## 第一章循环神经网络与反向传播的数学基础为了理解梯度消失与爆炸我们必须先回顾 RNN 的数学定义以及其训练算法——随时间反向传播。### 1.1 RNN 的前向传播考虑一个简单的循环神经网络Elman Network。在时间步 \( t \)输入为 \( x_t \in \mathbb{R}^{d_{\text{in}}} \)隐藏状态为 \( h_t \in \mathbb{R}^{d_{\text{hidden}}} \)输出为 \( y_t \in \mathbb{R}^{d_{\text{out}}} \)。RNN 的核心方程如下\[h_t \tanh(W_{hh} h_{t-1} W_{xh} x_t b_h)\]\[y_t \text{softmax}(W_{hy} h_t b_y)\]这里\( W_{hh} \) 是状态-状态权重矩阵循环核\( W_{xh} \) 是输入-状态权重矩阵\( W_{hy} \) 是状态-输出权重矩阵\( b_h \) 和 \( b_y \) 是偏置项。激活函数通常使用 \( \tanh \) 或 ReLU。直观来看\( h_t \) 聚合了当前输入 \( x_t \) 和过去所有信息 \( h_{t-1} \) 的压缩表示。这种递归结构使得信息能够沿着时间步传递。### 1.2 随时间反向传播RNN 的训练依赖于反向传播算法。由于网络在时间维度上展开我们将此过程称为**随时间反向传播**。假设我们有一个长度为 \( T \) 的序列定义损失函数 \( L \) 为每个时间步的损失之和\[L \sum_{t1}^{T} L_t(y_t, \hat{y}_t)\]为了更新权重 \( W_{hh} \)我们需要计算损失函数关于它的梯度。关键在于\( W_{hh} \) 在每一个时间步都被共享使用并且它对后续所有时间步的损失都有贡献。根据链式法则\[\frac{\partial L}{\partial W_{hh}} \sum_{t1}^{T} \frac{\partial L_t}{\partial W_{hh}}\]而 \( \frac{\partial L_t}{\partial W_{hh}} \) 的计算需要考虑从时间步 \( t \) 回溯到时间步 \( 1 \) 的路径。在时间步 \( t \)隐藏状态 \( h_t \) 依赖于 \( h_{t-1} \)而 \( h_{t-1} \) 又依赖于 \( h_{t-2} \)依此类推。因此对于 \( L_t \)其关于 \( W_{hh} \) 的梯度可以写为\[\frac{\partial L_t}{\partial W_{hh}} \sum_{k1}^{t} \frac{\partial L_t}{\partial h_t} \frac{\partial h_t}{\partial h_k} \frac{\partial^ h_k}{\partial W_{hh}}\]这里 \( \frac{\partial^ h_k}{\partial W_{hh}} \) 表示将 \( h_{k-1} \) 视为常数时的瞬时梯度。而关键的项是 \( \frac{\partial h_t}{\partial h_k} \)它代表了隐藏状态在时间步 \( k \) 对时间步 \( t \) 的影响。这又是一个链式乘积\[\frac{\partial h_t}{\partial h_k} \prod_{jk1}^{t} \frac{\partial h_j}{\partial h_{j-1}}\]其中\( \frac{\partial h_j}{\partial h_{j-1}} \) 是隐藏状态转移的雅可比矩阵\[\frac{\partial h_j}{\partial h_{j-1}} \text{diag}(\tanh(W_{hh}h_{j-1} W_{xh}x_j b_h)) \cdot W_{hh}\]这个公式揭示了梯度传递的本质。为了计算远距离的依赖即 \( t - k \) 很大我们需要将一系列雅可比矩阵相乘。---## 第二章梯度消失与爆炸的数学本质现在我们深入剖析为什么连续的矩阵乘积会导致梯度的不稳定性。这主要取决于雅可比矩阵 \( \frac{\partial h_j}{\partial h_{j-1}} \) 的范数。### 2.1 数学推导假设我们使用 \( \tanh \) 或 Sigmoid 作为激活函数。这些函数具有一个共同特点它们的导数在大多数区域都小于等于 1。对于 \( \tanh \)导数 \( \tanh(x) 1 - \tanh^2(x) \)取值范围在 (0, 1] 之间。对于 Sigmoid导数 \( \sigma(x) \sigma(x)(1-\sigma(x)) \)取值范围在 (0, 0.25] 之间。设激活函数导数的最大值为 \( \gamma \)。对于 \( \tanh \)\( \gamma 1 \)对于 Sigmoid\( \gamma 0.25 \)。同时考虑权重矩阵 \( W_{hh} \)。假设我们对其特征值进行谱分析。设 \( \| \frac{\partial h_j}{\partial h_{j-1}} \| \) 表示矩阵的范数例如谱范数。我们有\[\| \frac{\partial h_j}{\partial h_{j-1}} \| \le \| \text{diag}(\tanh(\cdot)) \| \cdot \| W_{hh} \| \le \gamma \cdot \| W_{hh} \|\]现在考虑从时间步 \( k \) 到 \( t \) 的梯度传播项\[\| \frac{\partial h_t}{\partial h_k} \| \le (\gamma \cdot \| W_{hh} \|)^{t-k}\]- **梯度爆炸**如果 \( \| W_{hh} \| \frac{1}{\gamma} \)那么当 \( t-k \) 很大时范数呈指数级增长导致梯度爆炸。这意味着参数的微小更新会导致隐藏状态发生剧烈变化训练过程不稳定梯度值可能变成 NaN。- **梯度消失**如果 \( \| W_{hh} \| \frac{1}{\gamma} \)那么当 \( t-k \) 很大时范数呈指数级衰减趋近于 0。这意味着远距离的梯度信号对权重的更新几乎没有贡献网络无法学习到长时依赖。### 2.2 更细致的分析特征值的作用即使 \( \| W_{hh} \| \) 恰好使得谱半径特征值绝对值的最大值 \( \rho(W_{hh}) 1 \)梯度消失问题依然可能发生。这是因为激活函数的导数总是小于 1其乘积会迅速衰减。实际上标准的 RNN 在面对超过 10 个时间步的依赖时梯度消失问题就会变得非常严重以至于远距离的信息无法影响当前的输出预测。### 2.3 代码验证梯度消失现象让我们通过一个简单的代码示例来直观感受梯度消失。我们将构建一个极简的 RNN 单元并观察反向传播时梯度随回溯时间步长的变化。pythonimport torchimport torch.nn as nnimport torch.optim as optimimport matplotlib.pyplot as pltimport numpy as np# 设置随机种子torch.manual_seed(42)# 定义简单的RNN单元class SimpleRNNCell(nn.Module):def __init__(self, input_size, hidden_size):super().__init__()self.hidden_size hidden_sizeself.W_xh nn.Linear(input_size, hidden_size, biasFalse)self.W_hh nn.Linear(hidden_size, hidden_size, biasFalse)self.activation nn.Tanh() # 使用tanh激活函数def forward(self, x, h_prev):# x: (batch, input_size)# h_prev: (batch, hidden_size)h_new self.activation(self.W_xh(x) self.W_hh(h_prev))return h_new# 参数设置batch_size 1input_size 10hidden_size 10seq_len 50# 初始化模型和输入model SimpleRNNCell(input_size, hidden_size)x torch.randn(batch_size, input_size)# 初始化隐藏状态h torch.zeros(batch_size, hidden_size)# 存储所有隐藏状态hidden_states [h]# 前向传播记录每个时间步的隐藏状态for t in range(seq_len):h model(x, h) # 注意这里重复使用同一个输入x仅为了模拟时间步hidden_states.append(h)# 为了计算梯度我们定义一个损失函数例如最后一个隐藏状态的L2范数loss hidden_states[-1].norm()# 反向传播loss.backward()# 观察每个时间步的梯度# 注意由于W_hh在每一步都被使用我们通过hook来获取梯度gradients []def hook_fn(grad):gradients.append(grad.norm().item())# 注册hookhandle model.W_hh.weight.register_hook(hook_fn)# 重新计算梯度因为上面已经backward过了所以需要重新做# 清零梯度model.zero_grad()# 重新前向和反向h torch.zeros(batch_size, hidden_size)hidden_states [h]for t in range(seq_len):h model(x, h)hidden_states.append(h)loss hidden_states[-1].norm()loss.backward()handle.remove()# 绘制梯度范数随时间步的变化plt.figure(figsize(10, 6))plt.plot(range(len(gradients)), gradients, markero)plt.xlabel(Time Step (t))plt.ylabel(Gradient Norm of W_hh)plt.title(Gradient Vanishing in RNN: Gradient Norm Decays Exponentially)plt.yscale(log)plt.grid(True)plt.show()**结果分析**运行上述代码我们会发现梯度范数随着时间步的增加呈指数级下降。在对数坐标下这表现为一条近似直线。这清晰地展示了梯度消失现象远距离时间步的梯度几乎为零网络无法学习到序列早期和晚期之间的依赖关系。---## 第三章LSTM 的救赎与局限长短期记忆网络LSTM的提出正是为了应对梯度消失问题。LSTM 通过引入“门控机制”和“细胞状态”设计了一条“信息高速公路”使得梯度能够更稳定地流动。### 3.1 LSTM 的核心思想LSTM 的核心是细胞状态 \( C_t \)它贯穿整个时间轴。细胞状态的更新由三个门控制遗忘门 \( f_t \)、输入门 \( i_t \)、输出门 \( o_t \)。其核心更新方程如下\[f_t \sigma(W_f \cdot [h_{t-1}, x_t] b_f)\]\[i_t \sigma(W_i \cdot [h_{t-1}, x_t] b_i)\]\[\tilde{C}_t \tanh(W_C \cdot [h_{t-1}, x_t] b_C)\]\[C_t f_t \odot C_{t-1} i_t \odot \tilde{C}_t\]\[o_t \sigma(W_o \cdot [h_{t-1}, x_t] b_o)\]\[h_t o_t \odot \tanh(C_t)\]### 3.2 梯度流动的数学分析LSTM 有效性的关键在于细胞状态 \( C_t \) 的更新公式中的加法操作\[C_t f_t \odot C_{t-1} i_t \odot \tilde{C}_t\]当计算梯度 \( \frac{\partial C_t}{\partial C_{t-1}} \) 时我们得到\[\frac{\partial C_t}{\partial C_{t-1}} \text{diag}(f_t) \dots\]其中 ... 表示涉及输入门和候选状态的项。**关键在于\( f_t \) 是一个介于 0 和 1 之间的向量**。虽然它仍然可能导致梯度衰减如果 \( f_t \) 很小但只要遗忘门的值接近 1梯度就可以几乎无损地传递。这打破了传统 RNN 中连续矩阵相乘导致的指数级衰减问题。此外LSTM 的设计使得梯度可以绕过激活函数 \( \tanh \) 和门控的复合函数通过加法路径直接流动大大缓解了梯度消失。### 3.3 LSTM 的局限顺序处理的瓶颈尽管 LSTM 在解决长时依赖方面取得了巨大成功但它仍然存在两个根本性局限1. **顺序计算**LSTM 必须按时间步顺序计算\( h_t \) 依赖于 \( h_{t-1} \)。这种天然的序列依赖性阻碍了并行计算导致训练速度慢尤其是对于长序列。2. **信息压缩**LSTM 将所有历史信息压缩到一个固定维度的隐藏状态 \( h_t \) 中。对于非常长的序列这种压缩必然导致信息丢失。它无法像后来的 Transformer 那样让序列中的任意两个位置直接交互。---## 第四章Transformer 的革命——绕开梯度问题Transformer 架构在 2017 年由 Vaswani 等人提出它完全摒弃了循环结构转而使用**自注意力机制**。这一变革不仅解决了并行化问题更从根本上绕开了循环网络固有的梯度消失与爆炸困境。### 4.1 自注意力机制直接建立长距离依赖自注意力的核心思想是在计算序列中某个位置的表示时让它能够直接“关注”序列中的所有其他位置并计算它们之间的相关性权重。这个过程可以形式化如下给定输入序列 \( X \in \mathbb{R}^{T \times d} \)我们通过三个可学习的权重矩阵 \( W_Q, W_K, W_V \in \mathbb{R}^{d \times d_k} \) 将其映射为查询矩阵 \( Q \)、键矩阵 \( K \)、值矩阵 \( V \)\[Q X W_Q, \quad K X W_K, \quad V X W_V\]然后计算注意力得分矩阵 \( A \)\[A \text{softmax}\left( \frac{QK^T}{\sqrt{d_k}} \right)\]最后输出为\[\text{Attention}(Q, K, V) A V\]**关键点**在计算输出 \( Z \) 的过程中任意位置 \( i \) 的表示 \( Z_i \) 是所有位置 \( j \) 的 \( V_j \) 的加权和权重由 \( Q_i \) 和 \( K_j \) 的点积决定。这意味着即使两个位置相距遥远例如第 1 个词和第 100 个词它们在单层自注意力中也能直接交互其路径长度为 **1**。### 4.2 为什么 Transformer 没有梯度消失/爆炸问题我们来分析 Transformer 中梯度流动的路径1. **无循环结构**Transformer 的前向传播不包含循环。它是一个从输入到输出的非循环图DAG。在反向传播中梯度沿着 DAG 直接反向传播不需要通过一系列的时间步矩阵相乘。2. **残差连接**Transformer 的每个子层注意力层或前馈网络层都包含一个残差连接output LayerNorm(x Sublayer(x))。这种结构使得梯度可以绕过子层的非线性变换直接通过“恒等路径”流动。这类似于 LSTM 的加法操作但更加彻底和普遍。3. **层归一化**层归一化LayerNorm有助于稳定每一层的激活值分布避免了在训练过程中因激活值过大或过小导致的梯度不稳定问题。4. **梯度路径长度恒定**在 Transformer 中从输出到输入的任何位置的梯度路径长度都是相同的等于层数与序列长度无关。在 RNN 中从输出到远距离输入的路径长度正比于距离。因此Transformer 完全规避了传统 RNN 中因时间步展开导致的指数级梯度爆炸或消失问题。它使得训练非常深的网络如 GPT-3 的 96 层成为可能且能够处理长达数千甚至数万个 token 的序列。### 4.3 位置编码的引入由于 Transformer 的自注意力机制本身是“置换不变的”即它不会考虑词语在序列中的顺序。为了注入位置信息Transformer 在输入嵌入中加入**位置编码**。原始论文中使用的是正弦和余弦函数\[PE_{(pos, 2i)} \sin\left( \frac{pos}{10000^{2i/d_{\text{model}}}} \right)\]\[PE_{(pos, 2i1)} \cos\left( \frac{pos}{10000^{2i/d_{\text{model}}}} \right)\]这种编码使得模型能够利用位置之间的相对关系。---## 第五章从零开始实现一个迷你 Transformer为了加深理解我们将从零开始使用 PyTorch 构建一个简化的 Transformer 模型。我们将实现多头注意力、位置编码、前馈网络等核心组件。pythonimport torchimport torch.nn as nnimport torch.nn.functional as Fimport mathclass PositionalEncoding(nn.Module):位置编码def __init__(self, d_model, max_len5000):super().__init__()# 创建位置编码矩阵 (max_len, d_model)pe torch.zeros(max_len, d_model)position torch.arange(0, max_len, dtypetorch.float).unsqueeze(1)div_term torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model))pe[:, 0::2] torch.sin(position * div_term)pe[:, 1::2] torch.cos(position * div_term)pe pe.unsqueeze(0) # (1, max_len, d_model)self.register_buffer(pe, pe)def forward(self, x):# x: (batch, seq_len, d_model)return x self.pe[:, :x.size(1), :]class MultiHeadAttention(nn.Module):多头注意力机制def __init__(self, d_model, num_heads):super().__init__()assert d_model % num_heads 0self.d_model d_modelself.num_heads num_headsself.d_k d_model // num_heads# 线性变换层self.W_q nn.Linear(d_model, d_model)self.W_k nn.Linear(d_model, d_model)self.W_v nn.Linear(d_model, d_model)self.W_o nn.Linear(d_model, d_model)def scaled_dot_product_attention(self, Q, K, V, maskNone):# Q, K, V: (batch, num_heads, seq_len, d_k)scores torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(self.d_k)if mask is not None:scores scores.masked_fill(mask 0, -1e9)attention_weights F.softmax(scores, dim-1)output torch.matmul(attention_weights, V)return output, attention_weightsdef split_heads(self, x):# x: (batch, seq_len, d_model)batch_size, seq_len, _ x.size()return x.view(batch_size, seq_len, self.num_heads, self.d_k).transpose(1, 2)def combine_heads(self, x):# x: (batch, num_heads, seq_len, d_k)batch_size, _, seq_len, _ x.size()return x.transpose(1, 2).contiguous().view(batch_size, seq_len, self.d_model)def forward(self, Q, K, V, maskNone):# 线性变换并分割多头Q self.split_heads(self.W_q(Q))K self.split_heads(self.W_k(K))V self.split_heads(self.W_v(V))# 计算注意力attn_output, _ self.scaled_dot_product_attention(Q, K, V, mask)# 合并多头并进行最终线性变换output self.W_o(self.combine_heads(attn_output))return outputclass FeedForward(nn.Module):前馈网络def __init__(self, d_model, d_ff):super().__init__()self.linear1 nn.Linear(d_model, d_ff)self.linear2 nn.Linear(d_ff, d_model)self.relu nn.ReLU()def forward(self, x):return self.linear2(self.relu(self.linear1(x)))class EncoderLayer(nn.Module):Transformer 编码器层def __init__(self, d_model, num_heads, d_ff, dropout0.1):super().__init__()self.self_attention MultiHeadAttention(d_model, num_heads)self.feed_forward FeedForward(d_model, d_ff)self.norm1 nn.LayerNorm(d_model)self.norm2 nn.LayerNorm(d_model)self.dropout nn.Dropout(dropout)def forward(self, x, maskNone):# 多头自注意力 残差连接 层归一化attn_output self.self_attention(x, x, x, mask)x self.norm1(x self.dropout(attn_output))# 前馈网络 残差连接 层归一化ff_output self.feed_forward(x)x self.norm2(x self.dropout(ff_output))return xclass TransformerEncoder(nn.Module):Transformer 编码器def __init__(self, vocab_size, d_model, num_heads, num_layers, d_ff, max_len, dropout0.1):super().__init__()self.embedding nn.Embedding(vocab_size, d_model)self.positional_encoding PositionalEncoding(d_model, max_len)self.layers nn.ModuleList([EncoderLayer(d_model, num_heads, d_ff, dropout)for _ in range(num_layers)])self.dropout nn.Dropout(dropout)def forward(self, x, maskNone):# x: (batch, seq_len)seq_len x.size(1)# 嵌入 位置编码x self.embedding(x) * math.sqrt(self.d_model) # 缩放x self.positional_encoding(x)x self.dropout(x)# 通过编码器层for layer in self.layers:x layer(x, mask)return x# 示例创建一个小型Transformerif __name__ __main__:# 超参数vocab_size 10000d_model 512num_heads 8num_layers 6d_ff 2048max_len 100batch_size 32seq_len 50# 创建模型model TransformerEncoder(vocab_size, d_model, num_heads, num_layers, d_ff, max_len)# 创建假数据input_ids torch.randint(0, vocab_size, (batch_size, seq_len))# 前向传播output model(input_ids)print(f输入形状: {input_ids.shape})print(f输出形状: {output.shape})print(f模型参数数量: {sum(p.numel() for p in model.parameters()):,})### 5.1 代码解读1. **PositionalEncoding**为每个位置的嵌入添加正弦波位置信息使模型感知序列顺序。2. **MultiHeadAttention**这是核心。scaled_dot_product_attention 函数计算了注意力权重它允许所有位置两两交互。多头机制允许模型从不同的表示子空间捕捉信息。3. **EncoderLayer**展示了 Transformer 的标准构建块多头注意力 残差连接 层归一化前馈网络 残差连接 层归一化。残差连接是梯度高效流动的关键。4. **TransformerEncoder**整合了嵌入层、位置编码和多个编码器层。### 5.2 训练稳定性验证我们可以通过一个简单的实验来验证 Transformer 在反向传播时梯度的稳定性。相比于 RNNTransformer 的梯度范数不会随序列长度的增加而发生指数级变化。python# 接上面的代码添加训练步骤的简单验证# 创建一个模拟的损失函数比如输出序列的某些统计量optimizer torch.optim.Adam(model.parameters(), lr0.001)# 记录梯度范数grad_norms []for step in range(10): # 模拟10个训练步optimizer.zero_grad()input_ids torch.randint(0, vocab_size, (batch_size, seq_len))output model(input_ids)# 定义一个简单的损失函数例如输出的均方误差到某个目标target torch.randn_like(output)loss F.mse_loss(output, target)loss.backward()# 计算所有参数的梯度范数total_norm 0for p in model.parameters():if p.grad is not None:param_norm p.grad.data.norm(2)total_norm param_norm.item() ** 2total_norm total_norm ** 0.5grad_norms.append(total_norm)optimizer.step()if step % 2 0:print(fStep {step}, Loss: {loss.item():.4f}, Grad Norm: {total_norm:.4f})# 绘制梯度范数变化plt.figure(figsize(10, 4))plt.plot(grad_norms)plt.xlabel(Training Step)plt.ylabel(Gradient Norm)plt.title(Gradient Stability in Transformer)plt.grid(True)plt.show()在多次运行中梯度范数通常保持在一个相对稳定的范围内既不会指数级爆炸也不会归零。这验证了 Transformer 架构在训练稳定性上的优越性。---## 第六章总结与展望### 6.1 核心回顾1. **RNN 的困境**循环结构的本质导致了在反向传播时需要将一系列雅可比矩阵相乘。若矩阵范数小于 1则梯度消失长时依赖无法学习若大于 1则梯度爆炸训练不稳定。2. **LSTM 的缓解**通过门控机制和细胞状态上的加法操作LSTM 为梯度提供了一条“高速公路”有效缓解了梯度消失但仍受限于顺序计算和信息压缩。3. **Transformer 的革命**- **架构上**完全摒弃循环采用自注意力机制使任意两位置间的路径长度为常数。- **训练上**结合残差连接、层归一化、非循环计算图从根本上消除了梯度消失与爆炸的根源。- **效果上**实现了前所未有的并行训练能力能够高效处理超长序列成为大语言模型的基础。### 6.2 进一步的思考尽管 Transformer 解决了长时依赖的训练问题但随着上下文窗口的不断增长如 100k、1M tokens其计算复杂度 \( O(T^2) \) 成为了新的瓶颈。这使得 Transformer 在处理“无限长”序列时仍面临挑战。近年来针对这一问题的研究层出不穷例如- **稀疏注意力**如 Longformer、BigBird将全连接注意力限制为局部窗口加少量全局 token将复杂度降至 \( O(T) \)。- **线性注意力**如 Performer、Linformer通过核技巧或低秩近似将复杂度降至线性。- **状态空间模型**如 Mamba重新引入了“状态”的概念但通过结构化状态空间实现了线性复杂度的长距离建模并避免了传统 RNN 的梯度问题。### 6.3 结语从 RNN 的梯度困境到 LSTM 的门控救赎再到 Transformer 的架构革命这是一段深刻反映深度学习核心挑战与解决思路的旅程。理解梯度消失与爆炸的本质不仅是掌握 Transformer 工作原理的关键更是理解和设计未来序列模型的基础。当我们面对“为什么 Transformer 如此强大”这一问题时最根本的答案之一便是它让信息能够在序列中自由、高效、稳定地流动而不受距离的束缚。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2470331.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!