惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

爱范儿
爱范儿
大猫的无限游戏
大猫的无限游戏
J
Java Code Geeks
MongoDB | Blog
MongoDB | Blog
Martin Fowler
Martin Fowler
GbyAI
GbyAI
Microsoft Azure Blog
Microsoft Azure Blog
Recent Announcements
Recent Announcements
F
Fortinet All Blogs
B
Blog
U
Unit 42
B
Blog RSS Feed
D
DataBreaches.Net
Google DeepMind News
Google DeepMind News
人人都是产品经理
人人都是产品经理
腾讯CDC
量子位
酷 壳 – CoolShell
酷 壳 – CoolShell
V
Visual Studio Blog
博客园 - 聂微东
MyScale Blog
MyScale Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - 三生石上(FineUI控件)
Engineering at Meta
Engineering at Meta

Пусть этот камень будет более крепким, чем человек

【MLIR】Transform Dialect【0】保持计算结构 【MLIR】跟着Toy语言学习MLIR【6】支持复合结构类型 【MLIR】跟着Toy语言学习MLIR【5】降级到LLVM及CodeGen 【MLIR】跟着Toy语言学习MLIR【4】部分降级到低层方言 【MLIR】跟着Toy语言学习MLIR【3】通过接口实现通用转换 【MLIR】跟着Toy语言学习MLIR【2】pattern匹配和重写 【MLIR】跟着Toy语言学习MLIR【1】Toy语言和 Toy Dialect 【琐记】你的爱好能坚持多少年 【琐记】烟火与尘埃 【Triton】Triton实现矩阵乘 【LLM推理加速】FlashAttention 【LLM推理加速】PagedAttention 【LLM推理加速】Online Softmax LLM基础知识【1】 【AI编译】LayerGroup Tiling Tile的疑惑和思考 【AI编译】深度优先的Tile调度,万事大吉? 【AI编译】多级流水线Tile调度策略 【CUDA C++】GPU内存使用【3】 【AI编译】Cache缓存地址映射 【CUDA C++】GPU存储【2】 【CUDA C++】GPU基本介绍【1】 【00】0序章-不受欢迎的来客 【转载】我来了——持续低熵 【Halide】调度优化【2】 【感想】写作进度报告5 【Halide】调度优化【1】 【转载】北大中文男足战报2 【BYOC】TVM切分子图 【转载】北大中文男足战报1 【AI编译】张量生命周期管理
Transformer模型
Post author: XianMu@Пусть этот камень будет более крепким, чем ч · 2026-04-19 · via Пусть этот камень будет более крепким, чем человек

# 前言

学习大模型,首先得学习 Transformer。Transformer 由论文《Attention is All You Need》提出,现在已经是大模型的基础。本文介绍 Transformer 模型的结构。

Transformer 太火了,网上能够找到足够多的图和介绍文章,所以本文文章出现的图均来自论文和网络,非本人所画。

参考链接:Transformer 模型详解(图解最完整版)Attention is All You NeedPytorch 版本的 Transformer 实现一文了解 Transformer 全貌(图解 Transformer)

Transformer 模型的总体结构包括三部分:Embedding 部分(包括 input Embedding 和 Output Embedding)、Encoder 和 Decoder。后续将采用从总到分的顺序来介绍结构细节。

其中 Embedding 将离散的输入符号(如单词、子词等 token)转换为模型可以处理的连续、稠密的向量表示。

Encoder 和 Decoder 都以单词向量表示作为输入,输出编码矩阵。

Transformer 总体结构图

以下是结构简图,Transformer 中 Encoder 部分有 6 个 Encoder Block,同样 Decoder 部分有 6 个 Decoder Block。

Transformer 总体结构简化图

# Embedding

模型无法直接处理文本、ID 等离散符号。Embedding 层的作用就像一个 “翻译官”,将这些符号转换为数值化的向量。

它本质上是一个可训练的 “查找表”(Lookup Table)。这个表是一个巨大的矩阵,其行数等于词汇表大小(vocab_size),列数等于预设的向量维度(embedding_dim,如 512 或 768)。

