news 2026/9/5 6:59:33

大模型工程落地指南:训练、微调与推理部署全流程解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
大模型工程落地指南:训练、微调与推理部署全流程解析

1. 一套开源权重和一套能用的服务之间,差的不是模型而是流程

最近我经常被问到一类问题:开源大模型已经满天飞,下载一个权重就能跑起来,为什么实际做项目时还要花时间讨论训练、微调和推理框架?很多人把“会用 Transformers 库”和“能把大模型放到业务里”混为一谈,结果模型加载成功了,到了高并发、长文本、任务定制这些环节就全露馅。

我的理解是这样:大模型的落地,本质上不是“模型”问题,而是“流程”问题。一条完整的项目链路,可以粗略分成预训练、微调、推理三个阶段,每个阶段有完全不同的瓶颈、成本结构和工程方法论。预训练是在海量文本上学习语言和知识,决定模型的上限;微调是把模型的普适能力校准到你的业务分布上,决定模型有没有用;推理部署则是把参数转化为每秒几十次的在线服务,决定体验能不能成立。三个词放在一起说叫“全流程”,但它们的优化目标几乎不共享。训练追求吞吐量,微调追求参数效率和方向校准,推理追求延迟和显存利用。

这篇文章面向两种人:一种是从算法转向工程的开发者,手里拿过开源模型,想系统理解框架怎么选、资源怎么算;另一种是已经开始用大模型做产品,但总觉得训练、微调、部署之间缺少一条方法论主线。我尽力把底层逻辑和工程落地的关键决策讲清楚,也会给可以直接抄作业的配置和命令。

2. 训练阶段的本质:把模型拆开塞进显存,然后让所有 GPU 假装成一个整体

2.1 为什么单卡跑 7B 全参训练这么尴尬

先做一个显存估算。一个 7B 参数规模的模型,如果只用 FP16 做前向和反向,权重本身就要 14GB,这个数看起来不大,但训练不等于只存权重。常用 AdamW 优化器配混合精度时,没有做任何显存优化的情况下,每个参数至少要占 16 到 20 字节:FP16 权重占 2 字节、FP32 的 master weight 占 4 字节、Adam 的 momentum 和 variance 分别占 4 字节、梯度再占 2 到 4 字节。按一个参数 16 字节粗略算,7B 模型光“参数、梯度、优化器状态”就要 112GB 上下,这还没算中间激活和临时缓冲区。

所以你会看到一个很常见的现象:拿 A100 80G 跑 7B 全参微调,只要序列长度稍微拉长、batch 稍微调大,显存就直接爆掉。市面上大多数训练框架,核心工作其实就两件事:一是把这块巨大的状态切成很多份,二是设计高效的通信方案让切出去的碎片在逻辑上仍然保持一致。如果你对显存和并行没有一个基本概念,拿到 DeepSpeed、Megatron、FSDP 这些框架时会很难受,因为它们解决的正是同一个问题的不同切法。

2.2 并行方案那么多,到底在切什么

要理解并行,先要分清模型训练时哪些东西可以切。最常见的是数据并行:每个 GPU 放一份完整的模型副本,各自处理不同的 batch,训练完同步梯度。一个 7B 模型用 16 张卡做 DDP,显存需求仍然是单份全参训练的大小,只是吞吐量上去了,所以它省不了显存。

ZeRO 把 DDP 里的冗余给去掉了。Stage 1 只切分优化器状态,Stage 2 在 Stage 1 基础上连梯度也开始切分,Stage 3 更进一步,把模型参数本身也分片存储。我用得最多的是 Stage 2 和 Stage 3。Stage 2 适合单机多卡、模型放得下一份完整参数的场景,通信开销可控;Stage 3 适合模型大到单卡完全塞不下的场景,但每个 Transformer 层的前向反向都要做 all-gather 和 reduce-scatter,通信压力明显更大,多机场景尤其明显。

张量并行和流水线并行是另一条路。张量并行把 Transformer 一层的权重按 row 或 column 切到多卡,比如 QKV 矩阵让不同 GPU 各算一部分,适合单机内 NVLink 带宽高的环境,因为每算一个小步骤都要同步。流水线并行则是按层切,GPU 0 算第 1 到第 8 层,GPU 1 算第 9 到第 16 层,数据是一段一段往下“流”的,通信频率低一些,但会留下流水线气泡。理解这点你想选型时就不会被名词绕晕:数据并行是各算各的然后同步梯度,张量并行是把一次计算拆开一起做,流水线并行是把计算切到不同阶段轮流做,ZeRO 是把状态分散到各卡之后仍然“拼成”一个完整模型。

