news 2026/7/8 11:41:53

Transformer 到底是什么?——从零理解大模型的核心基石引言

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Transformer 到底是什么?——从零理解大模型的核心基石引言

如果你关注 AI,一定听过这些词:GPT-4、ChatGPT、BERT、Claude、LLaMA。这些模型名字各不相同,但它们的底层架构都指向同一个东西——Transformer

2017 年,Google 团队在论文《Attention Is All You Need》中首次提出 Transformer。当时没人能预料到,这篇论文会彻底改变人工智能的走向。ChatGPT 能"听懂人话"、Midjourney 能"画图"、Sora 能"生成视频",底层都离不开 Transformer 的思想

这篇文章会带你从零理解 Transformer 的每一个组件,并用 Python 代码亲手实现一个迷你版 Transformer


一、Transformer 之前,世界是什么样?

1.1 RNN 的困境

在 Transformer 出现之前,处理文本主要靠RNN(循环神经网络)及其变体 LSTM、GRU。

Python

# RNN 的工作方式:一个词一个词地"啃" """ 输入句子:"我 爱 中国" RNN 处理流程: 第1步:读 "我" → 产生隐藏状态 h1 第2步:读 "爱" + h1 → 产生隐藏状态 h2 第3步:读 "中国" + h2 → 产生隐藏状态 h3 """

RNN 的致命缺陷

Python

# 问题1:无法并行计算 # 必须等第1个词处理完,才能处理第2个词 # 一个 1000 词的句子,要串行跑 1000 步 # 问题2:长距离依赖丢失 # 句子开头的信息,传到句子末尾时已经"衰减"得差不多了 sentence = "这只猫,它毛色纯白、眼睛湛蓝、性格温顺、喜欢晒太阳、爱吃三文鱼...(中间100个词)...非常可爱" # RNN 读到"非常可爱"时,已经很难记住主语是"猫"了

1.2 Transformer 的革命性改进

Transformer 的核心思想就一句话:让每个词都能直接"看到"句子中所有其他词

Plain Text

RNN 看句子: 我 → 爱 → 中国 (串行,像排队传递) Transformer: 我 ↔ 爱 ↔ 中国 (并行,像开会讨论)

这带来了两个巨大优势

  • 并行计算:所有词同时处理,训练速度大幅提升
  • 长距离依赖:无论词和词之间隔多远,都能直接建立联系

二、Transformer 的总体架构

Transformer 由编码器(Encoder)解码器(Decoder)两部分组成:

Plain Text

┌─────────────────────┐ 输入文本 │ Encoder 编码器 │ "我 爱 中国" ──→ │ (理解输入什么意思) │ └──────────┬──────────┘ │ 编码后的语义表示 ↓ ┌─────────────────────┐ │ Decoder 解码器 │ ──→ 输出文本 │ (生成输出) │ "I love China" └─────────────────────┘
  • GPT 系列只用了解码器(所以叫 GPT = Generative Pre-trained Transformer)
  • BERT 只用了编码器(所以适合做"理解"类任务,如分类、问答)

下面我们重点拆解编码器,因为它是所有 Transformer 变体的核心。


三、编码器的核心组件:逐层拆解

3.1 第一层:输入嵌入(Input Embedding)

问题:计算机不认识"中国"这两个字,它只认识数字。怎么办?

方案:把每个词转换成一个固定长度的向量(一串数字,比如 512 个浮点数)。

Python

