LLM 工程不是只有模型结构,数据、评测和失败处理同样是核心
项目目标
前面已经把模型内部和推理系统走了一遍。接下来进入更大的系统:稀疏模型、替代架构、数据管线、合成数据、scaling laws、后训练、量化、服务、评测、RAG、工具、Agent、多模态、可解释性和安全。
这部分看起来很散,但可以用一个问题串起来:模型进入真实应用后,哪些能力来自参数,哪些来自数据,哪些来自检索,哪些来自工具,哪些必须靠评测和安全边界兜住。
项目 17-18:MoE 与 sparse trade-offs
MoE 的动机是增加总参数量,但每个 token 只激活一部分 expert。
最小项目:实现一个 two-expert router。
token -> router -> expert A or expert B -> combine output
要记录:
- 每个 expert 的 token 数。
- router entropy。
- 是否出现 expert collapse。
- dense MLP 与 sparse MoE 的 FLOPs 对比。
破坏实验:人为给 router 加偏置,让它总选同一个 expert。观察另一个 expert 是否完全学不到东西。
最小 top-1 MoE 实现(两个 expert):
import torch
import torch.nn as nn
import torch.nn.functional as F
class TopKMoE(nn.Module):
def __init__(self, n_embd, n_experts=4, top_k=1):
super().__init__()
self.experts = nn.ModuleList([
nn.Sequential(nn.Linear(n_embd, 4*n_embd), nn.GELU(), nn.Linear(4*n_embd, n_embd))
for _ in range(n_experts)
])
self.router = nn.Linear(n_embd, n_experts, bias=False)
self.top_k = top_k
def forward(self, x):
B, T, C = x.shape
scores = self.router(x) # (B, T, n_experts)
top_val, top_idx = scores.topk(self.top_k, dim=-1)
weights = F.softmax(top_val, dim=-1) # 归一化权重
out = torch.zeros_like(x)
# 把每个 token 路由到选中的 expert
for k in range(self.top_k):
for i, expert in enumerate(self.experts):
mask = top_idx[..., k] == i # (B, T) 布尔掩码
if not mask.any():
continue
out[mask] += weights[..., k:k+1][mask] * expert(x[mask])
# 记录 router 行为
self.last_route = top_idx # 用于统计每 expert token 数
self.last_entropy = -(F.softmax(scores, dim=-1) * F.log_softmax(scores, dim=-1)).sum(-1).mean()
return out
# 统计 expert 利用率:跑完一个 batch 后看每 expert 处理了多少 token
moe = TopKMoE(n_embd=128, n_experts=4)
y = moe(torch.randn(2, 64, 128))
for i in range(4):
used = (moe.last_route == i).sum().item()
print(f"expert {i}: {used} tokens")
print(f"router entropy: {moe.last_entropy:.3f}")
expert collapse 怎么识别:理想情况 4 个 expert 各占 25%,entropy ≈ ln(4) ≈ 1.39;如果有一个 expert 拿到 70%+ 流量、entropy < 1.0,就是 collapse。生产 MoE 都会加 load balancing loss(鼓励均匀分配)来抑制 collapse。
项目 19-20:Transformer 之外的序列模型
Transformer 是主线,但不是唯一方案。
可以做两个 toy:
- state-space / linear-attention 模型:观察线性时间序列处理。
- diffusion-style language model:观察非自回归、mask-denoise 生成思路。
这里不追求复现大模型,而是建立判断力:当有人说“替代 Transformer”时,知道它到底是在改复杂度、改生成范式,还是改训练目标。
项目 21:预训练数据管线
数据质量常常比模型结构更早决定上限。
最小数据管线包括:
- 原始文本采集。
- 文档级去重。
- 低质量过滤。
- 语言检测。
- 敏感信息过滤。
- train/val/test 切分。
- tokenizer 统计。
- 数据卡片。
要做的图:
- 文档长度分布。
- token 长度分布。
- 重复率。
- 过滤前后样例。
- 不同数据子集训练 loss 对比。
最常被忽略的一步是文档级去重——预训练数据里 30~50% 是近似重复很常见,不去重会让模型反复看到同样内容、过拟合特定句式。最小 MinHash 去重实现:
from datasketch import MinHash, MinHashLSH
def minhash(text, num_perm=128):
"""把文本变成一个可以快速比较的指纹"""
m = MinHash(num_perm=num_perm)
# 用 5-gram 字符片段作为特征
for i in range(len(text) - 4):
m.update(text[i:i+5].encode())
return m
# 阈值 0.8 = 文档之间 Jaccard 相似度 ≥ 80% 视为重复
lsh = MinHashLSH(threshold=0.8, num_perm=128)
deduped = []
for doc_id, doc in enumerate(docs):
sig = minhash(doc)
# 查询是否有近似副本
if not lsh.query(sig):
lsh.insert(f"doc_{doc_id}", sig)
deduped.append(doc)
print(f"原始 {len(docs)} 篇 → 去重后 {len(deduped)} 篇,重复率 {1 - len(deduped)/len(docs):.1%}")
MinHash + LSH 是工业标准做法(C4 / RefinedWeb / The Pile 都用过),近似检测 O(N) 时间,准确度足够预训练场景。
项目 22:合成数据
合成数据不能只看“生成了多少”。它必须回答三个问题:
- 是否覆盖真实数据缺口。
- 是否被过滤和去重。
- 是否在独立评测集上真的提升。
最小实验:
- 用一个强模型生成某类任务数据。
- 加入噪声样例和重复样例。
- 做过滤、去重、质量打分。
- 分别训练 real-only、synthetic-only、mixed。
- 在干净 eval split 上比较。
如果 mixed 只提升训练集、不提升验证集,它很可能只是让模型学会了生成器的口癖。
项目 23:scaling curves
训练 tiny/small/medium 三个模型,固定数据与训练设置,画 loss vs 参数量、loss vs token 数、训练时间 vs 参数量。
这个项目的目的不是推导理想化 scaling law,而是形成直觉:
- 模型太小会欠拟合。
- 数据太少会过拟合。
- compute 固定时,参数和 token 要一起考虑。
- 更大不自动等于更好,尤其在数据和训练预算不足时。
项目 24-25:后训练与偏好优化
后训练把 base model 变成 assistant。
先做 SFT:把指令、输入、答案整理成 supervised dataset。
再做 LoRA/QLoRA:只训练低秩 adapter,降低显存成本。
再做偏好优化:DPO 是最容易上手的一类,RLHF/PPO/GRPO/RLVR 更接近 reasoning 模型训练讨论。
最小交付:
- 一个 SFT 数据集。
- 一个 LoRA adapter。
- 一组 before/after 输出对比。
- 一个偏好数据小样本。
- DPO 或 toy PPO 实验记录。
LoRA 最小实现(一段就能看清"低秩 adapter 是什么"):
import torch.nn as nn
class LoRALinear(nn.Module):
"""在原 Linear 旁边加一个低秩旁路:W' = W + (B A) * scale"""
def __init__(self, base: nn.Linear, rank=8, alpha=16):
super().__init__()
self.base = base
for p in self.base.parameters():
p.requires_grad = False # 冻结原权重
in_f, out_f = base.in_features, base.out_features
self.lora_A = nn.Linear(in_f, rank, bias=False)
self.lora_B = nn.Linear(rank, out_f, bias=False)
nn.init.zeros_(self.lora_B.weight) # 初始化为 0 → 初始等价于原模型
self.scale = alpha / rank
def forward(self, x):
return self.base(x) + self.scale * self.lora_B(self.lora_A(x))
# 用法:把模型里所有 nn.Linear(或只挑 q_proj / v_proj)替换成 LoRALinear
# 训练时只有 lora_A / lora_B 的参数会更新,参数量从几亿降到几百万
DPO 损失(直接从偏好对学习,不需要 PPO 那套 RL 复杂度):
import torch
import torch.nn.functional as F
def dpo_loss(policy_logp_chosen, policy_logp_rejected,
ref_logp_chosen, ref_logp_rejected, beta=0.1):
"""
每条偏好样本由 (prompt, chosen_response, rejected_response) 组成。
logp_* 是模型在对应回答上的对数概率求和。
"""
# 当前策略相对于参考模型的对数比
policy_ratio = policy_logp_chosen - policy_logp_rejected
ref_ratio = ref_logp_chosen - ref_logp_rejected
# DPO 目标:让 policy 更倾向 chosen,与 ref 拉开距离
logits = beta * (policy_ratio - ref_ratio)
loss = -F.logsigmoid(logits).mean()
return loss
beta 控制偏离 ref 模型的程度——大 beta 让 policy 离 ref 更近(保守),小 beta 让 policy 更激进地拟合偏好数据。DPO 比 PPO 简单的核心点:不需要 reward model,不需要 rollout,监督学习级别的训练成本就能做偏好对齐。
项目 26-27:量化与 serving stack
量化不是“把模型变小”这么简单。它改变数值行为、显存占用、带宽压力和有时的输出质量。
要比较:
- FP16/BF16。
- INT8。
- INT4。
- GGUF/AWQ/GPTQ 等格式。
serving stack 选择:
- llama.cpp:本地、CPU/GPU 混合、GGUF 生态。
- vLLM:高吞吐、PagedAttention、OpenAI compatible server。
- TensorRT-LLM:NVIDIA 生态深度优化。
- SGLang:结构化生成、调度、prefix cache。
记录首 token 延迟、吞吐、显存、并发、失败模式。
项目 28:evaluation harness
如果不能测,就不能改。
最小评测体系:
| 层次 | 示例 |
|---|---|
| 单元测试 | tokenizer round-trip、mask 不看未来 |
| 模型评测 | loss、perplexity、小任务准确率 |
| RAG 评测 | recall、faithfulness、answer relevance |
| Agent 评测 | success rate、tool error、step count |
| 安全评测 | prompt injection、越权工具调用、敏感输出 |
评测集要版本化。每次改 prompt、模型、检索、rerank、工具 schema,都要能回放。
项目 29-30:RAG、工具与 Agent
RAG 不是把 PDF 塞进向量库这么简单。它至少包含:切分、embedding、索引、召回、rerank、上下文构造、引用、答案验证。
工具调用也不是“让模型调用函数”这么简单。它包含 schema 设计、权限、幂等、超时、错误恢复、审计。
Agent loop 要在理解模型、检索和工具之后再做。否则框架会掩盖失败来源。
项目 31-33:多模态、可解释性与安全
多模态最小项目:接一个 vision encoder,把图像特征映射到 LLM 可消费的 token 空间。重点是理解 adapter,而不是追求效果。
可解释性最小项目:观察 attention、训练 probes、尝试 sparse autoencoder 的概念实验。
安全最小项目:建立红队样例库。包括 prompt injection、越权工具调用、数据泄露、RAG 污染、代码执行危险、内容安全边界。
本项目交付物
- two-expert MoE router 和 expert utilization 图。
- 数据管线报告。
- synthetic data 对比实验。
- scaling curve。
- LoRA/QLoRA 微调记录。
- 量化损伤表。
- serving stack benchmark。
- evaluation harness。
- RAG/Agent 失败样例库。
- 安全红队用例集。
和本站已有内容的连接
ml-basics/混合专家模型fine-tuning/document-processing-training-datainference-opt/04-LoRA与QLoRA微调vector-db/01-向量数据库到底是什么llm-app/RAG检索增强生成ai-agent/01-Agent是什么agent-eval/01-评判一个Agentcode-agent-harness/15-代码Agent安全边界
延伸阅读
版权声明: 如无特别声明,本文版权归 sshipanoo 所有,转载请注明本文链接。
(采用 CC BY-NC-SA 4.0 许可协议进行授权)
本文标题:项目 17-33:MoE、数据、后训练、评测、RAG、Agent 与安全
本文链接:https://www.sshipanoo.com/blog/ai/llm-roadmap/05-数据后训练评测与应用系统/
本文最后一次更新为 天前,文章中的某些内容可能已过时!