2.3 DeepSpeed、Megatron、FSDP 分别适合什么场景

这里我给一个非常主观但实用的对比。DeepSpeed 的核心优势是 ZeRO 和 offload,对 PyTorch 生态的改动小,命令行接入方式成熟,绝大多数想用十几张卡跑微调或继续预训练的团队,第一选择就是它。Megatron-LM 擅长 3D 并行,把数据并行、张量并行、流水线并行组合到一起,适合从头预训练或超大模型场景,但代码改动成本也更高。PyTorch 原生 FSDP 和 DeepSpeed Stage 3 思路接近,好处是不引入额外框架,版本兼容风险小,PyTorch 2.x 项目里我用得更顺。

框架主要切分思路上手成本最合适的场景
DeepSpeedZeRO-1/2/3 + offload中低中大规模微调、继续预训练
Megatron-LM张量并行 + 流水线并行 + 数据并行百亿以上模型预训练、NVLink 高带宽集群
PyTorch FSDP参数/梯度/优化器状态分片想少引依赖,直接用 PyTorch 原生生态

我的个人建议是:你连分布式训练都是刚上手,我建议先别碰 Megatron。用 DeepSpeed Stage 2 就能覆盖大多数 7B 到 13B 的微调场景;实在要预训练超大模型,自然会遇到瓶颈,那时候再切 Megatron 也不迟。不要一开始就把自己的系统复杂度拉满,通信 bug 排查起来比模型算法问题痛苦得多。

2.4 一份能直接跑的 DeepSpeed 配置长什么样

以 7B 模型全参微调或者继续预训练为例,我用过一个比较稳的 DeepSpeed 配置:

deepspeed --num_gpus=8 train.py \ --model_name_or_path Qwen/Qwen2.5-7B \ --deepspeed ds_config.json

ds_config.json关键字段大概是这样:

{ "train_batch_size": "auto", "train_micro_batch_size_per_gpu": 2, "gradient_accumulation_steps": "auto", "zero_optimization": { "stage": 2, "offload_optimizer": { "device": "cpu", "pin_memory": true } }, "optimizer": { "type": "AdamW", "params": { "lr": "auto", "betas": "auto", "eps": "auto", "weight_decay": "auto" } }, "scheduler": { "type": "WarmupCosineLR", "params": { "warmup_min_lr": "auto", "warmup_max_lr": "auto", "warmup_num_steps": "auto" } } }

几个字段慢慢解释。train_batch_size是整个任务期待达到的全局 batch,train_micro_batch_size_per_gpu是每张卡前向反向一次的真实 batch size,两者之间由gradient_accumulation_steps连接。举个例子,如果全局 batch 是 64,8 张卡,每卡 micro batch 是 2,那一次梯度更新前需要累积 64 ÷ (8 × 2) = 4 步。用"auto"后框架会自动帮你推,但你必须保证传入的训练脚本里的 per_device_train_batch_size 和配置里能对应上。

offload optimizer 到 CPU 是我在显存紧张时很喜欢开的选项。它的代价是 CPU 内存换了一些训练速度,但数据并行下配合 gradient accumulation,训练吞吐下降通常能接受。如果连 Stage 2 都塞不下,再考虑 Stage 3 加参数 offload。我开始分布式训练时踩过的最大坑是学习率没有重新调:原来单卡 batch 8 用 2e-5,换到 8 卡 global batch 32 后还保持 2e-5,结果 loss 飞得很高。线性缩放学习率有一些实际争议,但保守做法是当 global batch 明显变大时,先把学习率略微下调,再用一小段训练做验证。

3. 微调是“定向培养”,不是把整个语言模型推倒重来

3.1 先判断:这个问题到底该用提示词、RAG,还是微调

很多人一上来就说“我要微调模型把业务做掉”。我得泼一盆冷水:很多 NLP 场景根本不需要微调,用提示词工程加检索增强就够了。什么时候该微调?我给你一个判断顺序。

