交互式大语言模型可视化实验室

LLM
可视化实验室

通过动画与交互深入理解大语言模型核心机制。从 Tokenization 到 Attention,从采样到推理,把 Transformer 黑箱拆成你能看见的零件。

8
可视化模块
20+
交互参数
100%
面试覆盖
0
外部依赖
MODULE 01

Tokenizer 分词 (Tokenization)

将文本切分为 Token 序列。BPE / WordPiece / SentencePiece 是主流分词算法,分词粒度直接影响模型的词汇表大小与处理效率。

说明

输入文本被按不同算法切分为 Token。BPE 从字符级别开始,反复合并最高频字节对;WordPiece 类似但用似然度评分;SentencePiece 直接在原始文本上操作,不依赖空格。

面试要点

  • BPE:贪心合并最高频字节对,GPT 系列使用
  • WordPiece:用似然度评分选择合并,BERT 使用
  • SentencePiece:语言无关,支持中日韩等非空格语言
  • 词汇表大小:通常 30K-100K,影响模型参数量与 OOV 率
python · BPE
class BPETokenizer:
    def __init__(self, vocab_size=32000):
        self.vocab_size = vocab_size
        self.merges = {}

    def train(self, corpus):
        # 迭代合并最高频 pair
        for _ in range(self.vocab_size):
            pairs = get_pair_counts(corpus)
            if not pairs: break
            best = max(pairs, key=pairs.get)
            self.merges[best] = best[0] + best[1]
MODULE 02

Embedding 嵌入 (Token Embedding)

将离散 Token ID 映射为稠密向量。向量空间中的几何关系编码了语义相似性,是连接符号世界与连续世界的桥梁。

说明

每个 Token ID 通过查找嵌入矩阵 E ∈ R^(V×d) 得到一个 d 维向量。高维空间中语义相近的词距离也相近,如 king - man + woman ≈ queen。

面试要点

  • 嵌入矩阵:形状 [vocab_size, d_model],是模型最大参数块
  • 位置编码:绝对 / 相对 / RoPE,注入序列顺序信息
  • 维度选择:7B 模型 d≈4096,70B 模型 d≈8192
  • 语义类比:vec(king) - vec(man) + vec(woman) ≈ vec(queen)
python · Embedding
import torch.nn as nn

class TokenEmbedding(nn.Module):
    def __init__(self, vocab_size, d_model):
        super().__init__()
        # 嵌入矩阵 [V, d]
        self.embedding = nn.Embedding(vocab_size, d_model)
        # 位置编码
        self.pos = RoPE(d_model)

    def forward(self, token_ids):
        x = self.embedding(token_ids)
        x = self.pos(x)
        return x  # [batch, seq, d]
MODULE 03

Self-Attention 自注意力

序列中每个位置关注所有位置,通过 Q·K^T 计算注意力权重,再用权重加权 V。这是 Transformer 的核心创新。

说明

Attention(Q,K,V) = softmax(QK^T/√d_k)V。Q 查询当前词需要什么信息,K 描述每个词能提供什么,V 是实际内容。缩放因子 √d_k 防止点积过大导致梯度消失。

面试要点

  • 缩放因子:除以 √d_k 防止点积过大
  • Causal Mask:下三角掩码,防止看到未来 token
  • 复杂度:O(n²·d),序列长度平方级
  • Softmax:将分数归一化为概率分布
python · Attention
def scaled_dot_product_attention(Q, K, V, mask=None):
    # Q,K,V: [batch, heads, seq, d]
    scores = Q @ K.transpose(-2, -1) / sqrt(d_k)
    if mask is not None:
        scores = scores.masked_fill(mask==0, -inf)
    attn = softmax(scores, dim=-1)
    return attn @ V  # [batch, heads, seq, d]
MODULE 04

Multi-Head Attention 多头注意力

将 Q/K/V 拆分为多个头,每个头独立计算注意力,使模型能同时关注不同子空间的信息。

说明

多头注意力将 d_model 维向量拆成 h 个 d_k=d/h 维子空间。每个头学习不同的注意力模式——有的关注语法,有的关注语义,有的关注长距离依赖。

面试要点

  • 头数:GPT-3 用 96 头,Llama-2-7B 用 32 头
  • 维度拆分:d_k = d_model / n_heads
  • 输出合并:concat 后乘 W_O 回到 d_model
  • 并行性:各头完全并行,无信息交换
