Python 与深度学习基础
阶段 0 · 读懂数据流与训练循环的一切前置知识
看不懂这些代码?
scaler.scale(loss).backward()
scaler.unscale_(optimizer)
torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
scaler.step(optimizer)
optimizer.zero_grad(set_to_none=True)
这是 MiniMind 训练循环的核心五行。如果你觉得像天书,没关系——这一章把它们逐行拆开。
适合谁:有基础编程经验,但没系统学过 Python / PyTorch 的人。
不适合谁:如果你能独立用 PyTorch 写训练循环,直接跳到阶段 1。
本章路线图
Python 速查
数据类型与控制流、函数与 Lambda、面向对象 class、装饰器与生成器——项目中反复出现的语法
PyTorch 核心
Tensor 张量操作、自动求导 autograd、nn.Module 层定义、训练循环五步、DataLoader 加载、混合精度训练
数学最低限度
矩阵乘法、概率与 softmax、交叉熵与梯度——理解模型训练所需的最少数学知识
Part 1:Python 速查
MiniMind 全部用 Python 编写。这一节只覆盖项目中反复出现的语法,不追求面面俱到。
读懂 MiniMind 的代码不需要你是 Python 专家,但有几类语法在整个项目中反复出现——如果它们让你卡壳,后面每一节都会觉得吃力。这一节的目标就是把这些"拦路虎"一次性扫清。
- 数据类型速查 —— int、float、str、list、dict、tuple、bool 在 MiniMind 里分别扮演什么角色
- 控制流与函数 —— if/for/while 以及函数定义的基本写法
- 列表推导与 f-string —— 项目中最常见的一行式写法
- 面向对象:class 与继承 —— 理解
nn.Module的前提,搞清楚__init__、super()和self - 装饰器与生成器 ——
@staticmethod、@property等用法,以及yield在数据加载中的角色
数据类型速查
| 类型 | 示例 | MiniMind 中的用途 |
|---|---|---|
int | 768, 8 | 模型参数(hidden_size, layers) |
float | 5e-4, 0.85 | 学习率、温度参数 |
str | "pretrain" | 文件路径、配置名 |
list | [1, 2, 3] | token ID 序列 |
dict | {"role": "user"} | 对话消息结构 |
tuple | (xk, xv) | KV-Cache 存储对 |
bool | True | 开关(use_moe, use_cache) |
# MiniMind 配置文件 config.py 中实际出现的数据类型
dim = 768 # int: 隐藏层维度
n_layers = 8 # int: Transformer 层数
lr = 5e-4 # float: 学习率
data_path = "dataset/pretrain" # str: 数据路径
vocab = [i for i in range(6400)] # list: 词表索引
msg = {"role": "user", "content": "你好"} # dict: 对话
控制流与函数
if / for / 函数是读懂任何代码的基础
# ---- 条件分支:MiniMind 中的典型模式 ----
if use_cache: # bool 控制是否缓存 KV
xk, xv = past_kv[layer_idx]
else:
xk, xv = self.k_proj(x), self.v_proj(x)
# ---- 循环:逐层过 Transformer ----
for layer in self.layers:
x = layer(x, start_pos, mask)
# ---- 函数:封装可复用的逻辑 ----
def get_lr(step, total_steps, base_lr):
"""余弦学习率调度——训练中动态调整学习率"""
return base_lr * (0.1 + 0.45 * (
1 + math.cos(math.pi * step / total_steps)
))
Lambda 是一行函数的简写。项目中偶尔出现:key=lambda x: x[1],意思是"以第二个元素排序"。
推导式与格式化
两种 Pythonic 写法,项目中几乎每页都出现
# ---- 列表推导:一行创建列表 ----
layers = [TransformerBlock(config) for _ in range(n_layers)]
# 等价于:
# layers = []
# for _ in range(n_layers):
# layers.append(TransformerBlock(config))
# ---- f-string:在字符串中嵌入变量 ----
epoch, total, loss, lr = 1, 10, 3.1416, 0.0005
print(f'Epoch:[{epoch+1}/{total}] loss:{loss:.4f} lr:{lr:.8f}')
# 输出: Epoch:[2/10] loss:3.1416 lr:0.00050000
# {loss:.4f} → 保留 4 位小数
# {lr:.8f} → 保留 8 位小数
面向对象编程
class 把数据和操作绑在一起,MiniMind 全是 class
nn.Module # PyTorch 所有网络层的基类
├── parameters() # 收集所有可学习参数
├── forward(x) # 定义前向计算
├── state_dict() # 导出参数字典(保存模型)
└── load_state_dict() # 加载参数字典(加载模型)
MiniMindModel(nn.Module) # 继承 nn.Module
├── embed_tokens # 词嵌入层
├── layers # Transformer Block 列表
├── norm # 最终归一化
└── forward(input_ids) # 前向传播
Attention(nn.Module) # 也继承 nn.Module
├── q_proj: Linear # Query 投影
├── k_proj: Linear # Key 投影
├── v_proj: Linear # Value 投影
└── forward(x, mask) # 注意力计算
继承与 self
三步理解面向对象:定义蓝图 → 创建对象 → 调用方法
class Attention(nn.Module): # ① 继承 nn.Module
def __init__(self, dim): # ② 构造函数:买积木
super().__init__() # 调用父类构造(必须)
self.q_proj = nn.Linear(dim, dim, bias=False) # 用 self 存储层
def forward(self, x): # ③ 前向计算:搭积木
return self.q_proj(x) # self = "我自己"
# 使用:创建对象 → 调方法
attn = Attention(dim=768) # 触发 __init__
out = attn.forward(x) # 触发 forward
- 继承:
class Child(Parent),子类获得父类全部能力 self:对象自身的引用,类似日常说的"我的"super().__init__():先让父类完成初始化
装饰器与生成器
两个 Python 进阶语法,在项目中反复出现
# ---- 装饰器:给函数"穿外套",不改动原代码 ----
@torch.no_grad() # 推理时不计算梯度,节省显存
def generate(self, ...):
...
@abstractmethod # 强制子类必须实现此方法
def rollout(self, ...):
pass
# ---- 生成器 yield:一次只吐一行,不把文件全读进内存 ----
def get_training_texts(path):
with open(path, 'r') as f:
for line in f:
yield line.strip() # 暂停,把这一行交给调用方
for text in get_training_texts("data.txt"):
tokens = tokenizer(text) # 逐行处理,内存友好
Python 常见陷阱
def f(x, lst=[]) 的 lst 只创建一次,多次调用共享。修复:def f(x, lst=None): lst = lst or []
3 / 2 = 1.5(浮点),3 // 2 = 1(整除)。MiniMind 中维度计算常用 //,如 dim // n_heads。
b = a 只是让 b 指向 a。修改 b 会改 a。修复:用 b = a.copy() 或 import copy; b = copy.deepcopy(a)。
练习:Python
任务:阅读以下代码,回答三个问题。
class MyModel(nn.Module):
def __init__(self, dim):
super().__init__()
self.linear = nn.Linear(dim, dim)
@torch.no_grad()
def predict(self, x):
return self.linear(x)
model = MyModel(dim=768)
print(sum(p.numel() for p in model.parameters()))
问题
super().__init__()如果删掉,会怎样?@torch.no_grad()在这里的效果是什么?- 最后一行会输出什么数字?(提示:
nn.Linear(768, 768)有 bias)
参考答案:① 父类 nn.Module 的初始化被跳过,parameters() 等方法失效;② 推理时不计算梯度,节省显存;③ 768×768 + 768 = 590,592 个参数。
Part 2:PyTorch 核心
MiniMind 使用 PyTorch 框架。这一部分覆盖模型定义、训练循环、数据加载中反复出现的 API。
PyTorch 是 MiniMind 的"操作系统"——从数据加载到模型定义、从训练循环到权重保存,几乎每一行代码都在调用 PyTorch 的 API。这一节不会讲所有细节,但会把你在 MiniMind 中反复遇到的那些概念全部理一遍。
- Tensor 张量操作 —— PyTorch 的基本数据结构,包括创建、变形、索引和 GPU 加速
- 自动求导 autograd ——
requires_grad、.backward()、计算图的概念 - 训练循环五步 —— 前向 → 算损失 → 反向 → 梯度清零 → 更新参数,这个套路贯穿整个项目
- nn.Module 与层定义 —— 如何用
nn.Linear、nn.Embedding等搭建网络 - 混合精度与模型保存 —— 用
autocast加速训练,用state_dict存取模型权重
Tensor 张量
定义:多维数组 | 白话:GPU 上的 NumPy | 用途:模型中所有数据都是 Tensor
图:Tensor 维度从标量到四维的层级关系
【AI 生图提示词】请生成一张中文技术示意图,风格简洁、配色柔和、适合教学: - 画面主体:从左到右的递进式层级图,展示 Tensor 维度从标量到四维的递增关系 - 各元素关系:标量 scalar(shape: ())→ 向量 vector(shape: (4,))→ 矩阵 matrix(shape: (3,4))→ 3D 张量(shape: (2,3,4))→ 4D 张量(shape: (B, seq, heads, head_dim)),用带箭头的连线依次连接 - 关键标注:每个节点标注类型名称和具体 shape 值;右侧用小图标直观展示维度形状(标量用点、向量用线、矩阵用面、3D 用立方体、4D 用堆叠立方体) - 配色建议:浅灰背景,节点从左到右用渐变蓝色(浅蓝→深蓝),关键箭头用橙色标注 - 画面比例:16:9 - 避免:过度装饰、3D 效果、杂乱的颜色
import torch
x = torch.tensor([1.0, 2.0, 3.0]) # 从列表创建
print(x.shape) # torch.Size([3])
print(x.device) # cpu 或 cuda:0
# MiniMind 中的维度标注
# input_ids: (batch, seq_len) 例如 (4, 512)
# embeddings: (batch, seq_len, 768) 例如 (4, 512, 768)
# logits: (batch, seq_len, 6400) 例如 (4, 512, 6400)
MiniMind 数据流
从输入文本到输出概率,维度如何一步步变化
图:MiniMind 从输入文本到输出概率的数据流
【AI 生图提示词】请生成一张中文技术示意图,风格简洁、配色柔和、适合教学: - 画面主体:从左到右的水平流水线图,展示文本输入经过模型各阶段后输出概率的完整数据流 - 各元素关系:input_ids(B, seq)→ Embedding 层(B, seq, 768)→ Attention ×N 层(B, seq, 768)→ FFN ×N 层(B, seq, 768)→ lm_head 层(B, seq, 6400),用粗箭头依次连接 - 关键标注:每个方框内标注层名称和输出维度 shape;Embedding 旁标注"查表将 token 转向量";Attention 旁标注"自注意力机制";FFN 旁标注"前馈网络";lm_head 旁标注"线性映射到词表大小" - 配色建议:浅蓝/浅灰背景,输入节点用绿色,中间层用蓝色,输出节点用紫色,维度标注用深灰色小字 - 画面比例:16:9 - 避免:过度装饰、3D 效果、杂乱的颜色
每个方框对应模型中的一个 nn.Module。数据的 shape 在每个阶段都标注出来——这是调试 shape 错误时最重要的参考。
# 在 MiniMind 中你可以这样打印每一步的 shape
def forward(self, input_ids):
x = self.embed_tokens(input_ids) # (B, seq, 768)
for layer in self.layers:
x = layer(x) # 每层保持 (B, seq, 768)
x = self.norm(x) # 最后归一化
logits = self.lm_head(x) # (B, seq, 6400)
return logits
Tensor 高频操作
这些操作在 MiniMind 源码中反复出现
# ===== 形状操作(MiniMind 中最频繁) =====
x.view(batch, seq_len, 768) # 改形状,不复制数据
x.unsqueeze(1) # 加维度: (B,D) → (B,1,D)
x.transpose(1, 2) # 交换两个维度
torch.cat([a, b], dim=1) # 沿维度1拼接(KV-Cache)
# ===== 索引(取特定位置的数据) =====
x[:, -1, :] # 取最后一个 token 的表示
x[mask == 1] # 按条件筛选
# ===== 数学运算(神经网络的核心) =====
torch.softmax(x, dim=-1) # 转概率分布(生成时采样用)
torch.gather(logits, 2, idx) # 按索引取值(提取 log 概率)
torch.topk(x, k=50) # 取最大的 k 个(Top-K 采样)
view() 要求内存连续。遇到 RuntimeError: view size is not compatible,先调用 .contiguous() 再 .view(),或直接用 .reshape()。
自动求导
定义:PyTorch 自动追踪运算并计算梯度 | 白话:你说"算",它帮你求导 | 用途:训练的核心机制
图:PyTorch 自动求导的五步循环
【AI 生图提示词】请生成一张中文技术示意图,风格简洁、配色柔和、适合教学: - 画面主体:一个环形循环流程图,展示 PyTorch 自动求导的五个步骤 - 各元素关系:① 前向传播 Forward → ② 计算 Loss → ③ backward 求梯度 → ④ step 更新参数 → ⑤ zero_grad 清零 → 回到①,形成顺时针循环 - 关键标注:每个节点用圆角矩形,内部标注步骤编号和中文名称;箭头旁可标注简短说明,如①→②旁标"模型预测",②→③旁标"反向传播",③→④旁标"优化器更新",④→⑤旁标"为下一步准备" - 配色建议:浅灰背景,节点用浅蓝色,箭头用深灰色,循环箭头用橙色强调 - 画面比例:16:9 - 避免:过度装饰、3D 效果、杂乱的颜色
import torch
w = torch.tensor([2.0], requires_grad=True) # 标记:需要对 w 求导
y = w ** 2 + 3 * w # 前向传播:y = w² + 3w
y.backward() # 反向传播:自动算 dy/dw
print(w.grad) # tensor([7.]) → 2w+3=2×2+3=7
# 关键:只有 requires_grad=True 的张量才会计算梯度
# 推理时用 torch.no_grad() 可以完全关闭追踪,节省显存
训练循环五步
MiniMind 中 train_pretrain.py 的核心——记住这五步就够了
# ① 前向传播:输入数据,得到预测和损失
res = model(input_ids, labels=labels)
loss = res.loss
# ② 反向传播:计算每个参数的梯度
scaler.scale(loss).backward()
# ③ 梯度裁剪:防止梯度爆炸
scaler.unscale_(optimizer)
torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
# ④ 更新参数:用梯度调整权重
scaler.step(optimizer)
# ⑤ 梯度清零:为下一步准备
optimizer.zero_grad(set_to_none=True)
这五步在每一个训练 step 中重复执行。scaler 的作用见混合精度幻灯片。
nn.Module
定义:神经网络层的基类 | 白话:所有层都是它的子类 | 用法:__init__ 买积木,forward 搭积木
class Attention(nn.Module):
def __init__(self, dim):
super().__init__()
# __init__:定义需要哪些层(买积木)
self.q_proj = nn.Linear(dim, dim, bias=False)
self.k_proj = nn.Linear(dim, dim // 2, bias=False)
self.v_proj = nn.Linear(dim, dim // 2, bias=False)
self.out_proj = nn.Linear(dim, dim, bias=False)
def forward(self, x, mask=None):
# forward:定义计算流程(搭积木)
q = self.q_proj(x) # (B, seq, dim)
k = self.k_proj(x) # (B, seq, dim//2)
v = self.v_proj(x)
# ... 注意力计算(阶段 2 详解)
return self.out_proj(attn_output)
关键特性:在 __init__ 中赋值给 self 的 nn.Module 会被自动追踪。model.parameters() 能递归收集所有参数。
比喻卸甲:上面"买积木/搭积木"只是为了建立直观感受。在 PyTorch 内部,nn.Module 实际上是通过一个 _modules 字典注册子层、在 forward 中按你写的计算图顺序调用它们——并不存在物理意义上的"拼接",子层之间是函数调用关系,不是结构连接。
优化器与学习率
AdamW 控制参数更新方向和步长
import torch.optim as optim
optimizer = optim.AdamW(
model.parameters(), # 需要优化的参数
lr=5e-4, # 学习率:每步更新多大
weight_decay=0.1 # 权重衰减:防止过拟合
)
# MiniMind 使用余弦学习率调度
def get_lr(step, total, lr):
return lr / (1 + step * 1e-5) * \
(0.1 + 0.45 * (1 + math.cos(
math.pi * step / total)))
图:余弦学习率调度的四阶段变化
【AI 生图提示词】请生成一张中文技术示意图,风格简洁、配色柔和、适合教学: - 画面主体:左侧是一条余弦曲线的时间轴图,展示学习率随训练步数的变化;右侧或下方用四个阶段节点辅助说明 - 各元素关系:初始 lr 0.1x → warmup 升高 → 峰值 lr 1.0x → 余弦衰减 → 终 lr 0.1x,用带箭头的连线依次连接 - 关键标注:曲线上标注四个关键点的学习率值;warmup 阶段旁标注"稳步上升避免震荡";衰减阶段旁标注"余弦曲线平滑下降";横轴标注"训练步数",纵轴标注"学习率" - 配色建议:浅灰背景,曲线用深蓝色,warmup 阶段用绿色,峰值用橙色,衰减用浅蓝色,节点用圆角矩形 - 画面比例:16:9 - 避免:过度装饰、3D 效果、杂乱的颜色
学习率太大 → 训练发散;太小 → 收敛太慢。余弦调度是实践中最好用的策略之一。
DataLoader
定义:数据加载管道 | 白话:自动帮你分批、打乱、并行加载 | 用途:喂给模型训练数据
图:DataLoader 数据加载管道
【AI 生图提示词】请生成一张中文技术示意图,风格简洁、配色柔和、适合教学: - 画面主体:从左到右的流水线图,展示原始数据如何经过 DataLoader 管道喂给模型训练 - 各元素关系:原始数据 JSONL → Dataset.__getitem__()(逐条读取并转 tensor)→ DataLoader(自动组 batch、打乱顺序、并行加载)→ 训练循环 for batch in loader,用粗箭头依次连接 - 关键标注:原始数据旁标注"磁盘上的文本文件";Dataset 旁标注"自定义数据类";DataLoader 旁标注"batch_size=32, shuffle=True, num_workers=4";训练循环旁标注"每个 batch 送入模型" - 配色建议:浅蓝/浅灰背景,数据节点用绿色,处理节点用蓝色,输出节点用紫色,关键箭头用橙色标注 - 画面比例:16:9 - 避免:过度装饰、3D 效果、杂乱的颜色
from torch.utils.data import Dataset, DataLoader
class PretrainDataset(Dataset):
def __len__(self):
return len(self.samples)
def __getitem__(self, idx):
text = self.samples[idx]
tokens = self.tokenizer(text)
return torch.tensor(tokens[:-1]), torch.tensor(tokens[1:])
# 输入是 [0..n-1],目标是 [1..n](预测下一个 token)
loader = DataLoader(dataset, batch_size=32, shuffle=True)
for input_ids, labels in loader: # 每次取一个 batch
loss = model(input_ids, labels=labels).loss
混合精度训练
定义:用 BF16 算前向,FP32 存权重 | 白话:算得快、省显存、精度不掉 | 用途:MiniMind 默认开启
# MiniMind 中的混合精度写法
scaler = torch.amp.GradScaler('cuda')
with torch.amp.autocast('cuda', dtype=torch.bfloat16):
# 前向用 bf16(每个数 2 字节,比 fp32 的 4 字节省一半)
res = model(input_ids, labels=labels)
loss = res.loss
scaler.scale(loss).backward() # 反向传播(自动缩放梯度)
scaler.step(optimizer) # 更新参数(自动转回 fp32)
optimizer.zero_grad(set_to_none=True)
图:混合精度训练一个 Step 的精度转换
【AI 生图提示词】请生成一张中文技术示意图,风格简洁、配色柔和、适合教学: - 画面主体:一个环形数据流,展示混合精度训练一个 step 中数据精度的转换过程 - 流程节点(按顺时针排列):① FP32 主权重(深蓝色)→ ② 转为 BF16 前向传播(浅蓝色)→ ③ BF16 计算损失 → ④ GradScaler 放大梯度转回 FP32(橙色)→ ⑤ FP32 梯度裁剪 → ⑥ FP32 更新主权重(深蓝色,回到①) - 关键标注:②→③ 旁标注"计算密集、显存减半";④ 旁标注"防梯度下溢";⑥ 旁标注"精度无损" - 配色建议:浅灰背景,FP32 用深蓝色,BF16 用浅蓝色,Scaler 操作用橙色 - 画面比例:16:9 - 避免:过度装饰、3D 效果、杂乱的颜色
模型保存与加载
训练完了要存盘,推理时再读回来
# ---- 保存模型 ----
torch.save(model.state_dict(), "model.pth")
# state_dict() 返回一个 dict,key=层名, value=参数张量
# ---- 加载模型 ----
model.load_state_dict(torch.load("model.pth"))
model.eval() # 切换到评估模式(关闭 Dropout 等)
# ---- MiniMind 中的实际用法 ----
# 训练:每 N 步自动保存 checkpoint
# 推理:加载 checkpoint → generate() 生成文本
# HuggingFace 格式:save_pretrained() / from_pretrained()
model.eval() 和 torch.no_grad() 是两件事。前者关闭 Dropout 和 BatchNorm 的训练行为,后者关闭梯度计算。推理时两个都要用。
PyTorch 排错指南
shape 不匹配
症状:RuntimeError: mat1 and mat2 shapes cannot be multiplied
修复:打印 tensor.shape 逐步排查,对照数据流图检查维度
view() 报错
症状:RuntimeError: view size is not compatible
修复:先 .contiguous() 再 .view(),或直接 .reshape()
gradient is None
症状:loss.backward() 后 .grad 是 None
修复:确认 requires_grad=True;不在 no_grad() 内调用 backward()
CUDA OOM
症状:CUDA out of memory
修复:减小 batch_size;开启混合精度;用 del 释放不需要的张量
练习:PyTorch
任务:在终端运行以下代码,观察 Tensor 形状变化。
>>> import torch
>>> x = torch.randn(2, 3, 4)
>>> print("原始:", x.shape) # torch.Size([2, 3, 4])
>>> print("view:", x.view(2, 12).shape) # torch.Size([2, 12])
>>> print("unsqueeze:", x.unsqueeze(1).shape) # torch.Size([2, 1, 3, 4])
>>> print("transpose:", x.transpose(1,2).shape) # torch.Size([2, 4, 3])
>>> print("最后token:", x[:, -1, :].shape) # torch.Size([2, 4])
挑战
- 把
view(2, 12)改成view(2, 11),看报什么错?为什么? unsqueeze(1)和unsqueeze(-1)的结果有什么不同?- 对 transpose 后的张量直接
.view(2, 12),能成功吗?为什么?
Part 3:数学最低限度
不追求数学严谨,只记住结论和直觉。后面遇到具体公式时再回来查。
训练语言模型离不开几个关键的数学概念,但你完全不需要推导证明——只要能看懂公式在干什么、为什么需要它就够了。这一节用最直白的方式把这些概念讲清楚,后面真正遇到时不会一脸懵。
- 线性代数 —— 矩阵乘法和点积是 Transformer 里最基本的运算,理解"维度对齐"就够了
- 概率与 Softmax —— 模型输出的"下一个词的概率分布"是怎么算出来的
- 交叉熵与损失函数 —— 用一个数字衡量"模型猜得有多离谱"
- 梯度与链式法则 —— 为什么反向传播能自动算出每个参数该调多少
线性代数
神经网络的所有计算本质上是矩阵乘法
import torch
# 矩阵乘法:神经网络层的本质
x = torch.randn(1, 512, 768) # 输入: (batch, seq_len, dim)
W = torch.randn(768, 768) # 权重矩阵
y = x @ W.T # 线性变换: y = xW^T
print(y.shape) # (1, 512, 768)
# 点积:Attention 中衡量"相关性"
q = torch.randn(1, 512, 64) # Query 向量
k = torch.randn(1, 512, 64) # Key 向量
score = q @ k.transpose(-2,-1) # 点积 → 注意力分数
print(score.shape) # (1, 512, 512)
- 向量:一列数字,如
[0.1, 0.5, -0.3],可以理解为"方向" - 矩阵乘法:
nn.Linear(768, 768)本质就是y = xW^T + b - 点积:衡量两个向量的"相似度"。Attention 中 Q·K 就是用点积算相关性
概率与 Softmax
语言模型的核心任务:预测下一个 token 的概率
import torch
# Softmax:把任意数字变成概率分布(加起来等于 1)
logits = torch.tensor([2.0, 1.0, 0.0])
probs = torch.softmax(logits, dim=-1)
print(probs) # tensor([0.6654, 0.2447, 0.0900])
# 最大的 logits 对应最大的概率
# MiniMind 生成时:从概率分布中采样下一个 token
next_token = torch.multinomial(probs, num_samples=1)
- 概率分布:所有可能性之和 = 1,softmax 保证这一点
- 条件概率:语言模型就是 P(下一个词 | 前面的词)
- 训练目标:让正确 token 的概率尽可能大
交叉熵与损失
衡量预测与真实差距的标尺——Loss 越小,模型越准
图:交叉熵损失驱动的训练循环
【AI 生图提示词】请生成一张中文技术示意图,风格简洁、配色柔和、适合教学: - 画面主体:一个环形循环流程图,展示交叉熵损失如何驱动模型训练 - 各元素关系:① 交叉熵 Loss(衡量预测与真实的差距)→ ② backward 计算梯度 → ③ 梯度下降更新参数 → ④ Loss 降低 → 回到①,形成顺时针循环 - 关键标注:Loss 节点旁标注"越小=预测越准";backward 节点旁标注"链式法则自动求导";梯度下降旁标注"沿梯度反方向走";Loss 降低旁标注"模型越来越准" - 配色建议:浅灰背景,节点用浅蓝色圆角矩形,Loss 节点用橙色强调,循环箭头用深灰色,关键说明用小字标注在节点旁 - 画面比例:16:9 - 避免:过度装饰、3D 效果、杂乱的颜色
- 交叉熵:越小 = 预测越准。公式
-Σ y_i × log(p_i)。MiniMind 中CrossEntropyLoss就是它 - KL 散度:衡量两个分布的差异。蒸馏和 RL 中用来约束模型不要跑偏
梯度与链式法则
训练能work的数学原理——你不需要手算,但需要直觉
import torch
# 梯度:告诉参数"往哪个方向调、调多少"
w = torch.randn(3, requires_grad=True)
loss = (w ** 2).sum() # 前向:计算损失
loss.backward() # 反向:自动求梯度
print(w.grad) # tensor([2w1, 2w2, 2w3]) → ∂loss/∂w
# 链式法则:复合函数的导数 = 各层导数相乘
# 这就是"反向传播"的数学基础
# PyTorch 的 autograd 自动帮你算,你不需要手写
- 导数:函数在某点的变化率。正 = 上升,负 = 下降
- 梯度:多维导数,指向函数值增长最快的方向。训练沿梯度的反方向走
- 链式法则:复合函数的导数 = 各层导数相乘。反向传播就是链式法则的自动执行
学完自测
问题 1
nn.Linear(768, 768, bias=False) 有多少参数?
问题 2
loss.backward() 之后为什么必须 zero_grad()?
问题 3
torch.no_grad() 节省了什么?为什么推理时用它?
问题 4
x.view(B, -1, 768) 中 -1 是什么意思?
推荐学习资源
| 资源 | 适合阶段 | 为什么推荐 |
|---|---|---|
| 廖雪峰 Python 教程 | Python 零基础 | 简洁实用,1-2 天可读完 |
| PyTorch 官方教程 | 有 Python 基础 | 60 分钟入门,直接上手 |
| 3Blue1Brown 线性代数 | 想直觉理解矩阵 | 动画讲解,不枯燥 |
| 动手学深度学习 | 想系统学 DL | 理论 + 代码,PyTorch 版 |
阅读建议:不需要先学完这些再开始。遇到不懂的随时回来查。最好的学习方式是读 MiniMind 源码 + 查资料,交替进行。
阶段 0 完成
你已经掌握了读 MiniMind 代码所需的 Python 和 PyTorch 基础
下一章:阶段 1 · Tokenizer 分词器——文本如何变成模型能理解的数字