如果你的任务把 prompt 调好就能做到七八成,那就先别动模型。比如翻译、改写、通用问答,这些能力大模型已经很熟了。如果你的任务需要依赖私有知识、实时数据,或者用户问题是开放式的,那先考虑检索增强生成(把文档切片、向量化、召回后拼进 prompt)。检索增强的主要好处是不改权重,资料更新只需要换库,出现错误也不用重新训练。只有当任务存在稳定的输入输出格式、特定领域术语、非常独特的行为偏好,并且通过提示词很难稳定约束时,微调才真正值得做。一个典型例子:售后工单的总结必须输出固定字段,还要区分退款原因、情绪倾向、责任方,这时候微调比写一堆 prompt 规则可靠得多。

我的经验是:微调前的数据准备至少要占整个项目的一半时间。你说你要做“领域指令微调”,但数据格式是否统一、坏样本是否清理过、指令是否覆盖真实场景,这些决定最终效果的下限。框架只是把数据送进模型的手段,数据质量差,再牛的框架也救不回来。

3.2 全量微调、Freeze 微调和 LoRA,到底怎么选

全量微调会更新模型所有参数,适合目标数据分布和原始分布有明显差异的情况,或者你要把模型重新培养成一个完全不同风格的助手。代价是显存和算力门槛都很高,7B 全参微调用我之前算过的账,单卡基本跑不动。

Freeze 微调的意思是冻结大部分层,只训练最后几层或者某些模块。这个策略的优点是在不损失太多效果的前提下显著减少可训练参数,但缺点是你需要提前判断到底该解冻哪些层,这个判断经常不准。早期很多人微调 Bert 时习惯只调顶层分类头,但对生成式大模型而言,“表达风格”并不只在最后一层,这种行为调整往往很浅。

LoRA 是目前我默认的选择。它不是冻结整个模型,而是在线性层旁边添加一个小型低秩旁路,只训练旁路参数。它最大的优势是:无需为 7B 的每个参数保存优化器状态,训练参数的量级可能只有原来的百分之一甚至更少,显存压力大幅下降。我的基本策略是:任务陌生、数据量大于几万条、且要求效果极限优先,考虑全量微调;如果只是想改变输出风格、套用一个领域术语体系、或者让模型学会特定 JSON 格式,LoRA 通常是性价比最高且效果最稳的选择。

方案可训练参数7B 单卡显存压力稳定风险点推荐场景
全量微调全部很高,普遍需要多卡并行数据噪声会被完整记住,过拟合风险大大规模领域继续预训练、数据充分、算力充足
Freeze 微调部分层解冻层选择依赖经验,容易欠拟合或过拟合少量参数适配浅层格式
LoRA/QLoRA低秩旁路低到中rank 设置不合理可能欠拟合大部分业务定制、指令微调、格式学习

3.3 LoRA 的“低秩”到底是什么意思

LoRA 的核心思想可以用一句话解释:微调阶段的大模型并不需要那么高的参数量去改变行为,权重更新矩阵可以近似成一个很低秩的矩阵。假设某个线性层的权重是 W0,维度是 d × d,LoRA 会冻结 W0,同时引入两个小矩阵 A 和 B,让新增的更新量等于 B × A,其中 A 的维度是 r × d,B 的维度是 d × r,r 往往只有 8、16、32。这样实际参与更新的参数从 d×d 降到了 2×d×r。当 d 是 4096,r 是 16 时,可训练参数量直接缩小了三个数量级以上。

为什么这么小的参数改动可以不明显损害模型能力?因为微调的理想状态不是彻底改变预训练学到的语言能力,而是在原有语义空间里往目标任务方向“拨动”一个子空间方向。底层语义大多数时候是共享的,真正需要的变化没那么复杂。LoRA 论文特别强调一个实际工程价值:因为 W0 被冻结,你可以同时维护多个任务对应的低秩旁路,推理时选不同旁路合并,而不是每个任务保存一个完整副本。

在 LlamaFactory 这类工具里,你只需要设置lora_rank=16lora_alpha=32即可。lora_alpha可以理解为对低秩更新施加的缩放系数,常见的经验是 alpha 取 rank 的 2 倍左右。不是 rank 越大越好,我在小数据集上经常发现 r=64 的 LoRA 反而比 r=16 更容易过拟合。因为低秩更新本身是一种正则化,设置过大的秩相当于削弱了这个正则效应。