import torch import torch.nn as nn import math # ============================================ # 1. 输入嵌入层:把词变成数字向量 # ============================================ class InputEmbedding(nn.Module): """ 将每个词映射为一个稠密向量。 比如:"中国" → [0.12, -0.85, 0.33, ..., 0.67](512维) """ def __init__(self, vocab_size: int, d_model: int): """ vocab_size: 词汇表大小(比如 50000 个词) d_model: 每个词对应的向量维度(论文中是 512) """ super().__init__() # nn.Embedding 就是一个查找表: # 输入:词的编号(比如 3721) # 输出:这个词对应的 512 维向量 self.embedding = nn.Embedding(vocab_size, d_model) self.d_model = d_model def forward(self, x): # x 的形状: (batch_size, seq_len) # 比如: (32, 100) → 32个句子,每句100个词 # 输出形状: (batch_size, seq_len, d_model) # 比如: (32, 100, 512) → 每个词都变成了512维向量 # 论文中把嵌入向量乘以 sqrt(d_model),为了让数值稳定 return self.embedding(x) * math.sqrt(self.d_model) # 测试一下 vocab_size = 10000 # 假设词汇表有 10000 个词 d_model = 512 # 每个词表示成 512 维向量 embedding_layer = InputEmbedding(vocab_size, d_model) # 模拟输入:2 个句子,每句 10 个词(每个词用数字编号表示) sample_input = torch.randint(0, vocab_size, (2, 10)) embedded = embedding_layer(sample_input) print(f"输入形状: {sample_input.shape}") # torch.Size([2, 10]) print(f"嵌入后形状: {embedded.shape}") # torch.Size([2, 10, 512]) print(f"解释:2个句子,每句10个词,每个词变成512维向量 ✅")

3.2 第二层:位置编码(Positional Encoding)

关键问题:Transformer 是并行处理所有词的,它不知道"我爱你"和"你爱我"有什么区别!

方案:给每个位置(第1个词、第2个词...)加上一个独特的"位置签名"

Python

# ============================================ # 2. 位置编码:告诉模型每个词在句子的第几个位置 # ============================================ class PositionalEncoding(nn.Module): """ 使用正弦和余弦函数生成位置编码。 每个位置都有一个独特的"位置签名"向量, 直接加到词向量上,让模型知道词的顺序。 """ def __init__(self, d_model: int, max_len: int = 5000): """ d_model: 嵌入维度(512) max_len: 支持的最大句子长度 """ super().__init__() # 创建一个 (max_len, d_model) 的矩阵,存放所有位置编码 pe = torch.zeros(max_len, d_model) # position: 每个位置的编号 [0, 1, 2, ..., max_len-1] position = torch.arange(0, max_len).unsqueeze(1).float() # div_term: 用于控制正弦/余弦的频率 # 偶数维度用 sin,奇数维度用 cos div_term = torch.exp( torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model) ) # 偶数维度:sin pe[:, 0::2] = torch.sin(position * div_term) # 奇数维度:cos pe[:, 1::2] = torch.cos(position * div_term) # 增加 batch 维度,方便后续直接相加 pe = pe.unsqueeze(0) # (1, max_len, d_model) # 注册为 buffer(不参与训练,但会保存到模型中) self.register_buffer('pe', pe) def forward(self, x): """ x 形状: (batch_size, seq_len, d_model) 把位置编码加到词向量上 """ # x + pe 就是"含有位置信息的词向量" # 比如:"我"在位置0的向量 = 原始"我"的向量 + 位置0的编码 x = x + self.pe[:, :x.size(1), :] return x # 测试位置编码 pos_encoder = PositionalEncoding(d_model=512) sample = torch.zeros(2, 10, 512) # 2个句子,每句10个词 pos_encoded = pos_encoder(sample) print(f"位置编码后形状: {pos_encoded.shape}") # torch.Size([2, 10, 512]) print(f"位置编码已加入,不同位置的词有了不同的'位置签名' ✅")

3.3 第三层:自注意力机制(Self-Attention)—— 核心中的核心

这才是 Transformer 的"灵魂"

直观理解

读这句话:"太可爱了,我忍不住想摸"

人类读的时候,大脑会自动把"它"和前面的"猫"联系起来。自注意力机制就是让模型学会这种"关联"

三个关键概念:Q、K、V

Python

# 把自注意力想象成"搜索引擎": # # Q(Query,查询):你想搜什么? —— "我正在处理哪个词?" # K(Key,键): 被搜索的内容标签 —— "其他词的特征是什么?" # V(Value,值): 被搜索的内容本身 —— "其他词的实际内容是什么?" # # 流程: # Q 和每个 K 计算相似度 → 得到"注意力分数" # 注意力分数 × V → 加权求和 → 得到"这个词应该关注哪些其他词"
代码实现

