板块 01 · 基础原理

从感知机到 Transformer:
理解现代大模型的底层逻辑

这一章我们用最少的高等数学,把"AI 是怎么工作的"这件事彻底讲清楚。无论你是产品、研发还是销售,理解这一章后,后续所有模型和框架的讨论你都能跟上。

一、人工神经网络:从生物神经元到数学函数

1.1 神经网络的最小单元

人工神经网络(Neural Network)的灵感来自大脑神经元的工作方式。一个神经元本质上就是一个数学函数:

output = activation(w · x + b)

其中 x 是输入向量,w 是权重,b 是偏置,activation 是激活函数(常见的有 Sigmoid、ReLU、GELU)。把成千上万个这样的"神经元"按层堆叠、互相连接,就构成了现代深度学习的基础。

1.2 神经网络的训练三要素

💡 关键直觉:训练神经网络的过程,本质上就是"在数十亿维的参数空间里,找到一组权重,让损失函数的值最小"。这就是为什么大模型需要海量数据和算力 —— 参数空间太大了。

二、Transformer:现代大模型的基石

2017 年,Google 在论文 Attention Is All You Need 中提出 Transformer 架构,彻底改变了 AI 的发展轨迹。它的核心创新是:完全抛弃循环结构,只用注意力机制(Attention Mechanism)来处理序列数据。

2.1 为什么 Transformer 能横扫 NLP

特性RNN/LSTMTransformer
长距离依赖容易遗忘早期信息任意位置直接关联
并行计算必须按顺序可同时处理所有位置
训练速度快(充分利用 GPU)
扩展性难扩展到百亿参数原生支持千亿级参数

2.2 自注意力机制(Self-Attention)

自注意力的核心思想是:对于序列中的每个词,都要"看一遍"所有其他词,然后判断每个词对自己的重要性。

用一个具体的例子来说明 —— 句子 "小明 昨天去 北京 出差,今天 回来了":

  1. 每个词生成三个向量:Query(Q,查询向量)、Key(K,键向量)、Value(V,值向量)。
  2. 对于"他"这个词,计算它的 Q 与其他所有词的 K 的点积,得到相关性分数。
  3. 对分数做 Softmax 归一化,得到注意力权重(比如"他"对"小明"的权重是 0.8,对"北京"的权重是 0.1)。
  4. 用这些权重对所有词的 V 加权求和,得到"他"这个词的新的、富含上下文信息的表示。

数学公式可以简洁地写成:

Attention(Q, K, V) = softmax(Q · K^T / √d_k) · V

其中 d_k 是 Key 向量的维度,除以它的平方根是为了防止点积过大导致 Softmax 梯度消失。

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

多头注意力是自注意力的升级版。它让模型在不同的"表示子空间"里并行地做多次自注意力,然后把结果拼起来。这样模型可以同时关注不同维度的信息:

2.4 位置编码(Positional Encoding)

Transformer 是并行处理所有词的,天然丢失了"顺序"信息。比如"狗咬人"和"人咬狗"的字面 token 完全相同,只是顺序不同。位置编码通过给每个位置的词嵌入加上一个独特的"位置向量",让模型知道每个词在句子中的位置。

最初的 Transformer 使用固定的正弦/余弦函数生成位置编码。后续的模型(如 LLaMA、Qwen)改用 RoPE(旋转位置编码),可以更好地处理超长序列。

三、Transformer 整体架构

一个标准的 Transformer 块由两部分组成:

输入 → [LayerNorm → 多头注意力 → 残差连接]
    → [LayerNorm → 前馈网络(FFN) → 残差连接]
    → 输出

3.1 Transformer 整体架构图(一眼看明白)

Decoder-only Transformer(GPT 风格)
Input Embedding + Positional Encoding
×N 层 ►
Linear (LM Head) + Softmax
P(token₁), P(token₂), ...

关键三件套:

GPT 系列模型(ChatGPT、DeepSeek、Qwen)都是把几十层甚至上百层这样的块堆叠起来,组成 Decoder-only Transformer。

