大模型这个词这两年已经被说烂了,但真正能把"从预训练到Agent"这条链路讲清楚的人其实不多。我见过太多人一上来就冲着微调去,拿着LoRA脚本跑了一遍发现效果不对,回头才发现连基座模型是怎么来的、数据是怎么配比的都没搞明白。这篇东西是我自己从预训练一路踩到Agent落地之后梳理出来的完整图景,不打算写成教科书,而是按照一个从业者真正会关心的顺序来拆:模型怎么从零练出来、怎么让它听话、怎么让它能干活、以及每一步实际动手时最容易翻车的地方在哪。不管你是刚入门想搞清楚全貌,还是已经做过微调但觉得知识是碎的,这篇应该都能帮你把链条接上。
1. 预训练:大模型能力的真正来源
1.1 预训练到底在做什么
很多人把预训练理解成"喂数据让模型学知识",这个说法不算错但太粗糙。预训练的本质是让模型学会在给定上下文的情况下预测下一个token的概率分布。听起来简单,但当你把互联网级别的文本丢进去,模型为了把这个预测做好,被迫学会了语法、事实、推理模式、代码结构、甚至一些世界常识。这就是所谓的涌现能力——没有专门教它,但它为了降低预测误差自己学会了。
预训练的数据规模通常用token数衡量。以主流开源模型为例,7B级别的模型往往要吃掉1T到2T的token,而更大的模型会到10T以上。这个量级是什么概念?一本《三体》三部曲大概100万汉字,粗算约150万token,1T token相当于六十多万套三体全集。数据来源一般包括网页抓取、书籍、代码仓库、论文、百科等,配比是各家核心机密之一。
数据清洗在预训练里的重要性怎么强调都不过分。原始网页数据里充斥着导航栏、广告、重复内容、乱码,如果不做去重和质量过滤,模型会学到大量垃圾模式。常见的处理链路包括:URL过滤、文本提取、语言识别、去重(精确去重+模糊去重)、质量打分(用一个小模型给文本打困惑度分,低质量的丢掉)。这一步做得好不好,直接决定基座模型的下限。
1.2 预训练的核心架构选择
现在主流的大模型几乎都是Decoder-only的Transformer架构。这个选择不是偶然的。Encoder-only(比如BERT系列)擅长理解类任务,Encoder-Decoder(比如T5)擅长序列到序列的任务,而Decoder-only在规模化之后展现出了最好的通用性和涌现能力,而且推理时实现简单、易于做KV Cache优化。
具体到架构细节,有几个关键点值得关注:
- 位置编码:从绝对位置编码到RoPE(旋转位置编码),再到各种长度外推方案。RoPE之所以成为主流,是因为它通过旋转矩阵把相对位置信息编码进注意力计算,天然支持一定的长度外推,配合NTK-aware插值或者YaRN等方法可以把上下文窗口从4K扩展到128K甚至更长。
- 注意力机制:标准多头注意力在长序列下计算量是O(n²),所以有了各种优化——MQA(Multi-Query Attention)、GQA(Grouped-Query Attention)、FlashAttention等。GQA现在是主流选择,它在推理速度和效果之间取得了比较好的平衡。
- 归一化:从Post-LN到Pre-LN再到RMSNorm,趋势是越来越简单且训练更稳定。RMSNorm去掉了均值中心化,只做均方根归一化,计算更省且效果不差。
- 激活函数:SwiGLU基本取代了ReLU和GELU,在FFN层里表现更好。
这些架构选择不是拍脑袋定的,每一个背后都有大量的消融实验支撑。你在读论文的时候可以重点关注它们的ablation study部分,那才是真正有价值的信息。
1.3 预训练的实际工程挑战
真正做过预训练的人都知道,架构设计可能只占20%的精力,剩下80%全在工程上。几个绕不开的问题:
并行策略。一个7B模型用fp16存储就要14GB,加上优化器状态(Adam的话是模型参数的2倍)、梯度、激活值,单卡根本放不下。所以需要组合使用数据并行、张量并行、流水线并行。数据并行每张卡存完整模型副本、喂不同数据;张量并行把单个矩阵运算切到多卡;流水线并行把不同层放到不同卡上。实际配置要根据模型大小、卡的数量和互联带宽来定。
训练稳定性。大规模训练最怕loss spike,就是loss突然飙升甚至变成NaN。常见原因包括学习率过大、数据里混入了异常样本、梯度爆炸等。应对手段有:梯度裁剪、学习率warmup、在检测到spike时回滚到之前的checkpoint并跳过当前数据批次。我自己的经验是,warmup步数不要省,宁可多花几千步慢慢把学习率升上去,也比中途炸了重来划算。
显存优化。除了并行,还有ZeRO系列(把优化器状态、梯度、参数分片存储)、激活重计算(用计算换显存,前向时不存激活值,反向时重新算)、混合精度训练(用bf16做前向反向,fp32做参数更新)等手段。这些技术组合起来,才能让大模型训练在有限硬件上跑起来。
提示:如果你只是想在自己的数据上做领域适配,不要从零预训练。从开源基座模型出发做继续预训练(continue pre-training)或者直接微调,成本差着好几个数量级。
2. 微调:让基座模型变成你的专属助手
2.1 全量微调与参数高效微调的分水岭
预训练出来的基座模型是个"什么都懂一点但什么都不精"的状态,它不会按照你的指令格式回答问题,也不了解你的垂直领域。微调就是解决这个问题的。
全量微调就是拿基座模型的所有参数,在你的数据上继续训练。效果上限最高,但代价也最大——一个7B模型全量微调至少需要几张A100,而且容易过拟合,数据量不够的话效果反而会退化。更麻烦的是灾难性遗忘:模型在你的领域数据上训过头之后,通用能力会明显下降。
参数高效微调(PEFT)是现在的主流选择。核心思路是冻结大部分预训练参数,只训练一小部分新增的或选定的参数。最常用的几种:
| 方法 | 核心思路 | 可训练参数占比 | 适用场景 |
|---|---|---|---|
| LoRA | 在权重矩阵旁加低秩分解矩阵 | 0.1%-1% | 通用首选,效果稳定 |
| QLoRA | LoRA + 4bit量化基座 | 0.1%-1% | 显存极度受限 |
| Prefix Tuning | 在每层前面加可学习前缀 | <0.1% | 生成任务 |
| Adapter | 在层间插入小模块 | 1%-5% | 多任务切换 |
| P-Tuning v2 | 可学习prompt embedding | <0.1% | NLU任务 |
LoRA之所以成为事实标准,是因为它的设计非常优雅:对于原始权重矩阵W,不直接改它,而是学一个低秩增量ΔW = BA,其中B和A是远小于原矩阵维度的低秩矩阵。推理时可以把BA乘回去合并到W里,不增加任何推理延迟。这个特性让它在工程上极其友好。
2.2 LoRA微调的实操细节
拿Qwen系列做LoRA微调举例,几个关键参数的选择逻辑:
rank(秩):控制低秩矩阵的维度,越大表达能力越强但参数越多。经验值是:简单任务(风格迁移、格式对齐)用8-16,复杂任务(领域知识注入、推理能力提升)用32-64。我一般从16起步,效果不够再加。
alpha:缩放因子,实际增量是ΔW = (alpha/r) * BA。通常设成rank的1-2倍。alpha/r这个比例决定了增量更新的幅度,比例太大容易训崩,太小则学不动。
target_modules:决定把LoRA加在哪些层上。最常见的是加在注意力的q_proj、k_proj、v_proj、o_proj上。有实验表明,同时加在注意力和FFN层上效果更好,但参数会多一些。如果显存紧张,只加q_proj和v_proj也能work。
学习率:LoRA的学习率通常比全量微调大一个数量级,常见范围是1e-4到3e-4。因为可训练参数少,需要更大的步长才能有效更新。
数据格式:这是最容易被忽视但影响巨大的环节。指令微调的数据一般组织成对话格式,用chat template包起来。不同模型的template不一样,用错了模型学到的就是错误的格式。比如Qwen的ChatML格式是<|im_start|>user\n...<|im_end|>,Llama系列用的是[INST]...[/INST]。一定要用模型自带的tokenizer的apply_chat_template方法,不要自己手写。
# LoRA微调核心配置示例 from peft import LoraConfig, get_peft_model lora_config = LoraConfig( r=16, lora_alpha=32, target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"], lora_dropout=0.05, bias="none", task_type="CAUSAL_LM" ) model = get_peft_model(base_model, lora_config) model.print_trainable_parameters() # 输出类似: trainable params: 20M || all params: 7B || trainable%: 0.28%2.3 微调数据:质量和数量哪个更重要
我的答案是:质量远比数量重要。1000条精心构造的高质量指令数据,效果往往好过10万条从网上随便爬的脏数据。原因很简单——微调是在教模型"怎么做事",不是在教它"知道什么"。知识注入靠继续预训练,行为对齐靠指令微调。
高质量微调数据的几个特征:
- 多样性:任务类型要覆盖你实际使用场景的各种情况,不要全是同一类问题。
- 准确性:答案必须是对的。模型会忠实地学习你给它的模式,包括错误。
- 格式一致性:如果你希望模型输出特定格式(比如JSON),训练数据里就必须严格是这个格式。
- 难度梯度:从简单到复杂都要有,让模型逐步学习。
数据构造的常见方式包括:人工标注(质量最高但贵)、用更强的模型蒸馏(GPT-4生成、人工审核)、从现有数据集中筛选和改写。实际项目中往往是组合使用。
注意:微调数据里如果混入了测试集的样本,评估结果会严重虚高。做数据划分的时候一定要严格去重,最好用不同的数据来源做训练和测试。
3. 对齐与提示工程:从"能用"到"好用"
3.1 RLHF与DPO的路线之争
微调之后的模型已经能按照指令格式回答了,但它的回答可能不够有帮助、不够安全、或者风格不对。对齐阶段就是解决这个问题的。
传统的RLHF(基于人类反馈的强化学习)分三步:先训练一个奖励模型(让人类对模型的多个输出排序,用这些排序数据训练一个打分模型),然后用PPO算法优化策略模型,让它的输出获得更高的奖励分。这套流程效果好但极其复杂——要同时维护策略模型、参考模型、奖励模型、价值模型四个模型,训练不稳定,调参难度大。
DPO(直接偏好优化)是这两年的热门替代方案。它跳过奖励模型,直接用偏好数据(chosen/rejected对)优化策略模型。数学上它证明了最优策略和奖励函数之间存在解析关系,所以可以绕过显式奖励建模。实现简单、训练稳定,效果在很多场景下不输RLHF。
实际选型建议:资源有限、追求稳定就选DPO;如果对效果有极致要求且有工程能力维护RLHF管线,可以上PPO。现在还有一些DPO的变体比如IPO、KTO、ORPO,各有侧重,但核心思想类似。
3.2 提示词工程与上下文工程
模型对齐好了,接下来是怎么用好它。提示词工程(Prompt Engineering)这个词已经被说烂了,但真正有效的技巧其实就那么几条:
明确角色和任务。不要只说"帮我写个总结",要说"你是一个技术文档编辑,请把下面这段技术说明改写成面向非技术读者的通俗版本,控制在200字以内"。角色设定会激活模型在预训练中对应领域的模式。
给例子(Few-shot)。对于格式要求严格的任务,给1-3个输入输出示例比任何描述都管用。模型会从例子里推断出你想要的模式。
分步思考(Chain-of-Thought)。对于推理类任务,让模型"一步一步想"能显著提升准确率。这个技巧的本质是让模型把中间推理过程展开成token,相当于给它更多的计算空间。
上下文工程是提示词工程的延伸。当模型支持长上下文之后,问题从"怎么写prompt"变成了"怎么组织整个上下文窗口里的信息"。关键原则:重要的信息放在开头和结尾(中间容易丢失,这就是所谓的"lost in the middle"现象),用清晰的分隔符区分不同部分,把指令放在最后。
3.3 评估:怎么知道模型到底行不行
这一步太多人跳过了。模型训完,随便试几个case觉得还行就上线,结果真实场景里各种翻车。
评估要分层次做:
- 自动指标:困惑度(PPL)、BLEU、ROUGE这些传统指标对生成式模型参考价值有限,但可以作为快速筛查。
- LLM as Judge:用更强的模型(比如GPT-4级别)给输出打分。要注意位置偏差(模型倾向于给第一个选项高分)和长度偏差(倾向于给长回答高分),可以通过交换顺序多次评估来缓解。
- 人工评估:最可靠但最贵。设计好评测维度(准确性、完整性、流畅度、安全性),让标注人员独立打分。
- 公开榜单:Open LLM Leaderboard、C-Eval、CMMLU等可以作为参考,但要注意数据污染问题——很多模型在训练时见过测试集。
我自己的做法是:先构造一个50-100条的领域测试集,覆盖核心场景和边界情况,每次改动都跑一遍。这个测试集要持续维护,发现bad case就加进去。这比任何榜单都更能反映模型在你实际场景下的表现。
4. Agent:让大模型真正干活
4.1 Agent的本质是什么
如果说微调和对齐是让模型"会说话",那Agent就是让模型"会做事"。一个Agent的核心循环是:感知环境 → 规划下一步 → 执行动作 → 观察结果 → 继续规划,直到任务完成。
用更技术化的语言描述,Agent = LLM + 工具 + 记忆 + 规划。LLM是大脑,负责理解和决策;工具是手脚,让它能查资料、调API、操作软件;记忆让它能记住之前发生了什么;规划让它能把复杂任务拆成可执行的步骤。
这和单纯的对话模型有本质区别。对话模型是"你问我答",Agent是"你给我目标,我自己想办法达成"。比如你说"帮我查一下北京明天天气,如果下雨就提醒我带伞",对话模型只能告诉你它不知道实时天气,而Agent会调用天气API、判断结果、然后给出结论。
4.2 Agent的核心组件拆解
工具调用(Function Calling / Tool Use)是Agent的基础能力。模型需要能理解有哪些工具可用、每个工具需要什么参数、什么时候该调用哪个工具。实现上通常是把工具定义成JSON Schema,模型输出结构化的调用请求,外部系统执行后把结果返回给模型。
# 工具定义的典型结构 tools = [ { "type": "function", "function": { "name": "get_weather", "description": "查询指定城市的天气", "parameters": { "type": "object", "properties": { "city": {"type": "string", "description": "城市名"}, "date": {"type": "string", "description": "日期,格式YYYY-MM-DD"} }, "required": ["city"] } } } ]记忆系统分短期和长期。短期记忆就是当前对话的上下文,受限于上下文窗口长度。长期记忆需要外部存储——把重要信息向量化存到向量数据库,需要时检索出来。这里的关键是什么该记、什么该忘、什么时候检索,策略设计得好不好直接决定Agent的实用性。
规划能力是Agent最核心也最难的部分。简单任务一步就能完成,复杂任务需要拆解。常见的规划模式有:
- ReAct:交替进行推理(Reasoning)和行动(Acting),每一步都先想再做的。
- Plan-and-Execute:先制定完整计划,再逐步执行。适合步骤明确的流程化任务。
- Reflection:执行后反思结果,如果不对就调整重来。适合需要试错的场景。
多Agent协作是进阶玩法。让多个各有专长的Agent分工合作,比如一个负责拆解任务、一个负责写代码、一个负责审查。好处是每个Agent的prompt可以更聚焦,坏处是通信开销大、容易陷入循环。
4.3 Agent开发中的真实坑
并发问题。单用户测试的时候Agent跑得好好的,一上并发就各种问题。工具调用有速率限制、共享状态会冲突、上下文管理会混乱。解决方案包括:给每个会话独立的上下文空间、对工具调用做队列和限流、用异步IO处理等待。
错误处理。工具调用失败是常态——API超时、返回格式不对、参数错误。Agent必须能识别失败并决定是重试、换方案还是放弃。很多Agent demo看起来惊艳,一到生产环境就崩,就是因为没做好错误处理。
成本控制。Agent一次任务可能调用十几次LLM,token消耗是普通对话的几十倍。优化手段包括:用小模型做简单决策、缓存重复的工具调用结果、设置最大步数限制防止无限循环。
评估困难。对话模型可以用BLEU、ROUGE评估,Agent怎么评?一个任务完成了没有、完成得好不好、有没有更优解,这些很难自动判断。实践中通常用任务成功率作为核心指标,配合人工抽查执行轨迹。
提示:Agent开发不要一上来就追求全自动。先从"人在回路"(Human-in-the-loop)开始,让Agent执行关键步骤前需要人确认,跑稳了再逐步放开。
5. 部署与选型:把模型真正跑起来
5.1 推理框架怎么选
模型训好了,怎么高效地跑起来是另一个问题。主流推理框架各有侧重:
| 框架 | 核心优势 | 适用场景 |
|---|---|---|
| vLLM | PagedAttention,吞吐量高 | 高并发在线服务 |
| TGI | HuggingFace生态,易用 | 快速原型和中小规模 |
| llama.cpp | CPU/消费级GPU可跑,量化支持好 | 本地部署、边缘设备 |
| TensorRT-LLM | NVIDIA深度优化,延迟低 | 对延迟敏感的生产环境 |
| Ollama | 开箱即用,管理方便 | 个人开发、本地实验 |
选型的核心考量是:你的瓶颈是吞吐还是延迟?你的硬件是什么?你的并发量多大?高并发选vLLM,低延迟选TensorRT-LLM,本地玩选Ollama或llama.cpp。
量化是部署绕不开的话题。把fp16的模型量化到int8或int4,显存占用能降一半到四分之三,代价是精度损失。常用的量化方案有GPTQ、AWQ、GGUF等。经验是:int8量化基本无损,int4量化在大多数任务上损失可接受,但推理类任务要谨慎。
5.2 模型选型的决策框架
面对一堆开源模型,怎么选?我的决策顺序是:
- 先看任务类型。中文任务优先考虑Qwen、GLM、Baichuan等中文优化过的模型;代码任务看DeepSeek-Coder、CodeLlama;多模态看Qwen-VL、InternVL。
- 再看规模约束。显存有多少、延迟要求多高,决定了你能用多大的模型。7B模型int4量化后大概4-5GB显存,13B大概8GB,70B需要40GB以上。
- 然后看许可证。商用的话要确认license是否允许,有些模型只允许研究用途。
- 最后看社区活跃度。文档全不全、issue响应快不快、有没有人踩过坑,这些直接影响你的开发效率。
不要迷信榜单排名。Open LLM Leaderboard上的分数和实际使用体验经常对不上,因为榜单测的是通用能力,而你的场景是特定的。最好的模型是你在自己测试集上跑分最高的那个。
5.3 从Demo到生产的距离
一个模型在notebook里跑通,和它在生产环境稳定服务,中间隔着十万八千里。几个必须解决的问题:
服务化。用FastAPI或者Triton Inference Server把模型包装成HTTP服务,处理好请求队列、批处理、超时。
监控。记录每个请求的延迟、token数、成功率,设置告警阈值。模型服务的性能会随着负载变化,没有监控就是盲人摸象。
版本管理。模型更新、prompt调整、参数变更都要有版本记录,出问题能快速回滚。
安全防护。输入过滤(防止prompt注入)、输出审核(防止有害内容)、速率限制(防止滥用)。这些在demo阶段可以不管,生产环境一个都不能少。
6. 一条完整的技术链路复盘
把整条链路串起来看:预训练决定了模型的能力上限,微调决定了它在特定任务上的表现,对齐决定了它的行为是否符合预期,Agent让它从"会说"变成"会做",部署决定了它能不能稳定服务。
每个环节都有它的必要性,但也有它的成本。实际项目中不是每个环节都要做——大多数团队直接从开源基座模型开始,跳过预训练;如果通用模型已经够用,微调也可以跳过;Agent也不是所有场景都需要,简单的对话场景用prompt工程就够了。
我自己的经验是:先想清楚你要解决什么问题,再倒推需要哪些环节。不要因为某个技术火就硬往上套。见过太多团队花大力气微调了一个模型,结果发现用GPT-4加好的prompt效果更好——不是说微调没价值,而是要先算清楚投入产出比。
另外一个很实际的建议:把评估体系建在前面。没有评估就没有迭代方向,你会陷入"感觉好像好了一点"的玄学调优。哪怕一开始只有20条测试用例,也比没有强。随着项目推进不断补充,这个测试集会成为你最宝贵的资产。
最后说一个容易被忽视的点:数据飞轮。上线之后用户的实际使用数据(输入、输出、反馈)是最宝贵的训练素材。设计好数据收集和标注流程,让模型能持续从真实场景中学习,这比一次性的大规模微调更有长期价值。我见过做得好的团队,模型每个月都在肉眼可见地变好,靠的就是这个飞轮转起来了。