Python

# ============================================ # 3. 自注意力机制:Transformer 的灵魂 # ============================================ class SelfAttention(nn.Module): """ 缩放点积注意力(Scaled Dot-Product Attention) 公式:Attention(Q, K, V) = softmax(Q·K^T / sqrt(d_k)) · V """ def __init__(self, d_model: int, n_heads: int): """ d_model: 模型维度(512) n_heads: 多头注意力的头数(8) """ super().__init__() self.d_model = d_model self.n_heads = n_heads self.d_k = d_model // n_heads # 每个头的维度 = 512 / 8 = 64 # 三个线性变换层:把输入分别映射成 Q、K、V self.W_q = nn.Linear(d_model, d_model) # 生成 Query self.W_k = nn.Linear(d_model, d_model) # 生成 Key self.W_v = nn.Linear(d_model, d_model) # 生成 Value # 最终的输出映射 self.W_o = nn.Linear(d_model, d_model) def forward(self, x, mask=None): """ x 形状: (batch_size, seq_len, d_model) 返回: (batch_size, seq_len, d_model) """ batch_size, seq_len, _ = x.shape # ===== 第1步:生成 Q、K、V ===== # 把输入 x 通过三个不同的线性变换,得到 Q、K、V Q = self.W_q(x) # (batch, seq_len, d_model) K = self.W_k(x) # (batch, seq_len, d_model) V = self.W_v(x) # (batch, seq_len, d_model) # ===== 第2步:拆成多头 ===== # 把 d_model 拆成 n_heads 个头,每个头独立计算注意力 # (batch, seq_len, d_model) → (batch, n_heads, seq_len, d_k) Q = Q.view(batch_size, seq_len, self.n_heads, self.d_k).transpose(1, 2) K = K.view(batch_size, seq_len, self.n_heads, self.d_k).transpose(1, 2) V = V.view(batch_size, seq_len, self.n_heads, self.d_k).transpose(1, 2) # ===== 第3步:计算注意力分数 ===== # Q 和 K 做点积,得到"每个词对其他词的关注度" # Q @ K^T → (batch, n_heads, seq_len, seq_len) attention_scores = torch.matmul(Q, K.transpose(-2, -1)) # 缩放!除以 sqrt(d_k) 防止点积结果太大,导致 softmax 梯度消失 attention_scores = attention_scores / math.sqrt(self.d_k) # 如果有 mask(比如 padding 的位置),把对应位置设为 -∞ # 这样 softmax 之后这些位置的权重就是 0 if mask is not None: attention_scores = attention_scores.masked_fill( mask == 0, float('-inf') ) # ===== 第4步:softmax 得到注意力权重 ===== # 每个词对各个词的关注度,加起来等于 1 attention_weights = torch.softmax(attention_scores, dim=-1) # ===== 第5步:加权求和 ===== # 用注意力权重 × V,得到"包含上下文的词表示" # (batch, n_heads, seq_len, d_k) output = torch.matmul(attention_weights, V) # ===== 第6步:合并多头 ===== # (batch, n_heads, seq_len, d_k) → (batch, seq_len, d_model) output = output.transpose(1, 2).contiguous().view( batch_size, seq_len, self.d_model ) # ===== 第7步:最终线性映射 ===== output = self.W_o(output) return output # 测试自注意力 attention = SelfAttention(d_model=512, n_heads=8) sample = torch.randn(2, 10, 512) # 2个句子,每句10个词,512维 output = attention(sample) print(f"输入形状: {sample.shape}") # torch.Size([2, 10, 512]) print(f"输出形状: {output.shape}") # torch.Size([2, 10, 512]) print(f"每个词都"看"到了句子中所有其他词,并融合了上下文信息 ✅")
直观可视化:词与词之间学到了什么

Python

