Skip to content

数学基础

掌握线性代数、微积分和概率论的核心概念,是理解大模型原理的基石。没有数学直觉,你只能把模型当黑盒;有了数学直觉,你才能理解为什么这样设计、如何改进。

在大模型体系中的位置

数学基础 ◄── 你在这里
  ├── 线性代数 → Attention 的矩阵运算、LoRA 低秩分解
  ├── 微积分   → 反向传播、梯度下降、优化器设计
  └── 概率论   → Softmax、交叉熵损失、采样策略、KL 散度

数学是贯穿整个 LLM 学习路径的底层语言。Transformer 里的每一步计算——Query/Key 内积、Softmax 归一化、残差连接、LayerNorm——都可以用这三个数学分支来解释。


线性代数

向量与矩阵运算

神经网络的本质操作是矩阵乘法。一个全连接层 y=Wx+b 就是一次线性变换。

几何直觉:矩阵乘法 = 线性变换

矩阵 W 将输入向量 x 映射到新的空间。旋转、缩放、投影都是线性变换的特例。Transformer 中的 WQ,WK,WV 矩阵就是把输入投影到 Query、Key、Value 三个不同的子空间。

Q=XWQ,K=XWK,V=XWV

维度对齐的重要性: 矩阵 ARm×nBRn×p 相乘,要求 A 的列数等于 B 的行数,结果为 Rm×p。写代码时 shape 不匹配是最常见的 bug。

特征值与特征向量

对于方阵 A,如果存在非零向量 v 和标量 λ 使得:

Av=λv

v 是特征向量,λ 是特征值。特征向量是矩阵变换中"方向不变"的向量,特征值表示在该方向上的缩放因子。

SVD 分解: 任意矩阵 M 可以分解为:

M=UΣVT

其中 U,V 是正交矩阵,Σ 是对角矩阵,对角元素为奇异值(按降序排列)。

矩阵的秩与 LoRA 的关系

矩阵的(rank)是其线性无关行(或列)的最大数量。一个 m×n 的矩阵,秩最大为 min(m,n)

LoRA 的核心思想: 预训练模型的权重更新矩阵 ΔW 是低秩的。与其更新完整的 d×d 矩阵,不如将其分解为两个小矩阵的乘积:

ΔW=BA,BRd×r,ARr×d,rd

参数量从 d2 降低到 2dr。当 d=4096,r=16 时,参数量减少到原来的 2×1640960.78%

广播机制

NumPy/PyTorch 的广播(broadcasting)规则在实际编程中无处不在:

规则: 从最右维度开始对齐,每个维度要么相等,要么其中一个为 1。

python
import torch

# shape: [2, 3] + [3] -> [2, 3] + [1, 3] -> [2, 3]
A = torch.randn(2, 3)
b = torch.randn(3)
result = A + b  # b 自动广播到 [2, 3]

# Attention 中的 mask 广播
# scores: [batch, heads, seq_len, seq_len]
# mask:   [1, 1, seq_len, seq_len]  -> 广播到 4 维

微积分

导数与梯度

标量函数 f(x) 的导数 f(x) 表示函数在 x 处的变化率。对于多元函数 f(x1,x2,,xn)梯度是所有偏导数构成的向量:

f=(fx1,fx2,,fxn)

梯度指向函数值增长最快的方向。梯度下降的核心思想:沿着梯度的反方向更新参数,使损失减小。

链式法则

复合函数 f(g(x)) 的导数:

dfdx=dfdgdgdx

这是反向传播的数学基础。神经网络是多层复合函数的嵌套,链式法则让我们能从输出层逐层计算梯度回传到输入层。

雅可比矩阵

当函数的输入和输出都是向量时,导数变成雅可比矩阵(Jacobian Matrix):

J=[f1x1f1xnfmx1fmxn]

Softmax 的雅可比矩阵就是后面推导交叉熵梯度的关键。

梯度消失与梯度爆炸

