AI(人工智能)工程可以简单理解为:用数据训练出一个模型,再把模型部署成服务,让它能稳定、快速、低成本地回答问题或完成任务。
如果把整个过程拆开,核心问题其实不多:
- 模型是怎么从数据里学到规律的?
- 训练和推理为什么是两套不同的工作负载?
- Transformer 为什么能支撑大语言模型?
- 工程上为什么要做并行、量化、蒸馏和缓存?
- 不同优化手段分别省了什么,又会带来什么代价?
一条完整的 AI 工程链路大致如下:
flowchart LR
A[原始数据] --> B[数据清洗与标注]
B --> C[模型训练]
C --> D[模型评估]
D --> E{是否达标}
E -- 否 --> B
E -- 是 --> F[模型压缩与优化]
F --> G[推理服务部署]
G --> H[用户请求]
H --> I[模型输出]
I --> J[日志与反馈]
J --> B
这条链路里,训练负责“学会”,推理负责“使用”。Transformer 是当前大语言模型最常见的底层结构,而并行、量化、蒸馏、缓存这些工程手段,都是为了让模型训练得动、跑得快、用得起。
AI、机器学习、深度学习和大语言模型的关系
几个概念经常混在一起,但它们的范围不同。
flowchart TD
A[AI 人工智能] --> B[ML 机器学习]
B --> C[DL 深度学习]
C --> D[Transformer]
D --> E[LLM 大语言模型]
| 概念 | 含义 | 例子 |
|---|---|---|
| AI(人工智能) | 让机器表现出类似智能的能力 | 问答、识别图片、自动规划 |
| ML(机器学习) | 让程序从数据中学习规律,而不是完全靠人工规则 | 房价预测、推荐系统 |
| DL(深度学习) | 使用多层神经网络学习复杂模式 | 图像识别、语音识别 |
| Transformer | 一种神经网络架构,擅长处理序列数据 | 翻译模型、文本生成模型 |
| LLM(大语言模型) | 基于大量文本训练的大规模语言模型 | 聊天助手、代码生成、摘要生成 |
传统程序通常是“人写规则,机器执行”。机器学习换了一个思路:人准备数据和目标,模型自己从数据里拟合规律。
flowchart LR
A[传统程序] --> B[人工规则]
B --> C[输出结果]
D[机器学习] --> E[训练数据]
E --> F[模型学习参数]
F --> G[输出结果]
大语言模型也是机器学习模型,只是它的规模更大,训练数据更多,任务形式更通用。它通常把语言任务转成一个核心目标:根据上下文预测下一个 token。
token 可以理解为模型处理文本时的基本单位,可能是一个字、一个词,也可能是词的一部分。例如一句话:
我喜欢机器学习
经过分词器后,可能被切成:
["我", "喜欢", "机器", "学习"]
模型并不直接理解汉字或单词,而是先把 token 转成数字 ID,再转成向量,然后在神经网络里计算。
训练和推理:一个负责学习,一个负责回答
训练和推理是 AI 工程里最重要的两个阶段。
| 阶段 | 输入 | 输出 | 主要目标 | 资源特点 |
|---|---|---|---|---|
| 训练 | 大量样本、标签或自监督数据 | 模型参数 | 让模型学到规律 | 计算量巨大,需要反向传播 |
| 推理 | 用户请求、上下文、提示词 | 预测结果 | 快速返回答案 | 关注延迟、吞吐和成本 |
训练阶段会不断调整模型参数。推理阶段通常不再修改参数,只使用训练好的参数做预测。
训练是怎么跑的
一个神经网络模型本质上是一堆参数和计算规则。训练时,模型会经历这些步骤:
flowchart TD
A[取一批训练样本] --> B[前向传播]
B --> C[计算预测结果]
C --> D[计算损失 Loss]
D --> E[反向传播求梯度]
E --> F[优化器更新参数]
F --> G{是否继续训练}
G -- 是 --> A
G -- 否 --> H[保存模型权重]
关键概念如下:
| 概念 | 作用 |
|---|---|
| batch | 一次送进模型的一批样本 |
| epoch | 所有训练数据完整训练一遍 |
| loss | 衡量预测结果和目标之间的差距 |
| gradient | 参数应该往哪个方向调整 |
| optimizer | 根据梯度更新参数的算法,例如 Adam、SGD |
| checkpoint | 训练中保存的模型权重快照 |
用一段简化的 PyTorch 代码可以表示训练循环:
import torch
import torch.nn as nn
model = MyModel()
optimizer = torch.optim.AdamW(model.parameters(), lr=1e-4)
loss_fn = nn.CrossEntropyLoss()
for batch in dataloader:
inputs = batch["input_ids"]
labels = batch["labels"]
logits = model(inputs) # 前向传播
loss = loss_fn(logits, labels) # 计算损失
optimizer.zero_grad()
loss.backward() # 反向传播
optimizer.step() # 更新参数
训练的核心不是“记住答案”,而是通过大量样本调整参数,使模型在遇到相似但没见过的数据时也能给出合理预测。参数越多,模型表达能力通常越强,但训练成本也会迅速上升。
推理是怎么跑的
推理阶段不会执行反向传播,也不会更新模型参数。对于大语言模型来说,推理通常是一个逐 token 生成的过程。
sequenceDiagram
participant U as 用户
participant S as 推理服务
participant T as 分词器
participant M as 大语言模型
U->>S: 输入提示词
S->>T: 文本转 token ID
T-->>S: token 序列
S->>M: 送入模型
M-->>S: 下一个 token 的概率分布
S->>S: 采样或选择 token
S->>M: 带上新 token 继续生成
S-->>U: 返回完整答案
大语言模型每次通常只预测下一个 token。生成一句话时,它会反复执行:
已有上下文 -> 预测下一个 token -> 拼回上下文 -> 继续预测
例如:
输入:机器学习是一种
输出1:让
输出2:计算机
输出3:从
输出4:数据
输出5:中
输出6:学习
这个自回归过程带来一个重要工程问题:生成越长,计算次数越多。如果每次都从头计算整段上下文,推理会非常慢,所以 KV Cache(键值缓存)就变得很关键。
Transformer 的核心结构
Transformer 是当前大语言模型最常见的基础架构。它的核心能力来自 Attention(注意力机制):模型在处理一个 token 时,可以根据上下文判断哪些 token 更重要。
一个典型的 Transformer 层可以简化成这样:
flowchart TD
A[输入 token 向量] --> B[自注意力 Self-Attention]
B --> C[残差连接 Add]
C --> D[归一化 LayerNorm]
D --> E[前馈网络 FFN]
E --> F[残差连接 Add]
F --> G[归一化 LayerNorm]
G --> H[输出向量]
多个 Transformer 层堆叠起来,就形成了大模型。
flowchart LR
A[Token IDs] --> B[Embedding]
B --> C[位置编码]
C --> D[Transformer Layer 1]
D --> E[Transformer Layer 2]
E --> F[...]
F --> G[Transformer Layer N]
G --> H[输出词表概率]
Embedding:把 token 变成向量
模型不能直接处理文字,需要先把 token ID 映射成向量。这个映射表就是 Embedding。
假设词表大小是 50,000,向量维度是 4,096,那么 Embedding 表可以理解成一个矩阵:
50000 x 4096
每个 token ID 对应矩阵里的一行。模型训练时,这些向量也会一起被更新。
位置编码:告诉模型顺序信息
Transformer 的注意力机制本身不天然知道 token 的顺序。如果输入是:
小猫追小狗
和:
小狗追小猫
token 集合相同,但语义完全不同。位置编码负责把顺序信息加入到 token 向量中,让模型知道每个 token 在序列里的位置。
大语言模型常见的位置处理方式包括绝对位置编码、相对位置编码、RoPE(旋转位置编码)等。实现细节不同,目标都是让模型能区分位置。
Self-Attention:让每个 token 看上下文
Self-Attention(自注意力)会为每个 token 生成三组向量:
- Q(Query):当前 token 想查询什么信息
- K(Key):当前 token 能提供什么索引特征
- V(Value):当前 token 真正携带的信息内容
注意力计算可以写成:
Attention(Q, K, V) = softmax(QK^T / sqrt(d_k)) V
含义可以拆成三步:
- 用 Q 和 K 计算相关性分数;
- 用 softmax 把分数转成权重;
- 用权重对 V 做加权求和。
用图表示:
flowchart LR
A[输入向量 X] --> B[线性变换得到 Q]
A --> C[线性变换得到 K]
A --> D[线性变换得到 V]
B --> E[QK^T 计算相关性]
C --> E
E --> F[softmax 得到注意力权重]
F --> G[加权求和 V]
D --> G
G --> H[注意力输出]
简化版 PyTorch 实现如下:
import torch
import torch.nn.functional as F
def scaled_dot_product_attention(q, k, v, mask=None):
# q, k, v: [batch, heads, seq_len, head_dim]
head_dim = q.size(-1)
scores = torch.matmul(q, k.transpose(-2, -1)) / (head_dim ** 0.5)
if mask is not None:
scores = scores.masked_fill(mask == 0, float("-inf"))
weights = F.softmax(scores, dim=-1)
output = torch.matmul(weights, v)
return output
Multi-Head Attention:从多个角度看上下文
单个注意力头只能学习一种关系。Multi-Head Attention(多头注意力)会把向量拆成多个头,让不同头学习不同类型的依赖关系。
有的头可能更关注主谓关系,有的头可能更关注实体指代,有的头可能更关注局部短语。它们的输出会拼接起来,再经过线性变换。
flowchart TD
A[输入向量] --> B[Head 1]
A --> C[Head 2]
A --> D[Head 3]
A --> E[Head N]
B --> F[拼接 Concat]
C --> F
D --> F
E --> F
F --> G[线性变换]
G --> H[输出]
FFN:对每个位置做非线性变换
FFN(Feed Forward Network,前馈网络)通常由两层线性层和激活函数组成。它不负责 token 之间的信息交换,而是对每个 token 的表示做进一步加工。
常见形式是:
Linear -> Activation -> Linear
如果 Self-Attention 负责“在上下文里找相关信息”,FFN 更像是“对已经汇聚的信息做变换和提炼”。
Causal Mask:防止模型偷看未来
大语言模型生成文本时,只能根据前面的 token 预测后面的 token。训练时虽然整段文本都在显存里,但模型不能看到未来 token,否则训练目标就被破坏了。
Causal Mask(因果掩码)会遮住当前位置之后的信息。
flowchart LR
A[位置 1] --> B[只能看位置 1]
C[位置 2] --> D[能看位置 1 到 2]
E[位置 3] --> F[能看位置 1 到 3]
G[位置 4] --> H[能看位置 1 到 4]
矩阵形式可以理解为:
1 0 0 0
1 1 0 0
1 1 1 0
1 1 1 1
1 表示允许关注,0 表示不能关注。
训练大模型为什么难
大模型训练难,主要难在三个方面:算力、显存和数据。
参数量越大,显存压力越大
模型参数需要占显存,梯度需要占显存,优化器状态也需要占显存。训练时的显存占用远不只是模型权重本身。
以 Adam 优化器为例,一个参数通常会关联:
- 参数本身;
- 梯度;
- 一阶动量;
- 二阶动量。
如果使用 FP32(32 位浮点数),每个数占 4 字节。参数量上来后,显存会很快被吃满。
| 内容 | 是否训练时需要 | 是否推理时需要 |
|---|---|---|
| 模型参数 | 是 | 是 |
| 激活值 | 是 | 部分需要 |
| 梯度 | 是 | 否 |
| 优化器状态 | 是 | 否 |
| KV Cache | 通常否 | 是 |
训练显存压力通常比推理更大,因为训练要保存中间激活值用于反向传播,还要维护梯度和优化器状态。
序列越长,注意力计算越贵
Self-Attention 需要计算 token 两两之间的关系。序列长度是 n 时,注意力矩阵大小约为:
n x n
这意味着上下文长度翻倍,注意力部分的计算和显存开销可能接近变成四倍。长上下文模型很有用,但它不是免费的。
数据质量决定模型上限
模型会从数据中学习模式。如果训练数据里充满重复、错误、低质量内容,模型也会学到这些噪声。高质量数据通常包括:
- 格式清晰;
- 重复率低;
- 标注一致;
- 覆盖目标场景;
- 没有明显泄露评测集答案。
训练不只是堆更多数据,也要控制数据质量和数据分布。
工程优化一:并行,让多个设备一起干活
当模型太大、数据太多,单张 GPU(图形处理器)无法完成训练或推理时,就需要并行。常见并行方式有四类。
| 并行方式 | 拆分对象 | 解决的问题 | 典型代价 |
|---|---|---|---|
| 数据并行 | 数据 batch | 加快训练吞吐 | 需要同步梯度 |
| 张量并行 | 单层内部矩阵计算 | 单卡放不下大矩阵 | 设备间通信频繁 |
| 流水线并行 | 不同模型层 | 模型层数太多 | 存在流水线气泡 |
| 专家并行 | MoE 专家模块 | 只激活部分专家 | 路由和负载均衡复杂 |
数据并行
数据并行最容易理解:每张 GPU 放一份完整模型,但处理不同数据。每张卡算出梯度后,再做梯度同步。
flowchart TD
A[训练数据 batch] --> B[切分 batch]
B --> C[GPU 1: 完整模型]
B --> D[GPU 2: 完整模型]
B --> E[GPU 3: 完整模型]
C --> F[梯度同步]
D --> F
E --> F
F --> G[更新模型参数]
数据并行适合模型能放进单卡,但数据量很大、希望提高训练吞吐的场景。
张量并行
张量并行会把一层里的大矩阵拆到多张 GPU 上。比如一个巨大的线性层,单卡放不下或算不过来,就把矩阵按列或按行拆分。
flowchart LR
A[输入 X] --> B[矩阵分片 W1 on GPU1]
A --> C[矩阵分片 W2 on GPU2]
A --> D[矩阵分片 W3 on GPU3]
B --> E[合并结果]
C --> E
D --> E
它能解决单层太大的问题,但通信成本高,因为每层内部都可能需要跨设备交换数据。
流水线并行
流水线并行按层拆模型。前几层放在 GPU 1,中间层放在 GPU 2,后面层放在 GPU 3。
flowchart LR
A[Micro Batch 1] --> B[GPU1: Layers 1-8]
B --> C[GPU2: Layers 9-16]
C --> D[GPU3: Layers 17-24]
E[Micro Batch 2] --> B
为了减少设备空闲,通常会把 batch 再拆成多个 micro-batch,让不同 GPU 像流水线一样连续工作。问题是流水线开始和结束时会有空档,称为流水线气泡。
专家并行
MoE(Mixture of Experts,混合专家)模型会准备多个专家网络,但每个 token 只激活其中一小部分专家。专家并行就是把不同专家放到不同设备上。
flowchart TD
A[Token 表示] --> B[路由器 Router]
B --> C[Expert 1]
B --> D[Expert 2]
B --> E[Expert 3]
B --> F[Expert N]
C --> G[合并输出]
D --> G
E --> G
F --> G
MoE 的好处是模型总参数可以很大,但每次计算只用部分参数。代价是路由、通信和负载均衡更难处理。
工程优化二:量化,用更少位数表示参数
量化的目标是减少模型权重和计算所需的位宽。简单说,就是把原来用高精度浮点数表示的参数,压到低精度格式里。
| 格式 | 每个数占用 | 常见用途 |
|---|---|---|
| FP32 | 32 bit | 传统训练、高精度计算 |
| FP16 | 16 bit | 训练和推理加速 |
| BF16 | 16 bit | 大模型训练常用 |
| INT8 | 8 bit | 推理压缩 |
| INT4 | 4 bit | 更激进的推理压缩 |
假设一个模型有 70 亿参数:
| 精度 | 粗略权重大小 |
|---|---|
| FP32 | 约 28 GB |
| FP16 / BF16 | 约 14 GB |
| INT8 | 约 7 GB |
| INT4 | 约 3.5 GB |
量化的收益很直接:
- 显存占用变小;
- 模型加载更快;
- 显存带宽压力降低;
- 在支持低精度计算的硬件上,推理速度可能更快。
但量化不是无损压缩。位数越低,模型输出越可能发生变化,尤其是数学推理、代码生成、多轮复杂指令等任务更容易受影响。
常见量化方式可以分成两类:
| 方式 | 含义 | 适合场景 |
|---|---|---|
| 训练后量化 PTQ | 模型训练完后再压缩 | 快速部署、成本敏感 |
| 量化感知训练 QAT | 训练时模拟量化误差 | 对精度要求更高 |
PTQ(Post-Training Quantization,训练后量化)流程简单,不需要重新完整训练模型。QAT(Quantization-Aware Training,量化感知训练)成本更高,但能让模型提前适应低精度误差。
量化后的模型需要做评测,不能只看压缩率。更合理的检查方式是:
flowchart LR
A[原始模型] --> B[量化]
B --> C[离线评测]
C --> D{质量是否达标}
D -- 否 --> E[调整量化方案]
E --> B
D -- 是 --> F[灰度上线]
F --> G[监控质量和延迟]
工程优化三:蒸馏,让小模型学习大模型
蒸馏的核心思路是:用一个能力更强的大模型当老师,训练一个更小、更便宜的学生模型。
flowchart LR
A[训练样本] --> B[教师模型]
A --> C[学生模型]
B --> D[软标签或生成答案]
D --> E[蒸馏训练目标]
C --> E
E --> F[更新学生模型]
普通监督训练通常使用硬标签。例如分类任务里,正确答案就是:
猫:1
狗:0
鸟:0
蒸馏可以使用软标签,也就是教师模型输出的概率分布:
猫:0.82
狗:0.12
鸟:0.06
软标签包含更多信息。比如模型虽然判断“猫”最可能,但也知道“狗”比“鸟”更接近。这种相似性信息对学生模型学习有帮助。
大语言模型蒸馏还常用另一种方式:让教师模型生成高质量问答、推理过程或指令数据,再用这些数据训练学生模型。
| 蒸馏方式 | 做法 | 适合场景 |
|---|---|---|
| 软标签蒸馏 | 学生学习教师输出概率 | 分类、检索、排序 |
| 答案蒸馏 | 教师生成答案,学生模仿 | 问答、摘要、对话 |
| 过程蒸馏 | 教师给出推理步骤,学生学习过程 | 数学、代码、复杂推理 |
| 特征蒸馏 | 学生对齐教师中间层表示 | 模型结构相近时 |
蒸馏的收益是推理成本更低、延迟更小,适合固定场景或垂直任务。它的限制也很明显:学生模型容量有限,不可能完整复制教师模型的所有能力。如果任务范围经常变化,小模型可能需要频繁重新训练或补充数据。
工程优化四:KV Cache,避免重复算上下文
大语言模型推理分成两个阶段:
| 阶段 | 做什么 | 特点 |
|---|---|---|
| Prefill | 一次性处理输入提示词 | 计算量大,但可并行 |
| Decode | 逐个生成新 token | 每步依赖上一步,难并行 |
没有 KV Cache 时,每生成一个新 token,模型都要重新计算整个上下文的 Key 和 Value。上下文越长,浪费越严重。
KV Cache 会把已经计算过的 Key、Value 保存起来。生成新 token 时,只需要计算新 token 的 Q、K、V,然后让新 token 的 Q 去和缓存里的 K、V 做注意力计算。
flowchart TD
A[输入提示词 tokens] --> B[Prefill: 计算所有 K/V]
B --> C[写入 KV Cache]
C --> D[Decode 第 1 个新 token]
D --> E[只计算新 token 的 K/V]
E --> F[追加到 KV Cache]
F --> G[Decode 下一个 token]
G --> E
简化伪代码如下:
kv_cache = None
tokens = tokenizer.encode(prompt)
# Prefill 阶段
logits, kv_cache = model(tokens, kv_cache=None)
generated = []
for _ in range(max_new_tokens):
next_token = sample(logits)
generated.append(next_token)
# Decode 阶段只送入新 token,并复用历史 KV Cache
logits, kv_cache = model([next_token], kv_cache=kv_cache)
KV Cache 能明显减少重复计算,但它会占用显存。上下文越长、batch 越大、模型层数越多,KV Cache 越大。
KV Cache 粗略占用和这些因素相关:
层数 x batch 大小 x 序列长度 x hidden 维度 x 精度字节数
所以推理服务里经常要在三个指标之间做取舍:
- 支持更长上下文;
- 支持更大并发;
- 控制显存占用。
三者很难同时无限放大。
工程优化五:批处理和连续批处理
推理服务不只要让单个请求快,还要让整体吞吐高。批处理 batching 的做法是把多个请求合并成一个 batch,一次送进 GPU。
flowchart LR
A[请求 1] --> D[合并 batch]
B[请求 2] --> D
C[请求 3] --> D
D --> E[GPU 推理]
E --> F[分别返回结果]
批处理能提高 GPU 利用率,但会增加等待时间。如果服务为了攒够 batch 才开始推理,单个用户的首 token 延迟就会上升。
普通批处理还有一个问题:不同请求生成长度不一样。短请求结束后,长请求还在继续,batch 里会出现空位。
Continuous Batching(连续批处理)会动态把新请求插入正在运行的 batch,把已经结束的请求移除,从而让 GPU 更少空转。
flowchart TD
A[运行中的 batch] --> B{某些请求结束}
B --> C[移除完成请求]
C --> D{队列中有新请求}
D --> E[插入新请求]
E --> F[继续 decode]
批处理策略通常要同时关注:
| 指标 | 含义 | 调参影响 |
|---|---|---|
| TTFT | Time To First Token,首 token 延迟 | batch 等待越久,TTFT 越高 |
| TPOT | Time Per Output Token,单 token 生成耗时 | batch 太大可能变慢 |
| Throughput | 单位时间处理 token 数 | 合理 batch 能提高吞吐 |
| 显存占用 | 权重、激活、KV Cache 占用 | batch 越大通常越占显存 |
工程优化六:投机解码,用小模型帮大模型加速
Speculative Decoding(投机解码)是一种推理加速方法。它会让一个小模型先快速生成多个候选 token,再交给大模型验证。
sequenceDiagram
participant S as 小模型
participant L as 大模型
participant O as 输出
S->>S: 快速生成多个候选 token
S->>L: 提交候选序列
L->>L: 并行验证候选 token
L-->>O: 接受一部分 token
L-->>S: 从拒绝位置继续生成
如果小模型预测得比较准,大模型一次可以接受多个 token,整体生成速度就会上去。它适合大模型推理成本高、输出分布比较稳定的场景。
代价是系统复杂度变高,需要同时维护小模型和大模型,并处理候选 token 的接受与拒绝逻辑。
几种优化手段怎么选
不同优化手段解决的问题不一样,不能只看“能不能加速”。
| 问题 | 常见方案 | 主要收益 | 主要代价 |
|---|---|---|---|
| 单卡放不下模型 | 张量并行、流水线并行、量化 | 能运行更大模型 | 通信复杂或精度下降 |
| 训练太慢 | 数据并行、混合精度、梯度累积 | 提高训练吞吐 | 调参和稳定性要求更高 |
| 推理显存太高 | 量化、KV Cache 管理、限制上下文 | 降低显存占用 | 可能影响质量或上下文能力 |
| 单用户延迟高 | KV Cache、投机解码、小模型蒸馏 | 更快返回 token | 实现复杂或能力下降 |
| 并发吞吐低 | 批处理、连续批处理、请求调度 | 提高 GPU 利用率 | 首 token 延迟可能上升 |
| 垂直任务成本高 | 蒸馏、小模型微调 | 降低部署成本 | 泛化能力受限 |
常见组合如下:
| 场景 | 推荐组合 |
|---|---|
| 内部知识库问答 | 中等规模模型 + RAG + INT8/INT4 量化 + KV Cache |
| 高并发聊天服务 | 连续批处理 + KV Cache + 多副本部署 + 监控限流 |
| 代码生成 | 保留较高精度 + 长上下文优化 + 强评测集 |
| 移动端或边缘设备 | 小模型蒸馏 + 量化 + 本地推理框架 |
| 大模型预训练 | 数据并行 + 张量并行 + 流水线并行 + 混合精度 |
RAG(Retrieval-Augmented Generation,检索增强生成)是把外部知识库检索结果放进提示词,让模型基于检索内容回答。它不等同于训练模型,常用于企业知识库、客服、文档问答等场景。
训练、微调和 RAG 的区别
很多应用不需要从零训练大模型。从零训练成本最高,数据、算力和工程要求都很高。更常见的做法是使用已有基础模型,再根据业务做微调或接入 RAG。
| 方式 | 是否更新模型参数 | 成本 | 适合场景 |
|---|---|---|---|
| 预训练 | 是 | 极高 | 训练基础大模型 |
| 全量微调 | 是 | 高 | 模型整体适配新领域 |
| LoRA 微调 | 是,更新少量参数 | 中 | 垂直任务适配 |
| RAG | 否 | 中低 | 知识经常变化的问答 |
| Prompt Engineering | 否 | 低 | 简单任务编排 |
LoRA(Low-Rank Adaptation,低秩适配)会冻结大部分模型参数,只训练少量新增参数。它比全量微调省显存、省训练时间,常用于让模型适配特定风格或任务。
RAG 更适合知识经常变化的场景,比如企业制度、产品文档、工单系统。因为知识更新时,只需要更新索引库,不一定要重新训练模型。
常见坑和排查方向
只看平均延迟,不看首 token 延迟
生成式模型的体验很大程度取决于 TTFT。如果用户要等很久才看到第一个 token,即使后续生成速度快,体验也会变差。监控推理服务时,至少要拆开看:
- 首 token 延迟;
- 平均每 token 延迟;
- 总生成耗时;
- 请求排队时间;
- GPU 利用率;
- KV Cache 显存占用。
量化后只跑少量样例
量化可能在简单问答上看不出问题,但在数学、代码、长上下文任务上掉点明显。量化上线前应该准备覆盖真实业务的评测集,并和原精度模型对比。
batch 越大不一定越好
batch 大通常能提高吞吐,但也会占更多显存,并增加排队等待。在线服务要平衡吞吐和延迟,离线批处理任务才更适合把 batch 拉大。
上下文越长,成本越高
长上下文会增加 Prefill 成本,也会放大 KV Cache 显存占用。很多场景不应该无脑塞入所有资料,而应该先做检索、摘要或裁剪,把真正相关的信息放进上下文。
微调不能替代知识更新
如果业务知识经常变化,把知识写进模型参数并不方便。模型重新训练或微调成本高,也难以保证某条知识被准确记住。知识库问答更常见的方案是 RAG,把动态知识放在外部系统里维护。
一张表串起核心概念
| 模块 | 核心问题 | 关键概念 |
|---|---|---|
| 数据 | 模型从哪里学 | 清洗、标注、去重、数据分布 |
| 训练 | 模型怎么学 | 前向传播、损失、反向传播、优化器 |
| 架构 | 模型怎么表示和计算 | Embedding、Attention、FFN、Transformer |
| 推理 | 模型怎么回答 | token 生成、采样、Prefill、Decode |
| 并行 | 大模型怎么跑起来 | 数据并行、张量并行、流水线并行 |
| 压缩 | 怎么省显存和成本 | 量化、剪枝、蒸馏 |
| 缓存 | 怎么减少重复计算 | KV Cache、Prompt Cache |
| 服务 | 怎么支撑在线请求 | 批处理、调度、限流、监控 |
AI 工程的主线并不复杂:训练阶段用数据调整参数,推理阶段用参数生成结果,Transformer 负责建模上下文关系,工程优化负责把模型放到真实系统里稳定运行。理解这条主线后,再看并行、量化、蒸馏、缓存等技术,就不会觉得它们是零散技巧,而是围绕算力、显存、延迟和成本做取舍。