LLM

整理各种面试中常见的八股问题,涵盖 Python 基础、PyTorch 基础、算法与数据结构等内容

LLM

Attention

  • 点乘注意力中,QKV 矩阵都是什么作用?为什么 Q 和 K 不共用一个?

  • 点乘注意力为什么要 scale?scale 为什么是 dk\sqrt {d_k}?为什么不直接除以 dkd_k 或者其他数值?为什么不做 scale?

    • 为了防止点积结果方差随维度 dkd_k 变大而爆炸,导致 softmax 进入饱和区、梯度消失,所以要除以 dk\sqrt {d_k} 做归一化

    • 根据原始点乘注意力

      Attention(Q,K,V)=softmax(QK⊤dk)V\text{Attention}(Q,K,V) = \text{softmax}\left (\frac {QK^\top}{\sqrt {d_k}}\right) V
    • 如果不做 scale,就是:

      QK⊤QK^\top
    • 假设 Q,K 里的每个元素都是均值 0、方差 1 的独立随机变量,对单个向量 q∈Rdkq\in\mathbb {R}^{d_k} 和 k∈Rdkk\in\mathbb {R}^{d_k} 的点积:

      q⋅k=∑i=1dkqikiq\cdot k = \sum_{i=1}^{d_k} q_i k_i
    • 单个 qikiq_i k_i 的方差:V[qiki]=E[qi2]E[ki2]=1⋅1=1\mathbb {V}[q_i k_i] = \mathbb {E}[q_i^2]\mathbb {E}[k_i^2] = 1\cdot1=1

    • 一共 dkd_k 项独立相加,所以整体方差为:

      V[q⋅k]=dk\mathbb {V}[q\cdot k] = d_k
    • 也就是说维度 dkd_k 越大,点积的方差越大,数值越容易变得很大

    • 当数值太大时,经过 Softmax 后的分布会极度尖锐,几乎变成 one-hot 向量,梯度会变得极小,出现梯度消失,训练不动

    • 因此需要对点积进行缩放,而我们希望缩放后,点积的方差回到 1,设缩放因子为 c:

      V[q⋅kc]=1c2V[q⋅k]=dkc2\mathbb {V}\left [\frac {q\cdot k}{c}\right] =\frac {1}{c^2}\mathbb {V}[q\cdot k] =\frac {d_k}{c^2}
    • 令方差为 1:

      dkc2=1⇒c=dk\frac {d_k}{c^2}=1 \Rightarrow c=\sqrt {d_k}
    • 所以除以 dk\sqrt {d_k},可以让点积的方差稳定在 1,与维度无关

  • 注意力中有哪些类型的掩码?Padding Mask/Sequence Mask/Casual Mask 的作用?

    • 在 Transformer 注意力里,掩码本质就是把某些位置的注意力分数强行设为 −∞-\infty,让 softmax 后权重变成 0,从而 “看不见” 这些位置
    • Padding Mask(填充掩码):把句子末尾的 padding 位置遮住,不让注意力关注无效的填充位
    • Sequence Mask / Key Mask(序列掩码 / Key 掩码):对 key 序列中无效位置做掩码,让 query 不关注这些 key
    • Causal Mask(因果掩码 / Look-ahead Mask):Decoder 自注意力中,禁止当前位置看到未来位置的 token,保证自回归生成,本质上是一个下三角掩码,上三角全部设置为 −∞-\infty,保证每个位置只关注自己及之前的 token
  • Attention 计算复杂度,为什么很多时候 attention 不是瓶颈,反而是 MLP?在什么条件下 attention 会变成瓶颈?

    • 标准 attention 的复杂度是 O(s2⋅h)O (s^2 \cdot h),其中 s 是序列长度,h 是 hidden size
    • 而 FFN 的复杂度是 O(s⋅h2)O (s \cdot h^2),因为通常中间层是 4h(甚至更大)
    • 关键在于对比 n 和 d 的大小:在 LLM 训练里,常见情况是 h≫sh \gg s(例如 h=4096,s=2k),此时 h2≫shh^2 \gg sh,所以 FFN FLOPs 明显更多,所以在很多实际系统里,MLP 才是主要算力消耗者
    • 但当 s 很大时(比如长上下文 32k、128k),attention 的 s2s^2 开始爆炸,这时 attention 会成为瓶颈,甚至主导计算和显存
  • Attention 是 memory-bound 还是 compute-bound?

    • 理论上 attention 有很多矩阵乘,是 compute-heavy,但在实际 GPU 上往往是 memory-bound
    • 原因在于:
      • QKTQK^T、softmax、再乘 V,这些步骤之间需要频繁读写中间结果
      • softmax 需要做 reduce(max /sum),带来同步和访存压力
      • 序列维度访问模式不连续
    • 这也是为什么像 FlashAttention 这种方法,通过 “tile + on-chip memory(SRAM)复用” 来减少 HBM 访问,可以显著提速
  • 什么是 MHA?为什么要用多个注意力头?MHA 有什么缺点?

    • MHA 即 Multi-Head Attention,多头注意力,是 Transformer 的核心组件
    • 把 Q、K、V 分成 h 个 “头” 并行做 scaled dot-product attention,最后拼接所有头的输出并做一次线性变换
    • 为什么要用多个注意力头:
      • 单头注意力只能学到一种全局注意力模式,多头可以学到多组独立的注意力分布
      • 让模型能同时关注不同位置、不同类型的依赖关系(比如语法依赖、语义关联、长距离依赖)
      • 提升模型表达能力,让不同头专注不同子任务(如位置聚焦、语义匹配、指代消解)
      • 并行计算效率高,不显著增加计算量就能大幅提升效果
    • 缺点
      • KV Cache 显存占用极高:推理时每个头都要独立存储 K、V,序列变长时显存开销呈线性增长
      • 访存压力大:HBM 读写次数多,推理带宽受限,成为速度瓶颈
      • 计算冗余:多头注意力中部分头学到的模式高度相似,存在冗余计算
  • 什么是 MQA?什么是 GQA?

    • MQA
      • 所有 query 共享同一组 KV,只保留一套 KV 投影
      • KV Cache 显存大幅减少,推理速度显著提升,访存压力骤减
      • 效果相比 MHA 有明显下降,注意力表达能力受限
    • GQA
      • 将 Query 头分为若干组,每组内共享一套 K、V,介于 MHA 和 MQA 之间
      • 平衡速度、显存与精度,是当前大模型主流方案
  • 什么是 MLA?MLA 的原理是什么?

    • MLA 解决了传统注意力中 KV Cache 过大的问题,核心做了两件事:

      • 共享低维潜在向量:用一个低维向量 ctKV\mathbf {c}_t^{KV} 同时生成 K 和 V,避免重复缓存
      • 位置信息分离:将位置编码(RoPE)从主 K 中分离,单独缓存无位置的压缩向量,大幅降低显存占用
    • Query 侧:内容分支 + 位置分支分开构造

      ctQ=WDQhtqtC=WUQctQqtR=RoPE(WQRctQ)qt,i=[qt,iC;qt,iR]\begin {align*} \mathbf {c}_t^Q &= W^{DQ} \mathbf {h}_t\\ \mathbf {q}_t^C &= W^{UQ} \mathbf {c}_t^Q\\ \mathbf {q}_t^R &= \text{RoPE}(W^{QR} \mathbf {c}_t^Q)\\ \mathbf {q}_{t,i} &= [\mathbf {q}_{t,i}^C; \mathbf {q}_{t,i}^R] \end {align*}
      • 内容分支:qtC\mathbf {q}_t^C 负责语义信息,从压缩向量 ctQ\mathbf {c}_t^Q 生成
      • 位置分支:qtR\mathbf {q}_t^R 负责位置信息,通过 RoPE 编码
      • 最终 Query 是两个分支的拼接,兼顾内容与位置表达
    • Key/Value 侧:低维压缩 + 位置解耦

      ctKV=WDKVhtktC=WUKctKVktR=RoPE(WKRht)kt,i=[kt,iC;ktR]vtC=WUVctKV\begin {align*} \mathbf {c}_t^{KV} &= W^{DKV} \mathbf {h}_t\\ \mathbf {k}_t^C &= W^{UK} \mathbf {c}_t^{KV}\\ \mathbf {k}_t^R &= \text{RoPE}(W^{KR} \mathbf {h}_t)\\ \mathbf {k}_{t,i} &= [\mathbf {k}_{t,i}^C; \mathbf {k}_t^R]\\ \mathbf {v}_t^C &= W^{UV} \mathbf {c}_t^{KV} \end {align*}
      • 关键压缩点:ctKV\mathbf {c}_t^{KV} 是低维共享向量,推理时只缓存这一个向量,而不是缓存完整的 K 和 V
      • 位置分支分离:ktR\mathbf {k}_t^R 直接从原始 ht\mathbf {h}_t 生成并加 RoPE,不需要缓存,推理时可动态生成
      • 显存收益:传统 KV Cache 大小为 2 × seq_len × hidden_dim,MLA 仅需 seq_len × d_c(d_c 是低维压缩维度,远小于 hidden_dim),显存可降低 70% 以上
    • 注意力计算

      ot,i=∑j=1tSoftmaxj(qt,iTkj,idh+dhR)vj,iCut=WO[ot,1;ot,2;...;ot,nh]\begin {align*} \mathbf {o}_{t,i} &= \sum_{j=1}^t \text{Softmax}_j\left (\frac {\mathbf {q}_{t,i}^T \mathbf {k}_{j,i}}{\sqrt {d_h + d_h^R}}\right) \mathbf {v}_{j,i}^C\\ \mathbf {u}_t &= W^O [\mathbf {o}_{t,1}; \mathbf {o}_{t,2}; ...; \mathbf {o}_{t,n_h}] \end {align*}
      • 注意力分数同时考虑内容分支和位置分支,缩放因子是两个分支维度和的平方根
      • Value 直接使用从 ctKV\mathbf {c}_t^{KV} 生成的 vtC\mathbf {v}_t^C,不需要额外缓存
  • 为什么 RoPE 不能直接对 MLA 中低秩的 Q/K 做位置编码?

    • 无 RoPE 时:矩阵吸收 + 低秩压缩(MLA 的基础)

      • 在不加入 RoPE 的情况下,MLA 能实现极致的 KV Cache 压缩,核心就是矩阵吸收计算:

        qt,iT×kj,i=(W(i)UQctQ)T×W(i)UKcjKV=(ctQ)T×(W(i)UQ)TW(i)UK×cjKV\begin {align*} q_{t,i}^T \times k_{j,i} &= (W^{UQ}_{(i)} c_t^Q)^T \times W^{UK}_{(i)} c_j^{KV} \\ &= (c_t^Q)^T \times (W^{UQ}_{(i)})^T W^{UK}_{(i)} \times c_j^{KV} \end {align*}
      • (W(i)UQ)TW(i)UK(W^{UQ}_{(i)})^T W^{UK}_{(i)} 是一个固定矩阵,可以提前计算好

      • 推理时,只需要缓存低维的 cjKV\mathbf {c}_j^{KV},而不是高维的完整 Key

      • 直接用预计算的矩阵和 cjKV\mathbf {c}_j^{KV} 做一次矩阵乘法,就能还原出 Key 的内积结果

      • 缓存的是低维 ctKV\mathbf {c}_t^{KV}(维度 4dh4d_h),而不是高维 Key(维度 dh×nhd_h\times n_h)

      • KV Cache 显存占用直接降低了 12 倍,这就是 MLA 压缩的核心原理

    • 加入 RoPE 后:矩阵吸收失效

      • 当直接对 Key 应用 RoPE 时,矩阵吸收的前提被破坏了:

        qt,iT×kj,i=(RtW(i)UQctQ)T×RjW(i)UKcjKV=(ctQ)T×(W(i)UQ)TRtTRjW(i)UK×cjKV\begin {align*} q_{t,i}^T \times k_{j,i} &= (\mathcal {R}_t W^{UQ}_{(i)} c_t^Q)^T \times \mathcal {R}_j W^{UK}_{(i)} c_j^{KV}\\ &= (c_t^Q)^T \times (W^{UQ}_{(i)})^T \mathcal {R}_t^T \mathcal {R}_j W^{UK}_{(i)} \times c_j^{KV} \end {align*}
      • 中间的 (W(i)UQ)TRt−jW(i)UK(W^{UQ}_{(i)})^T \mathcal {R}_{t-j} W^{UK}_{(i)} 是随相对位置变化的,不是固定矩阵

      • RoPE 矩阵插在了两个权重矩阵之间,而矩阵乘法不满足交换律,无法再提前计算吸收

      • 这意味着必须为每个 token 重新计算完整的 RoPE Key,压缩收益完全消失,推理效率大幅下降

    • 为了解决 RoPE 与低秩压缩的矛盾,MLA 设计了双分支拼接结构:

      qt,iT×kj,i=[qt,iC;qt,iR]T×[kj,iC;ktR]=qt,iCkj,iC+qt,iRktRq_{t,i}^T \times k_{j,i} = [q_{t,i}^C; q_{t,i}^R]^T \times [k_{j,i}^C; k_t^R] = q_{t,i}^C k_{j,i}^C + q_{t,i}^R k_t^R
    • 分支双工

      分支生成方式缓存与否作用核心特点
      内容分支(C)qc,Kcq_c,K_c 从低维 cc 生成缓存 cKVc_{KV}承载语义信息可矩阵吸收,保持低秩压缩
      位置分支(R)qR,KRq_R,K_R 直接从 hth_t 生成并加 RoPE不缓存承载位置信息采用 MQA 范式,维度很小(64)
  • MLA 的显存占用和 KV Cache 大小如何?

    • 显存占用(以 DeepSeek-V2 为例)
      • ctKVc_t^{KV}:维度 4dh4d_h=512(低秩压缩向量,核心缓存)
      • ktRk_t^R:维度 dh/2d_h/2=64(共享位置分支,不缓存,动态生成)
    • KV Cache 大小
      • MLA 的 KV Cache 维度只跟潜在向量维度 d_c 有关,跟注意力头数 n_heads 完全无关
      • 普通 MHA / GQA / MQA 的 KV 维度都是头数 × 头维度,头越多,KV Cache 越大
      • MLA 完全不一样:
        • 所有头 共享同一个低维潜在向量 ctKVc_t^{KV},不管多少头,缓存的都是这个向量
        • K 和 V 都是 从这个 c 还原出来的,每个头用不同的投影矩阵从 ctKVc_t^{KV} 里提取自己的 K/V
        • 缓存的只有这个 c,不存任何按头分开的 K、V
  • MLA 的缺点是什么?

    • 推理实现复杂
      • MLA 不是标准 attention 结构,无法直接用 FlashAttention、FlashAttention-2、xFormers 等高度优化的核,必须自己写 双分支 QKV 构造 + 分拆注意力计算
      • 推理引擎(vLLM、TGI、TensorRT-LLM)都要专门适配 MLA,原生不支持
    • RoPE 必须解耦,不能简单套用
      • 位置分支不缓存,每次都要重新计算,增加一点点推理延迟
    • 计算量并没有减少,同时扩展性也不如 GQA/MQA 灵活,想微调精度 / 速度,只能改 dcd_c(潜在维度),不如 GQA 直观
    • 双分支 + 动态位置分支,访存模式更零散,对硬件不友好,老卡更慢