用具体数字说明: 假设一个 50 层的网络,每层的梯度乘以一个系数 α

  • α=0.9:经过 50 层后梯度变为 0.9500.005消失
  • α=1.1:经过 50 层后梯度变为 1.150117爆炸

这就是深层网络训练困难的根源。解决方案包括:

  • 残差连接(ResNet / Transformer):梯度可以跳过层直接回传
  • 梯度裁剪:限制梯度的最大范数
  • 适当的初始化:Xavier / He 初始化

概率与信息论

概率分布

离散概率分布: 骰子的每个面出现的概率 P(X=i)=16

连续概率分布: 高斯分布 P(x)=12πσe(xμ)22σ2

在 LLM 中,模型输出的 logits 经过 Softmax 后变成离散概率分布——每个 token 被选中的概率:

P(tokeni|context)=softmax(zi)=ezijezj

信息量与熵

自信息(Self-information): 一个事件的信息量与其概率成反比:

I(x)=logP(x)

直觉:越罕见的事件,信息量越大。"太阳从东方升起"信息量低;"太阳从西方升起"信息量极高。

熵(Entropy): 一个分布的平均信息量,衡量不确定性:

H(P)=iP(xi)logP(xi)

均匀分布的熵最大(最不确定);确定性分布的熵为 0。

交叉熵

交叉熵衡量用分布 Q 来编码真实分布 P 的平均信息量:

H(P,Q)=iP(xi)logQ(xi)

为什么交叉熵是 LLM 训练的损失函数? 在分类任务中,真实分布 P 是 one-hot 向量(只有正确类别为 1),Q 是模型预测的概率分布。此时交叉熵简化为:

H(P,Q)=logQ(正确类别)

这就是负对数似然(Negative Log-Likelihood)。

以下代码演示了交叉熵的直觉和计算:

python
import torch
import torch.nn.functional as F

# 从零实现交叉熵
def cross_entropy(p, q):
    """p 和 q 都是相同维度的概率分布"""
    return (-p * torch.log(q)).sum()

# 预测不准确时,交叉熵较大
q = torch.tensor([0.3, 0.5, 0.2])  # 预测分布
p = torch.tensor([0.0, 1.0, 0.0])  # 真实分布(类别 1)
print(cross_entropy(p, q))  # tensor(0.6931)

# 预测较准确时,交叉熵较小
q = torch.tensor([0.05, 0.9, 0.05])
print(cross_entropy(p, q))  # tensor(0.1054)

在分类任务中,可以进一步简化——只需要计算正确类别的负对数概率:

python
# 批量版本,仿 PyTorch 实现
def cross_entropy_with_batch_logits(label, logits):
    """
    label size : [bs]
    logits size: [bs, classes]
    """
    bs, _ = logits.shape
    prob = F.softmax(logits, dim=-1)
    idx = torch.arange(0, bs)
    logprob = prob[idx, label].log()
    CE_loss = -logprob.mean()
    return CE_loss

# 验证与 PyTorch 一致
loss_fn = torch.nn.CrossEntropyLoss()
logits = torch.randn(4, 10)
label = torch.randint(high=10, size=(4,))
print(cross_entropy_with_batch_logits(label, logits))
print(loss_fn(logits, label))  # 结果一致

KL 散度

KL 散度衡量两个分布之间的"距离"(非对称的):

DKL(PQ)=iP(xi)logP(xi)Q(xi)=H(P,Q)H(P)

在 RLHF/GRPO 中的应用: KL 散度被用作惩罚项,防止 RL 微调后的策略模型 πθ 偏离参考模型 πref 太远:

L=Lreward+βDKL(πθπref)

注意 KL 散度的不对称性: DKL(PQ)DKL(QP)。前向 KL 倾向于覆盖 P 的所有模式(mode-covering),反向 KL 倾向于集中在 P 的某个模式(mode-seeking)。DPO 中选择哪种方向的 KL 会影响训练效果。

最大似然估计

给定数据集 {x1,x2,,xN},最大似然估计(MLE)寻找使数据出现概率最大的参数 θ