四、预训练、SFT、RLHF:大模型的"三步训练法"

从"会说话"到"会听话"再到"讨人喜欢",大模型要走完三步:

4.0 三步训练法流程图

原始语料(几万亿 token) 人工标注问答(10万条)
① 预训练 Pre-Training 任务: Next-Token- Prediction
数据: 互联网文本 14.8T tokens 成本:~557 万$
[Base Model] [Chat Model] (会续写,不会对话) (会回答问题)
③ RLHF / DPO 人类反馈强化学习
任务: 学"讨人喜欢"
做法: 人类对多个回答 排序打分
[Aligned Model] (符合人类偏好)

三步逐层递进:从"读懂世界"到"听懂人话"再到"对齐人类价值观"。每一步都不可缺 —— 缺了任何一步,模型都"不好用"。

4.1 预训练(Pre-Training)

用海量互联网文本(几万亿 token)训练模型,让它学会"预测下一个词"。这一阶段耗时最长、成本最高 —— DeepSeek-V3 用了 14.8 万亿 token 训练,花了约 557 万美元(GPU 租赁费)。

4.2 监督微调(Supervised Fine-Tuning, SFT)

用人类标注的高质量"指令-回答"对训练模型,让它学会"听懂人话、按照要求回答"。这一阶段数据量小得多(几万到几十万条),但质量要求极高。

4.3 基于人类反馈的强化学习(RLHF)

让人类对模型的不同回答打分,训练一个"奖励模型"(Reward Model),然后用强化学习(常用 PPO 或 DPO 算法)让模型学会生成更受人类偏好的回答。

五、为什么需要 GPU 集群?

训练一个千亿参数的大模型,一次前向 + 反向传播需要:

单张 GPU 的显存(80GB)根本装不下,所以需要多种并行策略:

并行方式核心思想代表框架
数据并行把数据分到多卡,每卡跑完整模型PyTorch DDP
模型并行把模型不同层放到不同卡Megatron-LM
流水线并行模型分块,各卡处理不同 micro-batchGPipe, PipeDream
张量并行把单个矩阵运算拆分到多卡Megatron-LM

六、从大模型到 Agent:能力的跃迁

当大模型足够强以后,人们开始尝试给它"加外挂":让它不仅能"说话",还能"做事"。这就是 AI Agent 的起点:

这部分内容会在下一章"Agent 演化"里详细展开。

七、本章小结

八、手把手构建一个 Mini Transformer

理论讲完了,这一节我们用 PyTorch 从零搭建一个 6 层 Decoder-only Transformer,在小规模文本上演示训练和推理。完整代码约 250 行,CPU/GPU 都能跑,目标是让你彻底看清 Transformer 的每个齿轮是怎么咬合的。

8.1 目标与环境

环境准备(2 行命令):

pip install torch numpy
python -c "import torch; print('torch', torch.__version__)"

8.2 数据准备

我们用一段重复的"古诗风格"伪数据(也可以换成你自己的 input.txt):

cat > data/input.txt << 'EOF'
床前明月光,疑是地上霜。
举头望明月,低头思故乡。
春眠不觉晓,处处闻啼鸟。
夜来风雨声,花落知多少。
白日依山尽,黄河入海流。
欲穷千里目,更上一层楼。
静夜思
登鹳雀楼
春晓
EOF
wc -c data/input.txt

字符级语言模型直接把整个文件读成一个长字符串即可,无需 tokenizer。

8.3 完整代码 train.py

"""Mini Transformer:6 层 Decoder-only,字符级语言模型。

用法:
    python train.py
"""
from pathlib import Path



# ---------- 1. 数据加载 ----------
class CharDataset:
    """把文本切成 (block_size) 长度的训练样本。"""

    def __init__(self, path: str, block_size: int = 64):
        self.text = Path(path).read_text(encoding="utf-8")
        self.chars = sorted(set(self.text))
        self.vocab_size = len(self.chars)
        self.stoi = {c: i for i, c in enumerate(self.chars)}
        self.itos = {i: c for i, c in enumerate(self.chars)}
        self.block_size = block_size
        self.data = torch.tensor([self.stoi[c] for c in self.text], dtype=torch.long)
        print(f"[data] chars={self.vocab_size} len={len(self.data)}")

    def get_batch(self, batch_size: int = 32):
        ix = torch.randint(0, len(self.data) - self.block_size - 1, (batch_size,))
        x = torch.stack([self.data[i : i + self.block_size] for i in ix])
        y = torch.stack([self.data[i + 1 : i + 1 + self.block_size] for i in ix])
        return x, y


