写给没有 Python 基础、没有 AI 背景的读者。 建议按章节顺序阅读,每章大约 5-10 分钟。
┌──────────────────────────────────────────────┐
│ AI (人工智能) │
│ 让机器做出智能行为的任何技术 │
│ │
│ ┌────────────────────────────────────┐ │
│ │ 机器学习 (ML) │ │
│ │ 不是手工写规则,而是让机器从数据中学习 │ │
│ │ │ │
│ │ ┌──────────────────────────┐ │ │
│ │ │ 深度学习 (DL) │ │ │
│ │ │ 使用多层神经网络(几十到几百层)│ │ │
│ │ │ 所有大语言模型都在这个圈里 │ │ │
│ │ └──────────────────────────┘ │ │
│ └────────────────────────────────────┘ │
└──────────────────────────────────────────────┘
举例对比:
| 传统编程 | 机器学习 (ML) | 深度学习 (DL) |
|---|---|---|
人写规则:if 有猫耳朵 and 有胡须: return "猫" |
人给 1000 张猫照片 + 标签,机器自己找规律 | 给 1000 万张照片 + 几百层神经网络,机器学到的模式远超人类能描述的 |
模型就是一个 f(x) = y 函数,它的内部有大量参数(权重)——一堆数字。
普通函数: f(x) = 2x + 1 ← 只有 2 个参数
GPT-2: f(文本) = 下一个词 ← 有 1.24 亿个参数
GPT-4: f(文本) = 下一个词 ← 有 ~1.8 万亿个参数
参数越多 ≠ 越聪明,但参数多 + 数据多 + 训练充分 = 能力天花板更高。
训练 = 反复调整参数,让模型猜得越来越准。
类比教小孩认动物:
第 1 次:给看猫图 → 小孩说"狗" → 你说"错,这是猫"
第 2 次:给看猫图 → 小孩说"猫" → 你说"对!"
第 3 次:给看另一张猫图 → 小孩说"猫" → "对!"
...一万次后...
第 N 次:给看没见过的猫图 → 小孩说"猫" → 学会了!
AI训练就是做这件事,只不过"小孩"是数学模型,"我说对错"是数学公式(损失函数)。
计算机不认文字,只认数字。Tokenization(分词)就是把文本切成小块,每块给一个编号。
"我喜欢学习AI"
↓ GPT-2 分词
[我, 喜欢, 学习, AI]
↓
[16384, 28507, 19843, 31778] ← 这就是模型真正"看到"的东西
关键事实:
"happiness" → ["happ", "iness"])token ID 只是一个编号(16384 和 16385 只是相邻的数字,不代表它们含义相近)。
Embedding(嵌入) 把每个 token 变成一个高维向量,让含义相近的词在空间中距离也近。
Token: "猫" → Embedding: [0.23, -0.51, 0.89, ..., 0.12] (512 个数字)
Token: "狗" → Embedding: [0.25, -0.48, 0.91, ..., 0.10] (和"猫"很近!)
Token: "汽车" → Embedding: [-0.82, 0.31, -0.15, ..., 0.67] (和"猫"很远)
狗 ●
猫 ●
● 汽车
· 动物区 · 交通工具区
这些向量在训练过程中自动学会——模型发现”猫”和”狗”经常出现在相似的上下文中,自然把它们拉近了。
2017 年一篇论文《Attention Is All You Need》提出了 Transformer 架构。所有现代大模型(GPT、Claude、Gemini、Qwen、DeepSeek…)都基于它。
句子: "The bank of the river was muddy"
没有 Attention:
"bank" 被当成固定含义 → 模型可能理解成"银行"
有 Attention:
"bank" 看到后面的 "river" → 哦,是"河岸"!
每个词都会"看"句子中所有其他词,融合上下文
"bank" ←→ "river" 强关联(理解成"河岸")
"bank" ←→ "muddy" 中等关联(河岸是泥泞的,合理)
在数学上:
Attention(Q, K, V) = softmax(Q·Kᵀ / √d) × V
简化理解:
Q (Query): "我是谁?我在找什么?"
K (Key): "我能提供什么信息?"
V (Value): "我的实际内容是什么?"
每个词用 Q 去"问"所有词的 K → 得到匹配度 →
按匹配度加权取所有 V → 融合后的含义
输入 Token: [Once, upon, a, time, ...] (ID 序列)
↓
Token Embedding + Position Embedding (把 ID 变成向量 + 加上位置信息)
↓
┌─────────────────────────────────┐
│ Transformer Block × N 层 │ ← 核心:反复做 Attention + 前馈
│ ┌───────────────────────────┐ │
│ │ LayerNorm │ │ 归一化(稳定训练)
│ │ Self-Attention │ │ 理解上下文 ← 最关键的步骤
│ │ 残差连接 (+ input) │ │ 防止信息丢失
│ │ LayerNorm │ │
│ │ Feed-Forward (MLP) │ │ 非线性变换
│ │ 残差连接 (+ input) │ │
│ └───────────────────────────┘ │
└─────────────────────────────────┘
↓
LayerNorm → Linear → 50257 维向量 (每个位置对词表打一次分)
↓
取最高分的 token → 下一个词
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ 原始数据 │──→│ 分词编码 │──→│ 前向传播 │──→│ 计算损失 │──→│ 反向传播 │
│ 纯文本 │ │ 文本→数字 │ │ 模型预测 │ │ 对比答案 │ │ 算梯度 │
└──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘
│
↓
┌──────────┐
│ 更新参数 │
│ 梯度下降 │
└──────────┘
│
↓
← ← ← ← ← ← ← ← 重复几十亿次 → → → → → →
数据从输入到输出,一口气算过去。
输入 X: [Once, upon, a, time, ,] (5 个 token)
↓
模型内部: 经过 Embedding + 8 层 Transformer
↓
输出: 对每个位置的 50257 个候选词打分
位置0 → [upon: 9.2, the: 3.1, a: 1.5, ...] ← 最高分是 "upon" ✓
位置1 → [a: 8.7, upon: 2.3, time: 1.8, ...] ← 最高分是 "a" ✓
位置2 → [time: 7.9, a: 3.2, upon: 1.1, ...] ← 最高分是 "time" ✓
...
衡量”模型猜得有多差”。 最常用的是交叉熵损失 (Cross-Entropy Loss):
Loss = -log(模型给正确答案的概率)
例:
正确答案是 "upon"
模型给 "upon" 打分 9.2 → softmax 后概率 85% → Loss = -log(0.85) = 0.16 (很低,猜得准!)
模型给 "upon" 打分 1.0 → softmax 后概率 10% → Loss = -log(0.10) = 2.30 (很高,猜得差!)
Loss 越低 = 模型越好
训练目标: 让 Loss 不断下降
这是训练中最重要的数学操作:
1. 前向传播算出 Loss
2. 反向传播: 计算 Loss 对每个参数的偏导(梯度)
→ 梯度告诉你: "这个参数往哪个方向调,Loss 会减小?"
3. 梯度下降: 按梯度的反方向微调每个参数
参数_new = 参数_old - 学习率 × 梯度
比喻:
你在山上(高 Loss),想走到谷底(低 Loss)
梯度 = 脚下的坡度,告诉你最陡的下山方向
学习率 = 你每一步迈多大
→ 顺着坡度往下走,一步一步,最终到谷底
| 概念 | 一句话 | 我们项目中的设置 |
|---|---|---|
| Batch Size | 一次处理多少个样本 | 8 |
| 梯度累积 | 攒几次梯度一起更新(模拟更大 batch) | 4 步 → 等效 batch=32 |
| 学习率 (LR) | 参数更新的步长 | 3e-4(即 0.0003) |
| Warmup | 先用小 LR 热身,再加大 | 前 500 步 |
| Cosine Decay | 到后期逐渐减小 LR | warmup 后余弦衰减 |
| Epoch | 把整个数据集看一遍 | 我们 500 步看了约 15 遍 |
| AdamW | 一种自适应梯度下降优化器(比普通 SGD 好) | ✅ 使用 |
| 混合精度 (AMP) | 用 16 位浮点训练,速度快且省显存 | bfloat16 |
| 梯度裁剪 | 限制梯度的最大值,防止训练崩溃 | 1.0 |
Loss
10.8 │╲
│ ╲
9.5 │ ╲
│ ╲
5.0 │ ╲
│ ╲____
3.5 │ ____╲__
│ ╲_
└──────────────────────────→ 步数(step)
解释:
Loss 快速下降期(前几百步):模型学会了高频词、基础句法
Loss 缓慢下降期(几千步):模型学会了更细的模式
Loss 平坦期(几万步后):再训练提高不大(到了数据和模型的极限)
CPU (i9-13900HX): 24 个强核心,擅长复杂的操作
GPU (RTX 4060): 3072 个弱核心,擅长同时做简单操作 × 几百万次
训练模型 = 大量并行的矩阵乘法
CPU GPU
┌─┬─┬─┬─┬─┬─┬─┬─┐ ┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐
│1│ │ │ │ │ │ │ │ │1│2│3│4│5│6│7│8│... ...│3072│
└─┴─┴─┴─┴─┴─┴─┴─┘ └─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘
一次做 8 件事 (慢) 一次做 3072 件事 (快 100 倍!)
关键硬件指标:
| 指标 | 含义 | RTX 4060 |
|---|---|---|
| 显存 (VRAM) | 能装多大的模型 | 8 GB |
| CUDA 核心数 | 并行计算单元 | 3072 |
| 张量核心 (Tensor Core) | 专门加速矩阵乘法的硬件 | 有 |
| Compute Capability | CUDA 功能版本 | 8.9(支持 bfloat16) |
┌──────────────────────────────────────────────────────────┐
│ 你的代码 (train_gpt.py / train_lora.py) │
│ │
│ ├─ 定义模型结构 │
│ ├─ 加载数据 │
│ └─ 调用 train() │
├──────────────────────────────────────────────────────────┤
│ PyTorch(深度学习框架) │
│ │
│ ├─ torch.nn — 神经网络模块(Linear, Embedding...) │
│ ├─ torch.optim — 优化器(AdamW, SGD...) │
│ ├─ torch.autograd — 自动求导(反向传播) │
│ └─ torch.cuda — GPU 加速 │
├──────────────────────────────────────────────────────────┤
│ CUDA / cuDNN(NVIDIA GPU 驱动程序) │
│ │
│ 把 PyTorch 的高级命令翻译成 GPU 能执行的指令 │
├──────────────────────────────────────────────────────────┤
│ GPU 硬件 (RTX 4060) │
│ │
│ 3000+ 个核心并行执行矩阵运算 │
└──────────────────────────────────────────────────────────┘
| 包 | 作用 | 谁在用 |
|---|---|---|
torch (PyTorch) |
深度学习框架——定义模型、训练、GPU 加速 | ✅ 我们都用 |
transformers |
预训练模型仓库(GPT, LLaMA, Qwen…) | ✅ train_lora |
peft |
LoRA/QLoRA 微调 | ✅ train_lora |
datasets |
下载和加载公开数据集 | ✅ download.py |
accelerate |
多 GPU / 混合精度一键配置 | ✅ train_lora |
tiktoken |
OpenAI 的 BPE tokenizer | ✅ train_gpt |
tensorboard |
训练日志可视化(浏览器看 loss 曲线) | ✅ trainer.py |
# 查看所有包的版本
pip list | findstr "torch transformer peft tensorboard"
# 示例输出:
torch 2.6.0+cu124
│ │
主.次.补丁 CUDA 12.4 编译版本
transformers 5.12.1
有 +cu124 = GPU 版本(能用显卡)
没有后缀或 +cpu = CPU 版本(不能用显卡,训练慢 50 倍)
预训练模型(如 GPT-4)学会了”预测下一个词”,但它不会”听你的话回答问题”。
预训练模型:
输入: "解释什么是AI"
输出: "解释什么是AI,我们需要先了解..." ← 它在续写,不是在回答
微调后的模型:
输入: "解释什么是AI"
输出: "AI是人工智能的缩写,指的是..." ← 它在回答问题
原模型 (3 GB) + LoRA 补丁 (5 MB)
████████████████ + ▓
████████████████ + ▓
████████████████ + ▓
████████████████ + 只训练这一小块!
████████████████
████████████████
冻结不训练 ← 省 99% 显存
1. 预训练 (Pre-training)
→ 喂万亿 token 文本 → 学会语言
2. 监督微调 (SFT)
→ 喂 10 万条 "问题-答案" 对 → 学会聊天
3. 奖励模型 (Reward Model)
→ 人类对多个回答排序 → 训练一个"打分器"
4. 强化学习 (PPO/DPO)
→ 用打分器反馈优化模型 → 输出更符合人类偏好
这是 ChatGPT/Claude 能”好用”的关键步骤。
claude-benchmark/
├── src/ ← 核心代码
│ ├── model.py ← 手写的 GPT 模型(30+ 行注释)
│ ├── trainer.py ← 训练循环
│ ├── config.py ← 配置参数
│ ├── tokenizer.py ← 分词工具
│ └── dataset.py ← 数据加载
├── scripts/ ← 命令行入口
│ ├── train_gpt.py ← 从零训练入口
│ ├── train_lora.py ← LoRA 微调入口
│ └── generate.py ← 生成文本入口
├── data/
│ └── download.py ← 下载数据集
├── docs/
│ └── ai-training-guide.md ← 你正在读的文档
└── checkpoints/ ← 训练好的模型
python data/download.py tinystories
python scripts/train_gpt.py --max_steps 500 # 5 分钟入门
python scripts/generate.py --checkpoint checkpoints/best.pt --prompt "Once upon a time"
python data/download.py alpaca
set HF_ENDPOINT=https://hf-mirror.com
python scripts/train_lora.py --model HuggingFaceTB/SmolLM2-360M --max_steps 500
| 现象 | 正常吗? | 原因 |
|---|---|---|
| GPU 利用率 95-100% | ✅ | 训练在榨干显卡算力 |
| 显存占用 80-95% | ✅ | 模型 + 数据都在显存里 |
| 风扇狂转 / 机身发烫 | ✅ | GPU 满载功耗 ~35-100W |
| 内存占用 90-95% | ⚠️ | 接近上限但没崩就安全 |
| GPU 利用率很低 | ❌ | 检查是否没用 CUDA |
| 显存溢出 (OOM) | ❌ | 减小 batch_size 或数据量 |
# 安装所有依赖
pip install -r requirements.txt
# 快速测试(~2 分钟)
python scripts/train_gpt.py --max_steps 100 --max_data 10000000
# 标准训练(~10 分钟)
python scripts/train_gpt.py --max_steps 500 --max_data 20000000
# 完整训练(~30 分钟)
python scripts/train_gpt.py --max_steps 10000 --max_data 50000000
# 从 checkpoint 恢复训练
python scripts/train_gpt.py --resume checkpoints/step_005000.pt
# 生成文本
python scripts/generate.py --checkpoint checkpoints/best.pt --prompt "你的开头"
# 查看训练曲线
tensorboard --logdir logs
# 实时监控 GPU
nvidia-smi -l 1
OpenAI 2020 年发现了一个规律:模型性能与参数量、数据量、计算量的关系可以用精确的数学公式描述。
Loss ∝ (参数量)^(-0.076) × (数据量)^(-0.095) × (计算量)^(-0.050)
核心结论:
模型大小翻倍 → Loss 降 5%
数据量翻倍 → Loss 降 7%
要想模型变聪明 → 数据和模型必须同步增长
小模型(<10B 参数)做不到的事,大模型(>100B)突然就会了:
模型大小 → 能力
~1B → 基本语法、简单推理
~10B → 翻译、摘要、代码补全
~70B → 多步推理、数学证明
~200B+ → 涌现:思维链、笑话、类比、创造性写作
这也是为什么大家都在追求”更大”——有些能力不是线性的,是跳跃式的。
单个 GPU 装不下千亿参数模型,需要把模型切开:
| 并行策略 | 做法 | 需要什么 |
|---|---|---|
| 数据并行 | 每张 GPU 上复制整个模型,分不同数据 | 梯度同步(NCCL) |
| 模型并行 | 模型切到多张 GPU,每张负责一部分层 | NVLink 高速互联 |
| 流水线并行 | 层之间流水线,GPU1 算完传给 GPU2 | 高速网络 |
| ZeRO | 把优化器状态也切开分布存储 | DeepSpeed 框架 |
GPT-4 训练规模:
25000 张 A100 GPU
运行 90-100 天
电力消耗 ~50 MW(够一个小城市)
估计成本 $100M+
用更少的比特位存参数:
FP32 (全精度): 每个参数 32 bits → 7B 模型占 28 GB (8GB 显卡装不下)
FP16 (半精度): 每个参数 16 bits → 7B 模型占 14 GB (还是装不下)
INT8: 每个参数 8 bits → 7B 模型占 7 GB (刚好!)
INT4 (QLoRA): 每个参数 4 bits → 7B 模型占 3.5 GB (舒服)
代价:精度越低,模型可能”忘掉”一些细节。但 4-bit 已经做得非常好了。
| 方向 | 说明 |
|---|---|
| 多模态 | 文字 + 图片 + 音频 + 视频一起理解(GPT-4V) |
| 长上下文 | 从 4096 token 到 100 万+ token(Claude 领先) |
| 推理模型 | 让模型在回答前”思考”(Chain-of-Thought、o1/o3) |
| Agent | 模型不仅能聊,还能操作电脑、调用 API、写代码 |
| 小模型 | 1-3B 参数的高质量模型,手机/嵌入式设备上跑 |
| 合成数据 | 用大模型生成训练数据喂小模型 |
如果你是完全零基础:
generate.py。有感性认识最重要src/model.py 约 280 行,是从零实现的 GPT,每行都有注释最重要的:不要等到”学完”才开始。跑起来,边做边学。