Tokenizer

  • 有哪些分词器算法?各自的优缺点是什么?
    • BPE(Byte Pair Encoding)
      • 实现流程:
        • 初始化词表:所有单个字符 + 基础符号
        • 统计语料中相邻字符 / 子词对的出现频率
        • 选择频率最高的子词对合并成新子词,加入词表
        • 重复合并,直到词表达到预设大小
      • 贪心合并,无概率建模,不一定最优
    • WordPiece(BERT 所用)
      • 实现流程
        • 初始词表 = 所有单个字符
        • 每次选择能最大程度提升语料似然概率的子词对合并
        • 用 ## 前缀标记非词首的子词(如 playing → play ##ing)
      • 按概率 / 似然合并,语义拆分更合理
      • 单语言表现好,多语言较弱
    • Unigram LM(T5、Albert、部分日文分词)
      • 实现流程:
        • 从大词表(大量子词)开始
        • 用 Unigram 模型计算每个句子的生成概率
        • 计算去掉每个子词后对整体似然的损失
        • 删除损失最小的一部分子词(如 20%)
        • 反复裁剪,直到目标词表大小
      • 基于概率的 Viterbi 分词,不是贪心,可输出最优拆分
      • 概率建模,分词结果更优、更稳定,多语言效果也较好
      • 训练比较慢,实现比较复杂
    • SentencePiece(Google,常用于 LLaMA、Mistral)
      • 不是新算法,是一体化实现工具,内置了各种分词算法实现
      • 实现流程:
        • 把文本当作 Unicode 字节流,不依赖空格分词
        • 自动处理多语言、大小写、标点
        • 用特殊符号▁表示空格,统一处理空格与子词
  • 词表的大小应该怎么选择?
    • 通用原则:在覆盖度、粒度、计算开销之间做平衡
    • 英文单语言:通常 32k ~ 64k 最优
    • 多语言:一般 128k ~ 256k
    • 中文:建议 40k ~ 80k,太小会切得过碎,太大冗余高
    • 过小:序列变长、上下文利用率变低、信息密度下降
    • 过大:embedding 层参数量暴涨、训练更难收敛、冗余子词多
  • BPE 有哪些参数可以调整?
    • vocab_size:目标词表大小
    • min_frequency:合并的最低频率阈值,对于中文一般使用 0.995
  • 什么是 OOV?分词器如何处理未登录词?
    • OOV = Out-of-Vocabulary,未登录词:词表里不存在的词 / 符号
    • 现代子词分词器几乎不会输出 <unk>,处理方式:
      • 不断拆分成更小的子词,直到拆成单个字节
      • 最终兜底是 UTF-8 字节级别的 token
    • 旧式词表(全词分词):直接映射为 <unk>,信息完全丢失
    • 结论:BPE/WordPiece/Unigram 理论上无 OOV,只有粒度问题
  • 为什么中文分词比英文更难?对模型性能有什么影响?
    • 英文天然有空格分隔单词,中文无显式边界
    • 中文语义高度依赖单字组合,歧义多
    • 字符集巨大,生僻字多,Unicode 复杂
  • 分词不一致为什么会导致模型推理效果骤降?
    • 训练与推理使用不同分词器 / 词表,token ID 完全不匹配
    • 相同文本被切成完全不同的序列,语义分布与训练分布偏移
  • 长文本场景下,分词粒度如何影响上下文长度利用率?
    • 词表过大,稀有词、专业词、生僻字仍会被切碎,词表冗余高,embedding 负担大
    • 词表过细,常用词也被切成碎片,序列长度急剧膨胀;同样 max_seq_len 能承载的语义内容大幅减少,长文本建模能力下降
    • 常用词尽量成词,稀有词自动拆分,在相同长度下塞更多语义
  • 词表大小如何影响显存、参数量、训练速度?
    • 直接影响输入层和输出层的参数量,占用显存也会相应变化
    • 前向 / 反向时 embedding 与 lm_head 计算量增加,词表过大明显拖慢训练与推理吞吐

