字节技术总监30讲 AI课:3概率、信息论与损失函数|从Logits到Cross-Entropy带你吃透大模型训练

《现代 AI 原理与系统工程》全课程(共26讲)

第03讲|概率、信息论与损失函数:模型到底在预测什么?

本讲定位 :第01讲回答"现代 AI 为什么会走到预测下一个 Token";第02讲回答"模型内部拿什么东西计算";第03讲继续追问:模型算出来的一堆浮点数到底表示什么?它怎样变成概率?训练时又怎样把"预测得不好"变成一个可以优化的 Loss?

核心主线 :条件概率 → Logits → Softmax → 真实答案概率 → -log(p) → NLL / Cross-Entropy → 序列平均 Loss → Gradient

学习者基线 :有编程基础;大学阶段学过高等数学、概率论、线性代数,但这些知识已经多年没有系统使用;能够读程序,但第一次看到 P(A|B)、Σ、Π、log、exp、Softmax、Cross-Entropy 时,需要重新建立直觉。

本讲规则 :不删数学,但也不把数学当作先验。统一使用:问题 → 人话 → 最小手算 → 正式表达 → 代码 → AI / 工程。

表达规则:全文尽量采用纯文本公式、ASCII 图、C/Python 代码和普通 Markdown,不依赖 LaTeX 排版。这样更适合长期保存、搜索、复制、交给代码工具继续处理。


0. 先看完整地图:这一讲到底在解决什么

如果只看名词,很容易觉得这一讲很散:

text 复制代码
Probability
Logits
Softmax
Information
Entropy
Cross-Entropy
KL
Likelihood
NLL
Loss
Perplexity
Mask
Shift
LogSoftmax
LogSumExp

实际上,它们都在服务同一个问题:

模型对下一个 Token 有多个可能答案,它怎样表达自己的判断?真实答案出现后,又怎样衡量这次判断到底有多好?

把整讲压缩成一条工程链:

text 复制代码
输入上下文
    ↓
模型前向计算
    ↓
得到一组 logits
    ↓
logits = 原始分数,不是概率
    ↓
Softmax / LogSoftmax
    ↓
得到条件概率分布
    ↓
找到训练数据中的真实 Token
    ↓
查看真实 Token 被分配了多少概率
    ↓
-log(p_true)
    ↓
得到当前 Token 的 NLL / Cross-Entropy
    ↓
对有效 Token 做 sum / mean
    ↓
得到训练 Loss
    ↓
第04讲:Gradient / Backpropagation / Optimization

真正需要建立的心智模型是:

text 复制代码
模型先"报分数"
        ↓
再把分数解释成概率
        ↓
数据告诉模型真实答案
        ↓
Loss 衡量模型给真实答案多少支持
        ↓
Loss 把"预测质量"变成一个可优化的标量
        ↓
第04讲再负责回答:参数应该怎么改

1. 先约定本讲的数学符号

第一原则:符号第一次出现时,先用人话解释,再进入公式。

符号 人话 本讲最常见的程序对应
P(A) A 发生的概率 一个 0~1 之间的数
`P(A B)` 已知 B 后,A 的概率
x_t 第 t 个 Token tokens[t]
x_<t t 之前的全部 Token tokens[:t]
z_i 第 i 个类别的 logit logits[i]
p_i 第 i 个类别的概率 probs[i]
y_i 目标分布在第 i 类的值 one-hot 时通常是 0 或 1
Σ 把很多数加起来 for + sum += ...
Π 把很多数乘起来 for + prod *= ...
log 自然对数 math.log() / torch.log()
exp 自然指数 math.exp() / torch.exp()
L 损失 一个标量 Tensor
B Batch Size 一批样本数量
T 序列长度 Token 数
V 词表大小 可预测 Token 总数

一个阅读习惯:

看到一个数学式子时,先问每个符号是什么,再问整个式子在程序里对应什么。


2. 数学复健一:概率到底是什么

2.1 概率先只当成 0 到 1 之间的"可能性分配"

假设下一个 Token 只有三个候选:

text 复制代码
cat   0.7
dog   0.2
car   0.1

这就是一个离散概率分布,因为:

text 复制代码
0.7 + 0.2 + 0.1 = 1.0

它表达的是:

在当前条件下,模型把总的概率质量分配给三个可能结果。

对于离散分布,核心约束是:

text 复制代码
每个概率 >= 0

所有概率加起来 = 1

后面的 Softmax,本质就是把一组不受这些约束的原始分数变成这种形式。


2.2 条件概率 P(A|B) 到底是什么

第一次看到:

text 复制代码
P(A | B)

最简单的翻译:

在 B 已经发生、已经知道 B 的条件下,A 发生的概率。

例如:

text 复制代码
P(下雨 | 天上乌云密布)

不是问"下雨的概率是多少",而是问:

"在已经看到乌云密布以后,下雨的概率是多少?"

这个"已知条件"就是语言模型最重要的东西。

语言模型关心:

text 复制代码
P(x_t | x_<t)

直接翻译:

已经知道前面的 Token,当前这个位置出现某个 Token 的概率是多少?

所以:

text 复制代码
预测下一个 Token

从概率论角度说,就是:

text 复制代码
预测一个条件概率分布

2.3 条件概率的公式为什么这么写

定义是:

text 复制代码
P(A | B) = P(A 且 B) / P(B)

先不要背它。

可以把它理解成:

text 复制代码
在 B 已发生的人群里
    ↓
其中同时满足 A 的比例

分母 P(B) 的作用,就是把观察范围从"所有情况"缩到"B 已发生的情况"。

这个思维在机器学习里会反复出现:

text 复制代码
没有条件:P(A)
有上下文:P(A | context)

大语言模型不是只学一个固定的"词概率表",而是在不同上下文下生成不同的条件分布。


3. 数学复健二:Σ、Π、log、exp

3.1 Σ:就是求和循环

数学:

text 复制代码
Σ_i x_i

程序员翻译:

c 复制代码
float sum = 0.0f;
for (int i = 0; i < n; i++) {
    sum += x[i];
}

所以看到 Σ,先不要紧张。

Σ 就是把一批数全部加起来。


3.2 Π:就是连乘循环

数学:

text 复制代码
Π_i x_i

程序员翻译:

c 复制代码
float prod = 1.0f;
for (int i = 0; i < n; i++) {
    prod *= x[i];
}

语言模型的序列概率里会出现很多概率相乘,因此 Π 会自然出现。


3.3 exp(x):把任意实数变成正数

text 复制代码
exp(x) = e^x

几个最小值:

text 复制代码
exp(0) = 1
exp(1) ≈ 2.718
exp(2) ≈ 7.389

更重要的是:

text 复制代码
exp(x) > 0

所以它特别适合用来把任意正负的 logit 先变成正数,再做归一化。


3.4 log(x):最重要的是"把乘法变成加法"

自然对数满足:

text 复制代码
log(a × b) = log(a) + log(b)

所以:

text 复制代码
很多概率相乘

经过 log 以后变成:

text 复制代码
很多 log 概率相加

这正是最大似然训练里为什么会大量使用 log 的核心原因。

本讲默认 log 指自然对数 ln。

如果使用自然对数:

text 复制代码
信息量的单位常写成 nats

如果使用以 2 为底的对数:

text 复制代码
信息量的单位常写成 bits

在深度学习 Loss、NLL、Perplexity 的常见实现里,通常使用自然对数,因此后文都按自然对数理解。


4. Logits:模型最后一层算出来的那些数字到底是什么

4.1 先看模型最后一层

