外观
模型部署面试题库(80+ 题)
按 8 大模块组织,每题给出参考答案要点(3-5 条)与追问方向。目标不是背答案,而是建立「概念 → 场景 → 取舍」的回答骨架:先一句话定义,再讲原理/流程,最后落到工程取舍。
模块一:推理基础(11 题)
1. 训练与推理的区别是什么?
- 训练含前向 + 反向传播,推理只做前向;训练更新权重,推理使用固定权重
- 训练重吞吐(大 batch),推理重延迟(小 batch);训练用 FP32/AMP 保梯度,推理可用 FP16/INT8 量化
- 训练是计算密集型,推理是带宽密集型(每读一个权重只做很少计算)
- 推理需要工程化:模型加载、服务化、监控、容量规划
追问:为什么推理对延迟敏感?推理 batch 一般多大?
2. 一次前向传播经历了哪些计算?
- CNN:卷积 → 激活 → 池化 → 全连接,逐层张量变换
- 每层 = 输入/权重/输出三个张量,中间激活占内存
- 算子可融合(conv+bn+relu)减少内存读写与内核启动
- Transformer:Embedding → 自注意力 → FFN → LayerNorm,含 KV Cache
追问:为什么中间激活往往比权重更占内存?
3. 推理延迟由哪些部分组成?
- 模型加载与图编译(一次性开销)
- 请求排队等待(batch 聚合、限流)
- 预处理(解码、tokenize、resize)与后处理(softmax、NMS)
- 算子计算时间(核心,通常可被 profiling 拆解)
- 网络传输(序列化、RTT)
追问:如何确认瓶颈是计算还是带宽?用 Nsight/torch.profiler 怎么拆?
4. 模型大小怎么估算?为什么显存是关键资源?
- 权重大小 = 参数量 × 每参数字节数,如 7B FP16 ≈ 14GB、FP32 ≈ 28GB
- 推理显存还要装中间激活、运行时、GPU 上下文
- LLM 还要加 KV Cache:2 × num_layers × hidden × batch × seq_len × bytes
- 显存不够就装不下 → 换量化(量化)或分布式
追问:70B FP16 需要几张 A100-80G?(4 张,仅权重 140GB)
5. CPU 推理 vs GPU 推理的取舍?
- CPU:低延迟单请求、无显存限制、部署简单,适合小模型/在线低并发
- GPU:高并行、大 batch、适合大模型与高吞吐
- 瓶颈差异:CPU 受内存带宽限制,GPU 受显存带宽与 kernel 启动影响
- 边缘/移动端常走 CPU + INT8(见 TensorRT 边缘案例)
追问:什么情况下 GPU 反而比 CPU 慢?(batch=1 的小模型、kernel 开销主导)
6. 延迟、吞吐、QPS 分别指什么?关系如何?
- 延迟:单请求完成时间(P50/P99);吞吐:单位时间完成的请求数
- QPS(RPS)= 吞吐;吞吐 = 并发数 / 平均延迟(利特尔定律的近似)
- 提升吞吐的手段(批处理)通常会牺牲延迟,存在 trade-off
- 容量规划用「目标延迟下的最大 QPS」
追问:为什么 P99 会比 P50 大很多?(长尾、GC、争抢)
7. 什么是内存带宽瓶颈?为什么推理受带宽限制?
- 算力强度 = 总计算量 / 总访存量(FLOPs/Byte)
- 推理每读一个权重只做几次 FLOPs,算力强度低 → 受带宽限制
- 带宽受限场景下,减少字节数(量化、算子融合)收益最大
- GPU 显存带宽 >> CPU 内存带宽,这是 GPU 推理优势的来源之一
追问:量化为什么在带宽受限场景收益最大?
8. 什么是 batch?动态批处理为什么有用?
- batch = 一次前向处理多个样本,摊薄 kernel 启动与权重读取
- GPU 利用率随 batch 上升,但延迟随之增加
- 动态批处理:聚合排队请求(窗口时间/最大 batch),见 服务化
- batch 上限受显存(激活+KV Cache)与算力约束
追问:动态批处理有哪些参数(窗口、max_batch)?trade-off 怎么定?
9. 推理服务为什么要预热(warm-up)?
- 首次推理含图编译、算子选择、内存分配、缓存缺失,耗时显著偏高
- 预热后性能才进入稳定区,否则压测与线上数据失真
- K8s 里用 readiness 探针等到预热完成再放流量
- CUDA Graph / TensorRT engine 构建也是「预热」的一部分
追问:不预热直接上线的后果?(超时告警、容量误判)
10. 部署时如何验收模型精度?
- 用与训练同分布的评估集,对比基线(训练侧指标)与部署侧指标
- 检查预处理一致性(缩放、归一化、tokenizer),这是最常见的掉点来源
- 量化后做精度回归:跑全量评估集而非抽样
- 上线后继续用在线指标(点击率/准确率)做验证(见 上线流程)
追问:线上效果比离线差 5%,你先查什么?
11. 什么是计算图?动态图 vs 静态图的差异?
- 计算图 = 数据流图,节点是算子,边是张量
- 动态图(eager):逐算子执行,灵活、易调试;静态图:先构图后执行,可全局优化
- 静态图能做算子融合、常量折叠、内存复用;ONNX/TensorRT 都是静态图路线
- 趋势:
torch.compile/ JIT 兼顾两者
追问:ONNX 为什么是静态图?动态 shape 为什么难处理?
模块二:推理引擎(11 题)
12. ONNX 是什么?为什么要转 ONNX?
- ONNX = 开放神经网络交换格式,跨框架(PyTorch/TF)的中间表示
- 统一了算子集合与图表示,让后端(ORT/TensorRT)只针对一种格式优化
- 生态成熟:转换工具、量化、可视化齐全(模型格式)
追问:PTH 转 ONNX 常见坑?(动态 shape、不支持算子、控制流)
13. ONNX Runtime 的优化原理是什么?
- 图优化:算子融合、常量折叠、冗余消除
- 通过 Execution Provider(CPU/GPU/DirectML/OpenVINO)映射到不同硬件
- 支持量化模型(QDQ 格式)与 INT8 执行
- 内存规划:静态分析激活复用缓冲
追问:ORT 相对 PyTorch eager 为什么快?(图优化 + kernel 选择)
14. TensorRT 的完整优化流程?
- 流程:解析模型 → 构建 engine(含算子选择/融合/精度)→ 序列化 → 推理
- INT8 需要校准集校准量化 scale
- engine 与 GPU 型号、driver、TensorRT 版本强绑定
- 支持 CUDA Graph 捕获与多 stream 并发
追问:engine 序列化后换一张卡能直接用吗?(不能,需重新构建)
15. 什么是算子融合(operator fusion)?
- 把多个相邻算子合并为一个 kernel,减少内核启动次数与中间张量读写
- 典型:conv+bn+relu、GELU 并入注意力、残差+归一化融合
- 融合的收益在带宽受限场景最明显
- TensorRT/ORT/vLLM 都做不同粒度的融合
追问:融合会不会有精度损失?数值上要注意什么?
16. TensorRT INT8 校准怎么做?
- 用有代表性的校准集(几百~几千样本)统计每层激活分布
- 选择量化方案:min-max / 熵(entropy)校准 / 百分位
- 产出每层的 scale(可能 per-channel),构建 INT8 engine
- 校准集与真实推理分布不一致 → 精度崩,需要人工校验
追问:校准集怎么选?样本太少会怎样?
17. Triton Inference Server 的核心特性?
- 多框架模型统一服务(ONNX/PyTorch/TensorRT/TF)
- 动态批处理 + 模型并发实例(多 stream 并行执行)
- 模型集成(ensemble)/ 业务脚本(business logic)编排
- 提供 gRPC/HTTP 接口与性能分析工具(Model Analyzer)
追问:Triton 和 vLLM 的定位差异?(通用模型服务 vs LLM 专用)
18. vLLM 的核心机制?
- PagedAttention:KV Cache 分页管理,消除显存碎片与浪费
- Continuous batching:逐 token 粒度调度,生成完一个立即腾出位置
- 原生支持量化权重加载(GPTQ/AWQ/FP8)与张量并行
- 基于块表管理,长上下文时显存可复用
追问:PagedAttention 相比传统 KV Cache 省了什么?(见 LLM 推理)
19. 推理引擎怎么选型?
- 看框架来源:PyTorch 模型优先 ONNX Runtime / TorchServe;要极致性能选 TensorRT
- 看硬件:NVIDIA 生态选 TensorRT/Triton;Intel 选 OpenVINO;通用选 ORT
- 看场景:LLM 在线选 vLLM/SGLang;多框架混合选 Triton;边缘选 TensorRT-lite/TFLite
- 用 框架对比 的维度(性能/生态/易用性/团队经验)打分
追问:给你一个线上 1000 QPS 的 CV 模型,你会怎么选并说明理由?
20. Eager mode vs Graph mode?CUDA Graph 是什么?
- Eager:每算子一次 kernel 启动,灵活但慢;Graph:整体捕获后一次提交
- CUDA Graph:把一串 kernel 捕获为图,重复执行免去启动开销,适合固定 shape
- 动态 shape 下 CUDA Graph 需要「graph pool + 分段捕获」技巧
torch.compile做了图优化 + kernel 融合
追问:为什么 CUDA Graph 能显著降低小 batch 推理延迟?
21. GGUF 是什么?llama.cpp 为什么流行?
- GGUF = llama.cpp 的模型格式,内置多种量化档位(Q4_K_M 等)
- 权重预量化存储,加载即用,无需运行时校准
- CPU/Apple Silicon 上跑大模型的万能钥匙,边缘与本地部署常用
- 与 safetensors 的区别:GGUF 携带量化参数与 tokenizer 信息
追问:GGUF 的 Q4_K_M 里的 K_M 代表什么?(key 部分更高精度)
22. 模型加载慢怎么优化?
- 预加载/常驻(内存/显存 pool),避免请求时加载
- 模型文件放本地 NVMe,减少网络/共享存储读取
- 用多副本分摊;加载失败自动重试与回滚到旧版本
- K8s 中用 readiness 控制流量进入时机
追问:热更新模型时如何做到「不丢请求」?
模块三:量化与压缩(10 题)
23. 量化的基本原理是什么?
- 用 scale + zero_point 把浮点值映射到整数范围
- 对称量化(zero_point=0,适合权重)与非对称量化(适合激活)
- FP32 → INT8 体积减 75%,带宽/算力需求下降
- 推理能用量化,训练不能:梯度需要高精度,见 量化
追问:per-tensor 与 per-channel 量化的区别?
24. PTQ vs QAT 怎么选?
- PTQ:训练后量化,需要校准集,快,INT8 多数场景够用
- QAT:训练中模拟量化(fake quant),精度更高,成本高
- 精度敏感任务(检测/分割)或低 bit(INT4)优先 QAT
- 混合策略:敏感层保留高精度,其余量化
追问:PTQ 后精度崩了,你会怎么做?(校准集/混合精度/换 QAT)
25. INT8 推理精度问题如何评估与缓解?
- 用完整评估集对比 INT8 vs FP16 的指标,而非肉眼抽查
- 缓解手段:更好的校准数据、per-channel、敏感层跳过、混合精度
- 找敏感层:逐层误差分析(sensitive layer analysis)
- 激活异常值(outlier)是主要精度杀手,可用 clip 处理
追问:你怎么发现哪层最敏感?
26. FP16、BF16、FP8 有什么区别?
- FP16:1+5+10 位,范围窄易溢出;BF16:1+8+7 位,范围同 FP32,精度低
- BF16 训练友好(大动态范围);FP16 推理更成熟
- FP8(E4M3/E5M2):新一代推理格式,接近 INT8 体积、精度更好
- 选择看硬件支持(Ampere+ 支持 BF16,Hopper/Blackwell 支持 FP8)
追问:为什么 LLM 训练普遍用 BF16 而不是 FP16?
27. GPTQ vs AWQ 的区别?
- 都是 4-bit 权重量化,面向 LLM
- GPTQ:逐层用二阶信息(Hessian)最小化量化误差,一次性全局校准
- AWQ:激活感知,保护激活值大的重要权重通道,按通道缩放
- 工程上两者精度接近,AWQ 速度更快,实现更简单
追问:为什么 4-bit 权重 + 16-bit 激活(W4A16)是主流组合?
28. 权重量化 vs 激活量化?
- 权重量化(W8A16):只量化权重,激活保持 FP16,实现简单,兼容性好
- 激活量化(W8A8):需要统计激活动态范围,激活分布宽 → 精度难保
- 激活量化才有完整的 INT8 计算加速(int8 GEMM)
- LLM 常见方案:W8A8(如 SmoothQuant)或 W4A16
追问:激活量化为什么难?(每 token 动态范围不同)
29. 蒸馏、剪枝、稀疏化、量化的区别?
| 技术 | 原理 | 收益 | 成本 |
|---|---|---|---|
| 量化 | 低位数值表示 | 体积/带宽 | 低(PTQ) |
| 剪枝 | 去掉不重要权重/通道 | 稀疏化体积 | 中 |
| 蒸馏 | 大模型教小模型 | 结构变小 | 高(需重训) |
| 稀疏化 | 置零 + 稀疏存储 | 体积/算力(硬件支持时) | 中 |
组合顺序建议:先剪枝/蒸馏,再量化;见 模型压缩。
追问:稀疏化在 GPU 上为什么收益不如预期?(硬件稀疏加速支持有限)
30. 量化后为什么能加速?在计算受限场景还加速吗?
- 带宽受限:权重字节减半 → 搬运时间减半
- 计算受限:INT8 可用 tensor core(2-4× FLOPS),仍有收益
- 体积变小还意味着 cache 命中率提升、可装更大 batch
- 极端小模型场景:量化开销可能抵消收益,需实测
追问:怎么判断一个推理任务「计算受限」还是「带宽受限」?
31. 如何选择量化位宽?
- 按显存目标反推:能装下+余量即可,不盲目最低位
- 按精度要求:检测/分割/医疗等敏感任务保守用 INT8 或混合
- 按硬件:检查硬件是否支持对应格式(FP8 需 Hopper+)
- 用「精度-体积-速度」三角权衡,实测决定,见 量化
追问:7B 模型要装进 16GB 单卡,你会怎么配置?
32. 什么是 SmoothQuant?解决什么问题?
- 解决激活量化精度问题:把激活的难度「平滑」到权重上
- 对权重按通道缩放,让激活分布更均匀
- 属于 W8A8 方案的经典代表,可量化友好
- 需要少量校准统计激活分布
追问:SmoothQuant 与 AWQ 的相似思路?(都是通道缩放)
模块四:服务化(10 题)
33. REST vs gRPC 在推理服务中的取舍?
- REST:HTTP/JSON,调试方便、生态广,适合外部 API
- gRPC:protobuf + HTTP/2,多路复用、低延迟、强类型,适合内部服务
- 推理内部链路(服务间)常用 gRPC;对外暴露常用 REST
- Triton 同时提供两者,性能敏感场景推荐 gRPC
追问:HTTP/2 多路复用解决了 HTTP/1.1 的什么问题?
34. 动态批处理(dynamic batching)的工作原理?
- 请求进入队列,等待「窗口时间」或「达到 max_batch」
- 聚合后一次前向,摊薄 kernel 启动;生成完再拆回响应
- 参数:max_batch_size、delay(等待时长)、queue policy
- trade-off:窗口越大吞吐越高、延迟越大
追问:如何确定最优窗口时间?(压测扫描 + 延迟预算)
35. 推理服务的模型加载/热加载怎么设计?
- 模型版本化存储(模型仓库),加载到显存池
- 热切换:新版本就绪 → 切换路由 → 释放旧版本(无中断)
- 加载失败自动回滚,readiness 未就绪不放流量
- 多副本滚动更新的「一次一版本」策略
追问:显存不足导致热加载失败,你的兜底策略是什么?
36. 限流(rate limiting)怎么做?
- 令牌桶/漏桶算法,按 QPS 或并发限流
- 并发限制(semaphore)+ 队列 + 超时丢弃
- 返回 429 + Retry-After,客户端退避重试
- 限流与背压(backpressure):服务过载时向上游传递压力
追问:限流阈值怎么定?(基于压测的饱和点,见 压测)
37. 推理服务的线程/进程模型?
- Python 受 GIL 限制:CPU 密集推理应放进程/原生层
- FastAPI 中推理放线程池或独立进程,避免阻塞事件循环
- 模型副本(进程)常驻,请求按并发分片
- Triton 用模型实例(instance)+ stream 并行
追问:为什么 Python 推理服务 QPS 上不去?(GIL、解释器开销)
38. 模型服务如何水平扩展?
- 服务无状态化(状态放外部),副本数随流量伸缩
- GPU 服务受显存与卡数约束:每卡可放 N 个模型副本
- K8s HPA / 自定义指标(QPS、GPU 利用率)伸缩
- 负载均衡(Ingress/Service)分发到副本
追问:GPU 推理服务的 HPA 有什么坑?(本页模块五)
39. 什么是模型并发实例(model instance)?
- Triton 概念:同一模型多个实例,每实例一个执行线程/stream
- 实例数 > CPU/GPU 可并发执行数时会排队
- 与动态批处理配合:实例内批处理 + 多实例并行
- 并发度受显存与计算资源限制
追问:实例数设多了会发生什么?(排队、显存爆)
40. 优雅关停(graceful shutdown)怎么做?
- 停止接收新请求 → 排空在途请求 → 释放资源
- K8s preStop hook + terminationGracePeriodSeconds
- 关闭连接池、落盘状态、记录最后一批日志
- 直接 kill 会导致在途请求 5xx
追问:readiness 探针与优雅关停怎么配合?
41. 如何把模型打包成可部署产物?
- Docker 镜像:基础镜像(CUDA 版本匹配)+ 推理引擎 + 模型文件
- 模型文件建议外置挂载(镜像臃肿、更新慢),代码进镜像
- 用 ORT/TensorRT engine 预构建产物,避免运行时编译
- 多阶段构建减小镜像,锁定依赖版本
追问:模型进镜像 vs 外挂卷的取舍?
42. 如何做 API 版本管理与兼容?
- 路径/header 版本化(/v1/…),避免破坏性变更
- 请求/响应 schema 兼容:新增字段 optional
- 多版本并存灰度,客户端平滑升级
- 记录调用方,变更前沟通,见 上线流程
追问:模型升级(输入格式变化)如何做到无缝?
模块五:K8s 与容器(11 题)
43. Pod 的生命周期?
- Pending → Running → Succeeded/Failed;异常时 ContainerCreating/CrashLoopBackOff
- initContainer 先于主容器执行(环境准备)
- 删除时先执行 preStop,再 SIGTERM,宽限期后 SIGKILL
- 控制器(Deployment/StatefulSet)负责副本修复
追问:CrashLoopBackOff 的常见原因与排查思路?
44. liveness / readiness / startup 探针的区别?
- liveness:容器是否存活,失败则重启
- readiness:是否可接收流量,失败则摘除(不重启)
- startup:慢启动容器专用,保护 liveness 不被误杀
- 推理服务建议:startup 覆盖预热期,readiness 控制流量,liveness 探测进程
追问:推理服务为什么不用 liveness 探测「业务健康」?(误杀风险)
45. HPA 的原理?
- 控制面周期读取指标(metrics-server),计算期望副本数
- 目标值 = 当前使用率/目标使用率的比值向上取整
- 支持自定义/外部指标(QPS、GPU 利用率)
- 有冷却窗口(scale down 慢于 scale up)避免抖动
追问:GPU 推理服务 HPA 的坑?(显存不降级、Pod 拉起要预热、冷启动风暴)
46. GPU 资源怎么在 K8s 中调度?
- NVIDIA device plugin 把 GPU 上报为可扩展资源(nvidia.com/gpu)
- 显存粒度:一张卡一个单位,1 卡装不下就上不了
- 用「GPU 卡数 request」而不是显存(显存需要 MIG 或自定义调度器)
- 节点标签 + 污点/容忍把 GPU 任务钉在 GPU 节点
追问:一台 8 卡机器,如何避免 8 个小任务占满导致大任务进不来?
47. requests / limits 怎么设置?
- requests:调度依据(预留);limits:硬上限(CPU 节流、内存 OOM)
- GPU 只有 limits 语义(不可超卖)
- 内存 limits 过低 → OOMKilled 重启;过高 → 浪费
- 用压测结果设定,观察实际用量留 1.2-1.5× 余量
追问:容器 OOM 后会发生什么?如何定位是谁占的内存?
48. Deployment vs StatefulSet?
- Deployment:无状态、可替换、滚动更新;StatefulSet:稳定标识、有序、持久卷
- 模型推理通常无状态 → Deployment
- 需要固定网络标识/存储(如推理缓存、分布式引擎)时用 StatefulSet
追问:在线推理服务需要 StatefulSet 的场景举例?
49. ConfigMap / Secret 怎么用?
- ConfigMap 存非敏感配置(超时、批处理参数)
- Secret 存敏感信息(API key、模型加密密钥)
- 变更后需重启/滚动更新生效(不自动热更新)
- 敏感信息禁止提交到镜像/仓库
追问:模型路径、超时参数放哪合适?
50. 镜像构建与优化?
- 多阶段构建:编译环境与运行环境分离,镜像小
- 基础镜像匹配 CUDA/driver 版本,避免运行时缺库
- 利用层缓存,常用层前置;.dockerignore 排除大文件
- 推理引擎体积大:可预构建 engine 产物而非运行时编译
追问:镜像太大导致拉取慢,怎么优化?(分层、外挂模型、私有 registry)
51. Service / Ingress 的流量路径?
- Service:ClusterIP 集群内负载均衡,NodePort 暴露节点端口
- Ingress:7 层路由(域名/路径)到 Service,可做 TLS、限流
- 请求链路:Client → Ingress → Service → Pod
- 会话保持/流量切分(金丝雀)可在 Ingress 层做
追问:为什么引入 Service 而不直接连 Pod IP?
52. 节点亲和 / 污点容忍?
- nodeSelector/nodeAffinity:把 Pod 调度到特定节点(GPU 节点)
- taint/toleration:节点标记「专用」,只接受有对应容忍的 Pod
- 组合:GPU 节点打 taint + 推理 Pod 加 toleration,防非 GPU 任务抢占
- 拓扑分布约束(podTopologySpread)跨可用区容灾
追问:如何防止 GPU 节点被普通任务挤占?
53. 什么是 K8s Operator?为什么 ML 平台常用?
- Operator = 用 CRD 扩展 API + 控制器持续调谐期望状态
- 把「部署模型服务」这类复杂应用变成声明式自定义资源
- KServe/BentoML/Kubeflow 都是 Operator 模式
- 好处:自动处理加载、伸缩、滚动、回滚
追问:写一个推理服务的 Operator,你需要定义哪些自定义资源?
模块六:监控与可靠性(10 题)
54. SLO / SLI / SLA 的定义?
- SLI:实际测量指标(P99 延迟);SLO:目标值(P99 < 100ms,99% 时间)
- SLA:对外契约,带违约责任
- 错误预算 = 100% - SLO%,用掉多少决定是否冻结发布
- 制定流程:业务目标 → 关键指标 → 目标值 → 监控告警
追问:给一个推荐模型服务定 SLO,你选哪些 SLI?
55. 四大黄金指标(RED/USE)是什么?
- 延迟(Latency)、流量(Traffic)、错误(Errors)、饱和度(Saturation)
- 推理场景饱和度:GPU 利用率、显存、队列深度、batch 堆积
- 比「CPU 利用率」更能反映推理健康的是「GPU 利用率 + 队列」
- 每个指标都要有明确目标与告警
追问:饱和度指标怎么选?(GPU util、显存、排队长度)
56. Prometheus 指标类型与 P99 计算?
- counter(累计)、gauge(瞬时)、histogram(分桶累计)
- histogram 分桶 → 用 histogram_quantile 计算 P99,误差取决于桶宽
- 需要观测延迟的完整分布,不能只存平均值
- 指标采集影响性能:采样率与标签基数要控制
追问:histogram 桶怎么划分?(按延迟分布特征分桶)
57. 数据漂移检测怎么做?
- 特征漂移:统计分布变化(PSI、KS 检验);概念漂移:输入输出关系变化
- 监控模型输入/输出/预测分布,定时比对基线
- 轻量做法:对关键特征抽样算 PSI,超过阈值告警
- 漂移后动作:告警 → 人工评估 → 回滚或重训,见 监控
追问:漂移检测阈值误报怎么处理?
58. 告警设计的原则?
- 分级:P0(服务不可用)/P1(SLO 受损)/P2(隐患)
- 多窗口规则(连续 N 分钟)减少抖动误报;避免「告警风暴」
- 每条告警含:现象、影响、处理步骤(runbook)
- 用错误预算而非纯阈值:错误预算快耗尽才告警
追问:告警风暴怎么治?(分级、聚合、沉默、自动化处置)
59. 分布式追踪怎么做?如何定位慢请求?
- OpenTelemetry 埋点:trace + span,跨服务关联
- 链路含:网关 → 预处理 → 推理引擎 → 后处理 → 存储
- 慢请求定位:按 span 耗时排序,找热点 span
- trace 采样率与成本权衡(头部采样 + 尾部采样)
追问:一个推理请求 P99 变差,你按什么顺序排查?(见 可观测性实践)
60. 结构化日志应该记什么?
- 请求级:request_id、模型版本、延迟、batch 大小、结果摘要
- 系统级:OOM、加载失败、超时、降级事件
- JSON 结构化便于检索,避免敏感数据(用户内容)入库
- 日志、指标、追踪用 request_id 串联
追问:日志与指标的分工?(指标聚合、日志下钻)
61. 故障演练与混沌工程?
- 人为注入故障:kill Pod、断网、限流、模拟模型加载失败
- 验证自愈能力:HPA 扩容、Pod 重建、流量切换
- 演练结果沉淀为 runbook,改进 SLO
- 从低风险组件开始,控制爆炸半径
追问:你会对推理服务做哪些故障注入实验?
62. 容量与饱和度预警怎么做?
- 用压测建立「QPS-资源」模型,见 压测方法
- 监控趋势,提前预警(如 GPU 利用率 80% 持续上升)
- 保留扩容水位(如 30%),预留冷启动时间
- 大促/活动前做专项容量评估
追问:QPS 翻倍需要多少卡?给出估算过程。
63. 如何把指标、日志、追踪结合定位线上问题?
- 指标发现异常(P99 升高)→ 日志看该时段请求特征 → 追踪下钻到具体 span
- request_id 是三者串联的主键
- 常见问题模式:队列堆积(指标)、预热不足(日志)、引擎编译抖动(追踪)
- 沉淀「已知问题 → 症状 → 处置」知识库
追问:现场给一个真实场景演练排查流程。
模块七:MLOps(10 题)
64. CI/CD for ML 与软件 CI/CD 的区别?
- 软件 CI/CD 只管代码;ML 多了数据与模型两个「易变输入」
- ML 流水线:数据校验 → 训练 → 评估 → 打包 → 部署 → 监控
- 模型评估需要离线基准 + 在线验证,不能只看测试用例
- 可复现性:锁定数据版本、代码版本、环境(见 MLOps 流水线)
追问:模型「测试通过」和软件「测试通过」有什么本质不同?
65. 模型注册(model registry)解决什么问题?
- 模型版本化、元数据(指标/数据版本/超参)统一管理
- 阶段管理(staging/production),谁注册、谁上线可审计
- 让「部署哪个版本」变成配置而非代码
- MLflow / 自研 registry 是 ML 平台的核心组件
追问:模型注册表里要存哪些元数据?
66. 模型上线流程怎么设计?
- 流程:离线评估通过 → 登台验证 → 金丝雀(5%→25%→50%)→ 全量
- 每一步有明确放量条件(在线指标对比)
- 失败自动回滚,见 上线流程
- 上线自动化:配置驱动而非人工操作
追问:金丝雀怎么判断「新模型可行」?(指标置信区间、错误预算)
67. 回滚策略有哪些?
- 模型级回滚:切回上一模型版本(秒级,配置切换)
- 代码级回滚:回退推理服务镜像
- 数据级回滚:不常见但需预案
- 回滚要监控确认「回到基线」而非只回滚了事
追问:新模型全量 1 小时后效果下降,你的回滚流程是什么?
68. A/B 测试与影子部署?
- A/B:真实流量按比例分到两版本,用业务指标对比
- 影子部署:复制流量打到新版本,不对外响应,用于验证
- 流量切分工具:Istio/Ingress 权重路由、自研分流
- 关键:指标口径一致、样本充足、防止样本污染
追问:A/B 需要多大样本?(给定效应量与置信水平估算)
69. 训练与服务预处理不一致会怎样?
- 训练时归一化/缩放与线上不同 → 输入分布偏移 → 效果崩
- 常见坑:图像 resize 方式、tokenizer 版本、缺失值填充
- 解决:共享预处理代码(同一仓库/同一包),上线前跑一致性测试
- 这是「为什么需要 feature store」的核心动机之一
追问:线上效果比离线低,你怎么定位是不是预处理问题?
70. 实验跟踪怎么做?为什么需要?
- 记录:数据版本、代码 commit、超参、环境、评估指标
- 目标:可复现、可对比、可回放
- MLflow Tracking / WandB 等工具
- 对比实验:统一基线、控制变量
追问:两个实验看起来一样但结果不同,怎么排查?(环境/数据版本差异)
71. 数据版本管理(DVC 等)为什么需要?
- 数据会变,模型的可复现依赖数据版本
- 数据在对象存储(大文件),用 DVC 记录「文件 → 版本 → 仓库」
- 与代码版本联动,保证「这个 commit + 这个数据 = 这个模型」
- 数据合规:版本化便于审计与回退
追问:数据被覆盖后如何找回旧版本训练结果?
72. 模型验收(上线前)清单?
- 离线:完整评估集指标、分场景/分人群指标、与基线对比
- 健壮性:异常输入、缺失字段、超长输入的处理
- 性能:压测达标(延迟/QPS)、预热、容量
- 工程:日志/监控/告警就位、回滚预案、SLO 对齐
追问:你负责的模型上线前,验收清单最后三项是什么?
73. MLOps 平台自建还是买?
- 自建:控制力强,但成本高(人力 + 维护);适合大规模成熟团队
- 开源拼装:Kubeflow/MLflow/Argo + 云服务,起步快
- 买 SaaS:起步最快,但定制受限、数据外流风险
- 决策依据:团队规模、数据合规、业务复杂度
追问:小团队(10 人)上 MLOps,最低成本的起点是什么?
模块八:LLM 推理(10 题)
74. 自回归生成的过程?
- 每次只生成一个 token,作为下一次输入继续
- 串行依赖:第 t+1 个 token 依赖前 t 个 → 无法完全并行
- 两阶段:prefill(并行处理输入,算 KV Cache)与 decode(逐 token 生成)
- 性能指标:TTFT(首 token 延迟)、TPOT(每 token 时间)
追问:为什么 decode 阶段 GPU 利用率低?(batch 小、带宽受限)
75. KV Cache 是什么?显存怎么估算?
- 缓存每层注意力算出的 K/V,避免 decode 阶段重复计算
- 显存 ≈ 2 × layers × hidden × seq_len × batch × bytes
- 例:LLaMA-70B,80 层,hidden 8192,seq 2048,batch 32,FP16:2×80×8192×2048×32×2B ≈ 171GB
- KV Cache 是长上下文与大 batch 的主要显存开销
追问:给一个公式让你现场估算 KV Cache 显存。
76. 连续批处理(continuous batching)的原理?
- 传统静态批:整个 batch 等最慢的请求完成后才腾出位置
- 连续批:每个 decode 步结束后,完成的序列立即退出,新请求随时插入
- 每步粒度调度 → GPU 利用率大幅提升
- vLLM 首次将其实用化(见 vLLM 案例)
追问:连续批处理对调度器的要求?(每步检查完成序列、显存管理)
77. PagedAttention 解决了什么问题?
- 传统 KV Cache 连续分配:碎片化 + 预分配浪费
- 分页 + 块表:按需分配 16-token 块,物理不连续,逻辑连续
- 消除内部/外部碎片,支持更长上下文与更大 batch
- 块表映射让「复制时写(copy-on-write)」支持并行采样
追问:页大小(block size)怎么选?对效率的影响?
78. 投机解码(speculative decoding)的原理?
- 草稿模型快速生成 k 个候选 token,大模型并行验证
- 全部正确则一次接受 k 个 → 等效加速,且分布不变
- 加速上限取决于草稿模型正确率(acceptance rate)
- 工程上可用 N-gram 草稿替代小模型
追问:投机解码有精度损失吗?(无,因为拒绝采样保证分布一致)
79. 张量并行(tensor parallelism)的原理?
- 把单个权重矩阵按行/列切分到多卡,每卡算一部分,再 all-reduce
- 适合单层计算量大的模型(LLM),是 70B/175B 服务化的主力
- 通信开销随卡数增长 → 卡间互联(NVLink/RDMA)是关键
- 与流水线并行(按层切)、数据并行(复制模型)对比
追问:8 卡张量并行 vs 8 卡数据并行的取舍?(单请求延迟 vs 吞吐)
80. 如何估算 LLM 推理的显存?
- 总显存 = 权重 + KV Cache + 激活 + 框架开销
- 权重:参数量 × 字节(FP16:2B);KV Cache 用上题公式
- 激活在 prefill 阶段显著,decode 阶段较小
- 例子:7B FP16 权重 14GB,加上 KV Cache 与 CUDA 开销,32GB 卡才能稳妥跑长上下文
追问:想把 70B 装进 2×A100-80G,需要什么手段?(量化 + 张量并行)
81. prefill 与 decode 的优化差异?
- prefill:计算密集型、可并行,重点关注显存(长序列激活)
- decode:带宽密集型、串行,batch 是吞吐关键
- 优化方向不同:prefill 用 FlashAttention/长序列优化;decode 靠连续批处理
- 延迟预算:TTFT(首 token)由 prefill 决定,TPOT 由 decode 决定
追问:一个 LLM 服务 P50 正常但 TTFT 长,可能是什么原因?
82. 量化对 LLM 推理的影响?
- W4A16(GPTQ/AWQ):体积与带宽减 75%,decode 加速明显
- FP8(W8A8):精度好、Hopper+ 硬件加速,是新趋势
- KV Cache 也可量化(INT8/FP8)进一步省显存
- 量化后显存余量 → 可加 batch/上下文 → 吞吐提升
追问:INT4 权重 + FP8 KV Cache 的配置适合什么场景?
83. 长上下文推理的挑战与应对?
- KV Cache 随 seq_len 线性增长,显存爆炸
- 应对:量化 KV Cache、PagedAttention 复用、缓存淘汰/压缩
- 长上下文下 prefill 首 token 延迟变长,需要分块处理
- 相关优化:FlashAttention、稀疏注意力、位置编码外推
追问:给 128K 上下文做服务化,你列出显存与延迟上的三个挑战。
模块九:系统设计题(5 题)
84. 设计一个高并发推理服务(CV 分类/检测)
设计要点:
- 需求澄清:QPS、P99、模型大小、batch 偏好
- 架构:网关(限流/鉴权)→ 预处理 → 推理引擎(Triton/ORT)→ 后处理 → 存储
- 动态批处理 + 多副本 + 水平扩展;预热与 readiness
- 监控:延迟分布、GPU 利用率、错误率;SLO 与告警
- 容量:压测建模 → 卡数估算 → 扩容水位
追问:QPS 从 1k 到 10k,架构哪里最先崩?怎么演进?
85. 给 70B 模型做服务化
设计要点:
- 显存估算:权重(FP16 140GB / INT4 35GB)+ KV Cache
- 并行策略:张量并行(8 卡)为主,兼顾延迟;流水线并行扩展吞吐
- 推理引擎选型:vLLM/TensorRT-LLM(连续批处理 + PagedAttention)
- 量化:FP8/INT4 权衡精度与容量
- 弹性:按在线延迟还是离线吞吐分两种部署(见 部署模式)
追问:在线(低延迟)与离线(高吞吐)的部署差异?给的卡不同怎么调?
86. 容量规划一个推荐服务
设计要点:
- 明确目标:峰值 QPS、P99、可用性
- 压测得单机/单卡容量(QPS@P99),建立线性模型
- 公式:副本数 = 峰值 QPS / 单副本容量 × 冗余系数(1.3-1.5)
- 考虑故障域(AZ)与扩容时间(冷启动/镜像拉取)
- 大促前做专项压测与预扩容,平时按趋势预警
追问:突发流量 3 倍,扩容来得及吗?(预热时间 vs 扩容时间)
87. 设计一个多模型统一推理平台
设计要点:
- 目标:多框架(ONNX/TensorRT/PyTorch)统一接入
- 底座:K8s + 自定义资源(KServe 模式)+ 模型仓库
- 能力:自动加载、动态批处理、灰度、A/B、监控、配额(多租户)
- 分层:接入层(路由/鉴权/限流)→ 调度层(GPU 分配)→ 执行层(推理引擎)
- 扩展:模型预热、按流量伸缩、成本核算
追问:多个小模型 vs 一个大模型,调度策略有什么不同?
88. 设计边缘设备的模型部署方案
设计要点:
- 硬件约束:算力/内存/带宽/功耗,见 硬件基础
- 模型侧:蒸馏小模型 + INT8 量化 + 剪枝(压缩)
- 引擎选型:TensorRT/TFLite/OpenVINO/llama.cpp,按硬件生态
- 端云协同:边缘低延迟 + 云端兜底;模型 OTA 更新
- 评估:真机实测(延迟/功耗/精度),不能只看浮点性能
追问:边缘模型更新失败怎么回滚?离线运行如何监控?
模块十:现场手撕代码提示(6 题)
面试常考「短小、能体现并发/批处理思维」的题。以下每题给出考察点与参考思路,完整实现建议到 动手构建 的项目里练熟。
89. 写一个简单的 batch 合并函数
考察点:滑动窗口聚合、超时与最大容量、线程安全。
参考思路:实现一个带 max_batch 与 max_wait 的聚合器;请求进入队列,定时器触发或满额触发 flush();flush 时取出队列快照,回调执行推理并分发结果。
追问:如果用 asyncio 怎么实现?如果用多线程呢?
90. 实现固定容量 FIFO 队列
考察点:环形缓冲、线程安全、阻塞/非阻塞。
参考思路:数组 + head/tail 指针,size 计数器;put 满则等待或返回 false;get 空则等待;并发用锁或原子计数。
追问:读多写少场景怎么优化?
91. 实现滑动窗口统计 P99
考察点:数据流、近似统计。
参考思路:分桶直方图(固定分桶)近似 P99;或维护有序结构(SortedList/树);或采样。给出精度与复杂度权衡。
追问:线上要求 P99 统计不能阻塞推理线程,怎么办?
92. 实现带超时的请求聚合器(限时 batch)
考察点:并发原语(timeout、Condition)。
参考思路:每次 put 检查等待时间,用条件变量 + 时间戳;超时触发 flush;注意竞态(flush 与 put 并发)。
追问:如何避免 flush 时又有新请求进来导致批次混乱?
93. 实现令牌桶限流器
考察点:算法 + 并发 + 时间戳。
参考思路:记录 last_refill_time 与令牌数;按速率补充令牌;获取失败返回 429;用单飞/原子操作保证并发安全。
追问:令牌桶与漏桶的区别?突发流量下各自表现?
94. 给定一个 LLM 服务,写一个带并发控制的 generate 包装(伪代码)
考察点:信号量 + 队列 + 超时,工程思维。
参考思路:Semaphore(max_concurrency);入队等待;带超时的 wait_for;拿到槽位后调用推理接口,返回结果,finally 释放。
追问:为什么需要并发控制而不是无限并发?(显存、排队失控)
附:面试现场技巧
| 要点 | 说明 |
|---|---|
| 先定义后展开 | 每题先一句话说清「是什么」,再讲原理,最后给工程取舍 |
| 主动说 trade-off | 「选 Triton 不选 X 因为 Y」比罗列工具名高级得多 |
| 不会就讲思路 | 「我不确定具体实现,但我判断与 XX 有关,会先查 XX」 |
| 把题引向简历项目 | 用项目里的真实数字支撑抽象回答(配合 简历怎么写) |
| 结尾提问 | 问团队部署栈、SLO、GPU 规模,反向筛选公司 |
延伸阅读
- JD 知识点拆解 —— 每题的薄弱点反查对应页面
- JD 清单:国内外公司在招岗位 —— 了解目标岗位的考察侧重
- 学习路径:三条路线 —— 2 周求职冲刺的配套计划
- 推理基础 与 推理系统总体架构解剖 —— 模块一/二的深度地基
- 量化 与 LLM 推理 —— 模块三/八的高频深挖区
- 常见坑点 —— 面试官最爱考的「坑」都在这里
- 动手构建你的第一个推理服务 —— 手撕代码与作品的练手场