Transformer

  • Transformer 的基本结构

    • Transformer 的核心可以看成是「Attention + MLP」的堆叠
    • 每一层通常包含两大块:Multi-Head Self-Attention 和 Feed-Forward Network(FFN),每个子层外面包一层残差连接,再加归一化(现在主流是 Pre-LN)
    • Self-Attention 负责在序列维度上做信息交互,本质是让每个 token 根据内容去 “选择性读取” 其他 token;FFN 则是在 token 内部做非线性变换,相当于对每个位置做一个共享的 MLP,提升表达能力
    • 整体结构其实非常 “干净”:没有卷积的局部性约束,也没有 RNN 的时间依赖,完全依赖 attention 做全局建模
  • 为什么 Transformer 不用卷积 / RNN?

    • inductive bias:模型 “天生自带” 的先验假设 / 偏好,决定了模型更愿意学什么样的函数,更不信什么样的函数
    • 给模型一堆数据,理论上有无穷多种函数都能拟合这些数据,但模型会优先选某一类,这就是它的归纳偏置
      • 有偏置 → 模型缩小假设空间,更快学到真实规律
      • 归纳偏置 = 模型的 “先验世界观”
    • RNN
      • RNN 的问题本质是 “串行依赖”,它的归纳偏置是序列有序性和时序依赖,每一步都依赖前一步,导致无法高效并行
      • 同时长序列上存在梯度消失 / 爆炸问题,虽然 LSTM/GRU 有缓解,但本质没变
    • CNN
      • 卷积的问题是 “感受野受限 + inductive bias 太强”,它的归纳偏置是局部性(相信附近的像素 / 特征关系更紧密)、权值共享(相信同一模式在不同位置都会出现)、平移不变性
      • 虽然可以堆很多层扩大感受野,但远距离依赖建模仍然不如 attention 直接,而且卷积假设局部平移不变性,这对语言并不完全成立
    • Transformer 的优势在于几乎没有强结构偏置,使用注意力建模 token 之间的关联,使用位置编码进行
      • 第一,完全并行(序列维度可以一次性算)
      • 第二,attention 天然是全局的
      • 第三,表达能力更灵活,不强加局部或顺序 bias
  • 对比一下 Batch Norm 和 Layer Norm

    • Batch Norm(BN)
      • 沿 batch 维度做归一化,对同一个特征通道、不同样本做标准化
      • 均值方差在 (B, L) 上计算,对每个维度 d 独立归一化
      • 训练时用 batch 统计量,推理时用训练时固定的滑动均值 / 方差
      • 严重依赖 batch 内样本分布一致,且依赖 batch size,batch 越小,统计噪声越大,效果急剧下降
      • 自带强正则化,依赖 batch 内样本间信息
    • Layer Norm(LN)
      • 沿 feature 维度做归一化,对同一个样本、不同特征通道做标准化
      • 均值方差在 D 上计算,对每个样本 (B, L) 独立归一化
      • LN 只依赖单样本,与 batch 无关,batch size 不影响效果
      • 训练和推理计算方式完全一致,无滑动统计、无状态保存
      • 正则化较弱,更侧重稳定梯度、加速收敛
  • Transformer 为什么使用 LN 不用 BN?

    • BN 按批次归一,受 padding、小 batch、变长序列影响极大,NLP 不稳定
    • LN 按单样本自身特征归一,不受批次和长度影响,训练推理一致
    • Transformer 样本独立、全局注意力、小 batch 多,LN 更稳定、好训练
  • 为什么用 Pre-LN 不用 Post-LN?为什么 Pre-LN 更稳定?为什么 Post-LN 不稳定?为什么 Post-LN 会梯度爆炸 / 消失?

    • 结构对比
      • Post-LN(原始 Transformer):x → 子层(Attn/FFN)→ 残差相加 → LayerNorm —— LN (x + f (x))
      • Pre-LN(主流大模型:LLaMA、GPT 等):x → LayerNorm → 子层 → 残差相加 —— x + f (LN (x))
    • 残差路径与梯度传播
      • Pre-LN 的残差分支是纯恒等映射(identity path):梯度可以不经任何变换直接从顶层传回底层,路径干净、增益接近 1,让 f (x) 始终在稳定、标准化的特征空间内学习,输出幅值可控,从而使得深层网络也能保持梯度稳定,不容易消失或爆炸
      • Post-LN 的残差相加后还要经过 LN,LN 会对张量做缩放与归一化,导致梯度在反向传播时被反复缩放、叠加放大 / 衰减,层数越深,梯度累积畸变越严重
    • 梯度消失 / 爆炸(Post-LN)
      • Post-LN 中,子层输出 f (x) 会直接累加到 x 上,再进入 LN,随着层数加深,f (x) 的累积幅值容易失控,LN 为了压制幅值会不断缩放,导致梯度被持续衰减 → 梯度消失
      • 反之,若初始化不当,残差和幅值快速放大,经过 softmax 等模块会出现极端值,梯度被指数级放大 → 梯度爆炸
      • 深层 Transformer 下,这种效应被逐层放大,训练极其不稳定,loss 容易震荡、NaN
  • 为什么用 RMSNorm 而不用 LayerNorm?为什么去掉 mean 只用 variance 就够?

    • 公式对比

      • LayerNorm:先减均值,再除方差,含平移 + 缩放两步

        LayerNorm(x)=x−μσ2+ϵ∗γ+β\text{LayerNorm}(x) = \frac {x - \mu}{\sqrt {\sigma^2 + \epsilon}} * \gamma + \beta
      • RMSNorm:只做缩放,不移位,去掉均值

        RMSNorm(x)=xRMS(x)2+ϵ∗γRMS(x)=1d∑i=1dxi2\text{RMSNorm}(x) = \frac {x}{\sqrt {\text{RMS}(x)^2 + \epsilon}} * \gamma\\ \text{RMS}(x) = \sqrt {\frac {1}{d}\sum_{i=1}^d x_i^2}
    • 为什么去掉 mean 依然有效?

      • RMSNorm 通过保持非零均值而不强制均值对零的对齐,有效缓解了梯度消失问题
      • LayerNorm 的零均值对齐可能导致梯度在反向传播过程中逐层衰减
      • 与 LayerNorm 相比,RMSNorm 省去了均值计算、平移操作,减少访存和算子,训练 / 推理速度提升
    • 本质原因

      • 归一化的核心作用是稳定特征幅值、防止梯度异常,而控制幅值只需要方差缩放即可
      • LayerNorm 中的减均值在高维 Transformer 特征中是冗余操作,RMSNorm 保留核心、去掉冗余操作,更适合大规模深度模型训练
  • FFN 的作用是什么?

    • Attention 负责在不同 token 之间交换信息、建立上下文关联,本质是对序列做加权融合,本身缺乏强非线性的表达
    • FFN 则对每个 token 的特征独立地进行高维的映射与激活,为模型引入非线性表达能力,负责特征变换与模式拟合,提升表达上限
    • 缺少 FFN 会导致模型拟合能力严重不足,难以学习复杂语言模式与语义知识
  • 为什么现在主流模型都使用 Gated FFN(如 SwiGLU)?

    • 门控 FFN 结构可统一表示为:

      FFN(x)=(xW1⊙act(xW2))W3\text{FFN}(x) = \left (x W_1 \odot \text{act}(x W_2)\right) W_3
    • 通过门控机制对不同特征通道做动态选择与加权,让模型自适应控制信息流通,比普通 ReLU/GELU FFN 表达更精细、效率更高

    • 在相同参数量或计算量下,门控 FFN 效果显著优于传统 FFN,收敛更快且泛化更强,已成为 LLaMA、PaLM、GPT 等大模型标配

  • 为什么要用残差连接?

    • 残差的核心作用是提供一条恒等映射的直达路径(identity path),让信息和梯度可以无损穿过深层网络
    • 梯度传播:反向时梯度能直接沿残差分支传回底层,避免逐层衰减或放大,有效缓解梯度消失与梯度爆炸
    • 解决深层退化:没有残差时,网络越深训练误差反而越高,特征会被反复扭曲导致难以优化;残差让模型优先学习 “微小增量修正”,深层训练更稳定
    • 对 Transformer 尤其关键:层数动辄几十上百层,没有残差连接几乎无法收敛,训练极易震荡、崩 loss
  • 了解大模型的 Scaling Law(缩放定律)吗?Kaplan ?Chinchilla ?

    • 什么是 Scaling Law:大语言模型性能(损失 / 准确率)与参数量 N、数据量 D、计算量 C 呈幂律(Power Law)关系,可预测、可外推
    • 给定算力预算,Scaling Law 能够指导如何最优分配资源(模型大小 vs 数据量)
    • Kaplan Scaling Law(OpenAI)
      • 模型大小 > 数据量,算力应优先堆模型
      • 幂律公式(损失 L 越低越好)
        • L∝N−0.076L\propto N^{-0.076} (参数量)
        • L∝D−0.095L \propto D^{-0.095} (数据量)
        • L∝C−0.050L \propto C^{-0.050} (计算量)
      • 最优分配(给定算力 C)
        • N∝C0.73N \propto C^{0.73},D∝C0.27D \propto C^{0.27}
        • 算力 ×10 → 模型 ×5.5,数据 ×1.8
      • 理念:大模型数据效率更高,应做大模型、早停(不训到收敛)
      • 影响:GPT-3(175B, 300B tokens)、早期大模型普遍参数量大、数据少
    • Chinchilla Scaling Law(DeepMind)
      • 模型与数据应等比例扩展(1:1),早期模型普遍欠训练
      • 最优分配(给定算力 C)
        • N∝C0.5N \propto C^{0.5},D∝C0.5D \propto C^{0.5}
        • 算力 ×10 → 模型 ×3.2,数据 ×3.2
      • 黄金比例:每 1 参数 ≈ 20 训练 tokens
      • 验证:70B Chinchilla(1.4T tokens)= 280B Gopher 同算力,全面更强
      • 影响:LLaMA 系列、Llama 3、GPT-4 等均遵循小模型 + 多数据
  • 了解 Bert 模型吗?它和 Transformer、GPT 模型有什么区别?

    • Bert 是 Transformer Encoder 结构的模型,采用了双向 Transformer Encoder 堆叠
    • 预训练任务是 MLM(Masked Language Model)掩码语言模型和 NSP(Next Sentence Prediction)下一句预测
    • Bert 模型和 GPT 模型的区别
      • Bert 没有因果掩码,能看到完整上下文;GPT 带因果掩码,只能看到当前及左边 token,看不到未来
      • Bert 更擅长理解类任务:文本分类、情感分析、语义相似度、命名实体识别、抽取式问答,需要针对任务设计头部并微调
      • GPT 更擅长生成类任务:对话、续写、创作、摘要、翻译、代码生成,更适合零样本 / 小样本提示学习
    • Bert 的设计细节
      • 最终输入向量 = Token Emb + Segment Emb + Position Emb
      • Token Embedding:用 WordPiece 分词,包含 [CLS]、[SEP]、[MASK]、[UNK]、[PAD] 等特殊 token
      • Segment Emedding:只有 0 / 1 两种向量,用于区分两句话(NSP、QA、推理任务)
      • Position Embedding:可学习参数(非正弦),最大长度 512(BERT 上限),位置从 0 开始([CLS]=0)
    • [CLS] = Classification Token
      • 固定在序列第 0 位,无实际语义,专门用于句子级表示
      • [CLS] 能 Attend 到所有 Token(双向);在预训练时,[CLS] 输出接二分类层做下一句预测,从而强制学习全局语义用于 NSP 任务
      • 文本分类:取最后一层 [CLS] → 全连接层 → Softmax
      • 句对关系(NLI/QA):[CLS] 表示两句话整体关系
      • 命名实体识别:不用 [CLS],用每个 token 输出
      • 语义相似度:常用 [CLS] 或 均值池化(Mean Pooling)
    • [SEP]
      • 句子边界标记,帮助模型区分句 A / 句 B
      • [SEP] 只用于标记边界,不标记 token 归属;因此需要使用 Segment Embedding 告诉模型每个词属于哪一句
      • 同时,注意力机制是全局的,没有边界符号,模型会把两句话混在一起理解,因此也必须需要 [SEP]
  • 什么是 Temperature

    • Temperature(T)是用来控制概率分布 “陡峭 / 平坦” 的参数:

      • T 越小 → 分布越陡 → 模型越自信、越 deterministic、越重复
      • T 越大 → 分布越平 → 模型越随机、越多样、越容易胡说
    • 模型最后输出 logits(未归一化分数)logits=[z1,z2,z3,⋯ ,zn]logits=[z_1,z_2,z_3,\cdots,z_n]​,加 Temperature 后变为

      pi=exp⁡(zi/T)∑jexp⁡(zj/T)p_i = \frac {\exp (z_i / T)}{\sum_j \exp (z_j / T)}
    • 即把所有 logits 除以 T,再做 Softmax

    • 可见,T 越小趋近于 0,分布会越尖锐,经过 Softmax 后会指数爆炸,从而每次都选概率最高的词,确定性高

    • T 大于 1 时,分布越平滑,概率被拉平,低概率词也容易被选中,具体表现是创意强、发散、容易乱说话、语法错