# 假设句子是:"我 爱 中国" # 注意力矩阵可能是这样的(每个值表示"行"对"列"的关注度): """ 我 爱 中国 我 0.30 0.20 0.50 ← "我" 最关注 "中国"("我"是谁?我是"中国人") 爱 0.25 0.15 0.60 ← "爱" 最关注 "中国"(爱什么?爱"中国") 中国 0.40 0.35 0.25 ← "中国" 最关注 "我"(什么跟"中国"相关?"我") """

3.4 多头注意力(Multi-Head Attention)

为什么要多头?

一个头可能只关注"语法关系",另一个头关注"语义关系",还有一个关注"位置关系"。 多个头 = 从多个角度同时理解句子。

Python

# 前面的 SelfAttention 类已经实现了多头注意力 # 关键代码就是这一行: # Q = Q.view(batch_size, seq_len, self.n_heads, self.d_k).transpose(1, 2) # 理解:把 512 维的向量切成 8 份,每份 64 维 # 每个头独立计算注意力,最后再把 8 个头的输出拼回 512 维 # 这就是论文标题的含义: # "Attention Is All You Need" = 注意力机制就是全部你需要的东西

3.5 前馈网络(Feed-Forward Network)

注意力层让词之间互相"交流",前馈网络则让每个词"独立思考"

Python

# ============================================ # 4. 前馈网络:每个词独立思考,增加模型的表达能力 # ============================================ class FeedForward(nn.Module): """ 两层全连接网络 + ReLU 激活函数 公式:FFN(x) = ReLU(x·W1 + b1)·W2 + b2 先升维(d_model → d_ff,比如 512 → 2048), 再降维(d_ff → d_model), 中间用 ReLU 激活。 这个"先膨胀再收缩"的结构,让模型有更强的表达能力。 """ def __init__(self, d_model: int, d_ff: int, dropout: float = 0.1): """ d_model: 模型维度(512) d_ff: 前馈网络内部维度(2048,论文中设为 4 倍 d_model) """ super().__init__() self.linear1 = nn.Linear(d_model, d_ff) # 512 → 2048,升维 self.linear2 = nn.Linear(d_ff, d_model) # 2048 → 512,降维 self.dropout = nn.Dropout(dropout) # 防止过拟合 def forward(self, x): # x: (batch, seq_len, d_model) x = self.linear1(x) # 升维到 2048 x = torch.relu(x) # 非线性激活 x = self.dropout(x) # 随机丢弃一些神经元 x = self.linear2(x) # 降维回 512 return x # 测试 ff = FeedForward(d_model=512, d_ff=2048) sample = torch.randn(2, 10, 512) output = ff(sample) print(f"FFN 输入: {sample.shape} → 输出: {output.shape}") print(f"512→2048→512,先扩展再压缩,增强了模型表达能力 ✅")

3.6 残差连接 + 层归一化(Add & Norm)

层层叠加,够深才够强——但越深越难训练。残差连接就是来解决这个问题的。

Python

# ============================================ # 5. 残差连接 + 层归一化:确保深层网络能稳定训练 # ============================================ class AddAndNorm(nn.Module): """ Add(残差连接):把输入和输出加起来 → 就像"抄近道",让梯度能直接传回浅层,防止梯度消失 Norm(层归一化):把数值稳定在一个范围内 → 让训练更稳定、更快收敛 """ def __init__(self, d_model: int, dropout: float = 0.1): super().__init__() self.norm = nn.LayerNorm(d_model) # 层归一化 self.dropout = nn.Dropout(dropout) def forward(self, x, sublayer_output): """ x: 原始输入(残差) sublayer_output: 子层输出(注意力层或前馈网络层) """ # 残差连接:原始输入 + 子层输出,让信息绕过子层直达后面 # 然后做层归一化 return self.norm(x + self.dropout(sublayer_output))

3.7 组装:一个完整的 Encoder 层

Python