# ---------- 2. 模型组件 ----------
class PositionalEncoding(nn.Module):
    """正弦位置编码,固定不参与训练。"""

    def __init__(self, d_model: int, max_len: int = 512):
        super().__init__()
        pe = torch.zeros(max_len, d_model)
        position = torch.arange(0, max_len).unsqueeze(1).float()
        div_term = torch.exp(torch.arange(0, d_model, 2).float() * -(math.log(10000.0) / d_model))
        pe[:, 0::2] = torch.sin(position * div_term)
        pe[:, 1::2] = torch.cos(position * div_term)
        self.register_buffer("pe", pe.unsqueeze(0))  # (1, max_len, d_model)

    def forward(self, x):
        return x + self.pe[:, : x.size(1)]


class CausalSelfAttention(nn.Module):
    """多头因果自注意力(Decoder 用,带 causal mask)。"""

    def __init__(self, d_model: int, n_heads: int, max_len: int = 512, dropout: float = 0.1):
        super().__init__()
        assert d_model % n_heads == 0
        self.n_heads = n_heads
        self.head_dim = d_model // n_heads
        self.qkv = nn.Linear(d_model, 3 * d_model)
        self.proj = nn.Linear(d_model, d_model)
        self.dropout = nn.Dropout(dropout)
        # causal mask:上三角(不含对角)置 -inf
        mask = torch.triu(torch.ones(max_len, max_len), diagonal=1).bool()
        self.register_buffer("mask", mask)

    def forward(self, x):
        B, T, C = x.shape
        qkv = self.qkv(x).reshape(B, T, 3, self.n_heads, self.head_dim).permute(2, 0, 3, 1, 4)
        q, k, v = qkv[0], qkv[1], qkv[2]  # (B, h, T, head_dim)
        att = (q @ k.transpose(-2, -1)) / math.sqrt(self.head_dim)
        att = att.masked_fill(self.mask[:T, :T], float("-inf"))
        att = F.softmax(att, dim=-1)
        att = self.dropout(att)
        out = (att @ v).transpose(1, 2).reshape(B, T, C)
        return self.proj(out)


class FeedForward(nn.Module):
    """Transformer FFN:GELU + Linear + Dropout。"""

    def __init__(self, d_model: int, d_ff: int, dropout: float = 0.1):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(d_model, d_ff),
            nn.GELU(),
            nn.Linear(d_ff, d_model),
            nn.Dropout(dropout),
        )

    def forward(self, x):
        return self.net(x)


class Block(nn.Module):
    """Pre-LN Decoder Block:Attention -> FFN,各自带残差。"""

    def __init__(self, d_model: int, n_heads: int, d_ff: int, max_len: int, dropout: float):
        super().__init__()
        self.ln1 = nn.LayerNorm(d_model)
        self.attn = CausalSelfAttention(d_model, n_heads, max_len, dropout)
        self.ln2 = nn.LayerNorm(d_model)
        self.ffn = FeedForward(d_model, d_ff, dropout)

    def forward(self, x):
        x = x + self.attn(self.ln1(x))
        x = x + self.ffn(self.ln2(x))
        return x


