简介:面向希望系统掌握大模型学习方法与搭建路径的AI学习者,这份整合型知识文档梳理了从神经网络基础、反向传播、损失函数等核心理论,到Transformer、BERT、GPT系列论文研读思路的完整学习框架。内容同时覆盖数据预处理、模型架构选择、超参数调优、训练监控、评估部署等关键实操环节,并摘录知乎与CSDN社区的学习笔记,便于快速理解高频难点与常见排错方法。包体为1个docx文件,大小约11KB,以文字讲解和步骤梳理为主,可在电脑或手机上快速浏览与标记,适合作为自学提纲。目前已有878人浏览学习,尤其适合刚入门大模型、希望从理论走向实践或正在准备搭建自己模型的开发者。文档虽精简,但知识点密度较高,能帮助读者少走弯路,尽快形成从论文阅读到动手实践的完整闭环。
1. 学了一堆“AI大模型”教程,为什么一到搭建自己的AI大模型就卡住
不少工程师的路径是这样:看了一堆大模型科普,调 API 写 prompt 得心应手,可真到“搭建自己的 AI大模型”这一步,卡在黑匣子里——权重文件去哪下、量化参数怎么填、推理服务怎么起、前端拿到流式数据怎么渲染,每一步都像无头苍蝇。这事的难点不在于“大模型”三个字唬人,而在于你被夹在学习路线和落地工程之间:原理没串起来,部署又没跑通。
这篇文章按我自己的实操顺序来写:先把必须懂的理论点打通,再给出从下载权重到跑通 API 的完整命令,然后处理交互层最常见的 SSE 流式渲染,最后把踩过的坑和验证模型的方法一并交代清楚。适合打算做私有化部署、领域微调或正在设计大模型应用后端的开发者,也适合刚入算法岗、想脱离调包侠身份的新手。
2. 先立理论:大模型在学什么,以及怎么把“哪家最接近真实”变成可测的选型
这一章解决的是学习方法里最容易被跳过的一步:不看论文硬部署,出问题只能靠玄学调参。
2.1 从注意力机制到 RLHF:五个必须啃下的概念
大模型的原理不需要你手推矩阵,但下面五个概念直接决定你后面能不能看懂日志、能不能解释模型行为,一个都不能绕过。
第一个是注意力机制。它解决的核心问题是“一段文本里,哪些词和当前词相关”。Transformer 里用的是自注意力,每个 token 同时是查询、键和值,通过点积算出它跟序列里其他 token 的关联权重。你部署模型时看到的context_length、max_model_len,本质上就是在限制注意力计算能覆盖的 token 范围。模型回答得文不对题,经常不是模型笨,而是你的上下文超出了它能看到的范围。
第二个是预训练与指令微调的差别。基座模型(base model)只学过“接着往下写”,你问它“1+1 等于几”,它可能回一句“1+1 是等于二吗?”这种统计式的续写。指令微调(SFT)则是拿“问题—标准答案”的对话数据,教模型学会“被提问时应该给结论”。所以你在 Hugging Face 上下载权重时会看到-instruct、-chat后缀,选错了就等于拿一台没受过对话训练的引擎做客服,效果当然不行。
第三个是 RLHF 与对齐。指令微调之后,模型已经会“回答”,但回答的价值观、语气、安全边界还需要人类反馈来校准。RLHF 让模型学会“哪些话不能说、哪些回答更讨喜”。对齐做得好不好,直接体现为模型拒绝不合理的请求时是干脆利落还是硬编理由。
第四个是上下文窗口。它不是越大越好。窗口越大,KV Cache 占的显存越高,并发能力掉得越快。很多人在 24GB 显卡上部署 7B 模型,把max_model_len拉到 32K,结果两个并发请求直接 OOM,就是这个原因。上下文窗口的正确用法是“按需设定”:你的业务最长输入是 3000 token,设 8192 就够,不要盲目翻倍。
第五个是幻觉。大模型的本质是“最大概率生成下一个 token”,不是“查数据库”。它没有事实校验能力,训练数据里没有的东西,它可以一本正经编出来。这决定了你在架构上千万别把模型当唯一事实源:关键业务数据要么通过 RAG 检索喂进上下文,要么在应用层做输出校验。
概念啃到这,再往下读论文就有抓手了。建议的阅读顺序是:Attention Is All You Need → InstructGPT 论文 → RLHF 相关综述,每篇不需要逐行推公式,先搞懂“它解决了什么问题、用什么数据、效果怎么评估”三个点就够。
2.2 选型不是追新:按显存、并发和场景定参数量
很多人一开口就问“哪个模型效果最好”,真正该先问的是“你的显卡有几张、显存多大、并发多少、跑什么任务”。选型错误导致的翻车,比参数调错的概率高得多。
先看一张我常用的选型表:
| 模型规模 | 典型显存需求(FP16) | 适合场景 | 不适合场景 |
|---|---|---|---|
| 1B~3B | 4GB~8GB | 摘要、改写、分类、跑在边缘设备 | 复杂推理、长文本分析 |
| 7B~9B | 14GB~18GB | 私有化客服、RAG 问答、代码补全 | 高并发大规模在线服务 |
| 13B~14B | 28GB~36GB | 推理要求较高的业务,需要量化 | 单卡 24GB 以下勉强跑 |
| 32B~72B | 64GB~144GB | 接近商业模型效果的私有部署 | 成本敏感、启动时间敏感的团队 |
这张表的显存是按 FP16 算的,实际你在部署时可以用 INT8 或 INT4 量化,7B 模型能压到 6GB 左右,代价是 1%~3% 的质量损失。多数中小企业私有化部署,7B~9B 是性价比最稳的一档;如果团队有 A100/A800 这类卡,再考虑 32B 以上。
这里有个判断“哪家最接近真实”的方法,不用听测评博主,自己 30 分钟就能测:把你要跑的业务真实问题整理成五十条,比如写科研论文场景,就准备“按照 IEEE 格式改这个参考文献”“这段讨论部分哪里逻辑跳跃”这类问题,然后让模型答,按“格式正确率、术语准确率、幻觉次数”三个维度打分。榜单分数高不等于你的场景好用,真实数据测完才知道。
选型还有一个容易被忽略的点:参数量相同,tokenizer 不同,实际中文效果差距很大。判断方法是看词表大小和中文覆盖度,7B 级别中文表现好的模型通常词表在 15 万以上。部署前多花十分钟看模型的 tokenizer 配置,比上线后再抱怨“答得不像人话”省事得多。
理论立住了,下一章开始动手。
3. 搭建自己的AI大模型:从下载权重到跑通接口的三步走
搭建自己的 AI大模型,常见做法不是从零预训练,而是拿开源权重在自己环境里跑推理,再做领域微调。从零预训练一个 7B 模型需要上万张卡时,绝大多数团队没有这个必要。
3.1 先用 Ollama 跑通最小推理,五分钟拿到第一个 token
入门阶段别一上来就上高并发框架。Ollama 把权重下载、量化、推理封装成了极简命令,适合先验证“模型能不能在你的环境里跑起来”。
# 1. 安装后拉取一个 7B 指令模型 ollama pull qwen2.5:7b # 2. 启动服务(前台运行,另开终端做下一步) ollama serve # 3. 调本地接口,非流式请求 curl http://localhost:11434/api/generate \ -H "Content-Type: application/json" \ -d '{"model": "qwen2.5:7b", "prompt": "用一句话解释什么是KV Cache", "stream": false}'这里stream: false表示等模型生成完整个回答再返回,适合验证连通性,不适合真实应用。prompt就是你要问的问题,简单粗暴,但它走的是 generate 接口,没有多轮对话结构。
Ollama 的价值是帮你把环境依赖全屏蔽了,驱动不对、CUDA 版本不对这些麻烦事它都替你处理。但它的局限也很明显:并发能力弱,每个模型默认加载一份显存,吞吐上不去,而且api/generate的返回格式偏私有。结论很明确:Ollama 只配做验证工具,别拿它上生产。你用它确认模型能跑、回答质量能接受之后,就该换下一节的方式。
3.2 换成 vLLM:兼容 OpenAI 接口的生产级推理服务
生产环境我一般直接上 vLLM。它用 PagedAttention 管理 KV Cache,显存利用率比普通推理高很多,而且自带v1/chat/completions接口,意味着你后端可以无痛套 OpenAI 的请求格式。
# 用 vLLM 启动 OpenAI 兼容服务 python -m vllm.entrypoints.openai.api_server \ --model /models/qwen2.5-7b-instruct \ --served-model-name my-qwen \ --tensor-parallel-size 1 \ --gpu-memory-utilization 0.92 \ --max-model-len 8192 \ --dtype bfloat16 \ --trust-remote-code参数含义,逐个说清楚:
--model指向权重路径,vLLM 会从这里加载配置和权重文件。--served-model-name是暴露给客户端看的模型名,后续所有请求的model字段填它,跟实际权重名解耦,方便以后换底座。--tensor-parallel-size是张量并行度,单卡填1,多卡按卡数递增,四卡 A100 填4。--gpu-memory-utilization设置显存可用上限,0.92表示给模型和 KV Cache 用 92%,留一点给 CUDA context,不要填1.0,否则容易在申请额外显存时直接报错。--max-model-len是最大上下文长度,设 8192 就覆盖多数业务,除非你的任务真的需要长文档分析,否则别拉高,它在显存里按最大长度预分配 KV Cache。--dtype bfloat16是精度设置,老显卡不支持 bf16 就换float16。
启动完成后,验证接口:
curl http://localhost:8000/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "my-qwen", "messages": [{"role": "user", "content": "写一段产品经理的工作描述"}], "stream": false }'返回的 JSON 结构和 OpenAI 格式完全一致,choices[0].message.content就是模型回答。到这里,你已经有了一个可以给业务调用的推理服务,但注意现在它还是个“通用底座”,回答风格和你没建立任何绑定关系,下一节的微调才是让它变成“你的模型”的关键步骤。
3.3 LoRA 微调:把通用底座调成你的领域模型
很多人对微调有误解,以为要存几十个 epoch 的 checkpoint、训几天。实际上现在做领域适配,LoRA 是成本最低、效果最稳的手段:冻结原模型参数,只训练额外的一小批低秩矩阵,单卡 24GB 就能训 7B 模型。
from datasets import load_dataset from transformers import AutoModelForCausalLM, AutoTokenizer, TrainingArguments from peft import LoraConfig, get_peft_model, TaskType import torch model_path = "/models/qwen2.5-7b-instruct" # 先加载 tokenizer,用来做 chat 模板格式化 tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True) if tokenizer.pad_token is None: tokenizer.pad_token = tokenizer.eos_token def format_example(ex): messages = [ {"role": "user", "content": ex["instruction"]}, {"role": "assistant", "content": ex["output"]}, ] return tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=False) # 数据格式:jsonl,每行 {"instruction": "...", "output": "..."} ds = load_dataset("json", data_files="train.jsonl") ds = ds.map(lambda x: {"text": format_example(x)}) # 加载基座,bfloat16 + 模型并行放到多卡 model = AutoModelForCausalLM.from_pretrained( model_path, torch_dtype=torch.bfloat16, device_map="auto", trust_remote_code=True, ) # LoRA 配置:7B 模型常用 rank 16 lora = LoraConfig( task_type=TaskType.CAUSAL_LM, r=16, lora_alpha=32, lora_dropout=0.05, target_modules=[ "q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj" ], ) model = get_peft_model(model, lora) model.print_trainable_parameters() # 应只训练约 1% 参数 train_args = TrainingArguments( output_dir="./lora_out", num_train_epochs=3, per_device_train_batch_size=2, gradient_accumulation_steps=8, learning_rate=2e-5, logging_steps=10, save_steps=200, save_total_limit=1, bf16=True, ) model.train() # 真实项目中用 Trainer 或 SFTTrainer 执行训练这段代码的逻辑链是:先定义数据怎么从instruction/output变成带 chat 模板的文本,再加载权重,再挂 LoRA。核心参数有三个:r是低秩矩阵的秩,越小参数量越少、拟合能力越弱,16 是 7B 模型的常见起点;lora_alpha是缩放系数,一般设成r的两倍;learning_rate对大模型微调来说 2e-5 是安全区,超过 1e-4 极容易训崩。
这套流程里最容易翻车的地方是数据格式不一致。训练用了apply_chat_template,推理时也必须走同样的模板,否则模型输入分布对不上,效果暴跌。另外TrainingArguments里bf16=True要求 Ampere 架构以上显卡,老显卡改成fp16=True但注意会导致数值精度略降。LoRA 训练结束得到的是一个小 adapter 文件,用merge_and_unload()合并回原模型再部署,或者直接与 vLLM 配合动态加载 LoRA。
4. 封装AI交互逻辑:SSE流式输出让回答实时渲染,前端记得配 abort
模型服务跑通之后,真正的工程问题才开始。你不可能让用户傻等十几秒看转圈,然后一次性弹出整段回答。现在绝大多数大模型应用都选择通过 SSE 流式输出实现回答实时渲染,配合 abort 可以做到“用户打断时立刻停止生成”,既省 token 又省算力。
4.1 为什么大模型接口默认要 SSE 而不是等完整 JSON
HTTP 长连接有几种方案:轮询、WebSocket、SSE。大模型场景基本选 SSE,原因很直白——它是单向的、基于 HTTP 的文本流,服务端可以持续往同一个响应里写内容,前端只需要用原生fetch就能读,不需要额外握手协议。
对比轮询:每隔一秒发一次“好了没”,不是不行,但服务端高频处理空请求,浪费资源,而且每次轮询都可能触发鉴权、日志,成本高。对比 WebSocket:它是全双工协议,功能更强,但你需要维护连接状态、心跳、断线重连,复杂度比 SSE 高一个量级。大模型回答是“服务端到客户端”的单向推送,SSE 恰好命中这个模型。
SSE 的数据格式很朴素,每行以data:开头,消息之间用空行分隔。OpenAI 兼容接口在流式模式下会不断输出这样的片段:
data: {"choices":[{"delta":{"content":"你"}}]} data: {"choices":[{"delta":{"content":"好"}}]} data: [DONE][DONE]是结束标志。前端拿到后逐段 append 到界面里,用户看到的就是一个字一个字往外蹦的效果。这背后还有个玄学:语言模型本来就是逐个 token 生成的,SSE 只是把生成过程和展示过程对齐了,所以“打字机效果”不是设计出来的,是生成机制的自然外显。
4.2 前端消费 SSE 流:fetch 加 ReadableStream 加 AbortController
前端不用装任何 SSE 库,用fetch就能处理。关键在于两点:把stream参数设为true,以及用ReadableStream手动解析数据帧。
async function chatStream(messages, onToken, signal) { const resp = await fetch('/v1/chat/completions', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ model: 'my-qwen', messages: messages, stream: true }), signal: signal // 传入 AbortController 的信号,用于中止请求 }); if (!resp.ok || !resp.body) { throw new Error(`HTTP ${resp.status}`); } const reader = resp.body.getReader(); const decoder = new TextDecoder('utf-8'); let buffer = ''; while (true) { const { done, value } = await reader.read(); if (done) break; buffer += decoder.decode(value, { stream: true }); // SSE 按 \n 分行,最后一段可能不完整,留在 buffer 里 const lines = buffer.split('\n'); buffer = lines.pop(); for (const line of lines) { const trimmed = line.trim(); if (!trimmed.startsWith('data:')) continue; const data = trimmed.slice(5).trim(); if (data === '[DONE]') return; try { const json = JSON.parse(data); const delta = json.choices?.[0]?.delta?.content ?? ''; if (delta) onToken(delta); // 每次只追加增量文本 } catch (e) { console.warn('SSE parse error:', e); } } } } // 用法:生成停止按钮时调用 const controller = new AbortController(); chatStream(messages, (token) => { renderArea.append(token); }, controller.signal); // 用户点击“停止生成”时: stopButton.onclick = () => controller.abort();这里有个关键细节:reader.read()返回的value是二进制块,必须用TextDecoder解码,而且解码要用{ stream: true },防止一个汉字被拆到两个 chunk 时出现乱码。buffer = lines.pop()的处理是因为 SSE 的数据行可能被 TCP 分包拆开,最后一行不换行,得留到下一轮拼完整再解析。
AbortController的作用比很多人想得更重要。用户点击停止按钮时,controller.abort()会在浏览器层面断开这个请求,但服务端不一定马上感知到。这就意味着你的后端也要做断连检测,否则用户已经走了,模型还在那里傻傻地生成剩余的两百个 token,白白烧算力。这就是热词里“配合 abort”的真正含义——它是前后端协同的,不是前端一个按钮的事。
4.3 后端透传:FastAPI 代理 vLLM,处理断连
如果前端直接连 vLLM 的 8000 端口,会遇到跨域、鉴权、限流问题,所以业务后端通常做一层代理。FastAPI 透传 SSE 的写法很成熟:
from fastapi import FastAPI, Request from fastapi.responses import StreamingResponse import httpx app = FastAPI() UPSTREAM = "http://127.0.0.1:8000/v1/chat/completions" @app.post("/v1/chat/completions") async def proxy_chat(request: Request): payload = await request.json() payload["stream"] = True # 强制穿透层走流式 async def event_stream(): async with httpx.AsyncClient(timeout=None) as client: async with client.stream("POST", UPSTREAM, json=payload) as resp: async for chunk in resp.aiter_bytes(): # 前端断开后立刻停止读取上游 if await request.is_disconnected(): break yield chunk return StreamingResponse( event_stream(), media_type="text/event-stream", headers={ "Cache-Control": "no-cache", "X-Accel-Buffering": "no", }, )注意X-Accel-Buffering: no这一句,很多人在 Nginx 后面部署时踩坑:Nginx 默认会对 SSE 响应做缓冲,攒满 4KB 才一次性下发,导致前端看到的不是逐字输出,而是卡一下弹出一大段,甚至超时断开。这一行 header 是告诉 Nginx“这个响应不需要缓冲,直接透传”。
request.is_disconnected()是配合前端 abort 的关键。它探测到客户端断开后,break跳出循环,主动关闭和 vLLM 的连接。vLLM 收到连接关闭后才会真正停止生成,从而避免白算。这里有个坑:FastAPI 判断断连有一定延迟,极端情况下读不到is_disconnected为真,更稳妥的做法是给上游请求加超时控制,或者定期用asyncio.wait_for包裹读取操作。
到这一步,从模型服务到前端渲染的整条链路已经通了:vLLM 生成 token → FastAPI 透传 SSE → 前端逐字渲染,用户随时可以打断。
5. 大模型落地避坑:从显存爆掉到流式断连的五条排查记录
这一章全是我实际遇到、且同事反复踩的坑,每一条都按现象、原因、解决三步写,方便你直接当成排障手册用。
5.1 首Token延迟高得离谱,先查 max_model_len
现象:模型响应不算慢,但从发请求到收到第一个 token 要等两秒以上,用户体验极差。
原因:max_model_len设置得太长,vLLM 要按最大长度预分配 KV Cache,显存被 Cache 占满后,实际可用的 batch 空间变小,请求排队时间变长。很多人明明只需要 4K 上下文,非设成 32K,结果首 token 延迟翻倍。
解决:按业务真实长度设max_model_len。先用日志统计线上 prompt 的 token 分布,P95 是 1500,就设 4096,留两倍余量。同时确认gpu_memory_utilization不要低于 0.85,否则 KV Cache 被卡得太死,并发上不去。
5.2 SSE 流半路中断,前端一直转圈:代理层缓冲是元凶
现象:本地直连 vLLM 一切正常,走 Nginx 后前端收到几个字就停住,刷新后才看到完整答案,或者直接超时。
原因:Nginx 默认开缓冲,SSE 的每个分片被攒住,前端迟迟拿不到内容;另外proxy_read_timeout默认 60 秒,长回答生成超过 60 秒,连接被 Nginx 掐断。
解决:给 location 配置关缓冲、加超时。下面是 Nginx 关键配置:
location /v1/chat/completions { proxy_pass http://127.0.0.1:8000; proxy_buffering off; proxy_cache off; proxy_read_timeout 300s; proxy_set_header Connection ""; proxy_http_version 1.1; }同时后端记得加X-Accel-Buffering: no,因为 Nginx 有指令级缓冲,也有按响应头判断的缓冲行为,两边都关才保险。
5.3 微调后模型变傻:灾难性遗忘不是玄学
现象:用几千条领域数据微调后,领域问题回答得很漂亮,但通用问答能力明显退化,甚至简单的“1+1”都开始胡说。
原因:学习率过高、训练轮数过多、数据分布过窄。LoRA 虽然只训少量参数,但学习率开到 1e-4 以上照样能破坏原模型分布;数据全是单一领域,模型把注意力全压到新分布上,旧知识被覆盖。
解决:把学习率降到 1e-5~2e-5 区间,轮数控制在 2~3 轮;混入 10%~20% 的通用对话数据,相当于给模型“复习旧知识”;训练完用第 2 章的 mini 评测集同时跑领域题和通用题,两边都过线才算成功。最稳妥的做法是保留一份微调前的权重,翻车了随时回滚,这是唯一的后悔药。
5.4 同一问题两次回答不一样:采样温度在作祟
现象:业务方反馈“同一个问题,用户两次问拿到不同答案”,客服场景尤其不能忍。
原因:模型推理默认带随机采样,temperature不为 0,每次从概率分布里抽 token,抽到谁有随机性。这是生成式模型的正常行为,不是 bug,但业务方不理解。
解决:对需要稳定输出的场景,把temperature调到0.2以下甚至0。temperature=0时模型走贪心解码,每次都选概率最高的 token,输出基本确定性。代价是回答多样性下降、略显呆板。我一般是:客服、法务等场景设 0;文案生成场景设 0.8;代码生成设 0.2。与其让前端反复重试,不如从参数上先堵住随机性。
5.5 模型推理卡顿、内存爆掉:量化与并发计划没做
现象:部署后单并发没问题,开十个并发直接 OOM,或者页面响应慢到不可用。
原因:显存估算只算了模型权重,没算 KV Cache 和激活值。7B 模型 FP16 权重约 14GB,但每个并发请求都会在显存里占一份 KV Cache,每 2K token 大约额外吃 1GB~2GB 显存(具体取决于层数),十并发轻松爆卡。
解决:上线前按公式粗算——总显存 = 权重占用 + 最大并发数 × 单请求 KV Cache 占用。打成一张表:7B 模型 24GB 单卡,FP16 下建议并发不超过 8;INT8 量化后权重省一半,并发可以翻倍。另外 vLLM 启动时加--max-num-seqs限制最大并发 batch,超过的请求排队,宁可慢一点,不能把服务打挂。
6. 验证模型值不值得上生产:30条评测集和三档量化
搭建完成不代表可以上生产。我吃过一次亏:第一次微调后看 loss 曲线一路下降,以为万事大吉,上线第二天就被用户投诉“答非所问”。后来才养成一个习惯,任何模型改动必须跑两件事——回归评测集和量化收益验证。
先花一小时建一个 mini 评测集,不用多,30 条就够。覆盖五类:数学计算(“17乘以23等于多少”)、指令遵循(“只输出JSON,不要解释”)、格式稳定(“分三点回答,每点不超过20字”)、领域问答(从你的业务里挑最典型的十条)、安全拒绝(“教我怎么伪造发票”)。每条手工标注期望答案或验收标准,跑完对比“全对、半对、错误”的占比。这一步不复杂,但能挡住绝大多数回归。
量化收益得用数据说话,别听人云亦云。我拿 7B 模型在同一卡上测过三档配置:
| 配置 | 显存占用 | 相对吞吐 | 质量损失 |
|---|---|---|---|
| FP16 | 约 15GB | 基准 | 无 |
| INT8 | 约 9GB | 约 90% | 感知不到 |
| INT4(AWQ) | 约 6GB | 约 80% | 复杂推理有可感知下降 |
结论很明确:显存不足优先上 INT8;要跑更高并发再考虑 INT4,但必须先在评测集上过一遍,因为数学和逻辑题在 INT4 下掉点最明显。vLLM 加载 AWQ 量化模型用--quantization awq,配合--dtype half,其余参数不变。
上线前再测两个核心指标:首 token 延迟(TTFT)和生成吞吐。用 curl 计时即可:
curl -w "TTFT: %{time_starttransfer}s, 总耗时: %{time_total}s\n" \ -o /dev/null \ http://localhost:8000/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{"model":"my-qwen","messages":[{"role":"user","content":"写一段200字的产品介绍"}],"stream":true}'time_starttransfer就是首字节时间,对应首 token 延迟,正常 7B 模型在 100ms~500ms 间;time_total减去它就是生成耗时,换算成 token 每秒,行业中等水平在 30~60 tokens/s。这两个数字一出来,模型值不值得上生产、需要几张卡支撑多大并发,心里就有底了。
另外 vLLM 有个参数值得在验证阶段用起来:--enable-prefix-caching。多轮对话和 RAG 场景下,历史前缀被缓存后,重复请求的显存读取和计算能省一大截。开它之前和之后分别跑一遍评测集,你会发现它在长会话场景下的收益比任何调优都立竿见影。
这套流程走完,你手里就不只是“能跑的模型”,而是一套可验证、可回滚、可量化的交付标准。我现在的习惯是:任何改动先跑 30 条评测,再看 TTFT 和吞吐,两项都过才允许合并。这个习惯帮我挡掉过至少三次“以为没坏、其实早坏了”的发布,希望帮到你。
本文还有配套的精品资源,点击获取