3.4 实操:用 LlamaFactory 微调 Qwen2.5-7B 的思路

很多团队不用从零写训练循环,LlamaFactory 已经封装得比较完善。它把 SFT、LoRA、DPO 这些常见流程都预置好了,还支持 Web UI 和命令行。我的习惯是在命令行里操作,这样便于版本管理和重复执行。

数据准备阶段,LlamaFactory 会把数据集配置放在data/dataset_info.json里,指向一份 JSON 文件。以指令微调样本为例,理想格式是:

[ { "instruction": "请把下面的售后记录转成结构化工单:原因、责任方、建议动作。", "input": "用户反映收到货后外包装破损,产品屏幕碎裂,要求换货。", "output": "原因:物流运输导致外包装破损;责任方:物流方;建议动作:补发一台新机,并联系物流赔付。" } ]

然后执行 SFT 的命令大致是:

llamafactory-cli train \ --model_name_or_path Qwen/Qwen2.5-7B \ --stage sft \ --finetuning_type lora \ --dataset my_scene_data \ --template qwen \ --lora_rank 16 \ --lora_alpha 32 \ --output_dir output/my_lora \ --num_train_epochs 3 \ --learning_rate 1e-4 \ --per_device_train_batch_size 2 \ --gradient_accumulation_steps 8 \ --max_length 2048 \ --lr_scheduler_type cosine \ --warmup_ratio 0.1

这套参数里的细节很多。per_device_train_batch_size=2配合gradient_accumulation_steps=8,意味着更新一次梯度实际看了 16 条样本,这个量对 7B 来说非常常见。learning_rate=1e-4是 LoRA 类训练中常见的范围,如果做全量微调通常要降到 1e-5 甚至更低。max_length设为 2048 意味着超过长度的样本会被截断,如果你的业务文档普遍偏长,这里要谨慎调,因为长度越长显存和训练时间越长,不是所有任务都需要完整保留长上下文。

微调完成后,要进行 LoRA 权重合并。在 LlamaFactory 里有对应命令,本质是把 W0 + scale × B × A 算回去并保存成完整权重。这一步千万不能省,尤其当你把模型交给推理框架加载时,如果推理框架不支持 PEFT adapter,直接加载原始权重加 LoRA 权重文件很可能会得到错误输出。

3.5 微调训练的几个“危险信号”和排查方法

我最常看到的失败模式是 loss 一路下降,看起来训练很顺利,但真实业务测试一塌糊涂。出现这种问题,大概率是训练时数据分布和推理时任务分布不一致,或者模型只是背下了训练集里的格式,而不是真正学会了任务推理。

排查时不要只盯训练 loss。我会把训练集拿去随机抽 10 条看模型输出,如果连训练集都复述不出正确内容,说明欠拟合,考虑调大 epoch、调低学习率、检查数据截断;如果训练集输出完美但测试集输出崩坏,第一反应是过拟合,优先减少 epoch、增强数据多样性、降低 LoRA rank。还要警惕“格式记忆”问题:模型学会了输出 JSON 外壳,但里面的字段靠模仿训练集模板而不是真正理解,这种情况通常是因为训练样本的同质化太严重,需要加入更多反例和边界样本以及多样化的指令表达。

4. 推理框架的账本:显存复用、吞吐优化和量化带来的边界

4.1 推理的性能逻辑为什么和训练不一样

很多从训练切到推理的工程师一开始会不适应。训练阶段你可以接受一次前向反向跑几秒甚至几分钟,因为目标是最大化单位时间能处理的 token 数;推理阶段每一次请求都对应真实用户的等待,首 token 延迟、单 token 生成速度、吞吐上限都会直接影响产品体验。还有一个本质差异:训练时大量矩阵乘法能充分利用 GPU 算力;推理时,模型权重是固定的,一个请求从 prompt 输入到逐个 token 输出,每一步都需要扫一遍权重加计算注意力,但实际新增的计算量很小,所以推理很多时候是“访存密集”而不是“计算密集”。模型权重在显存里的读取带宽往往比 GPU 核心的 FLOPS 更早触顶。

