一、吞吐量基础概念
什么是训练吞吐量?
训练吞吐量,说白了就是你的模型在单位时间内能“吃”多少数据。
我习惯这样理解:你有一台超级跑车,吞吐量就是它每小时能跑多少公里。只不过在AI训练里,我们关心的是每秒能处理多少个样本,或者每秒能处理多少个token。
举个例子。你训练一个GPT模型,每次喂进去一批文本。如果每秒能处理1000个样本,那吞吐量就是1000 samples/sec。如果换成token来算,可能就是50000 tokens/sec。
嗯,这里要注意一个细节。吞吐量不是越高越好,它得跟你的硬件、模型、数据管道匹配。我在项目中遇到过有人盲目追求高吞吐,结果模型收敛反而变慢了。为什么?因为吞吐量上去了,但每个batch的质量下降了。
为什么吞吐量如此重要?
你想想看,训练一个大模型要花多少钱?
我算过一笔账。用8张A100训练一个70B的模型,如果吞吐量能提升20%,那就能省下几十万的电费和机时费。这不是小数目。
吞吐量直接影响三个关键指标:
- 训练时间:吞吐量翻倍,训练时间减半
- 硬件成本:同样的时间,能跑更多的实验
- 迭代速度:模型调优、超参搜索都快得多
我曾经接手过一个项目,团队花了两周才跑完一次训练。我优化了数据加载和通信策略,吞吐量提升了3倍。结果呢?原来两周的工作,现在四天搞定。团队士气一下子就上来了。
衡量指标:samples/sec vs tokens/sec
这两个指标,我建议你都要关注。它们看的是不同维度。
| 指标 | 适用场景 | 我的经验 |
|---|---|---|
| samples/sec | CV模型、固定尺寸输入 | 适合对比不同batch size下的性能 |
| tokens/sec | NLP模型、变长输入 | 更能反映真实计算效率 |
我个人习惯,在NLP任务里优先看tokens/sec。为什么?因为样本长度不一样,光看样本数会骗人。
举个例子。你处理一批短文本,每句10个token,每秒能处理1000个样本。换成每句1000个token的长文本,每秒可能只能处理100个样本。但tokens/sec这个指标,就能真实反映你的计算资源利用率。
核心要点:samples/sec适合快速估算,tokens/sec适合精细调优。两个指标结合着看,才能全面把握训练效率。
吞吐量的计算公式
其实公式很简单:
吞吐量 = 总处理量 / 总耗时
samples/sec = 总样本数 / 总训练时间
tokens/sec = 总token数 / 总训练时间
但实际计算时,我建议你注意两点:
- 排除预热阶段:刚开始训练时,GPU还没跑满,数据还没预热。前几个step的数据要剔除。
- 取稳定区间:等训练稳定后,取一段连续时间的平均值。我一般取100个step的均值。
小技巧:用PyTorch的Profiler或者NVIDIA的Nsight Systems,能精确测量每一步的耗时。我习惯在代码里加个计时器,每100步打印一次吞吐量,方便实时监控。
影响吞吐量的关键因素
我总结了一个框架图,帮你理清思路:
这个图我画了很多次。每次优化吞吐量,我都会对着它检查:是计算瓶颈?数据瓶颈?还是通信瓶颈?
避坑指南:我曾经犯过一个错误。只盯着计算效率优化,把算子调得飞快,结果数据加载跟不上,GPU一直在空等。后来加了数据预取和缓存,吞吐量直接翻倍。记住,瓶颈永远在最短的那块板子上。
如何测量吞吐量?
我分享一个我常用的测量脚本:
import time
import torch
def measure_throughput(model, dataloader, device, num_steps=100):
model.train()
total_samples = 0
total_tokens = 0
start_time = time.time()
for i, (inputs, labels) in enumerate(dataloader):
if i >= num_steps:
break
inputs = inputs.to(device)
labels = labels.to(device)
outputs = model(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
# 统计
batch_size = inputs.size(0)
total_samples += batch_size
# 假设每个样本的token数已知
total_tokens += batch_size * avg_tokens_per_sample
elapsed = time.time() - start_time
samples_per_sec = total_samples / elapsed
tokens_per_sec = total_tokens / elapsed
return samples_per_sec, tokens_per_sec
这个脚本虽然简单,但够用。我建议你跑的时候,把num_steps设成200以上,这样数据更稳定。
吞吐量优化的核心思路
说白了,就三件事:
- 让计算更高效:用更少的计算资源做更多的事
- 让数据不等待:数据加载永远比计算快一步
- 让通信不阻塞:梯度同步、参数更新别拖后腿
我在实际项目中,通常按这个顺序排查:先看数据加载,再看通信,最后优化计算。为什么?因为数据问题最容易解决,收益也最明显。
记住:吞吐量不是终点,是手段。最终目标是让模型更快收敛,让实验迭代更快。别为了追求吞吐量,牺牲了模型质量。
好了,这一章就到这里。吞吐量的基础概念,说白了就是这些。下一章我们会深入聊聊如何具体优化数据管道,那才是真正出活的地方。