# ============================================ # 6. 组装一个 Encoder 层 # ============================================ class EncoderLayer(nn.Module): """ 一个 Encoder 层 = 多头自注意力 + 前馈网络 每个子层后面都有残差连接和层归一化 结构: x → [Self-Attention] → Add&Norm → [FeedForward] → Add&Norm → 输出 """ def __init__(self, d_model: int, n_heads: int, d_ff: int, dropout: float = 0.1): super().__init__() self.attention = SelfAttention(d_model, n_heads) # 多头自注意力 self.feed_forward = FeedForward(d_model, d_ff, dropout) # 前馈网络 self.add_norm1 = AddAndNorm(d_model, dropout) # 注意力后的残差+归一化 self.add_norm2 = AddAndNorm(d_model, dropout) # 前馈后的残差+归一化 def forward(self, x, mask=None): # 第1步:自注意力 → 残差连接 → 层归一化 attn_output = self.attention(x, mask) x = self.add_norm1(x, attn_output) # 第2步:前馈网络 → 残差连接 → 层归一化 ff_output = self.feed_forward(x) x = self.add_norm2(x, ff_output) return x # ============================================ # 7. 组装完整的 Encoder(多个 Encoder 层堆叠) # ============================================ class Encoder(nn.Module): """ 完整的编码器 = 输入嵌入 + 位置编码 + N 个 Encoder 层 论文中 N=6,即堆叠 6 层 """ def __init__( self, vocab_size: int, # 词汇表大小 d_model: int, # 模型维度(512) n_layers: int, # Encoder 层数(6) n_heads: int, # 注意力头数(8) d_ff: int, # 前馈网络维度(2048) max_len: int, # 最大句子长度 dropout: float = 0.1 ): super().__init__() self.embedding = InputEmbedding(vocab_size, d_model) self.positional_encoding = PositionalEncoding(d_model, max_len) # 堆叠 n_layers 个 Encoder 层 self.layers = nn.ModuleList([ EncoderLayer(d_model, n_heads, d_ff, dropout) for _ in range(n_layers) ]) self.dropout = nn.Dropout(dropout) def forward(self, x, mask=None): # ① 词嵌入 x = self.embedding(x) # ② 位置编码 x = self.positional_encoding(x) # ③ 随机丢弃(正则化) x = self.dropout(x) # ④ 逐层传递 for layer in self.layers: x = layer(x, mask) return x # ============================================ # 8. 跑一个完整的 Encoder # ============================================ def test_transformer_encoder(): """测试迷你 Transformer Encoder""" vocab_size = 10000 # 词汇表 10000 个词 d_model = 512 # 每个词用 512 维向量表示 n_layers = 6 # 6 层 Encoder(论文标准配置) n_heads = 8 # 8 个注意力头 d_ff = 2048 # 前馈网络 2048 维 max_len = 100 # 最长 100 个词 encoder = Encoder(vocab_size, d_model, n_layers, n_heads, d_ff, max_len) # 模拟输入:2 个句子,每句 20 个词 sample_input = torch.randint(0, vocab_size, (2, 20)) output = encoder(sample_input) print(f"输入形状: {sample_input.shape}") # torch.Size([2, 20]) print(f"输出形状: {output.shape}") # torch.Size([2, 20, 512]) print() print("Encoder 工作流程:") print(" ① 词嵌入:每个词 → 512维向量") print(" ② 位置编码:加入位置信息") print(" ③ 6层Encoder:每层包含自注意力+前馈网络") print(" ④ 输出:每个词都融合了整句话的上下文信息 ✅") print() # 计算参数量 total_params = sum(p.numel() for p in encoder.parameters()) print(f"总参数量: {total_params:,}") print(f"(GPT-3 有 1750 亿参数,我们的迷你版只有 {total_params:,} 个)") test_transformer_encoder()

四、一张图总结 Transformer 的数据流动

Plain Text

输入:"我 爱 中国" │ ├─→ ① 词嵌入:每个词 → 512维向量 │ "我" → [0.12, -0.85, 0.33, ...] │ "爱" → [0.67, 0.21, -0.44, ...] │ "中国" → [-0.33, 0.78, 0.15, ...] │ ├─→ ② 位置编码:加上位置签名 │ "我" 位置0 → 加上 sin/cos 编码 │ "爱" 位置1 → 加上 sin/cos 编码 │ "中国" 位置2 → 加上 sin/cos 编码 │ └─→ ③ 6层 Encoder 处理: ┌─────────────────────────────────────┐ │ 第1层:Self-Attention → FFN │ │ 第2层:Self-Attention → FFN │ │ ... │ │ 第6层:Self-Attention → FFN │ └─────────────────────────────────────┘ ↓ 输出:每个词都融合了整句话的上下文 "中国" 的向量里,包含了"我"和"爱"的信息