这就解释了为什么同一个 GPU,训练时跑得挺快,推理时却经常发现 GPU 利用率不高。很多新手看到 GPU 利用率只有 30% 就怀疑服务有问题,其实对自回归推理来说,低利用率不一定异常,你需要关注的是并发压力和显存复用是否到位。推理框架解决的核心问题,就是如何在多用户并发下尽量复用已经读进显存的数据、减少空闲等待、提高整体吞吐量。

4.2 KV Cache 和 PagedAttention,模型每一次生成都要掂量这笔账

大模型推理的每个请求都会维护一个不断增长的键值缓存,叫 KV Cache。每生成一个新 token,都要把此前所有 token 的 Key 和 Value 记录缓存下来,避免重复计算,但这也导致显存占用随序列长度和并发数线性增长。我按 Qwen2.5-7B 的参数感性地算一笔账:它有 28 层,4 个 KV 头,每个头的维度是 128,FP16 精度下每个 token 的 KV Cache 大约要 56KB。如果并发 32 个请求、每个请求的上下文长度 4096,那光 KV Cache 就要占 7GB 左右,这还不算显存里还要放权重和激活。

所以推理框架早就不像我们想的那样,只负责把模型加载进去然后 forward 一次。vLLM 这样的框架提出了 PagedAttention 来管理 KV Cache,思路非常像操作系统对物理内存的分页管理:把 KV Cache 切分成固定大小的块,按需分配,减少显存碎片,同时让多个请求可以共享相同前缀的 KV Cache。这部分设计对长上下文和 high 并发场景的提升是决定性的。OpenAI 兼容接口普及之后,vLLM 很容易接入现有链路,本地起的服务看起来就是一个标准的chat/completions接口。

在显存紧张时,我的第一反应不是换小模型,而是先检查 KV Cache 的分配策略。模型能支持的并发数不完全由参数量决定,还跟 max-model-len、每请求平均长度、量化方式有关。调低max-model-len能够直接释放 KV Cache 空间,当产品场景本身不需要 32K 上下文时,一个 8K 上限通常可以换到更多并发能力。

4.3 量化不是免费午餐,要看你的下游任务吃不吃得下

推理阶段量化几乎成了标配。GPTQ 和 AWQ 都是在离线阶段把 FP16 权重压缩到 INT4 或 INT8,然后推理框架加载压缩后的权重。AWQ 的做法是根据激活值统计找到对模型输出更重要的权重通道,在量化时给予更多保护,因此在小模型和敏感任务上表现往往更稳。GGUF 则是从 llama.cpp 生态流行起来的格式,Ollama 这类工具大量使用它,好处是可以灵活选不同量化等级,CPU 和消费级 GPU 都能跑。

这里必须强调一个容易被忽视的事实:量化损失不是均匀分布的。如果你只是做通用对话,模型说不定能承受一定的量化损失;但当任务涉及代码生成、严格 JSON 输出、函数调用或者抽取实体边界时,4bit 量化经常会出现字段缺失、括号配平错误、甚至凭空多出一个参数的问题。我自己做过的项目中,用 AWQ 4bit 跑普通客服问答没什么问题,但跑工单结构化抽取时,错误率明显比 FP16 高。所以推理方案的确定千万别只在几个样例上判断行不行,最好准备一组带严格比对的测试集,把 FP16 和不同量化等级放在同一批请求上跑对比。

4.4 vLLM、TGI、SGLang、Ollama、llama.cpp,选型参照

现在市面上的推理框架非常多,我列出使用场景上差异最明显的几个。vLLM 是目前在线服务场景最主流的选择,吞吐量优化好,OpenAI 兼容接口完善,社区资料多,遇到问题基本能搜到答案。Hugging Face 的 TGI 也很成熟,与 Transformers 生态衔接天然,但实际表现上 vLLM 的优化更激进。SGLang 引入了 RadixAttention,对共享前缀的 prompt 缓存特别有利,如果你们的请求里有很多相同系统提示词或者固定前缀,可以重点试它。Ollama 和 llama.cpp 更适合本地开发、个人电脑、小并发场景,它们强在安装简单、量化灵活,不适合做高并发在线服务。

TensorRT-LLM 是 NVIDIA 系的深度优化方案,能做图融合、运行时优化、动态 shape 等,适合极致的线上吞吐要求,但开发和维护成本也高,通常用于搜推广这类流量极大的业务。目标检测、图像类任务里会看到很多 TensorRT engine 的概念,和本文说的 LLM 推理框架不是一回事,类似yolo engine常指的是 TensorRT 序列化后的加速模型,千万别混用。