工作流程:
1. 输入的文本首先被分词器(Tokenizer)转换成一串整数 ID。
2.Embedding 层根据这些 ID,从矩阵中查找并取出对应的行向量,即下图中的 词 Embedding。

Transformer 模型本身不具备处理序列顺序的能力(因为它可以并行处理整个序列),因此需要显式地注入位置信息。

为了告诉模型每个 token 在序列中的先后顺序,会为每个位置的 Embedding 向量加上一个专门表示位置的向量,即 “位置编码”(Positional Encoding)即下图中的 位置 Embedding.

位置 Embedding 用 PE 表示,PE 的维度与单词 Embedding 是一样的。PE 可以通过训练得到,也可以使用某种公式计算得到。在 Transformer 中采用了后者,计算公式如下:

Embedding计算公式

其中,pos 表示单词在句子中的位置,d 表示 PE 的维度 (与词 Embedding 一样),2i 表示偶数的维度,2i+1 表示奇数维度 (即 2i≤d, 2i+1≤d)。

所以最终输入向量 = 词 Embedding + 位置 Embedding。

Embedding示意图

以下代码用来计算 Transformer 的输入 Embedding,代码来自 Pytorch 版本的 Transformer 实现

class PositionalEncoding(nn.Module):
    """
    compute sinusoid encoding.
    """
    def __init__(self, d_model, max_len, device):
        """
        constructor of sinusoid encoding class
        :param d_model: dimension of model
        :param max_len: max sequence length
        :param device: hardware device setting
        """
        super(PositionalEncoding, self).__init__()
        
        self.encoding = torch.zeros(max_len, d_model, device=device)
        self.encoding.requires_grad = False  
        pos = torch.arange(0, max_len, device=device)
        pos = pos.float().unsqueeze(dim=1)
        
        _2i = torch.arange(0, d_model, step=2, device=device).float()
        
        
        self.encoding[:, 0::2] = torch.sin(pos / (10000 ** (_2i / d_model)))
        self.encoding[:, 1::2] = torch.cos(pos / (10000 ** (_2i / d_model)))
        
    def forward(self, x):
        
        
        batch_size, seq_len = x.size()
        
        return self.encoding[:seq_len, :]
        
        

# Encoder

Encoder 部分包含 6 个 Encoder Block,下一个 Encoder Block 的输入是上一个 Encoder Block 的输出。下图为 Encoder 示意图。其中 Transformer 中 N 取值为 6,也就是有连续的 6 个 Encoder Block。

Encoder示意图

从示意图中可以看到,其包含两个部分 : Multi-Head Attention + Add & Norm 部分 和 Feed Forward + Add & Norm 部分。两个部分都有残差结构。
下面分别介绍:

# Multi-Head Attention

Multi-Head Attention示意图

上图可以看到 Multi-Head Attention 包含 h (Transformer 为 8)个 Self-Attention 层(Q、K、V 为输入,经 Linear 和 Scaled Dot-Product Attention 得到 Z),首先将输入 X 分别传递到 h 个不同的 Self-Attention 中,计算得到 h 个输出矩阵 Z。

多个Self-Attention输出结果示意图

多头输出Concat示意图

上图展示了 8 个 Self-Attention 的输出 Z1 到 Z8,然后 Concat 一起,经线性变换得到最终的输出 Z。

class MultiHeadAttention(nn.Module):
    def __init__(self, d_model, n_head):
        super(MultiHeadAttention, self).__init__()
        self.n_head = n_head
        self.attention = ScaleDotProductAttention()
        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_concat = nn.Linear(d_model, d_model)
    def forward(self, q, k, v, mask=None):
        
        q, k, v = self.w_q(q), self.w_k(k), self.w_v(v)
        
        q, k, v = self.split(q), self.split(k), self.split(v)
        
        out, attention = self.attention(q, k, v, mask=mask)
        
        
        out = self.concat(out)
        out = self.w_concat(out)
        
        
        return out
    def split(self, tensor):
        """
        split tensor by number of head
        :param tensor: [batch_size, length, d_model]
        :return: [batch_size, head, length, d_tensor]
        """
        batch_size, length, d_model = tensor.size()
        d_tensor = d_model // self.n_head
        tensor = tensor.view(batch_size, length, self.n_head, d_tensor).transpose(1, 2)
        
        return tensor
    def concat(self, tensor):
        """
        inverse function of self.split(tensor : torch.Tensor)
        :param tensor: [batch_size, head, length, d_tensor]
        :return: [batch_size, length, d_model]
        """
        batch_size, head, length, d_tensor = tensor.size()
        d_model = head * d_tensor
        tensor = tensor.transpose(1, 2).contiguous().view(batch_size, length, d_model)
        return tensor