在一个分类或语言模型中,最后通常会有一个线性投影,把隐藏状态映射到词表大小:

text 复制代码
hidden state
    ↓
Linear / Vocabulary Projection
    ↓
logits

假设词表里只有三个候选:

text 复制代码
cat = 2.0
dog = 1.0
car = 0.0

那么:

text 复制代码
logits = [2.0, 1.0, 0.0]

这些数字叫做 logits。


4.2 Logits 为什么不是概率

因为它们根本没有被要求满足概率的约束。

它们可以:

text 复制代码
是正数
是负数
远大于 1
远小于 -1
总和不等于 1

例如:

text 复制代码
[10.0, -3.0, 1.7]

完全合法。

但它显然不能直接叫概率,因为:

text 复制代码
-3.0 < 0

所以最先建立的边界是:

text 复制代码
Logits != Probability

4.3 为什么神经网络自然得到 logits

因为最后一层本来就是普通的线性计算:

text 复制代码
y = xW + b

矩阵乘法和加法天然产生任意实数。

模型先产生自由的分数,再在需要概率解释的时候进行 Softmax,是一种非常自然的计算分层:

text 复制代码
模型负责比较不同候选的相对分数
        ↓
概率层负责把这些分数归一化

这比强行要求每次线性层输出都满足:

text 复制代码
每个数 >= 0
总和 = 1

更符合神经网络的计算结构。

注意:这里不需要把"logits 没有约束"理解成"越自由越高级"。它只是意味着:模型的最后线性输出没有概率归一化这个额外约束。


5. Softmax:把原始分数变成概率分布

5.1 Softmax 在解决什么问题

现在有:

text 复制代码
logits = [2, 1, 0]

但我们希望得到:

text 复制代码
每一项 > 0
所有项加起来 = 1

这就是 Softmax 要解决的问题。

最简洁的直觉是:

text 复制代码
原始分数
   ↓
先把它们转成正数
   ↓
再统一除以总和
   ↓
概率分布

5.2 Softmax 的纯文本公式

对第 i 个位置:

text 复制代码
softmax(z_i) = exp(z_i) / Σ_j exp(z_j)

不要急着背。

逐项翻译:

text 复制代码
分子:第 i 个分数先做 exp
分母:所有分数做 exp 后再加起来
最后:第 i 个 exp 值除以总和

所以:

text 复制代码
exp

负责"变成正数";

text 复制代码
除以总和

负责"归一化"。


5.3 为什么不能直接"除以总和"

比如:

text 复制代码
logits = [2, -1, 0]

直接求和:

text 复制代码
2 + (-1) + 0 = 1

再除:

text 复制代码
[2, -1, 0] / 1
= [2, -1, 0]

仍然出现:

text 复制代码
2
-1

这显然不满足概率要求。

所以必须先把原始分数变成非负数。

Softmax 的 exp 正好完成这个工作。


6. 手算 Softmax:[2,1,0] 从分数变成概率

这是本讲第一个必须亲手算的例子。

6.1 第一步:算 exp

text 复制代码
exp(2) ≈ 7.389
exp(1) ≈ 2.718
exp(0) = 1.000

6.2 第二步:求总和

text 复制代码
7.389 + 2.718 + 1.000
= 11.107

6.3 第三步:分别除以总和

text 复制代码
p0 = 7.389 / 11.107 ≈ 0.665
p1 = 2.718 / 11.107 ≈ 0.245
p2 = 1.000 / 11.107 ≈ 0.090

所以:

text 复制代码
[2, 1, 0]
    ↓ Softmax
[0.665, 0.245, 0.090]

验算:

text 复制代码
0.665 + 0.245 + 0.090 ≈ 1.000

这才是概率分布。


6.4 Softmax 保留了什么信息

如果:

text 复制代码
z1 > z2

那么:

text 复制代码
exp(z1) > exp(z2)

因此:

text 复制代码
p1 > p2

所以 Softmax 不会把排序关系凭空打乱。

更准确地说,Softmax 把原始分数的相对差异映射成一个归一化分布。


7. Softmax 最重要的数学性质:整体平移不改变结果

假设:

text 复制代码
z = [2, 1, 0]

同时减去 10:

text 复制代码
z' = [-8, -9, -10]

Softmax 结果完全一样。

原因是:

text 复制代码
exp(z_i + c) = exp(z_i) × exp(c)

分子与分母同时出现 exp(c),会约掉。

所以:

text 复制代码
softmax(z)
=
softmax(z + c)

这不是一句工程经验,而是数学性质。


8. Stable Softmax:为什么工程实现一定喜欢"减最大值"

8.1 问题:exp 增长太快

例如:

text 复制代码
exp(1000)

会远远超过常见浮点数格式能够表示的范围。

可能出现:

text 复制代码
inf

然后继续计算时出现:

text 复制代码
NaN

8.2 数学允许我们做什么

因为整体平移不改变 Softmax,所以可以先:

text 复制代码
m = max(z)

然后:

text 复制代码
z' = z - m

这样:

