一、Transformer总览:从RNN到Transformer的演进、核心思想、五组件概览

说实话,我第一次接触Transformer时,心里是有点抵触的。

2017年那会儿,我还在用LSTM做机器翻译。每次训练都要等好久,梯度消失、梯度爆炸轮着来。后来Google那篇"Attention is All You Need"出来,我第一反应是:又一篇标题党吧?

结果看完论文,我沉默了。

嗯,这玩意儿确实不一样。今天咱们就来聊聊,Transformer到底是怎么从RNN的泥潭里爬出来的,它的核心思想是什么,以及那五个组件到底长啥样。

1.1 从RNN到Transformer:我们到底在解决什么问题?

先说说RNN的问题。你想想看,RNN处理序列数据时,是一个词一个词往后传的。就像你在读一句话,必须一个字一个字地看,不能跳着读。

这带来两个麻烦:

  • ——没法并行计算。每个时间步都得等前一个算完。
  • 记不住——长距离依赖搞不定。句子一长,前面的信息就丢了。

我在做文本生成项目时遇到过这种情况:模型生成到后半段,完全忘了开头说了什么。你让它写一篇200字的短文,它写到第150字就开始胡言乱语。

LSTM和GRU虽然加了门控机制,但说白了只是"缓解",不是"解决"。就像给一个漏水的桶贴创可贴——能撑一会儿,但终究不是办法。

那Transformer是怎么做的?

它直接把整个序列扔进模型,让每个词都能"看到"所有其他词。不需要一步步传递,一步到位。

核心转变:从"逐步传递"变成"全局交互"。RNN是串行的,Transformer是并行的。就这么简单。

1.2 核心思想:Attention Is All You Need

Transformer的核心思想,说白了就一句话:用注意力机制替代循环结构

什么叫注意力机制?我给你举个我自己的例子。

有一次我在看一篇技术文档,里面提到"它支持分布式训练"。这个"它"指的是什么?我得回头看前文——哦,是"PyTorch"。这个过程就是注意力:你关注当前词时,会去"看"其他相关词来理解它。

Transformer里的Self-Attention(自注意力)做的就是这件事:

  • 每个词都跟所有其他词计算相关性
  • 相关性高的词,贡献更大
  • 最终每个词都融合了全局信息

你可能会问:那位置信息怎么办?RNN天然有顺序,Transformer没有啊。

嗯,这里要注意。Transformer用了位置编码(Positional Encoding)来告诉模型每个词在句子中的位置。就像给每个词发了一个"座位号",模型就知道谁在前谁在后了。

我的经验:位置编码的设计其实挺讲究的。我早期试过直接用整数编码,效果很差。后来用了正弦余弦函数,模型才真正"理解"了位置关系。这个细节后面会细讲。

1.3 五组件概览:一张图看懂Transformer

Transformer整体结构像是一个"编码器-解码器"的沙漏。但咱们先不纠结整体,先看它的五个核心组件:

组件 作用 一句话理解
1. 自注意力机制 计算词与词之间的关联度 让每个词"看到"所有词
2. 多头注意力 从不同角度做注意力 多个"视角"同时看
3. 前馈神经网络 对每个位置做非线性变换 给每个词"思考"的空间
4. 层归一化 稳定训练过程 防止数值爆炸
5. 残差连接 缓解梯度消失 让信息"抄近道"

这五个组件,每个都值得单独花一章来讲。但今天咱们先有个整体印象。

1.4 自注意力机制:最核心的那个

自注意力是Transformer的灵魂。没有它,其他组件都是摆设。

它的计算过程其实不复杂:

  1. 每个词生成三个向量:Query(查询)、Key(键)、Value(值)
  2. 用Query跟所有Key做点积,得到注意力分数
  3. 分数经过Softmax归一化
  4. 用归一化后的分数加权求和所有Value

写成代码就是:

import torch
import torch.nn.functional as F

def scaled_dot_product_attention(Q, K, V):
    # Q, K, V: [batch_size, seq_len, d_k]
    d_k = Q.size(-1)
    scores = torch.matmul(Q, K.transpose(-2, -1)) / (d_k ** 0.5)
    attn_weights = F.softmax(scores, dim=-1)
    output = torch.matmul(attn_weights, V)
    return output, attn_weights

你看,就这么几行。但效果惊人。

避坑指南:我曾经在实现时忘了除以√d_k,结果训练直接炸了。这个缩放因子不是随便加的——当d_k很大时,点积结果会很大,Softmax会进入梯度饱和区。除以√d_k就是让方差保持为1。

1.5 多头注意力:一个不够?来八个

单头注意力只能从一个角度捕捉关系。但一个词可能有多种含义——比如"苹果"可以是水果,也可以是公司。

多头注意力就是让模型同时从多个"视角"看问题:

  • 每个头独立做自注意力
  • 头与头之间参数不共享
  • 最后把所有头的结果拼起来

我习惯用8个头。为什么是8?其实没有特别的原因,论文里用8,大家就都跟着用8。你试试4个头或16个头,效果也差不多。

1.6 前馈神经网络:给每个词一点"思考"空间

注意力机制做完后,每个词都融合了全局信息。但光有"看"还不够,还得"想"。

前馈神经网络(FFN)就是干这个的。它对每个位置独立做两次线性变换,中间加一个ReLU激活:

class FeedForward(nn.Module):
    def __init__(self, d_model, d_ff):
        super().__init__()
        self.w1 = nn.Linear(d_model, d_ff)
        self.w2 = nn.Linear(d_ff, d_model)
    
    def forward(self, x):
        return self.w2(F.relu(self.w1(x)))

d_ff通常是d_model的4倍。比如d_model=512,d_ff=2048。这个比例是经验值,我试过3倍和5倍,4倍确实最稳。

1.7 层归一化与残差连接:训练的"稳定器"

这两个组件放在一起说,因为它们经常成对出现。

残差连接:把输入直接加到输出上。这样梯度可以"抄近道"传到前面,防止深层网络梯度消失。

层归一化:对每个样本的所有特征做归一化。跟BatchNorm不同,LayerNorm不依赖batch size,更适合序列模型。

我早期做Transformer训练时,不加LayerNorm,loss曲线像过山车。加上之后,稳得像老狗。

总结一下:Transformer用自注意力替代了RNN的循环结构,用位置编码保留了顺序信息,用多头注意力扩展了"视野",用FFN增加了表达能力,用残差连接和层归一化保证了训练稳定。五个组件,缺一不可。

1.8 下一章预告

这一章咱们把Transformer的来龙去脉和五个组件过了一遍。下一章,我会手把手带你实现自注意力机制——从数学公式到可运行的PyTorch代码,一个细节都不放过。

到时候你会看到,自注意力其实没那么神秘。它就是一个加权求和,只不过权重是动态算出来的。

嗯,咱们下章见。