python · MultiHead
class MultiHeadAttention(nn.Module):
    def __init__(self, d_model, n_heads):
        self.n_heads = n_heads
        self.d_k = d_model // n_heads
        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_o = nn.Linear(d_model, d_model)

    def forward(self, x):
        B, S, D = x.shape
        Q = self.W_q(x).view(B,S,H,D//H).transpose(1,2)
        # Q: [B, H, S, d_k]
        return self.W_o(attn.reshape(B,S,D))
MODULE 05

采样策略 (Sampling Strategies)

模型输出 logits 后如何选择下一个 Token?Greedy、Temperature、Top-k、Top-p 各有取舍,决定生成的确定性与多样性。

说明

Greedy 直接选最高概率 token,输出确定但容易重复。Temperature 缩放 logits 控制锐度。Top-k 只在概率最高的 k 个中采样。Top-p (nucleus) 选择累积概率达 p 的最小集合。

面试要点

  • Temperature:T<1 更确定,T>1 更随机,T=0 退化为 Greedy
  • Top-k:固定候选数量,忽略尾部但可能截断高概率区
  • Top-p:自适应候选数量,保留长尾但上限不定
  • 典型配置:T=0.7, top_p=0.9 是常见平衡点
python · Sampling
def sample(logits, temp=1.0, top_k=0, top_p=1.0):
    # Temperature 缩放
    logits = logits / temp
    # Top-k 截断
    if top_k > 0:
        vals, idx = logits.topk(top_k)
        logits[logits < vals[-1]] = -inf
    # Top-p 截断
    probs = softmax(logits)
    sorted_p, sorted_i = probs.sort(descending=True)
    cumsum = sorted_p.cumsum()
    mask = cumsum - sorted_p > top_p
    sorted_p[mask] = 0
    return multinomial(sorted_p, 1)
MODULE 06

KV-Cache 键值缓存

自回归生成时缓存已计算的 K 和 V,避免重复计算,是推理加速的核心技术。将 O(n²) 降为 O(n)。

说明

每生成一个 token,只需计算新 token 的 Q 与缓存中所有 K 做注意力。K/V 增量追加到缓存中,无需重新计算前面所有 token。这使得每个解码步从 O(n²) 降为 O(n)。

面试要点

  • 加速比:长序列推理可加速 10-50x
  • 显存开销:缓存大小 ∝ 2 × n_layers × seq × d × 精度
  • PagedAttention:vLLM 的分页缓存管理,碎片化解决
  • 量化缓存:FP8/INT8 缓存减少显存占用
python · KV-Cache
class KVCache:
    def __init__(self, n_layers, max_seq, d):
        self.cache = [
            (zeros(max_seq, d), zeros(max_seq, d))
            for _ in range(n_layers)
        ]
        self.pos = 0

    def update(self, layer, new_k, new_v):
        # 追加新 K/V 到缓存末尾
        k_cache, v_cache = self.cache[layer]
        k_cache[self.pos] = new_k
        v_cache[self.pos] = new_v
        return k_cache[:self.pos+1]
MODULE 07

训练过程 (Training Pipeline)

大模型训练三阶段:预训练学语言、指令微调学对话、RLHF/DPO 对齐人类偏好。每阶段目标与数据截然不同。

说明

预训练用海量无标注文本做下一 token 预测,学习通用语言能力。SFT 用指令-回答对微调,学会遵循指令。RLHF 用奖励模型优化策略,使输出更符合人类偏好。DPO 跳过奖励模型直接优化。

面试要点

  • 预训练:Next-token prediction, ~90% 计算量
  • SFT:指令数据 10K-100K 条即可有效
  • RLHF:Reward Model + PPO,三阶段最复杂
  • DPO:无需 RM,直接偏好优化,更简单稳定
python · DPO Loss
def dpo_loss(policy_chosen, policy_rejected,
             ref_chosen, ref_rejected, beta=0.1):
    # 策略模型与参考模型的 log-prob 差
    pi_logratios = policy_chosen - policy_rejected
    ref_logratios = ref_chosen - ref_rejected

    logits = pi_logratios - ref_logratios
    # Bradley-Terry 损失
    loss = -F.logsigmoid(beta * logits).mean()
    return loss
MODULE 08

推理流程 (Inference Pipeline)

自回归推理分两阶段:Prefill 并行处理 prompt,Decode 逐 token 生成。理解两阶段的资源特征是推理优化的基础。

说明

Prefill 阶段并行处理整个 prompt,计算密集型,GPU 算力瓶颈。Decode 阶段逐 token 生成,访存密集型,显存带宽瓶颈。KV-Cache 在 Prefill 填充,Decode 增量读取。

面试要点

  • Prefill:计算密集,parallel attention,高吞吐
  • Decode:访存密集,逐 token,低吞吐
  • Continuous Batching:动态拼批,提升 GPU 利用率
  • Speculative Decoding:小模型草拟+大模型验证,加速 2-3x
python · Inference
def generate(model, prompt_ids, max_tokens=100):
    # Phase 1: Prefill (并行处理 prompt)
    logits = model(prompt_ids)
    next_token = sample(logits[:, -1])
    generated = [next_token]

    # Phase 2: Decode (逐 token 生成)
    for _ in range(max_tokens - 1):
        logits = model(next_token, use_cache=True)
        next_token = sample(logits[:, -1])
        generated.append(next_token)
        if next_token == EOS: break
    return generated