下面介绍单个 Self-Attention 的结构。

# Self-Attention

Self-Attention示意图

Self-Attention 的输入向量是 Q (查询)、 K (键值)、 V (值), 而 Q (查询)、 K (键值)、 V (值) 是由前述介绍的最终输入向量 X 得到的,最终输入向量 X = 词 Embedding + 位置 Embedding。

先看 X 如何得到 Q、K、V。

如图所示,Q、K、V 由输入 X 与三个不同的权重矩阵相乘得到。

Q、K、V计算示意图

得到 Q、K、V 之后就可以计算出 Self-Attention 的输出,如下图所示:

attention计算

attention计算公式

class ScaleDotProductAttention(nn.Module):
    """
    compute scale dot product attention
    Query : given sentence that we focused on (decoder)
    Key : every sentence to check relationship with Qeury(encoder)
    Value : every sentence same with Key (encoder)
    """
    def __init__(self):
        super(ScaleDotProductAttention, self).__init__()
        self.softmax = nn.Softmax(dim=-1)
    def forward(self, q, k, v, mask=None, e=1e-12):
        
        
        batch_size, head, length, d_tensor = k.size()
        
        k_t = k.transpose(2, 3)  
        score = (q @ k_t) / math.sqrt(d_tensor)  
        
        if mask is not None:
            score = score.masked_fill(mask == 0, -10000)
        
        score = self.softmax(score)
        
        v = score @ v
        return v, score

# Feed Forward

Feed Forward 前馈网络, 结构简单,是两个全连接层。

FF计算公式

class PositionwiseFeedForward(nn.Module):
    def __init__(self, d_model, hidden, drop_prob=0.1):
        super(PositionwiseFeedForward, self).__init__()
        self.linear1 = nn.Linear(d_model, hidden)
        self.linear2 = nn.Linear(hidden, d_model)
        self.relu = nn.ReLU()
        self.dropout = nn.Dropout(p=drop_prob)
    def forward(self, x):
        x = self.linear1(x)
        x = self.relu(x)
        x = self.dropout(x)
        x = self.linear2(x)
        return x

# Add & Norm

Add & Norm 层由 Add 和 Norm 两部分组成,包含残差接结构,其计算公式如下:

AddNorm计算公式

class LayerNorm(nn.Module):
    def __init__(self, d_model, eps=1e-12):
        super(LayerNorm, self).__init__()
        self.gamma = nn.Parameter(torch.ones(d_model))
        self.beta = nn.Parameter(torch.zeros(d_model))
        self.eps = eps
    def forward(self, x):
        mean = x.mean(-1, keepdim=True)
        var = x.var(-1, unbiased=False, keepdim=True)
        
        out = (x - mean) / torch.sqrt(var + self.eps)
        out = self.gamma * out + self.beta
        return out

# Decoder

Decoder block 结构,与 Encoder block 相似,但是存在一些区别:

包含两个 Multi-Head Attention 层。
第一个 Multi-Head Attention 层采用了 Masked 操作。
第二个 Multi-Head Attention 层的 K, V 矩阵使用 Encoder 的编码信息矩阵 C 进行计算,而 Q 使用上一个 Decoder block 的输出计算。
最后有一个 Softmax 层计算下一个翻译单词的概率。

Decoder示意图