θ=argmaxθi=1NP(xi|θ)=argmaxθi=1NlogP(xi|θ)

LLM 的预训练目标——Next-Token Prediction——本质就是最大似然估计:

L=t=1TlogP(xt|x1,,xt1;θ)

最大化每个位置预测正确 token 的概率之和,等价于最小化交叉熵损失。


Softmax 函数

数学定义与直觉

Softmax 将任意实数向量(logits)转换为概率分布:

softmax(zi)=ezij=1Kezj

直觉: 指数函数将大的 logit 值放大、小的压缩,然后归一化使得所有输出之和为 1。温度参数 T 控制分布的"尖锐度":

softmax(zi;T)=ezi/Tjezj/T
  • T0:分布趋近 one-hot(贪心采样)
  • T:分布趋近均匀分布(完全随机)

数值稳定性问题

Overflow 问题: 当 logit 值很大时(如 10000),e10000 会溢出为 inf

Safe Softmax 解决方案: 减去最大值不改变结果:

softmax(zi)=ezicjezjc,c=maxjzj

以下是数值稳定的 Softmax 实现:

python
import torch

def SoftMax(logits):
    """Safe Softmax 实现"""
    logits_max, _ = logits.max(dim=-1)
    logits = logits - logits_max.unsqueeze(1)  # 减去最大值
    logits = logits.exp()
    logits_sum = logits.sum(-1, keepdim=True)
    prob = logits / logits_sum
    return prob

# 正常情况下没有问题
logits = torch.randn(8, 10)
prob = SoftMax(logits)
print(prob[0].sum())  # tensor(1.)

LogSoftmax 解决 log(softmax) 的溢出: 当我们需要 logP(最大似然估计中必须的),先 softmax 再 log 可能产生 -inf

python
# softmax 后再 log,数值不稳定
logits = torch.tensor([[10, 2, 10000, 4]], dtype=torch.float32)
prob = SoftMax(logits)
print(prob.log())  # tensor([[-inf, -inf, 0., -inf]])  ← 出现 -inf!

# LogSoftmax 直接计算,数值稳定
# log_softmax(x_i) = x_i - c - log(sum(exp(x_j - c)))
print(torch.nn.functional.log_softmax(logits, dim=-1))
# tensor([[-9990., -9998., 0., -9996.]])  ← 正确结果
python
def LogSoftMax(logits):
    """数值稳定的 LogSoftmax"""
    logits_max, _ = logits.max(dim=-1)
    safe_logits = logits - logits_max.unsqueeze(1)
    safe_logits_exp = safe_logits.exp()
    safe_logits_sum = safe_logits_exp.sum(-1, keepdim=True)
    log_logits_sum = safe_logits_sum.log()
    log_probs = safe_logits - log_logits_sum
    return log_probs
log_softmax(xi)=xiclogj=1dexjc

这就是为什么 PyTorch 的 CrossEntropyLoss 接收 logits 而不是概率——内部使用 log_softmax 直接计算,避免数值溢出,同时减少计算量。


苏格拉底时刻

在继续之前,试着回答以下问题来检验你的理解深度:

  1. 为什么 Transformer 中 Q、K、V 的计算本质上是线性变换?如果去掉这些投影矩阵会发生什么?
  2. 链式法则在反向传播中具体是如何工作的?当网络层数很深时,梯度可能出现什么问题?
  3. 交叉熵损失函数的概率意义是什么?为什么语言模型不用均方误差(MSE)作为损失函数?
  4. KL 散度不满足对称性,这在实际训练(如 RLHF 中的 KL 惩罚)中意味着什么?
  5. SVD 与 LoRA 低秩适配之间有什么联系?
  6. 为什么 CrossEntropyLoss 要求传入 logits 而非概率?(提示:数值稳定性 + 计算效率)
  7. Temperature 参数如何影响 Softmax 的输出分布?T=0.1T=10 分别产生什么效果?

推荐资源