text 复制代码
max(z') = 0

因此:

text 复制代码
exp(z'_i) <= 1

至少不会因为指数参数为巨大正数而发生上溢。

这个技巧不是"经验小妙招",而是由前面的平移不变性直接推出的。


8.3 Python 手写稳定 Softmax

python 复制代码
import numpy as np


def stable_softmax(logits):
    logits = np.asarray(logits, dtype=np.float64)
    m = np.max(logits)
    shifted = logits - m
    exps = np.exp(shifted)
    return exps / np.sum(exps)


logits = np.array([1000.0, 1001.0, 1002.0])
print(stable_softmax(logits))

重点不是背代码,而是记住:

text 复制代码
先减最大值
不是改变数学目标
而是在利用 Softmax 的平移不变性保护数值计算

9. 信息量:为什么一个概率可以变成"代价"

现在模型已经有概率了。

但训练还差一步:

这个概率到底好不好?

假设真实答案已经确定。

模型给它三个不同概率:

text 复制代码
0.9
0.5
0.1

直觉上:

text 复制代码
0.9:模型很看好真实答案
0.5:模型一般相信
0.1:模型几乎不相信

我们希望设计一个分数满足:

text 复制代码
概率越高 → 代价越低
概率越低 → 代价越高

信息论中一个经典的选择是:

text 复制代码
I(p) = -log(p)

10. 手算 -log(p):看到概率如何变成惩罚

自然对数下:

text 复制代码
-log(0.9)  ≈ 0.105
-log(0.5)  ≈ 0.693
-log(0.1)  ≈ 2.303
-log(0.01) ≈ 4.605

规律非常清楚:

text 复制代码
真实答案概率高
    ↓
Loss 小

真实答案概率低
    ↓
Loss 大

并且随着概率继续接近 0,Loss 会继续增大。


11. 为什么是 -log(p),而不是简单用 1-p

1-p 不是"完全不能用"的数学函数。

问题在于:它没有很好地表达概率模型里我们需要的累计结构。

11.1 第一层原因:概率会连乘

一整个序列的概率可以写成:

text 复制代码
P(sequence)
= P(x1)
× P(x2 | x1)
× P(x3 | x1,x2)
× ...

很多小于 1 的数相乘以后会非常小。

如果直接处理这个乘积:

text 复制代码
0.9 × 0.8 × 0.7 × 0.6 × ...

数值很快变得极小。

取 log 以后:

text 复制代码
log(a × b)
= log(a) + log(b)

乘法变成加法。


11.2 第二层原因:它给"低概率"更强的惩罚

比较:

text 复制代码
p = 0.5
1-p = 0.5
-log(p) ≈ 0.693

再看:

text 复制代码
p = 0.01
1-p = 0.99
-log(p) ≈ 4.605

当模型把真实答案概率压得非常低时,-log(p) 会继续显著增大。

这让训练目标能够区分:

text 复制代码
"没猜中"

和:

text 复制代码
"极度自信地给真实答案极低概率"

两者不是同一种严重程度。


11.3 一个必须纠正的误区:Loss 很大,不等于 logits 梯度也无限大

这里很容易出现一个重要混淆。

确实:

text 复制代码
p_true → 0

会导致:

text 复制代码
-log(p_true) → +∞

但是对于经典的:

text 复制代码
Softmax + Cross-Entropy

其对 logits 的梯度满足:

text 复制代码
dL/dz_i = p_i - y_i

所以梯度在常见情形下并不会因为 Loss 很大而直接变成无穷大。

这两个概念必须分开:

text 复制代码
Loss 的数值
    ≠
logits 梯度的数值

这比"用炮弹狠狠打醒模型"之类的比喻更准确,也更重要。

第04讲会完整推导这条公式。


12. 从单个事件走到整条序列:链式法则

假设序列是:

text 复制代码
x1, x2, x3, ..., xT

整个序列联合概率可以拆成:

text 复制代码
P(x1, x2, ..., xT)
=
P(x1)
× P(x2|x1)
× P(x3|x1,x2)
× ...
× P(xT|x1,...,xT-1)

也可以写成更紧凑的形式:

text 复制代码
P(x1:T) = Π_t P(x_t | x_<t)

这里:

text 复制代码
x_<t

就是第 t 个 Token 之前的全部 Token。

这一步非常重要,因为它把:

text 复制代码
一个完整句子的概率

拆成了:

text 复制代码
一串"预测下一个 Token"的条件概率

于是:

自回归语言模型的 next-token prediction,不只是一个训练技巧,它直接对应序列概率的链式分解。


13. 最大似然:训练到底在最大化什么

13.1 先用一句话理解

训练数据已经给出来了。

我们希望找到一组模型参数,使:

训练数据在这个模型下出现的概率尽可能大。

这就是 Maximum Likelihood,最大似然。


13.2 "概率"和"似然"为什么经常让人混

这是一个必须补清楚的概念。

看同一个式子:

text 复制代码
P(data | theta)

当:

text 复制代码
theta 固定

把数据看成随机结果时,它通常被理解成概率。

而在最大似然里:

text 复制代码
data 已经观察到

我们把 theta 当成变量,问:

哪一组参数 theta 能让这批已经看到的数据更可能出现?

这时同一个表达式从"对参数做比较"的角度被称为 likelihood,似然。

所以:

text 复制代码
概率:更关心结果在模型下有多可能
似然:更关心哪组参数更能解释已经看到的数据

这不是两个不同的数学对象,而是观察角度不同。


14. 最大似然为什么自然导向 NLL

假设训练数据有很多样本:

text 复制代码
D = {x(1), x(2), ..., x(N)}

在常见的独立样本建模下:

text 复制代码
P(D | theta)
=
Π_i P(x(i) | theta)

最大似然希望:

text 复制代码
最大化 P(D | theta)

直接最大化这个巨大乘积有两个问题:

text 复制代码
很多小于 1 的数相乘
→ 数值越来越小

大量乘法
→ 不方便累加与优化分析

因此取 log:

text 复制代码
log P(D | theta)
=
Σ_i log P(x(i) | theta)

由于 log 在正数上是单调递增的:

text 复制代码
最大化 P(D | theta)

与:

text 复制代码
最大化 log P(D | theta)

等价。

为了统一成机器学习里更习惯的"越小越好":

text 复制代码
NLL = -log P(data | theta)

所以整个来源链条是:

text 复制代码
最大似然
    ↓
Log-Likelihood
    ↓
取负号
    ↓
Negative Log-Likelihood
    ↓
Loss

这就是为什么 NLL 看起来像:

text 复制代码
-log(probability)

它不是拍脑袋定义出来的。


15. 从 NLL 到 Cross-Entropy:它们到底是什么关系

15.1 Cross-Entropy 的一般定义

如果真实分布是 p,模型分布是 q:

text 复制代码
H(p, q)
=
-Σ_i p_i log(q_i)

翻译:

按照真实分布 p 的权重,平均计算模型分布 q 给这些结果分配了多大的负对数代价。


15.2 One-hot 标签为什么会把公式变得特别简单

假设三个类别:

text 复制代码
cat   dog   car

真实答案是 dog。

one-hot 标签:

text 复制代码
y = [0, 1, 0]

模型预测:

text 复制代码
p = [0.1, 0.8, 0.1]

Cross-Entropy:

text 复制代码
CE
=
-[0×log(0.1) + 1×log(0.8) + 0×log(0.1)]

由于 0 项全部消失:

text 复制代码
CE = -log(0.8)
   ≈ 0.223

所以在 one-hot 分类中,一句最重要的话是:

Cross-Entropy 就是在看模型给真实类别分配了多少概率,然后取它的负对数。


15.3 NLL 与 Cross-Entropy 为什么经常看起来一样

对于 one-hot 目标:

text 复制代码
CE = -log(p_true)

而 NLL:

text 复制代码
NLL = -log(p_true)

因此数值上一样。

但概念角色不同:

text 复制代码
NLL
更强调"负对数似然"这一概率建模 / 统计学习来源

Cross-Entropy
更强调"真实分布与模型分布之间的交叉熵"这一信息论形式

在标准分类训练中,两者会高度重合,所以工程代码里经常把它们看成同一条计算链上的不同命名层次。


16. Cross-Entropy 为什么能惩罚"自信地犯错"

假设真实答案是 cat。

模型给 cat 的概率分别是:

text 复制代码
0.90
0.50
0.10
0.01

Loss:

text 复制代码
-log(0.90) ≈ 0.105
-log(0.50) ≈ 0.693
-log(0.10) ≈ 2.303
-log(0.01) ≈ 4.605

所以:

text 复制代码
真实答案概率高
→ Loss 小

真实答案概率低
→ Loss 大

尤其是:

text 复制代码
真实答案概率 = 0.01

模型已经极度不相信正确答案,却偏偏把它当成错误方向。

Cross-Entropy 会明显提高这次样本的损失。

这里应避免使用"无限火箭炮""一巴掌打醒"之类的说法。工程上真正有价值的是:

它对概率错误的惩罚不是简单的线性比例,而且与 Softmax 组合后能形成非常清晰的梯度结构。


17. Entropy:分布自己到底有多不确定

Entropy:

text 复制代码
H(p)
=
-Σ_i p_i log(p_i)

直觉:

如果只看分布 p 自己,它内部有多不确定?

17.1 完全确定的分布

text 复制代码
p = [1, 0, 0]

熵:

text 复制代码
H(p) = 0

因为结果已经完全确定。

17.2 均匀分布

text 复制代码
p = [1/3, 1/3, 1/3]

不确定性更大,因此熵更高。

所以:

text 复制代码
Entropy
= 分布自身的不确定性

不是模型好坏分数本身。


18. Cross-Entropy:用 q 描述 p 的平均代价

再次强调角色:

text 复制代码
真实分布:p
模型分布:q

Cross-Entropy:

text 复制代码
H(p,q)
=
-Σ_i p_i log(q_i)

如果模型 q 在真实分布 p 认为重要的位置给了很低概率,那么 Cross-Entropy 就会变大。

所以它天然适合"预测分布"的任务。


19. KL Divergence:它到底是什么,不要把它叫成普通距离

定义:

text 复制代码
KL(p || q)
=
Σ_i p_i log(p_i / q_i)

它常被用于衡量:

如果把 q 当成 p,额外付出了多少分布层面的编码代价。

重要边界:

text 复制代码
KL(p || q) >= 0

并且:

text 复制代码
KL(p || q) = 0

当且仅当两个分布在相关条件下相同。

但是:

text 复制代码
KL(p || q)
!=
KL(q || p)

所以它不是一般意义上的对称距离。


20. Entropy、Cross-Entropy、KL 三者为什么会连起来

有一个非常重要的分解:

text 复制代码
H(p,q) = H(p) + KL(p || q)

翻译:

text 复制代码
模型用 q 描述真实分布 p 的代价
=
真实分布自身的不确定性
+
模型额外付出的分布差异代价

当 p 固定时:

text 复制代码
H(p)

是常数。

因此优化 q 时:

text 复制代码
最小化 Cross-Entropy

与:

text 复制代码
最小化 KL(p || q)

具有相同的最优点。

这就是为什么 Cross-Entropy 不只是一个"好用的经验 Loss",它和概率分布逼近之间存在严格的数学关系。


21. 一个最小完整例子:从 logits 一直走到 Loss

现在把前面的知识全部串起来。

假设:

text 复制代码
logits = [2, 1, 0]

真实答案是第 0 类。

第一步:Softmax

text 复制代码
[2, 1, 0]
↓
[0.665, 0.245, 0.090]

第二步:取真实答案概率

真实答案是第 0 类,因此:

text 复制代码
p_true = 0.665

第三步:计算 NLL / CE

text 复制代码
Loss = -log(0.665)
     ≈ 0.408

所以一整个最小计算链是:

text 复制代码
logits
[2,1,0]
   ↓ Softmax
probabilities
[0.665,0.245,0.090]
   ↓ 取 target=0
p_true = 0.665
   ↓ -log
Loss ≈ 0.408

这条链必须真正能自己手算出来。


22. 为什么模型不直接用"猜对 / 猜错"作为训练目标

很多初学者会问:

"既然目标只是预测下一个 Token,直接算 Accuracy 不就行了?"

问题在于,Accuracy 是离散的。

例如真实答案是 cat:

text 复制代码
模型 A:cat = 0.49
dog = 0.51
→ 猜错

模型 B:cat = 0.01
dog = 0.99
→ 也猜错

Accuracy 看起来完全一样:

text 复制代码
0

但两个模型的"错误程度"显然不同。

Cross-Entropy 会区分:

text 复制代码
-log(0.49)

和:

text 复制代码
-log(0.01)

因此训练 Loss 保留了比单纯 Accuracy 丰富得多的信息。

更关键的是:

标准的 argmax Accuracy 不是一个适合直接做梯度优化的连续目标。

而 Cross-Entropy 具有适合反向传播的可导结构。


23. 一个重要的边界:Loss、Accuracy、Perplexity 不是一回事

可以先做一个定位表:

指标 主要看什么 是否使用概率信息 典型用途
Accuracy 最终是否猜中 很少 分类结果展示
Cross-Entropy / NLL 真实答案拿到多少概率 是 训练、评估
Perplexity 平均负对数似然的指数化表示 是 语言模型评估

因此:

text 复制代码
Accuracy
更像"最终选谁"

Loss
更像"你给真实答案多少支持"

PPL
更像"把平均 NLL 重新指数化后的尺度"

三者不要混成同一个概念。


24. Perplexity:从 Loss 到 PPL

24.1 数学关系

当使用自然对数定义平均 Cross-Entropy / NLL 时:

text 复制代码
PPL = exp(Loss)

例如:

text 复制代码
Loss = 0
→ PPL = 1

如果:

text 复制代码
Loss ≈ 2.303

那么:

text 复制代码
PPL ≈ exp(2.303) ≈ 10

24.2 "PPL 等于模型平均在多少个词里摇摆"到底对不对

这句话可以作为直觉,但不能当成严格定义。

更准确的是:

PPL 是平均负对数似然的指数化表示,可看成模型每个位置平均不确定性的一个等效尺度。

在某些简单、均匀分布的情况下,它会非常像"多少个等可能选项"。

但真实语言分布通常高度不均匀,而且不同 tokenizer、数据集、评估策略会影响 PPL。

所以:

text 复制代码
PPL 越低

在相同数据、相同 Tokenizer、相同评估口径下通常表示平均预测代价更低。

但不能直接推出:

text 复制代码
PPL 更低
= 模型所有能力都更强

尤其不能把 PPL 当作"综合智商"。


25. 信息量、Cross-Entropy 与语言模型训练为什么能闭环

把前面所有内容压缩成一条严格链:

text 复制代码
语言模型要预测 x_t
        ↓
预测的是条件分布 P(x_t | x_<t)
        ↓
神经网络输出 logits
        ↓
Softmax 把 logits 转成概率分布
        ↓
真实数据告诉我们 x_t 是谁
        ↓
取得真实 Token 的概率 p_true
        ↓
-log(p_true)
        ↓
当前 Token 的 NLL / CE
        ↓
对很多 Token 求平均
        ↓
训练 Loss
        ↓
第04讲:求梯度并更新参数

这就是本讲真正的"灵魂主线"。


26. 大模型训练的真实 Tensor:为什么经常看到 [B, T, V]

前面是在讲数学。

现在开始进入真实 PyTorch 训练代码。

一个典型语言模型在词表投影之后,会得到:

text 复制代码
logits.shape = [B, T, V]

含义:

text 复制代码
B = 一批样本有多少条序列
T = 每条序列有多少 Token
V = 每个位置要对多少个候选 Token 打分

例如:

text 复制代码
B = 4
T = 2048
V = 100000

那么元素数量:

text 复制代码
4 × 2048 × 100000
= 819,200,000

如果每个元素 2 字节:

text 复制代码
819,200,000 × 2
≈ 1.64 GB

这只是一个 logits 张量的近似存储量。

注意:实际训练显存占用还受到:

text 复制代码
激活值
权重
梯度
优化器状态
临时张量
并行策略
Kernel 实现

等因素影响。

所以这个计算真正想让你建立的是:

词表维度 V 很大,导致语言模型 Loss 本身就是一个很重的计算与内存访问问题。


27. 为什么标签要"错一位":Shift 到底在做什么

这是写语言模型训练代码最容易理解错的部分之一。

假设 Token 序列是:

text 复制代码
[我, 喜欢, 吃, 肉]

训练目标是:

text 复制代码
看到"我"
→ 预测"喜欢"

看到"我 喜欢"
→ 预测"吃"

看到"我 喜欢 吃"
→ 预测"肉"

所以位置对齐应该是:

text 复制代码
输入位置        目标 Token
--------------------------------
我              喜欢
我 喜欢          吃
我 喜欢 吃       肉

于是长度为 4 的序列,会形成:

text 复制代码
输入:
[我, 喜欢, 吃]

目标:
[喜欢, 吃, 肉]

27.1 PyTorch 常见写法

如果原始 logits 形状:

text 复制代码
[B, T, V]

那么:

python 复制代码
shift_logits = logits[:, :-1, :]
shift_labels = labels[:, 1:]

得到:

text 复制代码
shift_logits.shape = [B, T-1, V]
shift_labels.shape = [B, T-1]

代码背后的逻辑只有一句:

模型在位置 t 看到前面的上下文,目标是位置 t+1 的真实 Token。


28. Padding:为什么"补齐"不能算进语言模型 Loss

批量训练时,不同序列长度可能不同。

例如:

text 复制代码
A: [我, 喜欢, 苹果]
B: [我, 喜欢]

为了组成统一张量,B 可能补齐成:

text 复制代码
B: [我, 喜欢, PAD]

问题是:

text 复制代码
PAD

不是我们真正想学习的自然语言目标。

如果把 PAD 当成正常标签参与 Loss,就会把训练目标污染。


28.1 ignore_index=-100 的作用

常见做法是把需要忽略的目标位置设置成:

text 复制代码
-100

然后:

python 复制代码
loss_fn = torch.nn.CrossEntropyLoss(ignore_index=-100)

含义是:

目标标签等于 -100 的位置,不贡献这一项 Cross-Entropy Loss。

注意严谨边界:

text 复制代码
它不是把这个位置从整个模型计算里"删掉"

模型前向过程可能依然计算这个位置的 hidden state 与 logits。

只是:

text 复制代码
这个目标位置不参与 Loss
→ 不通过这一项 Loss 产生对应的参数梯度贡献

这一区别非常重要。


29. Causal Mask、Padding Mask、Loss Mask:三个"Mask"不要混

这是语言模型工程里非常容易混为一谈的一组概念。

29.1 Causal Mask

回答:

当前 Token 能不能看到未来 Token?

自回归语言模型通常要求:

text 复制代码
位置 3
不能偷看位置 4、5、6

因此需要 causal / look-ahead mask。


29.2 Padding Mask

回答:

哪些位置只是为了补齐长度,并不是真实输入?

它通常参与注意力计算,让模型不要把无意义的 PAD 当成正常上下文。


29.3 Loss Mask / ignore_index

回答:

哪些目标位置应该进入 Loss 统计?

例如:

text 复制代码
PAD 位置
或
训练时不想监督的位置

可以不进入 Loss。

因此:

text 复制代码
Attention Mask
≈ 控制"看谁"

Loss Mask
≈ 控制"算谁的损失"

两者不是同一个东西。


30. PyTorch 的 CrossEntropyLoss:为什么通常直接输入 logits

30.1 数学理解方式

学习数学时,可以把它拆成:

text 复制代码
Logits
  ↓
Softmax
  ↓
Probability
  ↓
取真实类别概率
  ↓
-log
  ↓
NLL

这条链没有问题。


30.2 工程实现为什么通常直接输入 logits

在 PyTorch 中,标准的 CrossEntropyLoss 期望输入的是:

text 复制代码
raw logits

不是你手动算完的概率。

典型代码:

python 复制代码
import torch
import torch.nn.functional as F

logits = torch.tensor([[2.0, 1.0, 0.0]])
target = torch.tensor([0])

loss = F.cross_entropy(logits, target)
print(loss.item())

不要在标准路径里先写:

python 复制代码
probs = torch.softmax(logits, dim=-1)
loss = F.cross_entropy(probs, target)

这里的核心原因是:

框架可以直接在 logits 域完成更稳定的 log-softmax + NLL 计算,而不需要先显式构造完整概率张量。

所以正确的工程心智模型是:

text 复制代码
数学理解:
Softmax → log → NLL

PyTorch 实现:
logits → CrossEntropyLoss

两者不是冲突的两套理论。


31. LogSoftmax 与 LogSumExp:为什么工程里喜欢待在 log 域

31.1 Softmax 后再 log 可以化简

Softmax:

text 复制代码
p_i = exp(z_i) / Σ_j exp(z_j)

两边取 log:

text 复制代码
log(p_i)
=
z_i - log(Σ_j exp(z_j))

右边这个:

text 复制代码
log(Σ_j exp(z_j))

就是 LogSumExp 的核心结构。

于是:

text 复制代码
log_softmax(z_i)
=
z_i - logsumexp(z)

31.2 为什么 LogSumExp 很重要

如果直接:

text 复制代码
exp(logits)

会有数值溢出的风险。

LogSumExp 有成熟的数值稳定计算方式:

text 复制代码
m = max(z)

log(Σ exp(z_i))
=
m + log(Σ exp(z_i - m))

这样就把最大的指数项压到了:

text 复制代码
exp(0) = 1

所以:

text 复制代码
Stable Softmax

和:

text 复制代码
Stable LogSumExp

背后其实用了同一个思想:

先利用平移不变性,把数值范围搬到更安全的区域。


32. 为什么工程上尽量避免物化整个 [B,T,V] 的 probability tensor

假设:

text 复制代码
B = 4
T = 2048
V = 100000

前面已经算过:

text 复制代码
[B,T,V]

可能非常大。

如果你真的做:

python 复制代码
probs = torch.softmax(logits, dim=-1)

然后再做:

text 复制代码
取 target 概率
→ log
→ reduce

就可能引入额外的巨大中间结果和显存读写。

所以实际高性能实现通常会尽量:

text 复制代码
减少中间张量
在 logit 域计算
融合相关算子
优化内存访问

但不要把它简单理解成:

"所有 Cross-Entropy 都必然是一个固定的神奇融合 CUDA Kernel,而且一定快几倍。"

具体实现会受到:

text 复制代码
GPU
PyTorch 版本
Kernel
dtype
shape
并行方式

等因素影响。

工程原则比固定数字更重要。


33. [B,T,V] 最终怎样交给 CrossEntropyLoss

常见的语言模型代码会看到:

python 复制代码
import torch
import torch.nn.functional as F

B, T, V = 2, 4, 5

logits = torch.randn(B, T, V)
labels = torch.tensor([
    [1, 2, 3, 4],
    [2, 3, -100, -100]
])

shift_logits = logits[:, :-1, :]
shift_labels = labels[:, 1:]

loss = F.cross_entropy(
    shift_logits.reshape(-1, V),
    shift_labels.reshape(-1),
    ignore_index=-100
)

print('shift_logits:', shift_logits.shape)
print('shift_labels:', shift_labels.shape)
print('loss:', loss.item())

这里的 Shape 变化非常重要:

text 复制代码
[B, T-1, V]
    ↓ reshape
[(B×(T-1)), V]

而标签:

text 复制代码
[B, T-1]
    ↓ reshape
[B×(T-1)]

于是 CrossEntropyLoss 看到了一个普通分类问题:

text 复制代码
每一行 logits
→ 对应一个 Token 的 V 类分类任务

这其实非常漂亮:

语言模型看起来是在处理"序列",但最终的 Token Loss 可以展平成大量并行的小型 V 类分类问题。


34. reduction:为什么别人的 Loss 不能直接和你的 Loss 比

Cross-Entropy 不只是"算一个 Loss",还需要决定很多位置最后怎么汇总。

常见有:

text 复制代码
reduction='none'
reduction='sum'
reduction='mean'

none

每个位置分别保留:

text 复制代码
[loss_1, loss_2, loss_3, ...]

适合调试、可视化、按样本分析。

sum

全部相加:

text 复制代码
Loss_total = Σ loss_i

序列越长、Token 越多,数值通常越大。

mean

计算平均:

text 复制代码
Loss_mean = Σ有效loss_i / 有效位置数

对语言模型训练来说,按有效 Token 平均是非常常见的比较方式。

所以:

看到别人报告 Loss 时,必须知道它是 sum、mean,还是别的归一化口径。

尤其在比较不同 Batch Size、不同序列长度、不同数据过滤策略时,这一点很重要。


35. 为什么 Language Model Loss 通常可以理解成"真实 Token 的平均负对数概率"

如果有效 Token 一共有 N 个:

text 复制代码
Loss
=
-(1/N) × Σ_t log P(x_t | x_<t)

这里的每一项:

text 复制代码
-log P(x_t | x_<t)

就是当前真实 Token 的负对数概率。

因此:

text 复制代码
一个 Token
→ 一个负对数代价

一批 Token
→ 一批负对数代价

对有效 Token 求平均
→ Training Loss

所以训练 Loss 最朴素的解释是:

模型平均给真实 Token 分了多少"负对数代价"。


36. 训练 Loss 和生成时概率分布不是一回事

这是一个经常出现的边界。

训练阶段:

text 复制代码
有真实答案
→ 计算真实 Token 的概率
→ Cross-Entropy
→ 反向传播

生成阶段:

text 复制代码
没有真实答案
→ 模型输出概率分布
→ 采样或选择一个 Token

所以训练的 Loss 评估的是:

text 复制代码
模型对真实数据的解释质量

而生成时还涉及:

text 复制代码
Temperature
Top-k
Top-p
Greedy
Sampling

这些是生成策略,而不是标准预训练 Cross-Entropy 本身。

注意措辞:它们通常属于生成 / 解码阶段;某些其他训练技术也可以使用温度等操作,因此不要把"temperature 永远只存在于推理阶段"说成绝对规律。


37. Temperature:为什么概率分布可以被"变尖或变平"

生成时常见:

text 复制代码
p_i ∝ exp(z_i / T)

这里的 T 是 temperature。

如果:

text 复制代码
T > 1

通常会让分布更平。

如果:

text 复制代码
0 < T < 1

通常会让分布更尖。

例如:

text 复制代码
原始 logits
[2, 1, 0]

温度更高时,不同候选之间的差异被缩小;温度更低时,最高分候选更加突出。

这里先记住:

Temperature 改变的是解码时的分布形状,不是"模型学没学会"的评价指标。


38. Top-k / Top-p:它们在干什么

Top-k

只保留概率最高的 K 个候选,再在这些候选中重新归一化。

Top-p

从高概率候选开始累计,直到累计概率达到指定阈值 p,再在保留下来的集合里采样。

所以:

text 复制代码
Temperature
Top-k
Top-p

解决的是:

text 复制代码
如何从模型的概率分布生成文本

而:

text 复制代码
Cross-Entropy
NLL

回答的是:

text 复制代码
模型对真实数据给出的概率分配到底好不好

两条线不要混在一起。


39. 从系统角度理解 Cross-Entropy:它其实是一个"接口层"

现在可以把 Loss 放到整个系统里看。

text 复制代码
数据
  ↓
Token
  ↓
Embedding / Transformer
  ↓
Hidden State
  ↓
Vocabulary Projection
  ↓
Logits
  ↓
Cross-Entropy
  ↓
Loss
  ↓
Gradient
  ↓
Weight Update

Loss 位于一个非常关键的位置:

text 复制代码
模型的预测输出
        ↕
训练数据的真实标签

它把两者连接起来。

所以 Loss 不只是一个"分数"。

更准确的理解是:

Loss 是模型概率预测与训练目标之间的数学接口;它把"预测质量"转换成后续优化算法可以处理的标量目标。

第04讲会继续回答:

text 复制代码
既然 Loss 已经算出来了
那参数 θ 到底往哪个方向改?

40. 一个特别重要的数学预告:dL/dz = p-y

经典的 Softmax + Cross-Entropy 组合有一个非常漂亮的结果:

text 复制代码
dL/dz_i = p_i - y_i

其中:

text 复制代码
p_i = 模型预测概率

y_i = 目标分布对应位置

one-hot 标签时:

text 复制代码
y_true = 1
其它类别 = 0

于是直觉上:

text 复制代码
预测太高
→ 梯度推动它下降

真实类别预测不够高
→ 梯度推动真实类别上升

这条式子之所以漂亮,是因为它直接把:

text 复制代码
概率误差

连到了:

text 复制代码
logit 的修改方向

第04讲会从最基础的导数开始,把它一步一步推出来。


41. 一个非常重要的工程事实:大模型 Loss 本身就是系统性能问题

数学上:

text 复制代码
Cross-Entropy

好像只是一个公式。

但在大模型里:

text 复制代码
B 很大
T 很长
V 很大

于是最终的 [B,T,V] logits 会带来:

text 复制代码
大量数据
大量归约
大量显存读写
大量并行计算

因此 Loss 计算会进一步变成:

text 复制代码
Kernel
Memory Bandwidth
Tensor Layout
Parallelism
Numerical Stability

的问题。

这也是现代 AI 工程一个很典型的现象:

一个看起来只是数学课上的公式,最终会一路落到 GPU Kernel 与显存访问。


42. 一个最小 Python 实验:自己验证 [2,1,0] 的 Softmax

python 复制代码
import numpy as np

logits = np.array([2.0, 1.0, 0.0])

shifted = logits - np.max(logits)
exps = np.exp(shifted)
probs = exps / np.sum(exps)

print('logits :', logits)
print('probs  :', probs)
print('sum    :', probs.sum())

你应该看到接近:

text 复制代码
[0.66524096 0.24472847 0.09003057]
1.0

这里要观察三件事:

text 复制代码
1. 所有概率都 > 0
2. 概率和 ≈ 1
3. logit 最大的位置概率最大

43. 一个最小 Python 实验:自己验证 Cross-Entropy

python 复制代码
import math

p_true = 0.8
loss = -math.log(p_true)

print('p_true =', p_true)
print('loss   =', loss)

再测试:

python 复制代码
for p_true in [0.9, 0.5, 0.1, 0.01]:
    print(p_true, -math.log(p_true))

你会看到:

text 复制代码
0.9  -> 小
0.5  -> 更大
0.1  -> 明显更大
0.01 -> 非常大

这就是 Cross-Entropy 为什么特别关注"模型给真实答案多少概率"。


44. 一个最小 PyTorch 实验:手算和框架输出对上

python 复制代码
import math
import torch
import torch.nn.functional as F

logits = torch.tensor([[2.0, 1.0, 0.0]])
target = torch.tensor([0])

loss = F.cross_entropy(logits, target)

probs = torch.softmax(logits, dim=-1)
manually = -math.log(probs[0, 0].item())

print('PyTorch CE :', loss.item())
print('Manual     :', manually)
print('Probability:', probs.tolist())

你应该看到两者非常接近。

这个实验的重要性比背定义更高,因为它把:

text 复制代码
数学
↓
Softmax
↓
真实类别概率
↓
-log
↓
PyTorch CrossEntropyLoss

真正闭环了。


45. 一个最小 PyTorch 实验:验证 dL/dz = p-y

python 复制代码
import torch
import torch.nn.functional as F

logits = torch.tensor([[2.0, 1.0, 0.0]], requires_grad=True)
target = torch.tensor([0])

loss = F.cross_entropy(logits, target)
loss.backward()

probs = torch.softmax(logits.detach(), dim=-1)
grad = logits.grad

print('probs =', probs)
print('grad  =', grad)
print('p-y   =', probs - F.one_hot(target, num_classes=3))

重点观察:

text 复制代码
grad

与:

text 复制代码
p-y

一致。

这就是第03讲与第04讲之间最重要的桥。


46. 复杂度与系统成本:为什么词表 V 会成为热点

对于每个 Token,最基本的 Cross-Entropy 思路需要在 V 个候选上进行概率归一化或对应的 log-sum-exp 计算。

因此从粗粒度上看,计算量会随:

text 复制代码
Token 数量
×
词表大小 V

增长。

更准确的工程实现还会受到:

text 复制代码
Batch
Sequence Length
Vocabulary Projection
Tensor Parallelism
Kernel
Memory Bandwidth
dtype

等影响。

所以在真实大模型系统中,输出词表这一端并不是一个"免费操作"。

这也为后续 GPU、Kernel、并行训练、推理优化埋下了伏笔。


47. 12 个最容易犯的误区

误区 1:Logits 就是概率

错误。

text 复制代码
Logits
≠
Probability

Softmax 后才得到标准离散概率分布。


误区 2:Softmax 就是为了"让数字看起来像概率"

不只是形式转换。

它把一组原始分数映射到:

text 复制代码
非负
+
和为 1

的分布空间,同时保留候选之间的相对偏好结构。


误区 3:Cross-Entropy 就是"答错了几次"

不是。

它看的是:

text 复制代码
真实答案拿到了多少概率

因此两个都预测错误的模型,Loss 也可能差很多。


误区 4:Entropy、Cross-Entropy、KL 是一回事

不是。

text 复制代码
Entropy
= 一个分布自身的不确定性

Cross-Entropy
= 用 q 描述 p 的平均负对数代价

KL
= 分布差异的一种非对称度量

误区 5:KL 是普通距离

不严格。

因为通常:

text 复制代码
KL(p||q) != KL(q||p)

误区 6:CrossEntropyLoss 之前一定要手动 Softmax

标准 PyTorch 用法不是这样。

text 复制代码
logits
→ CrossEntropyLoss

通常比:

text 复制代码
softmax(logits)
→ 再交给 CrossEntropyLoss

更正确、更稳定。


误区 7:Loss 越大,logits 梯度就一定无限大

不对。

经典 Softmax + CE 下:

text 复制代码
grad = p - y

所以必须分清:

text 复制代码
Loss 数值

和:

text 复制代码
梯度数值

是两个不同概念。


误区 8:PPL 越低就代表模型综合能力一定越强

不可以这样直接推。

PPL 是语言模型评估指标,必须结合:

text 复制代码
数据集
Tokenizer
评估口径
语言
任务

解释。


误区 9:Causal Mask 和 Loss Mask 是同一个东西

不是。

text 复制代码
Causal Mask
→ 控制能看到哪些位置

Loss Mask / ignore_index
→ 控制哪些目标位置参与损失

误区 10:Padding 位置在前向计算里彻底不存在

不是。

Padding 可以仍然存在于张量中。

ignore_index 主要控制的是:

text 复制代码
Loss 统计

而不是自动删除整个前向过程。


误区 11:Label Shift 就是"把整条输入随便错开一位"

不准确。

真正含义是:

text 复制代码
位置 t 的上下文
→ 监督位置 t+1 的真实 Token

Shift 是为了让输入上下文与目标 Token 对齐。


误区 12:看到一个 Loss 数值,直接拿来和别人比较

至少先检查:

text 复制代码
tokenizer
数据
sequence length
reduction
忽略位置
loss 定义

否则比较可能没有意义。


48. 本讲知识树

text 复制代码
第03讲:概率、信息论与损失函数
│
├── 1. 概率基础
│   ├── 概率分布
│   ├── 条件概率
│   ├── Σ / Π
│   ├── log / exp
│   └── 链式法则
│
├── 2. 模型输出
│   ├── logits
│   ├── Softmax
│   └── Stable Softmax
│
├── 3. 信息论
│   ├── Information = -log(p)
│   ├── Entropy
│   ├── Cross-Entropy
│   └── KL Divergence
│
├── 4. 统计学习
│   ├── Likelihood
│   ├── Maximum Likelihood
│   ├── Log-Likelihood
│   └── NLL
│
├── 5. Language Model
│   ├── P(x_t | x_<t)
│   ├── Token-level NLL
│   ├── Cross-Entropy
│   └── Perplexity
│
├── 6. LLM Engineering
│   ├── [B,T,V]
│   ├── Label Shift
│   ├── Causal Mask
│   ├── Padding Mask
│   ├── Loss Mask
│   ├── ignore_index
│   ├── reduction
│   └── reshape
│
└── 7. Systems
    ├── LogSoftmax
    ├── LogSumExp
    ├── Numerical Stability
    ├── Memory
    ├── Kernel
    └── GPU

49. 本讲必须脱口而出的 15 句话

  1. Logits 是原始分数,不是概率。
  2. Softmax 把 logits 映射成非负且总和为 1 的概率分布。
  3. Softmax 的整体平移不会改变最终概率,所以可以先减最大值提高数值稳定性。
  4. 语言模型预测的是条件概率 P(x_t | x_<t)。
  5. 链式法则把整条序列的概率拆成一串条件概率的乘积。
  6. log 能把概率乘积变成概率对数的加法。
  7. 最大似然自然导向 Log-Likelihood,再自然导向 NLL。
  8. One-hot 监督下,Cross-Entropy 就是 -log(真实类别概率)。
  9. 真实类别概率越低,Loss 越大。
  10. Loss 很大不代表 logits 梯度一定无限大;Softmax + CE 的经典梯度是 p-y。
  11. Entropy 看分布自身的不确定性,Cross-Entropy 看用 q 描述 p 的平均代价。
  12. KL 不是对称距离。
  13. LLM 的常见训练 Loss 可以理解成有效真实 Token 的平均负对数概率。
  14. [B,T,V] 中 V 是词表维度,也是语言模型输出侧的重要系统成本来源。
  15. 第03讲把"模型在预测什么"连接到了"训练到底优化什么",第04讲再回答"参数往哪里改"。

50. 10 个自测问题

Q1:为什么 [2,1,0] 不是概率?

至少说出两个原因。


Q2:P(A|B) 中的 | 怎么解释?

不能只说"条件概率",要用人话解释。


Q3:Softmax 做了哪两件核心事情?

应该答出:

text 复制代码
exp
+
归一化

Q4:为什么 Softmax 可以先减最大值?

要说出:

text 复制代码
整体平移不改变 Softmax

而不是只背"防止溢出"。


Q5:真实答案概率是 0.1,为什么 Loss 会明显高于 0.9?

请自己算:

text 复制代码
-log(0.1)
-log(0.9)

Q6:为什么最大似然会出现 NLL?

至少说出:

text 复制代码
最大似然
→ log
→ 求和
→ 取负
→ NLL

Q7:为什么 one-hot Cross-Entropy 可以简化成 -log(p_true)?

请自己把另外几个类别的 0 项消掉。


Q8:Causal Mask 与 ignore_index=-100 有什么本质不同?

回答"控制看谁"和"控制算谁的损失"是否足够?为什么?


Q9:为什么 PyTorch CrossEntropyLoss 通常直接输入 logits?

请回答:

text 复制代码
数学理解
vs
工程实现

两条路径有什么关系。


Q10:为什么 PPL = exp(Loss) 不能直接理解成"模型只需要从这么多个词里选"?

请至少提到:

text 复制代码
非均匀分布
Tokenizer
数据集 / 评估口径

51. 三个必须亲手完成的练习

练习 A:手算 Softmax

输入:

text 复制代码
[1, 0]

要求自己得到:

text 复制代码
[0.731, 0.269]

练习 B:手算 Loss

真实类别概率:

text 复制代码
0.731

自己计算:

text 复制代码
-loss = ?

注意这里真正计算的是:

text 复制代码
Loss = -log(0.731)

练习 C:完成一个 [B,T,V] 小实验

设置:

text 复制代码
B = 2
T = 4
V = 5

完成:

text 复制代码
生成 logits
↓
生成 labels
↓
shift
↓
reshape
↓
CrossEntropyLoss

然后自己打印:

text 复制代码
每个张量的 shape
最终 Loss

如果你能解释每个轴代表什么,就说明这一讲真正开始进入工程状态。


52. 本讲最重要的三个"不要"

text 复制代码
不要把 logits 当概率。

不要在标准 PyTorch CrossEntropyLoss 前手动 softmax。

不要把 Causal Mask、Padding Mask、Loss Mask 混成一个概念。

53. 本讲最重要的三个"必须会"

text 复制代码
必须会手算:
[2,1,0] → Softmax → [0.665,0.245,0.090]

必须会解释:
CE = -log(p_true)

必须会读代码:
[B,T,V] → Shift → Reshape → CrossEntropyLoss

54. 与第02讲怎么接起来

第02讲解决:

text 复制代码
模型内部到底拿什么计算?

答案:

text 复制代码
Vector
Matrix
Tensor

第03讲继续问:

text 复制代码
这些数值算出来以后
到底代表什么?

于是连接成:

text 复制代码
Tensor
 ↓
Linear / Matrix Multiplication
 ↓
Logits
 ↓
Probability
 ↓
Loss

这就是:

text 复制代码
第02讲:怎么算
第03讲:怎么算得好不好

55. 与第04讲怎么接起来

本讲结束时,已经知道:

text 复制代码
Loss = 一个可以计算出来的标量

但是只知道 Loss 还不够。

下一步必须回答:

哪些参数应该增大?哪些参数应该减小?改多少?

于是进入:

text 复制代码
Loss
 ↓
Derivative
 ↓
Partial Derivative
 ↓
Gradient
 ↓
Chain Rule
 ↓
Backpropagation
 ↓
Gradient Descent
 ↓
Optimizer

所以:

text 复制代码
第03讲:模型错得有多严重
第04讲:知道错了以后参数怎么改

这两讲必须连起来看。


56. 最终总图:从模型输出一直走到训练

text 复制代码
             输入上下文
                  │
                  ↓
          Transformer / LLM
                  │
                  ↓
            Hidden State
                  │
                  ↓
       Vocabulary Projection
                  │
                  ↓
               Logits
                  │
          ┌───────┴───────┐
          │               │
          ↓               ↓
     Softmax          LogSoftmax
          │               │
          ↓               ↓
    Probability         Log Prob
          │               │
          └───────┬───────┘
                  ↓
          真实 Token / Label
                  │
                  ↓
          -log(p_true)
                  │
                  ↓
        NLL / Cross-Entropy
                  │
                  ↓
          Mask / Reduction
                  │
                  ↓
                Loss
                  │
                  ↓
              Gradient
                  │
                  ↓
          Parameter Update
                  │
                  ↓
          下一轮 Forward

如果把整讲最后压缩成一句话:

大语言模型先用 logits 表示对不同 Token 的相对偏好,再通过概率建模表示不确定性;训练数据给出真实 Token,Cross-Entropy / NLL 通过 -log(p_true) 把模型对真实答案的支持程度变成可优化的 Loss,第04讲再通过梯度告诉参数应该怎样改变。


57. 本讲最终过关标准

不要用"我把名词都看过一遍"判断自己会不会。

真正过关至少需要做到:

text 复制代码
1. 看到 P(A|B),能立即翻译成"已知 B 后 A 的概率"。

2. 看到 logits,知道它是原始分数,不是概率。

3. 看到 Softmax,能解释 exp + 归一化。

4. 能自己手算 [2,1,0] 的 Softmax。

5. 知道为什么可以先减最大值。

6. 知道为什么 -log(p_true) 是核心惩罚形式。

7. 能从最大似然解释为什么会出现 NLL。

8. 能说清 Entropy、Cross-Entropy、KL 的区别。

9. 能解释 [B,T,V] 的三个轴。

10. 能看懂 Shift、ignore_index、reduction。

11. 知道 Causal Mask 与 Loss Mask 不同。

12. 知道 PyTorch CrossEntropyLoss 通常直接接 logits。

13. 能手算并用 PyTorch 验证一次 CE。

14. 能理解 dL/dz = p-y 是下一讲的入口。

如果以上内容可以自己解释,而不是只能照着讲义复述,那么第三讲才算真正学会。


58. 参考资料

  1. Ian Goodfellow, Yoshua Bengio, Aaron Courville, Deep Learning.
  2. Christopher M. Bishop, Pattern Recognition and Machine Learning.
  3. Thomas M. Cover and Joy A. Thomas, Elements of Information Theory.
  4. Bengio et al., A Neural Probabilistic Language Model, 2003.
  5. Vaswani et al., Attention Is All You Need, 2017.
  6. Radford et al., Improving Language Understanding by Generative Pre-Training, 2018.
  7. Brown et al., Language Models are Few-Shot Learners, 2020.
  8. PyTorch CrossEntropyLoss / log_softmax / NLLLoss 官方文档。
  9. NVIDIA CUDA 与相关 GPU Kernel / 数值计算技术资料。

59. 一页式复盘

text 复制代码
第03讲
概率、信息论与损失函数

概率
 ↓
条件概率
 ↓
P(x_t | x_<t)
 ↓
Logits
 ↓
Softmax
 ↓
Probability
 ↓
真实 Token 的概率 p_true
 ↓
-log(p_true)
 ↓
NLL / Cross-Entropy
 ↓
Mask / Reduction
 ↓
Language Model Loss
 ↓
Perplexity
 ↓
Gradient
 ↓
第04讲

最终只留下一个最核心的心智模型:

text 复制代码
模型输出"分数"
        ↓
这些分数定义了一个概率分布
        ↓
训练数据指出真实答案
        ↓
看真实答案拿到了多少概率
        ↓
把这个概率变成 -log(p_true)
        ↓
对大量 Token 做统计
        ↓
得到 Loss
        ↓
再交给梯度与优化算法

这条链真正贯通以后,Softmax、Cross-Entropy、NLL、Perplexity 就不再是四个孤立名词,而是同一套概率建模系统中的不同位置。

相关推荐
云表无代码开发1 小时前
日本开发者彻底破防:中文太强了!换成英文直接裂开
大数据·服务器·人工智能·microsoft·信息可视化
致Great1 小时前
不止自动写论文!谷歌 ScientistTwo 让 AI 自己做实验、补消融、回审稿
人工智能·深度学习·机器学习
XMAIPC_Robot1 小时前
CODESYS 实时控制 + RK182X 大模型算力扩展|RK3576 工业边缘控制器设计
人工智能·fpga开发·机器人·rk3588+fpga
迪飞特科技1 小时前
【无标题】
android·人工智能·本地化大模型
anda01091 小时前
A2UI 协议: AI 直接画界面,而不是只会打字
人工智能·ai编程
IT_陈寒1 小时前
JavaScript闭包的这个坑,我居然今天才爬出来
前端·人工智能·后端
张3蜂1 小时前
Laya、Kev、NanoJev调用体验
人工智能
皮皮学姐分享-ppx1 小时前
地级市、省级人才政策强度测算(2000-2025)
大数据·数据库·人工智能·百度·高考
m0_587383001 小时前
西安同城拼车软件开发实战指南:从零搭建高效系统
人工智能·小程序·数据挖掘·系统架构·需求分析