class MiniTransformer(nn.Module):
    """6 层 Decoder-only Transformer,字符级语言模型。"""

    def __init__(
        self,
        vocab_size: int,
        d_model: int = 128,
        n_heads: int = 4,
        d_ff: int = 512,
        n_layers: int = 6,
        max_len: int = 256,
        dropout: float = 0.1,
    ):
        super().__init__()
        self.tok_emb = nn.Embedding(vocab_size, d_model)
        self.pos_enc = PositionalEncoding(d_model, max_len)
        self.blocks = nn.ModuleList(
            [Block(d_model, n_heads, d_ff, max_len, dropout) for _ in range(n_layers)]
        )
        self.ln_f = nn.LayerNorm(d_model)
        self.head = nn.Linear(d_model, vocab_size, bias=False)
        # 权重共享:输入 embedding 和输出 head 共享权重(BERT/GPT 常用 trick)
        self.head.weight = self.tok_emb.weight
        self.apply(self._init_weights)

    def _init_weights(self, m):
        if isinstance(m, nn.Linear):
            nn.init.normal_(m.weight, std=0.02)
            if m.bias is not None:
                nn.init.zeros_(m.bias)
        elif isinstance(m, nn.Embedding):
            nn.init.normal_(m.weight, std=0.02)

    def forward(self, idx, targets=None):
        x = self.tok_emb(idx)
        x = self.pos_enc(x)
        for blk in self.blocks:
            x = blk(x)
        x = self.ln_f(x)
        logits = self.head(x)
        loss = None
        if targets is not None:
            loss = F.cross_entropy(logits.view(-1, logits.size(-1)), targets.view(-1))
        return logits, loss

    @torch.no_grad()
    def generate(self, idx, max_new_tokens: int = 100, temperature: float = 1.0, top_k: int = 20):
        """自回归生成,带 temperature + top-k 采样。"""
        for _ in range(max_new_tokens):
            idx_cond = idx[:, -self.pos_enc.pe.size(1) :]
            logits, _ = self(idx_cond)
            logits = logits[:, -1, :] / temperature
            if top_k:
                v, _ = torch.topk(logits, min(top_k, logits.size(-1)))
                logits[logits < v[:, [-1]]] = float("-inf")
            probs = F.softmax(logits, dim=-1)
            nxt = torch.multinomial(probs, num_samples=1)
            idx = torch.cat([idx, nxt], dim=1)
        return idx


# ---------- 3. 训练循环 ----------
def main():
    torch.manual_seed(0)
    ds = CharDataset("data/input.txt", block_size=64)
    device = "cuda" if torch.cuda.is_available() else "cpu"
    model = MiniTransformer(vocab_size=ds.vocab_size).to(device)
    print(f"[model] params={sum(p.numel() for p in model.parameters()) / 1e6:.2f}M device={device}")

    opt = torch.optim.AdamW(model.parameters(), lr=3e-4, weight_decay=0.1)
    steps = 1000
    batch_size = 32
    t0 = time.time()
    for step in range(steps):
        x, y = ds.get_batch(batch_size)
        x, y = x.to(device), y.to(device)
        _, loss = model(x, y)
        opt.zero_grad()
        loss.backward()
        torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
        opt.step()
        if step % 100 == 0 or step == steps - 1:
            dt = time.time() - t0
            print(f"step {step:4d} loss={loss.item():.4f} elapsed={dt:.1f}s")
    torch.save({"model": model.state_dict(), "stoi": ds.stoi, "itos": ds.itos}, "ckpt.pt")
    print("[done] saved ckpt.pt")


if __name__ == "__main__":
    main()

8.4 推理脚本 sample.py

"""用训练好的 ckpt.pt 生成文本。"""
from train import MiniTransformer

ckpt = torch.load("ckpt.pt", map_location="cpu")
stoi, itos = ckpt["stoi"], ckpt["itos"]
model = MiniTransformer(vocab_size=len(itos))
model.load_state_dict(ckpt["model"])
model.eval()

prompt = "静夜思\n"
idx = torch.tensor([[stoi[c] for c in prompt]], dtype=torch.long)
out = model.generate(idx, max_new_tokens=120, temperature=0.8, top_k=10)[0].tolist()
print("=== generated ===")
print("".join(itos[i] for i in out))

8.5 运行 & 预期输出

mkdir -p data
# (把 8.2 节的 input.txt 写进去)
python train.py

预期输出(CPU 大约 30-60 秒,MPS/GPU 几秒):