Position Encoding

  • 原始 Transformer 用的是什么位置编码?有什么缺点?为什么外推性不好?

    • 使用的正余弦位置编码,公式为

      PE(pos,2i)=sin⁡(pos100002i/dmodel)PE(pos,2i+1)=cos⁡(pos100002i/dmodel)\begin {align*} \text{PE}_{(pos, 2i)} &= \sin\left (\frac {pos}{10000^{2i/d_{\text{model}}}}\right) \\ \text{PE}_{(pos, 2i+1)} &= \cos\left (\frac {pos}{10000^{2i/d_{\text{model}}}}\right) \end {align*}
    • 位置编码直接加在嵌入向量上,经过注意力计算的推导如下:

      QKT=(EWQ+PEWQ)(EWK+PEWK)T=(EWQ)(EWK)T+(EWQ)(PEWK)T+(PEWQ)(EWK)T+(PEWQ)(PEWK)T\begin {align*} Q K^T &= (E W_Q + PE W_Q) (E W_K + PE W_K)^T \\ &= (E W_Q)(E W_K)^T + (E W_Q)(PE W_K)^T + (PE W_Q)(E W_K)^T + (PE W_Q)(PE W_K)^T \end {align*}
    • 关注两个特定位置 tt 和 t+kt+k 之间的注意力分数,则有

      (PEtWQ)(PEt+kWK)T=PEtWQWKTPEt+kT=PEtTWQTWKPEt+k\begin {align*} (PE_t W_Q)(PE_{t+k} W_K)^T &= PE_t W_Q W_K^T PE_{t+k}^T\\ &=PE_t^T W_Q^T W_K PE_{t+k} \end {align*}
    • 由此可见,位置编码在进入 attention 层后,内积的距离意识的模式遭到了破坏

    • 外推性不好的原因:正余弦编码基于固定三角函数生成,训练时仅学习了指定长度序列的位置分布,当输入序列长度超过训练最大长度时,超出部分的位置编码未在训练中见过,无法被模型适配,会导致注意力权重计算异常,模型性能大幅下降

  • 为什么 Transformer 必须加位置编码,CNN/RNN 不需要?

    • Transformer 基于自注意力机制,本身不具备序列顺序感知能力,自注意力计算会平等处理所有位置的 token,无法区分序列的先后顺序,必须通过位置编码注入位置信息
    • CNN 通过卷积核的滑动窗口天然捕捉局部位置关系,卷积操作自带空间位置感知特性
    • RNN 按序列顺序逐词迭代处理,输入顺序直接决定计算流程,天然保留时序位置信息
  • 介绍下可学习位置编码(Bert)?为什么外推性不好?

    • 可学习位置编码是将位置信息初始化为随机向量,随模型训练同步更新优化,Bert 中直接将位置嵌入向量与词嵌入向量逐元素相加,位置向量的维度与词嵌入维度一致
    • 外推性不好的原因:编码长度在模型初始化时固定,训练时仅学习了预设长度内的位置向量,当输入序列超出该长度时,没有对应的位置向量可使用,无法直接生成新位置的有效编码,导致模型无法处理超长序列
  • 相对位置编码与绝对位置编码的核心区别与优劣?

    • 核心区别
      • 绝对位置编码:给每个位置 idx 分配一个固定 / 可学习的向量,表示 “这个 token 在序列的第几个位置”,模型学习的是绝对位置信息
      • 相对位置编码:不关心具体在第几位,只关心两个 token 之间的距离,注意力权重由相对距离决定,与绝对位置无关
    • 绝对位置编码(原生正弦、BERT 可学习)
      • 优点:实现简单,直接相加即可;短上下文下位置信息清晰,拟合速度快;与模型结构兼容性好
      • 缺点:外推性极差,超过训练长度后无对应编码可用;位置信息与语义耦合,长序列容易混乱;无法自然表达相对距离关系
    • 相对位置编码(Transformer-XL、ALiBi、RoPE 类)
      • 优点:天然具备外推能力,超长序列仍可计算相对距离;注意力更关注局部与相对关系,长文本更稳定;不受固定位置上限限制
      • 缺点:实现更复杂,需要修改注意力计算;部分相对编码会增加计算量;在极短序列上未必比绝对编码更优
  • ALiBi 位置编码原理,为什么不用显式嵌入也能做外推?

    • ALiBi(Attention with Linear Biases)不修改 token embedding,也不使用显式位置向量,而是直接在注意力分数中加入距离惩罚:

      scoreij=Qi⋅Kjd−mh(i−j)\text{score}_{ij} = \frac {Q_i \cdot K_j}{\sqrt {d}} - m_h (i - j)
      • i 为当前 token 的位置
      • j 为被关注的 token 的位置
      • mhm_h 为第 h 个注意力头的固定斜率(不同头使用不同斜率)
    • 该设计使得:

      • 距离越远,惩罚越大,注意力权重越小,模型天然偏向关注局部上下文
      • 不引入额外位置向量,能够在训练长度之外自然外推,可用于长序列建模
    • 对于任意一对位置 (i,j),ALiBi 只关注它们的相对距离 |i−j|​,并对注意力得分施加线性、无参数增长的惩罚

    • ALiBi 为每个注意力头分配不同的线性斜率(H 是注意力头个数):

      mh=12h/Hm_h = \frac {1}{2^{h/H}}
      • 低序号头 (h 小):衰减快,偏向捕捉局部依赖
      • 高序号头 (h 大):衰减慢,偏向捕捉长距离依赖
      • 这种设计使多头注意力在序列长度上天然分工,提高模型对不同范围依赖的建模能力
  • RoPE 编码的原理?为什么能编码相对位置?

    • 在正余弦位置编码中提到

      (sin⁡θ(t+Δt)cos⁡θ(t+Δt))=(cos⁡θΔtsin⁡θΔt−sin⁡θΔtcos⁡θΔt)∗(sin⁡θtcos⁡θt)=RΔt∗(sin⁡θtcos⁡θt)\begin {pmatrix} \sin\theta (t + \Delta t) \\ \cos\theta (t + \Delta t) \end {pmatrix} = \begin {pmatrix} \cos\theta\Delta t & \sin\theta\Delta t \\ -\sin\theta\Delta t & \cos\theta\Delta t \end {pmatrix} * \begin {pmatrix} \sin\theta t \\ \cos\theta t \end {pmatrix} = \mathcal {R}_{\Delta t} * \begin {pmatrix} \sin\theta t \\ \cos\theta t \end {pmatrix}
    • 由于 RΔt\mathcal {R}_{\Delta t} 是一个表示顺时针旋转的正交矩阵(不改变向量的模长,只改变方向)

    • 则 PEt+ΔtPE_{t+\Delta t} 其实就是以 PEtPE_t 为基础,顺时针旋转 θ∗Δt\theta * \Delta t 角度而来,因此有

      PEtT∗PEt+Δt=∣PEt∣∗∣PEt+Δt∣∗cos⁡(θΔt)PE_t^T * PE_{t+\Delta t} = \vert PE_t\vert * \vert PE_{t+\Delta t}\vert * \cos (\theta \Delta t)
    • 固定某个 tt 不变,慢慢增大 Δt\Delta t 时,上式变小,因此相距较远的两个位置编码的内积越小,即内积可以用于反馈两个位置向量在绝对位置上的远近

    • RoPE(Rotary Position Embedding)把每个 token 的表示按维度两两分组,当成复数:

    x=(x1,x2,x3,x4,… )⇒(x1+ix2,x3+ix4,… )x = (x_1, x_2, x_3, x_4, \dots) \Rightarrow (x_1 + i x_2, x_3 + i x_4, \dots)
    • 对第 pp 个位置,引入一个角度 θp\theta_p,做旋转:

      xp′=xp⋅eiθpx_p^{\prime} = x_p \cdot e^{i \theta_p}
    • 展开成实数就是:

      x2k′=x2kcos⁡θp−x2k+1sin⁡θpx2k+1′=x2ksin⁡θp+x2k+1cos⁡θp\begin {aligned} x_{2k}^{\prime} &= x_{2k} \cos \theta_p - x_{2k+1} \sin \theta_p \\ x_{2k+1}^{\prime} &= x_{2k} \sin \theta_p + x_{2k+1} \cos \theta_p \end {aligned}
    • 关键在 attention 的内积:

      ⟨qp′,kq′⟩\langle q_p^{\prime}, k_q^{\prime} \rangle
    • 代入旋转:

      qp′=qp⋅eiθp,kq′=kq⋅eiθqq_p^{\prime} = q_p \cdot e^{i\theta_p}, \quad k_q^{\prime} = k_q \cdot e^{i\theta_q}
    • 内积变成:

      ⟨qp′,kq′⟩=⟨qp,kq⟩⋅ei(θp−θq)\langle q_p^{\prime}, k_q^{\prime} \rangle = \langle q_p, k_q \rangle \cdot e^{i (\theta_p - \theta_q)}
    • attention 的结果只依赖相对位置差 p−qp - q,而不是绝对位置 —— 这就是 RoPE 能编码相对位置的本质

  • RoPE 和正余弦位置编码有什么不同

    • 传统 sinusoidal PE:

      xp=x+PE(p)x_p = x + PE (p)
    • 问题是位置是 “加进去” 的(additive),attention 内积变成:

      (q+p)⋅(k+q)(q + p) \cdot (k + q)
    • 会混杂内容和位置,无法干净表达相对位置

    • 而 RoPE 位置是 “乘进去”(旋转),attention 内积天然变成下式,更干净、更结构化

      content×relative position\text{content} \times \text{relative position}
  • RoPE 的代码实现

    import torch
    import torch.nn as nn
    
    def rotate_half(x):
        # 拆分偶数/奇数维度,构造旋转
        x1, x2 = x[..., ::2], x[..., 1::2]
        return torch.cat([-x2, x1], dim=-1)
    
    def apply_rotary_pos_emb(q, k, cos, sin):
        # 核心:旋转公式 (a*cos - b*sin, a*sin + b*cos)
        q = (q * cos) + (rotate_half(q) * sin)
        k = (k * cos) + (rotate_half(k) * sin)
        return q, k
    
    class RotaryEmbedding(nn.Module):
        def __init__(self, dim, base=10000):
            super().__init__()
            self.dim = dim
            self.base = base
            # 预计算 theta = 10000^(-2i/d)
            inv_freq = 1.0 / (base**(torch.arange(0, dim, 2).float() / dim))
            self.register_buffer("inv_freq", inv_freq)
    
        def forward(self, seq_len):
            # 生成位置序列 0,1,2...seq_len-1
            t = torch.arange(seq_len, device=self.inv_freq.device)
            # 外积得到 m*theta
            freqs = torch.einsum("i,j->ij", t, self.inv_freq)
            # 拼接成 [cos,cos,...] [sin,sin,...]
            emb = torch.cat((freqs, freqs), dim=-1)
            return emb.cos(), emb.sin()
    
    # ========== 使用示例 ==========
    if __name__ == "__main__":
        head_dim = 64  # 每个头的维度
        rope = RotaryEmbedding(head_dim)
    
        q = torch.randn(1, 10, 128)  # [batch, seq, dim]
        k = torch.randn(1, 10, 128)
        cos, sin = rope(10)
    
        q_rot, k_rot = apply_rotary_pos_emb(q, k, cos, sin)
        print("RoPE 编码完成")
    • 其中,cos/sin 是预先根据位置算好的:θp(k)=p/100002k/d\theta_p (k) = p / 10000^{2k/d}
    • rotate_half:将向量拆分为偶数 / 奇数维度,构造旋转分量
    • apply_rotary_pos_emb:实现复数旋转的实数形式运算
    • 预计算 cos/sin 表,推理时直接查表,速度极快
  • RoPE 为什么能外推(extrapolation)?

    • 模型训练时用 2k 序列长度,推理时直接输入 4k/8k 长度,这就是外推

    • RoPE 不是 learned embedding,而是一个函数:

      θp=p⋅ωk\theta_p = p \cdot \omega_k
    • 所以即使训练只见过 p≤2048p \leq 2048,推理也可以用 p=4096p=4096,理论上函数是连续的,可以外推

  • 为什么 RoPE 在长序列时外推又会崩溃?

  • 为什么还要 NTK scaling / Yarn?RoPE 的线性 scaling 和 NTK scaling 的不同?线性 scaling vs NTK scaling 本质区别?

    • 上述改进的本质目标都是让 RoPE 在长序列下 “频率变慢”,避免:

      θp=p⋅ωk≫2π\theta_p = p \cdot \omega_k \gg 2\pi
    • 最简单方法是线性 scaling,拉伸旋转角度,让模型把长序列当成短序列看

      p′=p/αp^{\prime} = p / \alpha
    • 相当于:

      θp=(p/α)⋅ωk\theta_p = (p/\alpha) \cdot \omega_k
    • 其效果是所有频率统一变慢,简单粗暴;问题在于破坏了绝对位置精度,远距离能区分了,但近距离位置编码变粗糙,长文本效果一般

    • NTK scaling 的核心思想是不同频率缩放不同:

      basenew=baseold×(SpostSpre)dimdim−2base_{new} = base_{old} \times (\frac {S_{post}}{S_{pre}})^{\frac {dim}{dim-2}}
    • NTK 既拉伸低频维度(保证远距离区分),又保留高频维度(保证近距离精度),利用神经网络的 NTK 无限宽特性,实现无训练外推

    • 线性 scaling 压缩位置坐标,而 NTK scaling 重新映射频率空间

  • RoPE 的改进版本 Yarn-RoPE 的原理?为什么更稳定?

    • NTK Scaling 仍有问题,长序列下向量范数失衡(旋转导致向量模长变化)、注意力分数分布偏移、极端长度(8k+)效果下降

    • YaRN(Yet another RoPE extension)核心思想是分段控制频率 + 插值,而不是整体缩放,其同时解决两个问题

      • 频率外推(用 NTK 思路)
      • 向量范数校正(新增衰减因子,保持向量长度稳定)
    • 核心公式

      f(q,m)=q⋅eimθ⋅g(m)f (q,m) = q \cdot e^{im\theta} \cdot g (m)
    • 其中,g (m) 是位置相关的衰减因子,保证长距离下向量范数不爆炸、不消失

    • 低频保持,高频压缩:类似 NTK scaling,但更平滑

      ωk′={ωkk≤k0interpolate(ωk)k>k0\omega_k^{\prime} = \begin {cases} \omega_k & k \leq k_0 \\ \text{interpolate}(\omega_k) & k > k_0 \end {cases}
    • 位置插值(position interpolation):把长序列映射回训练区间

      p′=f(p)p^{\prime} = f (p)
    • Yarn 保留 NTK 的频率外推能力,并新增范数约束,让注意力分布和训练时一致

    • 支持无微调直接扩展到 32k+ 序列,是目前长文本大模型的最优方案

  • 总结

    • RoPE 的本质是用复数旋转把位置编码进向量,使 attention 内积自然变成相对位置函数
    • 但它的频率是指数分布的,在长序列下高频维度会发生 alias,导致外推失败
    • NTK scaling 和 YaRN 的核心目标都是 “减慢高频旋转”,区别在于线性 scaling 直接缩位置,而 NTK/YaRN 通过调整频率分布来更好地保持原有 attention kernel,因此更稳定

MoE

  • 介绍什么是 MoE 模型?稀疏模型与稠密模型的对比和优缺点?

    • MoE 也即 Mixture of Experts,混合专家模型
    • 核心思想是一个 Transformer 层里不只用一个 FFN,而是设置多个 “专家网络”(Expert),每个 token 只被部分专家处理
    • 用稀疏激活的方式,实现在不显著增加单步计算量的前提下,大幅扩大模型总参数量
    • 工作流程
      • 每个 token 经过一个门控网络(Router/Gating Network)
      • 门控为每个专家打分,选择分数最高的 k 个专家(通常 k=1 或 k=2)
      • 该 token 只被这 k 个专家进行前向计算
      • 对专家输出按门控分数加权求和,作为该层最终输出
      • 不同 token 可以被分配到不同专家,实现负载分流
    • 为什么会被广泛使用?
      • scaling 效率极高:总参数量可轻松破万亿,但每步计算量接近稠密小模型
      • 效果提升显著:更多专家对应更强的特征提取与知识存储能力
      • 训练成本可控:同等参数量下,计算成本远低于全稠密大模型
      • 适合多语言、多任务、长文本:不同专家可专门化学习不同领域 / 模式
  • MoE 怎么做负载均衡的?为什么会不均衡?

    • 门控网络(gating)天然倾向于给少数高分专家发 token

    • 模型为了 loss 下降快,会学会只激活 1~2 个 “超级专家”

      • 其他专家永远收不到 token → 不更新 → 废掉
      • 最终:专家利用率极低,性能崩塌
    • 均衡辅助损失(Aux Loss):直接在门控输出上加损失,强制让每个专家分到的 token 数量接近,让所有专家的平均概率

      aux_loss=(gate_prob.mean (dim=0)⋅expert_count).sum()\text{aux\_loss} = (\text{gate\_prob.mean (dim=0)} \cdot \text{expert\_count}).sum ()
    • Top-K 随机平滑:选专家时加一点噪声,避免模型永远只选固定专家

    • Expert Capacity(专家容量上限):强制每个专家最多收 N 个 token,溢出就丢给其他专家 / 共享专家

  • DeepSeek 的辅助损失是怎么做的?

    • 计算公式

      aux_loss=λ⋅∑iPi⋅Ci\text{aux\_loss} = \lambda \cdot \sum_i P_i \cdot C_i
    • PiP_i:第 i 个专家被选中的概率

    • CiC_i:第 i 个专家实际收到的 token 占比

    • λ\lambda:平衡系数(一般 0.01~0.02)

    • 让 “被选中概率高” 的专家,真的拿到 “对应数量的 token” → 防止门控乱打分,防止某些专家永远不工作

    • 比普通均衡 loss 更稳定,让 MoE 训练不塌陷、专家利用率 >90%

  • MoE 激活参数量如何计算的?主流大模型的 MoE 命名是什么意思?

    • 计算公式

      激活参数量 = 专家数量 × 每个专家参数量 × 每个token选的专家数(K)
    • 总专家:16 个、每个专家:2048×5440(FFN)、每个 token 选 2 个专家、激活参数量 = 16 × FFN × 2 / 16 = 2×FFN

    • MoE 总参数大,但激活参数 = K 个稠密 FFN,推理速度 ≈ 稠密小模型

  • MoE 为什么难训练?MoE 为什么 MFU 低?

    • 难训练主要因为专家负载不均、门控路由不稳定、梯度容易碎片化,且辅助损失与负载均衡约束难调,还容易出现部分专家闲置或过拟合,同时多专家并行还会依赖大量的 all2all 通信
    • MFU 低是因为显卡在等通信,没在计算矩阵,MoE 并行会引入大量的 all-to-all 通信,开销巨大;MoE 算力利用率通常只有 20%~40%,稠密模型 50%~70%
  • 为什么 MoE 适合大模型 scaling?

    • 能在不显著增加单步计算量的前提下大幅扩大参数量,用更低算力成本实现能力提升
    • 稠密模型:参数翻倍 → 训练 / 推理成本翻倍
    • MoE:总参数翻倍,但激活参数不变 → 成本几乎不涨
    • 能轻松做到 70B/140B/280B 规模,推理却像 7B/13B 一样快
  • 为什么有的模型会设置一个共享专家?

    • 在 MoE 模型里,每一层除了自己的 N 个本地专家(Local Experts),还共享一个全局共享的 FFN 层,所有层都能调用它
    • 主要负责承接 overflow 溢出的 token(专家满了就丢这里,不丢信息),提升模型稳定性,防止专家塌陷,让模型基础语义能力更强
    • 解决 Expert Capacity Overflow
    • 解决 MoE 训练塌陷:本地专家可能躺平,但共享专家永远被激活,保证基础表达能力
    • 提升泛化性:共享专家学习通用语言模式,而本地专家学习细分领域 / 模式
  • MoE 是 compute-bound 还是 communication-bound?

    • 现代 MoE 100% 是 Communication-bound(通信受限)
    • 矩阵计算很快,但 All-to-all 通信极慢,通信占比 50%~70%,因此优化 MoE 性能 = 优化通信
  • expert capacity overflow 怎么处理?

    • 直接丢弃(效果差)
    • 送到共享专家(shared expert)(最常用)
    • 送到本轮分数第二的专家
    • batch 内排队 / 累积到下一个 token
    • 主流方案:所有溢出 token → 共享 FFN 专家,不影响速度,不损失精度,最稳定
  • 如果你的框架里要支持 MoE,怎么设计并行,并且怎么 overlap all-to-all?

    • MoE 的核心问题在于稀疏计算和专家分布,每个 token 只经过少数几个专家,而不同专家可能在不同 GPU 上
    • 为了充分利用显存和计算资源,框架设计通常需要将 MoE 并行拆分成几个层次
    • 数据并行(DP)负责整体 batch 分割和梯度同步
    • 专家并行(EP)负责每个专家在不同 GPU 上的计算分布
    • 张量并行(TP)仍然用于每个专家内部的矩阵计算加速
    • 在实现上,最关键的性能瓶颈是 All-to-All 通信,因为每个 GPU 上的 token 需要发送到对应专家所在的 GPU
    • 为了减轻 All-to-All 的阻塞,可以通过通信 - 计算重叠来优化:在发送数据的同时先对本地 token 进行前向计算或部分后向计算,使用异步通信(比如 NCCL 的 non-blocking send/recv)和流水线式的 tensor chunk 分块传输,将通信时间和计算时间尽量重叠,从而减少 GPU 空闲等待
    • 总之,MoE 的并行设计不仅要考虑计算负载平衡,还要精细调度 All-to-All,确保通信不会成为瓶颈
  • MoE + pipeline 怎么调度?

    • MoE 模型往往非常深,因此经常和流水线并行(PP)结合
    • 调度的关键在于 token 在 pipeline stage 和专家之间的流动
    • 在标准 pipeline 并行中,每个微批次在不同 stage 上按顺序流动,而 MoE 会在某些 layer 内产生 All-to-All 分布,这会打乱原本流水线的顺序
    • 解决方法是采用 “微批次拆分 + 动态调度”:将每个微批次分成若干小 chunk,使得一部分 token 在 stage 1 前向计算时可以异步发送到专家所在 GPU,而 stage 2 的 GPU 可以同时处理上一批次的其他 token
    • 这样,pipeline stage 之间不会因为 MoE 的 All-to-All 而完全阻塞,同时可以通过 1F1B 或 Interleaving 等调度策略进一步减少 pipeline bubble
    • 设计时还需要考虑专家数量和容量,保证每个 pipeline stage 的负载均衡,否则某个 stage 等待 All-to-All 过慢会拖慢整体流水线
  • expert parallel 和 TP/DP 怎么组合?

    • 在大规模 MoE 系统中,专家并行(EP)和张量并行(TP)、数据并行(DP)需要合理组合
    • 一般逻辑是,DP 先切分 batch,把不同微批次分给不同 GPU 节点;EP 切分专家,把同一 layer 的不同专家分布在不同 GPU 上;TP 则用于每个专家内部的矩阵计算加速
    • 组合时的关键原则是 “通信最小化”:DP 主要做梯度同步,EP 主要做 All-to-All token 分发,TP 只在每个专家内部做张量拆分计算
    • 为了保证效率,需要在计算图中明确依赖关系,将 TP 计算和 EP 的 All-to-All 尽量重叠,同时在 DP 维度做梯度通信的重叠
    • 这样可以在保持高 MFU 的同时充分利用 GPU 显存,避免单一维度成为瓶颈
    • 最终设计通常是 “DP × EP × TP” 的三维并行结构,每个维度负责不同层次的并行优化,确保 MoE 层既能稀疏高效计算,又能和整体模型训练流水线无缝衔接

Multi-Modal

ViT

  • 简单介绍 Vision Transformer(ViT)的整体结构

    • ViT 是 Encoder-only 结构,和 BERT 架构高度相似,核心流程:
    • 图像分 Patch:把 2D 图像切成固定大小的小 Patch,作为视觉 Token
    • 线性投影 + 加入 Class Token:将每个 Patch 映射为隐向量,并额外添加一个可学习的 class token
    • 加入位置编码:给序列注入空间位置信息
    • 堆叠 Transformer Encoder:多层 Pre-LN + MSA + MLP + 残差连接
    • 分类输出:取 class token 对应的最终特征,经过线性层 + Softmax 完成分类
  • ViT 是如何把图像转换成序列输入 Transformer 的?

    • 切 Patch:输入图像 H×W×C,按 P×P 大小切分成不重叠 Patch,共 N=(H×W)/(P2)N = (H×W)/(P^2) 个 Patch
    • 展平 + 线性投影:每个 Patch 展平为 P2⋅CP^2 \cdot C 维向量,再通过可学习矩阵 E 投影到 D 维隐空间,得到 N 个 D 维 Token
    • 添加 Class Token:在序列最前面拼接一个可学习的 class token,序列长度变为 N+1
    • 加入位置编码:加上可学习 1D 位置编码,最终得到 (N+1, D) 的序列,送入 Transformer Encoder
    • 实际实现常用卷积等价实现:用 P×P、stride=P 的卷积核直接得到 Patch Embedding
  • ViT 中的 class token 有什么作用?可以去掉吗?

    • 类比 BERT 的 class token,作为全局特征聚合器,在自注意力中与所有 Patch Token 交互,最终用它代表整张图的全局语义
    • 方便直接接分类头,结构简洁统一
    • 去掉 class token 后,可以对所有 Patch Token 做平均池化 / 最大池化得到全局特征,但原始 ViT 设计保留 class token,训练更稳定,效果更易对齐
  • ViT 的位置编码有哪些方式?可学习位置编码和正弦位置编码区别是什么?

    • 常见编码方式
      • 无位置编码:性能大幅下降
      • 1D 可学习位置编码:ViT 原生方案,与 BERT 一致
      • 2D 可学习位置编码:分别学习行、列位置再拼接
      • 相对位置编码 RPE:在 Attention 里加相对距离偏置
    • ViT 选择可学习 1D 位置编码
  • ViT 相比 CNN 有哪些优势和劣势?

    • 优势
      • 全局感受野:自注意力直接建模全局依赖,远超 CNN 局部卷积
      • 更强表达能力:无手工归纳偏置,大数据下拟合能力更强
      • 灵活架构:易于扩展到多模态、大模型、检测分割等任务
      • 动态权重:注意力权重随内容自适应,而非固定卷积核
    • 劣势
      • 计算量大:自注意力 O(N2)O (N^2),高分辨率代价极高
      • 缺乏天然归纳偏置:无局部性、平移等变性,小数据易过拟合
      • 对位置编码敏感:分辨率变化需要插值处理
      • 训练更不稳定:依赖更强优化、更大 Batch、更长训练
  • ViT 在小数据集上表现为什么不如 CNN?

    • CNN 自带强图像归纳偏置,而 ViT 没有
    • CNN 天然满足空间局部性 + 平移等变性,先验与图像高度匹配
    • ViT 完全靠数据学习结构,小数据下无法学到合理的空间关系与局部模式
    • 注意力容易学到无意义的全局关联,泛化远差于 CNN
  • 为什么 ViT 需要大量数据训练?

    • ViT 缺少图像先验,所有结构信息(局部、边缘、纹理、全局关系)都必须从数据中学习
    • Transformer 参数量大、表达能力强,小数据极易过拟合
    • 只有在大规模数据下,才能让注意力学到合理的空间关联与语义依赖,弥补归纳偏置不足
  • ViT 的计算复杂度是多少?和图像分辨率、patch 大小的关系?

    • 自注意力复杂度

      O(N2D),N=HWP2O (N^2D),\quad N=\frac {HW}{P^2}
    • N 是序列长度,D 是隐层维度,与图像分辨率 H×W 成正比

    • 与 Patch 大小 P2P^2 成反比:P 越大,N 越小,计算量急剧下降

  • ViT 的多头注意力在视觉任务上有什么特点?

    • 不同头会学到不同粒度的依赖:有的关注局部纹理,有的关注全局结构
    • 注意力矩阵呈现结构化模式:对角附近强(局部),远距离弱
    • 相比 CNN,能动态建模长距离依赖,比如跨区域物体关联
    • 早期层关注局部,深层关注全局语义,类似 CNN 层级特征
  • 介绍 DeiT、Swin Transformer 相对于 ViT 的改进点

    • DeiT(Data-efficient Image Transformer)
      • 解决 ViT 数据低效问题,不用 JFT 也能在 ImageNet 训好
      • 引入蒸馏 token,用 CNN 教师模型监督训练
      • 更强训练策略:增强、优化器、正则化更精细
      • 更小模型更快,更适合落地
    • Swin Transformer
      • 窗口注意力 + 移位窗口,把复杂度从 O(N2)O (N^2) 降到 O(N)O (N)
      • 层级特征金字塔:类似 CNN 下采样,多尺度特征,适合检测 / 分割
      • 天然具备局部性先验,小数据表现远好于 ViT
      • 成为视觉 Transformer 落地主流 backbone
  • Swin Transformer 的窗口注意力、移位窗口是怎么实现的?

    • 窗口注意力(Window Attention)
      • 将特征图划分为不重叠窗口,只在窗口内算注意力
      • 复杂度从全局 O(N2)O (N^2) → 窗口内 O((window_size)2)O ((\text{window\_size})^2),大幅降低
    • 移位窗口(Shifted Window)
      • 下一层将窗口平移半窗,让相邻窗口信息交叉
      • 用 mask 注意力避免无效区域计算
      • 交替使用普通窗口 + 移位窗口,实现跨窗口信息流动
  • ViT 为什么能更好地建模长距离依赖?

    • CNN 靠堆叠卷积 + 池化扩大感受野,是间接、渐进式的长距离建模
    • ViT 自注意力每一层都能直接连接任意两个 Patch,是真正全局建模
    • 无论物体在图中距离多远,注意力都能直接建模相关性,尤其适合大目标、稀疏目标、跨区域语义关联
  • ViT 做分类、检测、分割分别需要怎么修改结构?

    • 分类(原生):加 class token → 取 class token → 线性层分类
    • 检测
      • 去掉 class token,使用所有 Patch Token 作为特征点
      • 接入 FPN / RetinaNet 式检测头或用 DETR 式端到端检测,直接用 Transformer 输出预测框
    • 分割
      • 对所有 Patch Token 上采样恢复分辨率
      • 接逐像素分类头(如 UPerNet、FCN 头)
      • 结合层级特征(如 Swin)效果更好

CLIP

  • 简单介绍 CLIP 的训练流程和核心思想

    • CLIP 是 OpenAI 提出的双塔对比学习多模态模型,通过海量弱监督 <图像,文本> 数据,把图像和文本映射到同一个语义特征空间,实现跨模态对齐,从而支持零样本图像分类和图文检索
    • 训练流程
      • 构建双塔:图像编码器 + 文本编码器,分别提取特征
      • 投影 + L2 归一化,把两种特征映射到统一空间
      • 计算一个 batch 内所有图像 - 文本对的相似度矩阵
      • 使用对称对比损失,让对角线上的正样本对相似度更高,非对角线负样本更低
      • 预训练完成后,无需微调即可做零样本分类和检索
  • CLIP 的预训练任务是什么?对比学习目标是什么?

    • 预训练任务
      • 任务:图像 - 文本对比匹配任务
      • 输入:一个 batch 内 N 对 <图像,文本>
      • 目标:对任意图像,找出其对应的正确文本;对任意文本,找出对应的正确图像
    • 对比学习目标
      • 拉近匹配对(正样本)的特征距离,推远不匹配对(负样本)的特征距离
      • 最终学到一个共享语义空间,语义相似的图文在空间中相近
  • CLIP 中图像编码器和文本编码器分别用了什么结构?

    • Text:采用 GPT-2 结构(Transformer Decoder),取 [EOS] 位置的输出作为文本全局特征
    • Image:支持 ResNet 和 ViT,通常取 token 输出作为图像全局特征
  • CLIP 的损失函数是什么?为什么要用对称对比学习?

    • 对称双向交叉熵损失(对称 InfoNCE)
    • 按行:图像检索文本的交叉熵,按列:文本检索图像的交叉熵,两者平均作为总损失
    • 通过对称对比学习,保证图文双向对齐 —— 图像能找到文本,文本也能找到图像,避免某一模态特征主导,让两个编码器学习更均衡
  • InfoNCE loss 和交叉熵损失的区别

    • InfoNCE:专门用于对比学习,本质就是在一个包含正样本 + 大量负样本的集合上做交叉熵,目标是让正样本概率最大,负样本被抑制
    • 交叉熵:更通用的分类损失,不一定带对比学习的负样本构造逻辑
    • CLIP 的损失 等价于对称的 InfoNCE,只是在实现上直接用标准交叉熵表达
  • CLIP 的 temperature 参数有什么作用?

    • 相似度 logits 计算:

      logits=(Ie⋅Te⊤)×eτlogits = (I_e \cdot T_e^\top) \times e^\tau
    • 缩放相似度分布,控制 “锐度”

    • τ\tau 越大,相似度被放大,分布更陡峭,正样本更突出

    • τ\tau 越小,分布越平滑,梯度更稳定

    • 是可学习参数,自动优化正负样本区分度

  • CLIP 是如何实现零样本分类(Zero-shot Classification)的?

    • 构造类别文本:对每个类别用 prompt 模板,如 A photo of a {class}.,输入文本编码器得到类别文本特征
    • 图像编码:输入图片 → 图像编码器 → 图像特征
    • 相似度匹配:计算图像特征与所有类别文本特征的余弦相似度,最高的即为预测类别
  • CLIP 的相似度是怎么计算的?余弦相似度还是点积?

    • 本质是余弦相似度,但实现上用点积
    • 图像、文本特征都做了 L2 归一化,归一化后的向量点积 = 余弦相似度
  • CLIP 为什么能具备很强的泛化能力?

    • 训练数据极其广泛:4 亿 <图像,文本> 对,覆盖海量场景
    • 学习的是通用语义对齐,不是固定类别分类边界
    • 无人工标注类别,避免过拟合到特定数据集标签体系
    • 文本侧是自然语言,自带丰富语义先验,能泛化到未知类别与分布偏移场景
  • CLIP 的优缺点分别是什么?

    • 优点
      • 支持零样本图像分类,泛化强、鲁棒性好
      • 弱监督训练,不需要精细标注
      • 架构简单双塔,易扩展、易复用
      • 成为视觉生成模型、多模态检索的基础基座
    • 缺点
      • 零样本 ImageNet 精度不如有监督 SOTA
      • 模型规模大,训练成本极高
      • 缺乏逻辑推理(计数、复杂纹理弱)
      • 对极端分布偏移泛化有限(如 MNIST 表现一般)
      • 只能判别匹配,不具备生成能力
      • 存在数据偏见与刻板印象
  • CLIP 训练时的 batch size 对效果有什么影响?

    • batch 越大,负样本越多,对比学习信号越强
    • 负样本更丰富,模型更难 “偷懒”,学到更精细的语义对齐
    • CLIP 本身就是大 batch 友好型模型,大 batch 显著提升性能
  • CLIP 存在哪些 bias 或局限性?

    • 数据偏见:从互联网爬取数据,带有人群、性别、文化刻板印象
    • 泛化上限有限:极端分布偏移(如手绘、简笔画)效果下降明显
    • 不支持开放词汇生成:只能从给定标签里选,不能自由描述
    • 无推理能力:不会计数、不会复杂逻辑理解
    • 零样本精度不如有监督模型
    • 训练成本极高,难以复现
  • 介绍几个基于 CLIP 的改进工作(如 BLIP、ALIGN、FLAVA 等)

  • CLIP 能不能直接用于生成任务?为什么?

    • 不能直接用于生成,CLIP 是判别式模型,只做图文特征对齐与相似度计算
    • 没有解码器、没有生成式架构,无法从文本生成图像,也无法从图像生成描述
    • 视觉生成模型(Stable Diffusion、DALL・E)是用 CLIP 做文本编码器,但自身另有生成网络

VLM

  • 多模态模型的几种范式

    阶段代表模型核心结构训练方式优势
    第一代CLIP、BLIP、FLAVA、SigLIP双编码器(图像 + 文本)对比学习 / 掩码重建 / 多目标预训练简单、零样本强、检索效果好
    第二代LLaVA、GPT-4V、BLIP2、Qwen-VL视觉编码器 + Projector + LLM冻结主干 + 轻量适配器微调(PEFT 等)推理强、指令遵循好、易落地
    第三代Emu3、LLaMA 4、CM3leon、Chameleon统一 Token + 单 Transformer端到端自回归生成式预训练对齐好、生成强、多任务统一
  • 什么是多模态对齐?CLIP 是怎么做模态对齐的?

  • 对比学习和监督学习在多模态预训练中的区别?

  • 多模态模型常见的预训练任务有哪些?

  • 图文检索的评价指标有哪些?

  • 为什么 CLIP 这类模型不适合做细粒度识别?

  • 多模态融合有哪些方式?

  • RL 如何与多模态模型结合(如多模态 Agent 的决策优化)

Visual Generation

  • 视觉生成模型的一般流程
    • 文本编码器:CLIP/T5 text encoder 把 prompt 变成条件特征
    • VAE(变分自编码器):负责把图像压缩到低维隐空间
    • UNet / DiT(核心生成网络):接收噪声隐变量 ztz_t + 时间步 tt + 文本条件,预测噪声 ϵθ(zt,t,text)\epsilon_\theta (z_t, t, \text{text})
    • 扩散过程:前向给图像逐步加高斯噪声,反向从噪声逐步去噪生成图像
  • 视觉生成模型的理论基础演变
    • DDPM:离散马尔可夫链
    • Score-SDE:连续随机微分方程
    • Probability Flow ODE:SDE 的确定性等价(去随机)
    • Flow Matching:直接 ODE + 最优传输 + 闭式目标
    • Rectified Flow:拉直路径 → 超快采样
  • 视觉生成模型的训练难点
    • 模态对齐困难,文本条件控制(prompt 跟随)较弱
    • 计算量巨大、显存爆炸:图像是高维数据(哪怕 latent 依然很大),时序扩散要走几十上百步
  • 视觉生成模型训练 vs LLM 训练
    • 数据空间不同:LLM 是 1D 离散 token 序列,视觉生成模型是连续稠密像素 / 隐变量(2D/3D / 时空)
    • 目标函数不同:LLM 是下一个 token 的分类交叉熵损失,视觉生成模型是扩散去噪损失(预测噪声)/ MSE 为主
    • 结构归纳偏置不同:LLM 是纯 Transformer,依靠自回归和注意力建模序列依赖;视觉生成模型早期依靠 UNet 的强图像先验,现在 DiT 仍需要位置、时间、条件编码
    • 显存与计算压力不同:LLM 以矩阵乘法为主,视觉生成模型参数量不一定大,但是特征图巨大、时空维度高,显存更紧张
    • 并行策略不同:LLM 以 TP/DP/PP 为主,而视觉生成模型更依赖空间并行、模型并行、微批次,对通信优化要求极高
  • 视觉生成模型训练 vs LLM 的强化学习训练
    • LLM
      • 让模型输出更符合人类意图或偏好,或者提升模型的推理能力
      • LLM 的 RL 是序列决策问题:一步步生成 token,每一步都影响最终结果,靠策略梯度调整分布
    • 视觉生成模型 / 扩散模型
      • 让图更好看、更清晰、细节更足,更遵循文本提示(prompt alignment),构图、人体、手部、脸部更合理,减少畸形、扭曲、崩坏
      • 典型做法
        • Reward 引导扩散(CLIP reward、美学 reward、人脸检测 reward)
        • Diffusion RL / DreamBooth + RL / 可控性优化
        • 直接在去噪过程中梯度上升提升奖励分数
      • 主要特点
        • 动作空间:连续的隐变量 z / 噪声预测
        • 奖励:CLIP score、FID、美学模型、人脸检测器、手部检测器
        • 优化方式:引导去噪方向,而不是更新整个策略分布
        • 训练比 LLM 的 RL 稳定得多
      • 扩散模型的 RL 不是 “序列决策”,而是连续优化 + 引导式去噪,每一步去噪都可以注入奖励梯度,把图往 “更好看、更对齐” 的方向拉
  • 了解过视觉生成模型的 RL 训练吗
  • DDPM 和 DDIM 的区别
    • DDPM:Denoising Diffusion Probabilistic Models
      • 扩散模型基础框架,基于马尔可夫链
      • 正向、反向都是随机过程(SDE)
    • DDIM:Denoising Diffusion Implicit Models
      • DDPM 的改进 / 加速变体
      • 把反向过程变成确定性 ODE,可跳步
    • DDIM 不需要重新训练,直接用 DDPM 训练好的权重
这篇文档有帮助吗?

On this page

页面信息