一、人工神经网络:从生物神经元到数学函数
1.1 神经网络的最小单元
人工神经网络(Neural Network)的灵感来自大脑神经元的工作方式。一个神经元本质上就是一个数学函数:
output = activation(w · x + b) 其中 x 是输入向量,w 是权重,b 是偏置,activation 是激活函数(常见的有 Sigmoid、ReLU、GELU)。把成千上万个这样的"神经元"按层堆叠、互相连接,就构成了现代深度学习的基础。
1.2 神经网络的训练三要素
- 前向传播(Forward Propagation):数据从输入层流向输出层,产生预测结果。
- 损失函数(Loss Function):衡量预测结果与真实答案之间的差距。分类任务常用交叉熵,回归任务常用均方误差。
- 反向传播 + 梯度下降(Backpropagation + Gradient Descent):从输出层往输入层反向计算每个参数对损失的"贡献度"(梯度),然后沿着梯度的反方向微调权重,让下一次预测更准。
💡 关键直觉:训练神经网络的过程,本质上就是"在数十亿维的参数空间里,找到一组权重,让损失函数的值最小"。这就是为什么大模型需要海量数据和算力 —— 参数空间太大了。
二、Transformer:现代大模型的基石
2017 年,Google 在论文 Attention Is All You Need 中提出 Transformer 架构,彻底改变了 AI 的发展轨迹。它的核心创新是:完全抛弃循环结构,只用注意力机制(Attention Mechanism)来处理序列数据。
2.1 为什么 Transformer 能横扫 NLP
| 特性 | RNN/LSTM | Transformer |
|---|---|---|
| 长距离依赖 | 容易遗忘早期信息 | 任意位置直接关联 |
| 并行计算 | 必须按顺序 | 可同时处理所有位置 |
| 训练速度 | 慢 | 快(充分利用 GPU) |
| 扩展性 | 难扩展到百亿参数 | 原生支持千亿级参数 |
2.2 自注意力机制(Self-Attention)
自注意力的核心思想是:对于序列中的每个词,都要"看一遍"所有其他词,然后判断每个词对自己的重要性。
用一个具体的例子来说明 —— 句子 "小明 昨天去 北京 出差,今天 他 回来了":
- 每个词生成三个向量:Query(Q,查询向量)、Key(K,键向量)、Value(V,值向量)。
- 对于"他"这个词,计算它的 Q 与其他所有词的 K 的点积,得到相关性分数。
- 对分数做 Softmax 归一化,得到注意力权重(比如"他"对"小明"的权重是 0.8,对"北京"的权重是 0.1)。
- 用这些权重对所有词的 V 加权求和,得到"他"这个词的新的、富含上下文信息的表示。
数学公式可以简洁地写成:
Attention(Q, K, V) = softmax(Q · K^T / √d_k) · V 其中 d_k 是 Key 向量的维度,除以它的平方根是为了防止点积过大导致 Softmax 梯度消失。
2.3 多头注意力(Multi-Head Attention)
多头注意力是自注意力的升级版。它让模型在不同的"表示子空间"里并行地做多次自注意力,然后把结果拼起来。这样模型可以同时关注不同维度的信息:
- 一个头可能专注于语法关系(主谓宾)
- 另一个头可能专注于语义关系(同义词、反义词)
- 第三个头可能专注于长距离指代(代词指代哪个名词)
2.4 位置编码(Positional Encoding)
Transformer 是并行处理所有词的,天然丢失了"顺序"信息。比如"狗咬人"和"人咬狗"的字面 token 完全相同,只是顺序不同。位置编码通过给每个位置的词嵌入加上一个独特的"位置向量",让模型知道每个词在句子中的位置。
最初的 Transformer 使用固定的正弦/余弦函数生成位置编码。后续的模型(如 LLaMA、Qwen)改用 RoPE(旋转位置编码),可以更好地处理超长序列。
三、Transformer 整体架构
一个标准的 Transformer 块由两部分组成:
输入 → [LayerNorm → 多头注意力 → 残差连接]
→ [LayerNorm → 前馈网络(FFN) → 残差连接]
→ 输出 3.1 Transformer 整体架构图(一眼看明白)
关键三件套:
- 多头注意力层:负责「找关系」 —— 让每个词看到其他所有词(Decoder 用了 Causal Mask 保证只看历史)。
- 前馈网络(FFN):负责「加工信息」 —— 对每个词单独做非线性变换,提取高级特征。
- 残差连接 + LayerNorm:把上一层的输入直接加到输出上,避免梯度消失,让深层网络能训练。
GPT 系列模型(ChatGPT、DeepSeek、Qwen)都是把几十层甚至上百层这样的块堆叠起来,组成 Decoder-only Transformer。
四、预训练、SFT、RLHF:大模型的"三步训练法"
从"会说话"到"会听话"再到"讨人喜欢",大模型要走完三步:
4.0 三步训练法流程图
三步逐层递进:从"读懂世界"到"听懂人话"再到"对齐人类价值观"。每一步都不可缺 —— 缺了任何一步,模型都"不好用"。
4.1 预训练(Pre-Training)
用海量互联网文本(几万亿 token)训练模型,让它学会"预测下一个词"。这一阶段耗时最长、成本最高 —— DeepSeek-V3 用了 14.8 万亿 token 训练,花了约 557 万美元(GPU 租赁费)。
4.2 监督微调(Supervised Fine-Tuning, SFT)
用人类标注的高质量"指令-回答"对训练模型,让它学会"听懂人话、按照要求回答"。这一阶段数据量小得多(几万到几十万条),但质量要求极高。
4.3 基于人类反馈的强化学习(RLHF)
让人类对模型的不同回答打分,训练一个"奖励模型"(Reward Model),然后用强化学习(常用 PPO 或 DPO 算法)让模型学会生成更受人类偏好的回答。
五、为什么需要 GPU 集群?
训练一个千亿参数的大模型,一次前向 + 反向传播需要:
- 输入 token 的嵌入(参数量 × 序列长度)
- 注意力矩阵(序列长度² × 注意力头数)
- 梯度(参数量)
- 优化器状态(动量、方差,3-4 倍参数量)
单张 GPU 的显存(80GB)根本装不下,所以需要多种并行策略:
| 并行方式 | 核心思想 | 代表框架 |
|---|---|---|
| 数据并行 | 把数据分到多卡,每卡跑完整模型 | PyTorch DDP |
| 模型并行 | 把模型不同层放到不同卡 | Megatron-LM |
| 流水线并行 | 模型分块,各卡处理不同 micro-batch | GPipe, PipeDream |
| 张量并行 | 把单个矩阵运算拆分到多卡 | Megatron-LM |
六、从大模型到 Agent:能力的跃迁
当大模型足够强以后,人们开始尝试给它"加外挂":让它不仅能"说话",还能"做事"。这就是 AI Agent 的起点:
- ReAct(推理 + 行动):让模型先思考该做什么,再决定调用哪个工具。
- Function Calling:让模型可以输出结构化的"工具调用请求",由外部系统执行。
- 多 Agent 协作:让多个 Agent 扮演不同角色,互相配合完成任务。
这部分内容会在下一章"Agent 演化"里详细展开。
七、本章小结
- ✅ Transformer 用"注意力机制"取代循环结构,实现并行训练和长距离依赖建模。
- ✅ 自注意力让每个词能"看到"所有其他词,多头机制让模型能同时关注多种关系。
- ✅ 预训练 + SFT + RLHF 是训练大模型的三板斧。
- ✅ 千亿参数模型离不开 GPU 集群和多种并行策略。
八、手把手构建一个 Mini Transformer
理论讲完了,这一节我们用 PyTorch 从零搭建一个 6 层 Decoder-only Transformer,在小规模文本上演示训练和推理。完整代码约 250 行,CPU/GPU 都能跑,目标是让你彻底看清 Transformer 的每个齿轮是怎么咬合的。
8.1 目标与环境
- 实现 6 层 Decoder-only Transformer,参数量约 1.2M。
- 在 ~500 句的极小语料上做字符级语言模型训练,观察 loss 下降。
- 提供
train.py和sample.py两个可运行脚本。
环境准备(2 行命令):
pip install torch numpy
python -c "import torch; print('torch', torch.__version__)" 8.2 数据准备
我们用一段重复的"古诗风格"伪数据(也可以换成你自己的 input.txt):
cat > data/input.txt << 'EOF'
床前明月光,疑是地上霜。
举头望明月,低头思故乡。
春眠不觉晓,处处闻啼鸟。
夜来风雨声,花落知多少。
白日依山尽,黄河入海流。
欲穷千里目,更上一层楼。
静夜思
登鹳雀楼
春晓
EOF
wc -c data/input.txt 字符级语言模型直接把整个文件读成一个长字符串即可,无需 tokenizer。
8.3 完整代码 train.py
"""Mini Transformer:6 层 Decoder-only,字符级语言模型。
用法:
python train.py
"""
from pathlib import Path
# ---------- 1. 数据加载 ----------
class CharDataset:
"""把文本切成 (block_size) 长度的训练样本。"""
def __init__(self, path: str, block_size: int = 64):
self.text = Path(path).read_text(encoding="utf-8")
self.chars = sorted(set(self.text))
self.vocab_size = len(self.chars)
self.stoi = {c: i for i, c in enumerate(self.chars)}
self.itos = {i: c for i, c in enumerate(self.chars)}
self.block_size = block_size
self.data = torch.tensor([self.stoi[c] for c in self.text], dtype=torch.long)
print(f"[data] chars={self.vocab_size} len={len(self.data)}")
def get_batch(self, batch_size: int = 32):
ix = torch.randint(0, len(self.data) - self.block_size - 1, (batch_size,))
x = torch.stack([self.data[i : i + self.block_size] for i in ix])
y = torch.stack([self.data[i + 1 : i + 1 + self.block_size] for i in ix])
return x, y
# ---------- 2. 模型组件 ----------
class PositionalEncoding(nn.Module):
"""正弦位置编码,固定不参与训练。"""
def __init__(self, d_model: int, max_len: int = 512):
super().__init__()
pe = torch.zeros(max_len, d_model)
position = torch.arange(0, max_len).unsqueeze(1).float()
div_term = torch.exp(torch.arange(0, d_model, 2).float() * -(math.log(10000.0) / d_model))
pe[:, 0::2] = torch.sin(position * div_term)
pe[:, 1::2] = torch.cos(position * div_term)
self.register_buffer("pe", pe.unsqueeze(0)) # (1, max_len, d_model)
def forward(self, x):
return x + self.pe[:, : x.size(1)]
class CausalSelfAttention(nn.Module):
"""多头因果自注意力(Decoder 用,带 causal mask)。"""
def __init__(self, d_model: int, n_heads: int, max_len: int = 512, dropout: float = 0.1):
super().__init__()
assert d_model % n_heads == 0
self.n_heads = n_heads
self.head_dim = d_model // n_heads
self.qkv = nn.Linear(d_model, 3 * d_model)
self.proj = nn.Linear(d_model, d_model)
self.dropout = nn.Dropout(dropout)
# causal mask:上三角(不含对角)置 -inf
mask = torch.triu(torch.ones(max_len, max_len), diagonal=1).bool()
self.register_buffer("mask", mask)
def forward(self, x):
B, T, C = x.shape
qkv = self.qkv(x).reshape(B, T, 3, self.n_heads, self.head_dim).permute(2, 0, 3, 1, 4)
q, k, v = qkv[0], qkv[1], qkv[2] # (B, h, T, head_dim)
att = (q @ k.transpose(-2, -1)) / math.sqrt(self.head_dim)
att = att.masked_fill(self.mask[:T, :T], float("-inf"))
att = F.softmax(att, dim=-1)
att = self.dropout(att)
out = (att @ v).transpose(1, 2).reshape(B, T, C)
return self.proj(out)
class FeedForward(nn.Module):
"""Transformer FFN:GELU + Linear + Dropout。"""
def __init__(self, d_model: int, d_ff: int, dropout: float = 0.1):
super().__init__()
self.net = nn.Sequential(
nn.Linear(d_model, d_ff),
nn.GELU(),
nn.Linear(d_ff, d_model),
nn.Dropout(dropout),
)
def forward(self, x):
return self.net(x)
class Block(nn.Module):
"""Pre-LN Decoder Block:Attention -> FFN,各自带残差。"""
def __init__(self, d_model: int, n_heads: int, d_ff: int, max_len: int, dropout: float):
super().__init__()
self.ln1 = nn.LayerNorm(d_model)
self.attn = CausalSelfAttention(d_model, n_heads, max_len, dropout)
self.ln2 = nn.LayerNorm(d_model)
self.ffn = FeedForward(d_model, d_ff, dropout)
def forward(self, x):
x = x + self.attn(self.ln1(x))
x = x + self.ffn(self.ln2(x))
return x
class MiniTransformer(nn.Module):
"""6 层 Decoder-only Transformer,字符级语言模型。"""
def __init__(
self,
vocab_size: int,
d_model: int = 128,
n_heads: int = 4,
d_ff: int = 512,
n_layers: int = 6,
max_len: int = 256,
dropout: float = 0.1,
):
super().__init__()
self.tok_emb = nn.Embedding(vocab_size, d_model)
self.pos_enc = PositionalEncoding(d_model, max_len)
self.blocks = nn.ModuleList(
[Block(d_model, n_heads, d_ff, max_len, dropout) for _ in range(n_layers)]
)
self.ln_f = nn.LayerNorm(d_model)
self.head = nn.Linear(d_model, vocab_size, bias=False)
# 权重共享:输入 embedding 和输出 head 共享权重(BERT/GPT 常用 trick)
self.head.weight = self.tok_emb.weight
self.apply(self._init_weights)
def _init_weights(self, m):
if isinstance(m, nn.Linear):
nn.init.normal_(m.weight, std=0.02)
if m.bias is not None:
nn.init.zeros_(m.bias)
elif isinstance(m, nn.Embedding):
nn.init.normal_(m.weight, std=0.02)
def forward(self, idx, targets=None):
x = self.tok_emb(idx)
x = self.pos_enc(x)
for blk in self.blocks:
x = blk(x)
x = self.ln_f(x)
logits = self.head(x)
loss = None
if targets is not None:
loss = F.cross_entropy(logits.view(-1, logits.size(-1)), targets.view(-1))
return logits, loss
@torch.no_grad()
def generate(self, idx, max_new_tokens: int = 100, temperature: float = 1.0, top_k: int = 20):
"""自回归生成,带 temperature + top-k 采样。"""
for _ in range(max_new_tokens):
idx_cond = idx[:, -self.pos_enc.pe.size(1) :]
logits, _ = self(idx_cond)
logits = logits[:, -1, :] / temperature
if top_k:
v, _ = torch.topk(logits, min(top_k, logits.size(-1)))
logits[logits < v[:, [-1]]] = float("-inf")
probs = F.softmax(logits, dim=-1)
nxt = torch.multinomial(probs, num_samples=1)
idx = torch.cat([idx, nxt], dim=1)
return idx
# ---------- 3. 训练循环 ----------
def main():
torch.manual_seed(0)
ds = CharDataset("data/input.txt", block_size=64)
device = "cuda" if torch.cuda.is_available() else "cpu"
model = MiniTransformer(vocab_size=ds.vocab_size).to(device)
print(f"[model] params={sum(p.numel() for p in model.parameters()) / 1e6:.2f}M device={device}")
opt = torch.optim.AdamW(model.parameters(), lr=3e-4, weight_decay=0.1)
steps = 1000
batch_size = 32
t0 = time.time()
for step in range(steps):
x, y = ds.get_batch(batch_size)
x, y = x.to(device), y.to(device)
_, loss = model(x, y)
opt.zero_grad()
loss.backward()
torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
opt.step()
if step % 100 == 0 or step == steps - 1:
dt = time.time() - t0
print(f"step {step:4d} loss={loss.item():.4f} elapsed={dt:.1f}s")
torch.save({"model": model.state_dict(), "stoi": ds.stoi, "itos": ds.itos}, "ckpt.pt")
print("[done] saved ckpt.pt")
if __name__ == "__main__":
main() 8.4 推理脚本 sample.py
"""用训练好的 ckpt.pt 生成文本。"""
from train import MiniTransformer
ckpt = torch.load("ckpt.pt", map_location="cpu")
stoi, itos = ckpt["stoi"], ckpt["itos"]
model = MiniTransformer(vocab_size=len(itos))
model.load_state_dict(ckpt["model"])
model.eval()
prompt = "静夜思\n"
idx = torch.tensor([[stoi[c] for c in prompt]], dtype=torch.long)
out = model.generate(idx, max_new_tokens=120, temperature=0.8, top_k=10)[0].tolist()
print("=== generated ===")
print("".join(itos[i] for i in out)) 8.5 运行 & 预期输出
mkdir -p data
# (把 8.2 节的 input.txt 写进去)
python train.py 预期输出(CPU 大约 30-60 秒,MPS/GPU 几秒):
[data] chars=28 len=87
[model] params=1.21M device=cpu
step 0 loss=3.3321 elapsed=0.0s
step 100 loss=2.7102 elapsed=2.1s
step 200 loss=2.3504 elapsed=4.0s
...
step 900 loss=1.8421 elapsed=32.5s
step 999 loss=1.7903 elapsed=36.0s
[done] saved ckpt.pt
python sample.py
=== generated ===
静夜思
床前明月光,疑是地上霜。
举头望明月,低头思故乡。
春眠不觉晓,处处闻啼鸟。 截图位置:训练曲线(loss vs step)→ 推荐用 tensorboard 或 matplotlib 把 loss.item() 画出来,存为 loss_curve.png。生成样例输出 → 直接拷贝终端文字即可。
8.6 调参建议
- loss 降不下去:数据太短 → 换更长
input.txt(如《全唐诗》);学习率 → 试 1e-4 / 5e-4。 - 生成全是同一字:温度过低,调到 0.8-1.2;top_k 调到 20-50。
- 显存不够:把
batch_size调到 8,或用torch.amp.autocast走半精度。 - 想快一点:
d_model=96、n_layers=4,参数降到 ~400K。
8.7 进阶方向
- 完整 GPT:加
top-p (nucleus)采样、KV-cache 加速推理。 - LLaMA 架构:用
RMSNorm替 LayerNorm、RoPE替正弦位置编码、SwiGLU替 GELU FFN、GQA分组注意力降显存。 - 多卡训练:
torchrun --nproc_per_node=2 train.py+DistributedDataParallel。 - 用真实 tokenizer:把
CharDataset换成tiktoken(GPT-2 BPE)或sentencepiece。
九、视频学习:看大神手把手演示
文字看完理论后,看大神亲自手写一遍代码,理解速度翻倍。下面三个视频是 AI 基础领域公认最值得一看的:3Blue1Brown 用最直观的方式讲解神经网络,Andrej Karpathy 在 4 小时里从空文件手写一个完整的 GPT,以及他的姊妹篇 Tokenizer 详解。
But what is a neural network? | 深度学习系列 DL1
展开介绍
3Blue1Brown 经典入门第一集。用 19 分钟手绘风格动画讲清神经网络核心:神经元 → 加权和 + 激活函数 → 层与层堆叠 → MNIST 手写数字识别。强烈建议先看这集建立直觉,再回头看本章的数学细节,效果翻倍。视频语言:英文(配中文字幕)。
Let's build GPT: from scratch, in code
展开介绍
4.5 小时硬核长片。Karpathy 从空文件起步,带你手写 Bigram Language Model → Self-Attention → Multi-Head → 完整 Decoder-only Transformer,每一步都在 Jupyter 里现场跑。配套 GitHub 仓库: github.com/karpathy/ng-video-lecture。看完这集,本章第 2-3 节所有公式都会变成你眼睛能看见的代码。视频语言:英文(配中文字幕)。
Let's build the GPT Tokenizer (minbpe)
展开介绍
Tokenizer 是 LLM 训练流水线最容易被忽视却影响最大的环节。Karpathy 带你从 0 写一个完整的 BPE 词表训练 + encode / decode 流程,然后揭示 10 个「为什么 LLM 不擅长 XX」的怪现象(为什么不会拼写、为什么中文消耗更多 token、为什么 SolidGoldMagikarp 会让模型崩溃)。视频语言:英文。
十、中文视频:跟李沐老师学动手
英文吃力?中文社区也有两位公认讲得最清楚的老师。前亚马逊首席科学家李沐老师的《动手学深度学习》课程是无数中国 AI 从业者的入门课,配套开源教材《Dive into Deep Learning》。
动手学深度学习 PyTorch 版(完结合集 76 集)
展开介绍
李沐老师经典中文 AI 入门课,76 集超 28 万人参与过直播,B 站回放上百万播放。覆盖线性神经网络 → MLP → CNN → RNN/LSTM/GRU → 注意力机制 → Transformer → 优化算法 → BERT → 计算机视觉 → NLP 全链路。配套开源教材:zh-v2.d2l.ai。视频语言:中文。