框架所属生态并发优化最典型使用场景
vLLM开源社区连续批处理 + PagedAttention高并发服务、OpenAI 兼容 API
TGIHugging Face连续批处理Transformers 生态快速部署
SGLang开源社区RadixAttention、结构化输出优化大量共享前缀、Agent 场景
TensorRT-LLMNVIDIA图优化、kernel 级优化极致吞吐、生产环境高优化需求
Ollama / llama.cpp本地生态简单并发控制开发调试、离线单机运行

我用 vLLM 启动在线服务的命令大致是:

vllm serve Qwen/Qwen2.5-7B-Instruct \ --tensor-parallel-size 1 \ --gpu-memory-utilization 0.85 \ --max-model-len 8192 \ --max-num-seqs 32 \ --dtype auto

gpu-memory-utilization 0.85表示让 vLLM 尽量使用 85% 的显存作为 KV Cache 和运行缓冲,剩下 15% 留给一些动态显存需求。max-num-seqs控制并发请求上限,调得太高容易导致排队或者 OOM,调得太低又无法压满吞吐。比较务实的做法是先按最大并发 16 到 32 起,压测后看首 token 延迟和 P99 延迟,再上下调整。

4.5 吞吐、时延和显存真的能同时兼顾吗

推理优化的核心其实是在三个维度上找平衡:你想服务更多并发请求,就可能让每个请求排队更长,TTFT(首 token 时间)上升;你想降低单个请求延迟,就要给更多并发资源,可能降低吞吐;你想提升并发上限,KV Cache 就要吃更多显存。不可能三角的味道弥漫在每一步决策里。

所以接受现实:不是所有场景都需要最低延迟。离线批处理任务更看重吞吐,可以让 batch 尽量大、最大并发尽量高;在线客服场景更看重 P95/P99 延迟,需要限制并发请求数来保护单请求体验。没有一套参数适合所有服务,部署上线前最好根据自己真实流量做一次压测,用脚本模拟不同并发和提示词长度,记录 TTFT、单 token 生成速度和错误率。相比训练阶段可以慢慢调,推理阶段每一次修改都能很快用压测验证,值得多花时间。

5. 端到端闭环里那些容易被忽略的“脏活累活”

5.1 从实际业务问题到微调数据的完整路线

我建议把项目切成六个环节:业务问题定义、数据准备、实验管理、模型微调、服务部署、线上观测。大多数人会把精力放在训练和部署上,但我越来越相信数据准备和业务问题定义才是决定成败的地方。

先明确你希望模型在什么输入下做出什么输出。比如客服文本分类任务,你要收集真实会话记录,把这些记录拆成“问题描述”和“期望回复”的对子。数据不是越多越好:质量差的大规模数据可能反而让模型学到错误模式,几千条精心挑选的样本往往效果会比十万条冗余样本更好。清洗时我会做几件事:去掉重复、去掉明显的 OCR 乱码、去掉格式极端不合理的数据、把答案里直接复制原文的情况标记出来,否则模型会学成“抄写员”。

实验管理也值得从一开始就做。每个版本至少要在训练目录里保存:数据集版本、数据文件 hash、微调参数、训练日志、验证集表现。我有一次微调效果回退,排查半天发现是同事改了训练集里一个字段的映射方式,但没人记录。后来我强行把版本信息写进 checkpoint 文件名,比如qwen7b_lora_v3_data20240601_r16_ep3,再也不靠记忆追溯了。

5.2 模型微调完,上线前的评估不能只看 loss

评估分两层:一层是通用能力回归,另一层是业务场景回归。通用能力可以选开源评测集,看模型有没有“变笨”;业务场景回归必须自己构造。我的做法是从真实用户问题里挑出 30 到 100 条作为冒烟集,每条给出预期行为描述,比如“必须识别收货地址”“不得把退款原因归结为用户”“输出必须是合法 JSON”。每次训练完,用固定 Prompt 和固定温度让模型跑一遍这批数据,人工或脚本跑分。

