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

推荐订阅源

Forbes - Security
Forbes - Security
L
Lohrmann on Cybersecurity
Simon Willison's Weblog
Simon Willison's Weblog
P
Proofpoint News Feed
P
Privacy International News Feed
The Hacker News
The Hacker News
AWS News Blog
AWS News Blog
S
Securelist
P
Proofpoint News Feed
Recent Announcements
Recent Announcements
GbyAI
GbyAI
B
Blog RSS Feed
A
About on SuperTechFans
C
CXSECURITY Database RSS Feed - CXSecurity.com
Y
Y Combinator Blog
Microsoft Azure Blog
Microsoft Azure Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Cyberwarzone
Cyberwarzone
I
Intezer
T
Tor Project blog
T
The Blog of Author Tim Ferriss
The GitHub Blog
The GitHub Blog
云风的 BLOG
云风的 BLOG
Recorded Future
Recorded Future
aimingoo的专栏
aimingoo的专栏
Cisco Talos Blog
Cisco Talos Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
W
WeLiveSecurity
D
DataBreaches.Net
U
Unit 42
Project Zero
Project Zero
Martin Fowler
Martin Fowler
V
V2EX
The Last Watchdog
The Last Watchdog
Security Archives - TechRepublic
Security Archives - TechRepublic
C
Cisco Blogs
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
V2EX - 技术
V2EX - 技术
Hacker News - Newest:
Hacker News - Newest: "LLM"
T
Threat Research - Cisco Blogs
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
T
Tenable Blog
F
Full Disclosure
T
The Exploit Database - CXSecurity.com
H
Heimdal Security Blog
Latest news
Latest news
Webroot Blog
Webroot Blog

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

【琐记】烟火与尘埃 【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编译】张量生命周期管理 SystemC 用寄存器同步建模方法 【脉动阵列】脉动阵列类型 【im2col】AScend conv accelerate 【感想】写作进度报告4 【BYOC】TVM添加自定义编译器 ccompiler 【感想】写作进度报告3 【Tengine】推理流程脑图【2】 【Tengine】推理流程脑图【1】 【NCNN】学习ncnn模型转换 【编译器】使用llvm编译自定义语言【3】编译 object 【编译器】使用llvm编译自定义语言【2】转llvm IR 【编译器】使用llvm编译自定义语言【1】构建AST 【AI编译】如何进行内存分配 【感想】写作进度报告2 【AI编译】layer-group之后如何tiling 【AI编译】如何进行layer-group 【量化】连续卷积层首尾量化的可行性 【Gemm】内存对齐 【gemm】Gemm计算加速 【TVM】通过代码学习编译流程【5】FuseOps 【TVM】通过代码学习编译流程【6】CodeGen 【TVM】通过代码学习类【3.5】Pass 【TVM】通过代码学习编译流程【4】BuildRelay 【AI编译】Tiling操作能优化什么时间 【TVM】通过代码学习编译流程【3】模型编译 【TVM】通过代码学习编译流程【2】模型转换 【TVM】通过代码学习编译流程【1】必要知识 【感想】写作进度报告1 【Winograd】卷积加速算法原理及实现 SystemC 等待异步事件解决方案 【TVM】Python脚本实现模型编译和保存 【推理引擎】常见AI推理框架 【3D建模】T110E3卡迪夫蓝调皮肤模型 【TVM】C++部署运行TVM 【推理引擎】NCNN和Tengine量化推理逻辑对比 【3D建模】IS-7攻城锤流纹岩皮肤展示 【TVM】根据例子走通代码库 博客汇总目录 【Im2Col】卷积加速算法【2】NHWC 【Im2Col】卷积加速算法【1】 NCHW openBlas库的安装与简单使用 C语言工程调用Cpp库解决方案 foo Hello World
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,并指明哪一篇博客,看到一定及时回复!