PyTorch 推理工程(10):里程碑、简历转化与面试表达

1. 本节定位

第 09 篇提供可运行骨架;本篇讨论如何以里程碑拆分交付、如何记录实验配置与结果以便复核,以及如何将测量结论改写为可检验的陈述(含与简历、技术面试中常见问题的对齐方式)。重点在过程与表述,而非扩充功能。


2. 为什么项目最容易烂尾:五种典型模式

烂尾模式 现象 根因
目标膨胀 一开始想做 serving 平台、多卡、动态 batch 没有从最小可用版本开始
无验收标准 一直在写代码,不知道什么叫"完成" 任务描述模糊
无结构化结果 benchmark 跑了没保存,profiler 看了忘了 没有结果记录规范
无交付视角 做完感觉像是课堂练习 未用可对外复述的工程语言与数字概括
过早追求复杂展示 先做 Web UI,推理逻辑还没稳定 优先级错误

核心原则

先把"推理闭环 + 性能测量 + 分析解释"做扎实,再考虑展示层。


3. 里程碑执行计划(5 周)

按里程碑推进,而不是按"今天想加什么功能"推进。

M1(第 1 周):最小正确推理闭环

目标:能跑、能对、device/dtype 正确

每天任务

1
2
3
4
5
Day 1: 建目录结构,写 models/mlp.py(MLP 模型定义)
Day 2: 写 runner/infer.py(eval + inference_mode + autocast 三层)
Day 3: 写 main.py --mode infer,命令行跑通
Day 4: 加 memory_tracker.py,推理前后显存打印
Day 5: 跑通 CPU 和 CUDA 两种 device,整理 README Day 1 section

验收标准(必须全部满足才算过关):

  • [ ] python main.py --mode infer --model mlp --device cuda 能正确运行
  • [ ] 输出包含:输入 shape、输出 shape、dtype、device、显存变化
  • [ ] model.eval()torch.inference_mode() 在代码里有正确使用
  • [ ] 换 --device cpu 也能跑,不报 device 错误

M2(第 2 周):配置化与精度实验

目标:同一个项目,不改源码就能切换实验配置

每天任务

1
2
3
4
5
Day 1: 实现 dtype 参数(fp32/fp16/bf16)和 autocast 切换
Day 2: 验证 AMP 和 FP32 输出误差(max_abs_error 对比)
Day 3: 加入多个 batch_size 选项
Day 4: 打印配置摘要(model/device/dtype/batch_size)
Day 5: 把配置摘要保存到 results/logs/ 里

验收标准

  • [ ] python main.py --dtype fp16 --batch-size 64 能正确运行
  • [ ] AMP 和 FP32 的输出最大绝对误差 < 0.01
  • [ ] 每次运行自动保存一份配置摘要 JSON

M3(第 3 周):Benchmark 对比实验

目标:能产出结构化的 benchmark 对比表

每天任务

1
2
3
4
5
Day 1: 接入 torch.utils.benchmark.Timer,替换手写计时
Day 2: 实现 run_full_sweep(遍历 dtype × batch_size)
Day 3: 把结果保存为 JSON(包含 latency/throughput/memory)
Day 4: 在命令行打印对比表(对齐列宽)
Day 5: 写第一份实验结论(见第 4 节的结论模板)