五、GPT 和 BERT 的区别

维度BERTGPT
用的部分只用 Encoder只用 Decoder
看信息的方向双向(能看到前后所有词)单向(只能看到前面的词)
适合任务理解:分类、问答、实体识别生成:写文章、对话、翻译
训练方式完形填空(猜被遮住的词)预测下一个词
代表模型BERT、RoBERTaGPT-3、GPT-4、ChatGPT

六、为什么 Transformer 是"革命性"的?

Python

# 对比总结 """ Transformer 之前(RNN/LSTM 时代): ❌ 串行处理:1000个词要跑1000步,太慢了 ❌ 长距离遗忘:句子太长,开头的信息就丢了 ❌ 难以扩展:层数深了训练不稳定 Transformer 之后: ✅ 并行处理:所有词同时计算,速度飞起 ✅ 任意距离:第1个词和第1000个词直接建立联系 ✅ 可无限堆叠:层数越多效果越好(GPT-3 有 96 层) ✅ 通用架构:能处理文本、图像、语音、视频、代码... """

结语

Transformer 的本质,就是让机器学会"读句子时关注哪些词"。这看似简单的想法,却引发了 AI 领域的革命。

从 2017 年的一篇论文,到 2023 年 ChatGPT 横空出世,再到今天 AI 能写代码、画图、做视频,这一切的起点都是——Attention Is All You Need

理解 Transformer,就是拿到了理解现代 AI 的钥匙。希望这篇文章能帮你打开这扇门。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/8 11:39:16

东芝H桥驱动器与PIC微控制器的直流电机驱动方案

1. 项目背景与核心器件选型 在工业自动化和消费电子领域,直流有刷电机因其结构简单、控制方便、成本低廉等优势,依然占据着重要地位。本次项目采用东芝TC78H651AFNG H桥驱动器与Microchip PIC18F86K22微控制器组合,构建了一套高性能直流有刷电…

作者头像 李华
网站建设 2026/7/8 11:38:26

ollama与vllm的区别‌

大模型主流推理框架对比:Ollama、vLLM、SGLang 核心区别与适用场景随着大模型落地场景持续普及,各类轻量化、生产级推理框架快速涌现,目前行业主流推理工具包含 Ollama、vLLM、LMDeploy、SGLang、Xinference 等。其中 Ollama、vLLM、SGLang 凭…

作者头像 李华
网站建设 2026/7/8 11:36:46

算法设计 第7讲 线性规划 章节练习

第7讲 线性规划 章节练习一、单项选择题(共30题) 线性规划基本概念与标准形(第1-8题) 第1题 线性规划问题的标准形(standard form)的基本要求是: A. 目标函数为最小化,所有约束为 ≤…

作者头像 李华
网站建设 2026/7/8 11:36:28

10G/25G Tunable可调光模块:毫秒级波长自由切换技术全解

在DWDM波分组网、骨干传输、数据中心互联、政企专线扩容场景中,传统固定波长彩光模块存在波长固化、备货繁琐、扩容受限、故障倒换低效等痛点,极大增加了网络运维与升级成本。安科士(ANDXE)10G/25G Tunable可调谐DWDM光模块&#…

作者头像 李华
网站建设 2026/7/8 11:35:36

BMI160与PIC18F27K42实现运动追踪系统设计

1. 项目背景与硬件选型解析在运动追踪和姿态检测领域,6自由度惯性测量单元(6DOF IMU)已成为核心传感器。Bosch Sensortec推出的BMI160是一款集成了3轴加速度计和3轴陀螺仪的MEMS传感器,其16位分辨率、2g至16g可调量程以及低至950μA的功耗表现&#xff0…

作者头像 李华