[data] chars=28 len=87
[model] params=1.21M device=cpu
step    0 loss=3.3321 elapsed=0.0s
step  100 loss=2.7102 elapsed=2.1s
step  200 loss=2.3504 elapsed=4.0s
...
step  900 loss=1.8421 elapsed=32.5s
step  999 loss=1.7903 elapsed=36.0s
[done] saved ckpt.pt

python sample.py
=== generated ===
静夜思
床前明月光,疑是地上霜。
举头望明月,低头思故乡。
春眠不觉晓,处处闻啼鸟。

截图位置:训练曲线(loss vs step)→ 推荐用 tensorboardmatplotlibloss.item() 画出来,存为 loss_curve.png。生成样例输出 → 直接拷贝终端文字即可。

8.6 调参建议

8.7 进阶方向

📚 上一节:七、本章小结 · 下一章:Agent 演化 - 从 LLM 到 Agentic Agent

九、视频学习:看大神手把手演示

文字看完理论后,看大神亲自手写一遍代码,理解速度翻倍。下面三个视频是 AI 基础领域公认最值得一看的:3Blue1Brown 用最直观的方式讲解神经网络,Andrej Karpathy 在 4 小时里从空文件手写一个完整的 GPT,以及他的姊妹篇 Tokenizer 详解。

YouTube ⏱ 19:13

But what is a neural network? | 深度学习系列 DL1

👤 3Blue1Brown (Grant Sanderson) 📅 2017-10-05 🔗 在原平台打开 ↗
展开介绍

3Blue1Brown 经典入门第一集。用 19 分钟手绘风格动画讲清神经网络核心:神经元 → 加权和 + 激活函数 → 层与层堆叠 → MNIST 手写数字识别。强烈建议先看这集建立直觉,再回头看本章的数学细节,效果翻倍。视频语言:英文(配中文字幕)。

YouTube ⏱ 04:33:00

Let's build GPT: from scratch, in code

👤 Andrej Karpathy (前特斯拉 AI 总监 / OpenAI 创始成员) 📅 2023-01-17 🔗 在原平台打开 ↗
展开介绍

4.5 小时硬核长片。Karpathy 从空文件起步,带你手写 Bigram Language Model → Self-Attention → Multi-Head → 完整 Decoder-only Transformer,每一步都在 Jupyter 里现场跑。配套 GitHub 仓库: github.com/karpathy/ng-video-lecture。看完这集,本章第 2-3 节所有公式都会变成你眼睛能看见的代码。视频语言:英文(配中文字幕)。

YouTube ⏱ 02:13:00

Let's build the GPT Tokenizer (minbpe)

👤 Andrej Karpathy 📅 2024-03-08 🔗 在原平台打开 ↗
展开介绍

Tokenizer 是 LLM 训练流水线最容易被忽视却影响最大的环节。Karpathy 带你从 0 写一个完整的 BPE 词表训练 + encode / decode 流程,然后揭示 10 个「为什么 LLM 不擅长 XX」的怪现象(为什么不会拼写、为什么中文消耗更多 token、为什么 SolidGoldMagikarp 会让模型崩溃)。视频语言:英文。

十、中文视频:跟李沐老师学动手

英文吃力?中文社区也有两位公认讲得最清楚的老师。前亚马逊首席科学家李沐老师的《动手学深度学习》课程是无数中国 AI 从业者的入门课,配套开源教材《Dive into Deep Learning》。

Bilibili ⏱ 15:48:22

动手学深度学习 PyTorch 版(完结合集 76 集)

👤 跟李沐学 AI(BosonAI 联合创始人 / 前亚马逊首席科学家) 📅 2021-04-06 🔗 在原平台打开 ↗
展开介绍

李沐老师经典中文 AI 入门课,76 集超 28 万人参与过直播,B 站回放上百万播放。覆盖线性神经网络 → MLP → CNN → RNN/LSTM/GRU → 注意力机制 → Transformer → 优化算法 → BERT → 计算机视觉 → NLP 全链路。配套开源教材:zh-v2.d2l.ai。视频语言:中文。