首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >手写大模型核心:从零实现一个最小生成式Transformer

手写大模型核心:从零实现一个最小生成式Transformer

原创
作者头像
闪 学it
发布2026-08-30 14:08:14
发布2026-08-30 14:08:14
740
举报

很多人觉得大语言模型(LLM)是一个深不可测的黑箱,动辄千亿参数、万卡集群。但剥开工程化的外壳,其核心推理架构用几十行代码就能讲清楚。今天,我们就手写一个“微缩版”大模型——它具备GPT系列最根本的因果自注意力前馈网络,能真正运行并生成文本。代码极简,但灵魂俱全。


1. 大模型的“心脏”:因果自注意力

大模型之所以能看懂上下文,全靠注意力机制。而“因果”(Causal)意味着模型在预测下一个词时,只能看到当前位置之前的词,不能“偷看”未来。这是生成式任务的根本原则。

下面我们用PyTorch手写一个纯正的因果注意力模块:

代码语言:javascript
复制
import torch
import torch.nn as nn
import torch.nn.functional as F

class CausalAttention(nn.Module):
    def __init__(self, d_model, n_head):
        super().__init__()
        assert d_model % n_head == 0
        self.n_head = n_head
        self.head_dim = d_model // n_head
        
        # 一次性计算 Q、K、V,提升效率
        self.qkv = nn.Linear(d_model, 3 * d_model)
        self.proj = nn.Linear(d_model, d_model)

    def forward(self, x):
        B, T, C = x.shape  # 批次、序列长度、特征维度
        qkv = self.qkv(x).reshape(B, T, 3, self.n_head, self.head_dim)
        qkv = qkv.permute(2, 0, 3, 1, 4)  # 拆出 Q、K、V
        q, k, v = qkv[0], qkv[1], qkv[2]

        # 缩放点积注意力
        att = (q @ k.transpose(-2, -1)) * (self.head_dim ** -0.5)
        
        # 生成下三角掩码,屏蔽未来位置(核心!)
        mask = torch.tril(torch.ones(T, T, device=x.device)).view(1, 1, T, T)
        att = att.masked_fill(mask == 0, float('-inf'))
        att = F.softmax(att, dim=-1)

        y = (att @ v).transpose(1, 2).reshape(B, T, C)
        return self.proj(y)

重点解释torch.tril生成的下三角矩阵,就是大模型“自回归”性质的物理体现。一旦去掉这个掩码,它就变成BERT那样的双向编码器,无法做续写生成。


2. 构建标准Transformer Decoder层

仅靠注意力还不够,大模型还需要前馈网络(FFN)来做非线性变换,以及残差连接层归一化来稳定训练。一个标准的Decoder Block长这样:

代码语言:javascript
复制
class FeedForward(nn.Module):
    def __init__(self, d_model):
        super().__init__()
        # 经典扩缩结构:4倍维度膨胀
        self.net = nn.Sequential(
            nn.Linear(d_model, 4 * d_model),
            nn.GELU(),           # 比ReLU更平滑的激活
            nn.Linear(4 * d_model, d_model)
        )
    def forward(self, x): return self.net(x)

class DecoderBlock(nn.Module):
    def __init__(self, d_model, n_head):
        super().__init__()
        self.ln1 = nn.LayerNorm(d_model)
        self.attn = CausalAttention(d_model, n_head)
        self.ln2 = nn.LayerNorm(d_model)
        self.ffwd = FeedForward(d_model)

    def forward(self, x):
        # 残差连接(Pre-Norm结构,现今主流)
        x = x + self.attn(self.ln1(x))
        x = x + self.ffwd(self.ln2(x))
        return x

把上述DecoderBlock重复堆叠 N 层(如12层、24层),再加上最底层的Embedding和最顶层的Linear输出头,就是一个完整的GPT骨架。


3. 手写生成逻辑:如何“吐出”一篇文章

模型搭建好后,生成文本的核心在于迭代采样。大模型每次只预测下一个词的概率分布,我们将这个分布除以温度(Temperature)软化后,按概率抽样选出下一个词,再拼回输入,如此循环往复。

以下是绝不依赖高级框架的原生生成函数:

代码语言:javascript
复制
@torch.no_grad()
def generate(model, idx, max_new_tokens, temperature=1.0, top_k=None):
    """
    idx: 初始输入索引,形状 (1, T)
    model: 我们手写的Decoder堆叠实例
    """
    model.eval()
    for _ in range(max_new_tokens):
        # 若上下文过长,可截断(此处只取最后1024位,模拟滑动窗口)
        idx_cond = idx[:, -1024:] 
        logits = model(idx_cond)          # 前向传播,得到 (B, T, vocab_size)
        logits = logits[:, -1, :]         # 只取最后一个时间步
        logits = logits / temperature     # 温度控制随机性

        # 可选:Top-K采样,只保留概率最高的K个
        if top_k is not None:
            v, _ = torch.topk(logits, min(top_k, logits.size(-1)))
            logits[logits < v[:, [-1]]] = float('-inf')

        probs = F.softmax(logits, dim=-1)
        idx_next = torch.multinomial(probs, num_samples=1)  # 按概率抽样
        idx = torch.cat((idx, idx_next), dim=1)             # 拼回序列
    return idx

仅此三块代码,一个可运行的生成式大模型内核已经完整。如果你把d_model设为768,n_head设为12,堆叠12层,参数量就达到了1亿级别——这已经接近初代GPT-2的规模。


4. 为什么说“这就是大模型”?

你可能会问:没有反向传播、没有海量数据训练,这能算大模型吗? 请注意,前向推理架构训练方法是正交的。上面手写的CausalAttention + FeedForward + 残差连接,正是当今所有主流大模型(GPT-4、Llama、Claude)的基础数学操作。它们之间的区别仅在于:

  • 参数规模(宽度d_model和深度n_layers的放大)
  • 工程优化(如FlashAttention、分组查询)
  • 数据质量(训练语料的清洗与配比)

意识的核心,那套让机器理解上下文的注意力公式,已经被我们用20行代码牢牢握在手中。


5. 从“手写”到“实用”的跨越

手写这段代码的意义,并非让你用它去训练ChatGPT(那需要数千张GPU和数月时间),而是帮你祛魅。当你看到masked_fill那行代码时,你就明白了“因果”不是玄学,只是一张下三角矩阵;当你看到multinomial时,你就懂了“创造性”不过是概率抽样。

若你想让这套代码真正跑起来生成一句英文,只需额外补上:

  • 一个简单的Tokenizer(如字符级编码器)
  • 随机初始化的nn.Embeddingnn.Linear输出头
  • 将上述DecoderBlock循环堆叠成一个nn.Sequential

然后输入[ [1, 2, 3] ],它就能像模像样地吐出[4, 5, 6, ...]——尽管由于未经训练,内容是乱码,但数学逻辑完全正确


结语

大模型不是魔法,而是一套被极致放大的、优雅的数学公式。今天我们手写了它的最小完备子集:因果注意力保证自回归,前馈网络提供非线性,残差连接保障梯度流动,温度采样赋予生成多样性。

当你下次与AI对话时,可以回想这几行代码——所有深刻的智慧,都始于对q @ ktril的透彻理解。手写一遍,胜过阅读百篇论文。现在,你不只在使用大模型,你已经握住了它的灵魂骨架。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 1. 大模型的“心脏”:因果自注意力
  • 2. 构建标准Transformer Decoder层
  • 3. 手写生成逻辑:如何“吐出”一篇文章
  • 4. 为什么说“这就是大模型”?
  • 5. 从“手写”到“实用”的跨越
  • 结语
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档