《现代 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 句话
- Logits 是原始分数,不是概率。
- Softmax 把 logits 映射成非负且总和为 1 的概率分布。
- Softmax 的整体平移不会改变最终概率,所以可以先减最大值提高数值稳定性。
- 语言模型预测的是条件概率
P(x_t | x_<t)。 - 链式法则把整条序列的概率拆成一串条件概率的乘积。
log能把概率乘积变成概率对数的加法。- 最大似然自然导向 Log-Likelihood,再自然导向 NLL。
- One-hot 监督下,Cross-Entropy 就是
-log(真实类别概率)。 - 真实类别概率越低,Loss 越大。
- Loss 很大不代表 logits 梯度一定无限大;Softmax + CE 的经典梯度是
p-y。 - Entropy 看分布自身的不确定性,Cross-Entropy 看用 q 描述 p 的平均代价。
- KL 不是对称距离。
- LLM 的常见训练 Loss 可以理解成有效真实 Token 的平均负对数概率。
[B,T,V]中 V 是词表维度,也是语言模型输出侧的重要系统成本来源。- 第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. 参考资料
- Ian Goodfellow, Yoshua Bengio, Aaron Courville, Deep Learning.
- Christopher M. Bishop, Pattern Recognition and Machine Learning.
- Thomas M. Cover and Joy A. Thomas, Elements of Information Theory.
- Bengio et al., A Neural Probabilistic Language Model, 2003.
- Vaswani et al., Attention Is All You Need, 2017.
- Radford et al., Improving Language Understanding by Generative Pre-Training, 2018.
- Brown et al., Language Models are Few-Shot Learners, 2020.
- PyTorch
CrossEntropyLoss/log_softmax/NLLLoss官方文档。 - 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 就不再是四个孤立名词,而是同一套概率建模系统中的不同位置。