验收标准

  • [ ] python main.py --mode benchmark 能自动跑多组对比
  • [ ] 至少对比 3 种配置(如 fp32/fp16/fp32-compile)
  • [ ] 结果保存在 results/benchmarks/*.json
  • [ ] 能说出"某配置的吞吐比某配置高 X%,原因是…"

M4(第 4 周):Profiler 热点分析

目标:能找到"慢在哪里",而不只是"多慢"

每天任务

1
2
3
4
5
Day 1: 跑 profiler,看 key_averages().table() 输出
Day 2: 找到 self_cuda_time_total 最高的 op,查是什么计算
Day 3: 导出 Chrome trace,用 chrome://tracing 打开,看时间线
Day 4: 对比 fp32 和 fp16 的 profiler 结果,找差异
Day 5: 写一段 profiler 分析结论(参考第 5 节格式)

验收标准

  • [ ] python main.py --mode profile --dtype fp16 能输出热点表
  • [ ] 能说出"排名第一的 op 是 X,它是 Y 层做的 Z 计算"
  • [ ] 能比较 fp32 和 fp16 的 profiler 热点分布差异
  • [ ] 有一份 trace.json 文件并能在浏览器中打开

M5(第 5 周):扩展 + 总结 + 收尾

目标:让项目"能展示"

每天任务

1
2
3
4
5
Day 1: 选一个扩展方向(ONNX 导出 or FastAPI 服务)并实现
Day 2: 验证扩展功能(ONNX 误差对比 or API 调用测试)
Day 3: 完善 README(加实验结果表格和结论)
Day 4: 写简历项目描述(参考第 6 节模板)
Day 5: 准备技术问答提纲(参考第 7 节)

验收标准

  • [ ] GitHub README 包含:项目介绍、快速开始命令、实验结果表格、结论摘要
  • [ ] 简历中有 3 行以上、含量化指标的项目描述
  • [ ] 能口述约 5 分钟项目介绍,并包含具体数字

4. 怎么写"工程水平"的实验结论

不推荐写法(缺少对照与边界):

实验结果显示,使用 AMP 后速度有所提升,显存也有所减少,效果不错。

推荐结构:对比对象 + 数字结果 + 机制原因 + 成立条件与反例。

模板

1
2
3
4
5
6
7
8
9
10
11
12
13
【对比对象】[配置 A] vs [配置 B],在 [模型/输入规模/设备] 条件下

【数字结果】
- latency:A = X ms,B = Y ms,变化 ±Z%
- throughput:A = X samples/s,B = Y samples/s,变化 ±Z%
- memory_allocated:A = X MB,B = Y MB

【原因分析】
出现这种差异是因为:[说机制,不要说感觉]

【边界说明】
该结论在以下条件下成立:[限制范围]
在以下情况下可能不成立:[反例或例外]

一个用真实数字写的完整例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
【对比对象】FP32 eager vs AMP-FP16 autocast
模型:MLP(4层,512→2048→512)
batch_size = 64,device = CUDA(A100)

【数字结果】
- latency:FP32 = 1.85ms,FP16 = 0.95ms,下降 48.6%
- throughput:FP32 = 34,594 samp/s,FP16 = 67,368 samp/s,提升 94.7%
- memory_allocated:FP32 = 128MB,FP16 = 64MB,减少 50%

【原因分析】
MLP 的主体计算是矩阵乘法(aten::addmm)。在 CUDA 上,float16 的矩阵乘
法走 Tensor Core 路径,理论峰值吞吐是 float32 的 2 倍。
同时,float16 每个参数占 2 字节(vs float32 的 4 字节),使得内存带宽
压力减半,缓存命中率更高。
Profiler 结果显示,aten::addmm 的 self_cuda_time 从 1.2ms(FP32)
降到 0.6ms(FP16),恰好印证了 Tensor Core 加速。

【边界说明】
该结论在以下条件下成立:
- batch_size ≥ 16(小 batch 时 Tensor Core 利用率不够,收益不明显)
- 模型主体是矩阵乘法(非 softmax/LayerNorm 等敏感 op)
- 硬件支持 Tensor Core(如 V100/A100/RTX 系列)

在 batch_size=1 下实测:
- FP32 = 0.21ms,FP16 = 0.18ms,仅提升 14%(Tensor Core 吃不满)

5. Profiler 结论的标准写法

Profiler 的分析结论也要有格式,不能只说"aten::addmm 很慢"。

模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
【分析条件】[模型/batch/dtype/device]

【热点 Top 3】
排名 1:[op名] self_cuda_time = Xμs 占比 Y%
输入 shape: [shape] 来自:[代码位置]
排名 2:...
排名 3:...

【CPU vs CUDA 分布】
CPU 总时间:Xms CUDA 总时间:Yms 比值:1:Z
结论:[计算密集 or 受 Python 调度开销影响 or ...]

【时间线观察】(需要 Chrome trace)
CPU/GPU 是否重叠:[是/否]
是否有大量小 kernel:[有/无]
是否有明显空洞:[有/无,原因]

【结论与建议】
[当前瓶颈在哪,下一步可以尝试什么优化]

6. 简历项目描述:不同方向的写法

可按投递方向选择侧重点(下列为示例条目,须替换为个人实测数据)。

方向 A:AI Infra 偏后端/Runtime

1
2
3
4
5
搭建单机 PyTorch 推理性能分析平台:
• 实现 FP32/AMP-FP16/AMP-BF16 精度切换与 torch.compile() 对比实验框架
• 集成 torch.profiler 进行算子级热点分析(aten::addmm 耗时占比 ~60%),导出 Chrome trace 定位 CPU/GPU 重叠问题
• 完成 ONNX 导出(dynamo=True)并验证输出误差 < 1e-4,打通 PyTorch→ONNX Runtime 部署链路
• 实验结论:AMP-FP16 在 batch=64 下吞吐较 FP32 提升 ~2×,显存减少 50%;小 batch 下收益 <15%

方向 B:MLSys 工程师 / 系统优化

1
2
3
4
5
实现 PyTorch 推理 benchmark 与性能归因系统:
• 基于 torch.utils.benchmark.Timer 构建稳健的延迟测量(自动 warmup + CUDA 同步),支持多配置矩阵对比
• 通过 profiler 定位矩阵乘法(aten::addmm)为主要瓶颈,验证 kernel fusion 在 torch.compile 下的加速效果
• 量化不同 batch size 下吞吐/延迟的 trade-off:batch 1→64,吞吐提升 20×,单请求延迟增加 9×
• 分析 GPU 冷启动开销(首次运行慢 40 倍),建立 warmup → benchmark → profiler 的标准分析流程

方向 C:应用工程师 / 算法工程师偏部署

1
2
3
4
5
构建 PyTorch 推理实验平台并封装最小推理服务:
• 搭建支持 FP32/AMP/compile 的推理实验框架,完成多精度、多 batch 对比实验
• 使用 FastAPI 封装 /infer 和 /benchmark 接口,验证推理服务基本功能
• 完成 ONNX 导出并使用 onnxruntime 进行推理结果一致性验证
• 系统梳理 eval()/inference_mode()/autocast 三层推理上下文的功能差异

7. 常见问题与可验证回答要点

将口头表述与仓库中的日志、JSON、Profiler 表对齐时使用下列结构:考查点可引用度量须声明的前提(设备、同步、batch、模型结构)。下文示例数字与第 4、5 节模板一致,正式使用时应替换为实测结果。

1. AMP 与推理加速

  • 考查点:混合精度对延迟、吞吐、显存的影响及边界。
  • 可引用:同结构 MLP 下 FP32 与 AMP-FP16 的 latency、throughput、memory_allocatedaten::addmm 在 Profiler 中的占比;batch=1 与 batch=64 的对比表。
  • 前提:GPU 型号、是否 warmup、计时是否含设备同步或采用 torch.utils.benchmark.Timer

2. torch.compile 的收益与风险

  • 考查点:冷启动与稳态;图断裂与 guard。
  • 可引用:首调与多次调用后的延迟;Profiler 是否体现 kernel fusion;变更 batch 是否触发重编译。
  • 前提:PyTorch/CUDA 版本;forward 是否存在依赖张量取值的控制流。

3. CUDA 计时与同步

  • 考查点:异步执行导致的测量偏差。
  • 可引用:插入 torch.cuda.synchronize() 前后的 wall-clock 差异;Timer 输出的中位数与 IQR。
  • 前提:明确报告的是主机返回时刻还是内核完成时刻。

4. Profiler 与热点

  • 考查点self_cuda_time_total、调用栈与 shape 记录。
  • 可引用:热点算子名称与占比;with_stack=True 映射到的源码行;FP32/FP16 热点对比。
  • 前提:profile 轮次、record_shapes 是否开启。

5. batch 与吞吐、延迟

  • 考查点:单请求延迟与系统吞吐随 batch 的变化及饱和区。
  • 可引用:batch–latency–throughput 表;高 batch 区边际收益下降现象。
  • 前提:静态/动态 batch、padding 策略。

6. KV Cache 与并发(参见第 08 篇)

  • 考查点:缓存体量公式与显存预算。
  • 可引用2 × layers × heads × seq_len × head_dim × dtype_bytes 的代入计算;PagedAttention 等缓解碎片化的工程手段。
  • 前提:模型规模、序列长度、精度为显式假设。

7. 典型故障与排查

  • CUDA 计时不含同步 → 补充同步或标准 Timer。
  • torch.compile 首调过慢 → 归因 JIT/warmup,报告稳态。
  • AMP 数值误差 → 记录 max_abs_error 与任务容忍度。
  • ONNX 固定 batch → 检查导出时动态维声明(如 Dim)。

8. 项目快照清单:每个里程碑留什么证据

每完成一个里程碑,留下这些材料:

里程碑 需要留的证据
M1 ① 能成功运行的命令 + 输出截图 ② 输出显示正确的 shape/device/dtype
M2 ① FP32 和 FP16 都能运行的日志 ② 两者输出误差对比数字
M3 results/benchmarks/*.json 文件 ② 打印的对比表格截图 ③ 一段结论文字
M4 profiler.key_averages().table() 输出截图 ② Chrome trace 文件 ③ 热点解释文字
M5 ① ONNX 文件 + 误差验证截图 或 API 调用成功截图 ② 更新后的 README

上述材料可作为 README、版本库附件及技术讨论中的可复核依据。


9. README 结构建议

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# PyTorch Inference Lab

**单机 PyTorch 推理性能分析平台** | 延迟 · 吞吐 · 显存 · 热点

## 项目目标
比较不同推理配置(精度/batch/compile)对性能的影响,建立推理优化的量化分析流程。

## 快速开始
```bash
pip install -r requirements.txt

# 推理验证
python main.py --mode infer --model mlp --batch-size 32 --device cuda

# 完整 benchmark
python main.py --mode benchmark --model mlp --device cuda

# 热点分析
python main.py --mode profile --model mlp --batch-size 64 --dtype fp16

实验结果

精度 batch latency throughput mem_alloc
fp32 1 0.21ms 4,761/s 128MB
fp32 64 1.85ms 34,594/s 128MB
fp16 64 0.95ms 67,368/s 64MB
fp16 + compile 64 0.78ms 82,000/s 64MB

关键结论

  • AMP-FP16 在 batch=64 下较 FP32 吞吐提升 ~94%,显存减少 50%
  • torch.compile 在大 batch 下额外提升 22%(kernel fusion 收益)
  • 小 batch(batch=1)下 AMP 收益不明显(仅 14%),GPU Tensor Core 利用率低

Profiler 热点(FP16, batch=64)

  • 排名 1:aten::addmm 占 CUDA 时间 62% → 来自 Linear 的矩阵乘法
  • CPU/GPU 重叠良好,无调度瓶颈

技术栈

PyTorch 2.x · CUDA · torch.profiler · torch.utils.benchmark · ONNX Runtime · FastAPI

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

---

## 10. 最小可交付的 6 条判据

在扩展功能前,可先对照下列条目评估当前仓库是否构成完整叙事:

| 标准 | 判断方式 |
|------|---------|
| ① 目标明确 | 能一句话说清楚项目在研究什么问题 |
| ② 推理闭环 | `model.eval() + inference_mode()` 正确用,device 正确 |
| ③ 有效对比 | 至少 3 组配置对比,有数字,有结论 |
| ④ 结构化结果 | `results/` 目录下有 JSON 或 CSV |
| ⑤ 热点解释 | 能说出"排名第一的 op 是 X,因为 Y" |
| ⑥ 术语与量化 | 对外描述中含 benchmark、profiler、AMP、compile 等可核查术语及数字 |

---

## 11. 项目之后的扩展路线图

当前:单机 MLP / CNN 推理实验平台

├──→ 路线 A:序列模型 / LLM
│ 加入简单 Transformer block
│ 实验 seq_len 对 prefill 的 O(L²) 影响
│ 实现极简 KV Cache 演示

├──→ 路线 B:部署 / Runtime
│ ONNX Runtime 对比(CPU vs CUDA provider)
│ TensorRT 导入和推理对比(进阶)
│ 量化实验(INT8 weight-only)

└──→ 路线 C:服务化 / 系统
FastAPI 完整推理服务
请求队列 + 简单 batching 模拟
tail latency(P50 / P95 / P99)测量
ab / locust 压测工具体验


根据投递岗位选择最匹配的路线继续深入,不需要三条全做。

---

## 12. 电梯陈述提纲(约 60–90 秒)

- **问题**:在固定模型上比较 FP32 / AMP / `torch.compile` 等对延迟、吞吐与显存的影响,并做热点归因。
- **手段**:`torch.utils.benchmark`、`torch.profiler`、ONNX 导出与误差核对;`eval` + `inference_mode` + 按需 `autocast`。
- **结果(示例,须换成本地数字)**:batch=64 时 AMP-FP16 相对 FP32 吞吐约 ×2、显存约减半;batch=1 时收益收窄;`torch.compile` 首调慢、稳态约再省一部分时间,Profiler 可指向融合。
- **延伸**:与第 08 篇中 KV、批调度等系统话题的衔接点。

---

## 13. 本节要点与自检

- 按里程碑推进,每周有可验收交付物
- 实验结论采用「对比 + 数字 + 机制 + 边界」结构
- Profiler 结论含热点算子、占比及与代码位置的对应关系
- 简历/自述与实测日志一致,并按方向裁剪侧重点
- 技术问答能与仓库中表格、JSON、trace 对应
- README 足以令他人在相同环境下复现实验
- 能用约 60–90 秒陈述项目问题、方法与量化结果

---

## 14. 小结

> 可交付性主要来自:里程碑化执行、可复核的实验记录,以及将测量结果与机制解释对齐的表述;功能数量本身不是核心指标。

---

## 系列导航

- 上一篇:[PyTorch推理工程:09 最小可展示推理项目骨架](/posts/432e473/)
- 下一篇:建议回到 [系列导读](/posts/846fbbaf/),对照文首「学习成果检验」自查。
- [系列索引(00 导读)](/posts/846fbbaf/)