class DecoderLayer(nn.Module):
    def __init__(self, d_model, ffn_hidden, n_head, drop_prob):
        super(DecoderLayer, self).__init__()
        self.self_attention = MultiHeadAttention(d_model=d_model, n_head=n_head)
        self.norm1 = LayerNorm(d_model=d_model)
        self.dropout1 = nn.Dropout(p=drop_prob)
        self.enc_dec_attention = MultiHeadAttention(d_model=d_model, n_head=n_head)
        self.norm2 = LayerNorm(d_model=d_model)
        self.dropout2 = nn.Dropout(p=drop_prob)
        self.ffn = PositionwiseFeedForward(d_model=d_model, hidden=ffn_hidden, drop_prob=drop_prob)
        self.norm3 = LayerNorm(d_model=d_model)
        self.dropout3 = nn.Dropout(p=drop_prob)
    def forward(self, dec, enc, trg_mask, src_mask):    
        
        _x = dec
        x = self.self_attention(q=dec, k=dec, v=dec, mask=trg_mask)
        
        
        x = self.dropout1(x)
        x = self.norm1(x + _x)
        if enc is not None:
            
            _x = x
            x = self.enc_dec_attention(q=x, k=enc, v=enc, mask=src_mask)
            
            
            x = self.dropout2(x)
            x = self.norm2(x + _x)
        
        _x = x
        x = self.ffn(x)
        
        
        x = self.dropout3(x)
        x = self.norm3(x + _x)
        return x
class Decoder(nn.Module):
    def __init__(self, dec_voc_size, max_len, d_model, ffn_hidden, n_head, n_layers, drop_prob, device):
        super().__init__()
        self.emb = TransformerEmbedding(d_model=d_model,
                                        drop_prob=drop_prob,
                                        max_len=max_len,
                                        vocab_size=dec_voc_size,
                                        device=device)
        self.layers = nn.ModuleList([DecoderLayer(d_model=d_model,
                                                  ffn_hidden=ffn_hidden,
                                                  n_head=n_head,
                                                  drop_prob=drop_prob)
                                     for _ in range(n_layers)])
        self.linear = nn.Linear(d_model, dec_voc_size)
    def forward(self, trg, src, trg_mask, src_mask):
        trg = self.emb(trg)
        for layer in self.layers:
            trg = layer(trg, src, trg_mask, src_mask)
        
        output = self.linear(trg)
        return output

Decoder 的第一个 Multi-Head Attention 采用了 Masked 操作,因为在翻译的过程中是顺序翻译的,即翻译完第 i 个单词,才可以翻译第 i+1 个单词。通过 Masked 操作可以防止第 i 个单词知道 i+1 个单词之后的信息。下面以法语 "Je suis etudiant" 翻译成英文 "I am a student" 为例,了解一下 Masked 操作。

在 Decoder 的时候,需要根据之前翻译的单词,预测当前最有可能翻译的单词,如下图所示。首先根据输入 "<Begin>" 预测出第一个单词为 "I",然后根据输入 "<Begin> I" 预测下一个单词 "am"。

Decoder循环输入示意图

Decoder 在预测第 i 个输出时,需要将第 i+1 之后的单词掩盖住,Mask 操作是在 Self-Attention 的 Softmax 之前使用的,下面以前面的 "I am a student" 为例。

第一步:是 Decoder 的输入矩阵和 Mask 矩阵,输入矩阵包含 "<Begin> I am a student"4 个单词的表示向量,Mask 是一个 4 * 4 的矩阵。在 Mask 可以发现单词 "<Begin>" 只能使用单词 "<Begin>" 的信息,而单词 "I" 可以使用单词 "<Begin> I" 的信息,即只能使用之前的信息。

Decoder Mask示意图

第二步:接下来的操作和之前 Encoder 中的 Self-Attention 一样,只是在 Softmax 之前需要进行 Mask 操作。

Decoder attention Mask示意图

第三步:通过上述步骤就可以得到一个 Mask Self-Attention 的输出矩阵 Z,然后和 Encoder 类似,通过 Multi-Head Attention 拼接多个输出 Z 然后计算得到第一个 Multi-Head Attention 的输出 Z, Z 与输入 X 维度一样。

# 最终输出

编码器 Decoder 最后的部分是利用 Softmax 预测下一个单词,在 Softmax 之前,会经过 Linear 变换,将维度转换为词表的个数。

# 后记

本博客目前以及可预期的将来都不会支持评论功能。各位大侠如若有指教和问题,可以在我的 github 项目 或随便一个项目下提出 issue,并指明哪一篇博客,看到一定及时回复!