一个很容易被忽略的细节是温度参数对评估的影响。评估时如果把 temperature 调得过高,同一个输入可能输出差异很大,难以对比不同 checkpoint 的稳定表现。我一般用 temperature=0 或接近 0 来测确定性任务,用 temperature 偏高来测创造性任务。上线前还要重点做一次“拒绝类样本”验证,看看模型会不会在它不该回答的问题上也强答,比如不在业务范围内的闲聊,这时你希望它明确说不知道,而不是编一个答案。

5.3 部署之后还有几条容易踩的路

第一,Transformer 的模板一致性。微调阶段用的 chat template 和推理阶段必须完全一致,如果你训练时用了 Qwen 模板,上线时却直接拼了一句 “System: …” 而没有走 tokenizer 的 chat template,模型很可能会出现极其别扭的格式。解决方法是:训练和推理都统一走模版函数,别自己在外面拼。

第二,LoRA 合并后不能把测试建立在 adapter 加载这种临时方案上。我之前偷懒在推理脚本里动态挂 LoRA adapter 做测试,模型输出看着没问题,后来换到正式的合并权重方案,结果出现了明显差异。原因可能是注意力层排序或者缩放倍数实现不完全一致,所以请在上线前以合并完的完整权重路径做一次端到端回归。

第三,用 OpenAI 兼容客户端接入时,base URL、model name 和 API key 看起来简单,但很容易配错。vLLM 启动时--served-model-name决定客户端请求里 model 字段,如果默认的模型名和客户端配置不一致,服务端会返回 model not found。这个小问题在框架升级后尤其容易出现。

第四,服务的超时和重试策略要设计。大模型推理天然比普通接口慢,一个长请求可能超过 30 秒,网关层的 read timeout 如果设得比模型最大生成时间还短,用户就会莫名收到超时错误。我处理过的线上故障里,有很大比例不是模型变笨了,而是 batch 内的慢请求拖垮了 P99,然后网关误报为模型异常。

5.4 我现在的固定操作习惯,给后来者少走弯路

做了几年大模型项目,我的固定心法是:默认先上 LoRA,而不是全参;默认先收集 100 条真实业务样本做冒烟集,而不是急着把训练集扩到几十万;默认评估脚本和训练脚本一起提交,而不是评估代码单独放在某个人的笔记本里;推理阶段优先用 vLLM 的 OpenAI 兼容接口,能少写一层 Web 封装。如果产品需求还在频繁变化,就别过早做全量微调和量化优化,先用 prompt 工程和 RAG 扛住变化,等到交互形态稳定下来再固化到模型权重里。

从训练、微调到推理,每个环节都存在可以深挖的优化空间,但优秀工程实践一定是在正确优先级上分配有限资源。需要先跑通最简单闭环,随后你自然会遇到该优化的地方。个人觉得最值得做的提前量

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/5 6:56:37

Omakase源码尽调:DHH的开箱即用式Arch开发环境

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/5 6:54:39

解说类视频选什么配音?六个细分方向的选声逻辑

做解说类内容的人,大多经历过这个阶段:文案改了七八版,画面剪得也用心,发出去数据就是起不来。回头一听才发现,问题出在声音上——不是声音不好听,是声音和内容气质没对上。同样是"解说"两个字&a…

作者头像 李华
网站建设 2026/9/5 6:54:32

基于STM32的充电桩环境监测系统设计与仿真实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/5 6:52:40

Vue3+Uniapp全栈电商实战:从开源芋道商城到多端部署与二次开发

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/5 6:51:45

RISC-V、ARM、x86中断流程对比:从控制器到返回指令的体系结构差异

1. 为什么我把中断流程当作理解三种指令集的钥匙 先交代一下这篇文章的来头。我最近在几块不同架构的开发板上做同一个内部分发逻辑的移植,x86 上跑得好好的代码,挪到 RISC-V 和 ARM 上就频繁丢中断,甚至死锁。排查到最后,问题全部…

作者头像 李华
网站建设 2026/9/5 6:50:09

计算机毕业设计之基于JavaWeb的中医养生知识 科普平台的设计与实现

随着新世纪无纸化办公方式的普及,自动化信息处理和基于网络的信息交互方式已被广泛应用。现在很多行业基本上都是交由计算机进行管理和测试,网络与计算机已成为整个线上管理体系中的重要组成部分。虽然信息技术广泛应用和数据存取更加方便,但…

作者头像 李华