2、Batch推理的数学原理:矩阵运算与向量化、数据并行与模型并行的概念、Batch Size对计算效率的影响

2.1 矩阵运算与向量化:Batch推理的数学基础

咱们先聊聊Batch推理最底层的数学逻辑。说白了,深度学习模型推理的本质就是一连串的矩阵乘法。你想想看,一个全连接层,输入向量x,权重矩阵W,输出就是y = xW + b。单条样本是这样,那多条样本呢?

我个人习惯把Batch理解为「把多条样本摞起来一起算」。假设你有N条样本,每条样本是d维向量,那输入就变成了一个N×d的矩阵。权重矩阵是d×k,输出就是N×k。你看,一次矩阵乘法,N条样本的结果全出来了。

这里有个关键点——向量化。我在项目中遇到过不少新手,写推理代码时喜欢用for循环一条条算:

# 低效做法:逐条推理
outputs = []
for sample in batch:
    out = np.dot(sample, W) + b
    outputs.append(out)

嗯,这样写逻辑没错,但效率极低。为什么?因为CPU/GPU的矩阵运算单元是专门为并行计算设计的。你一条条算,相当于让一个八车道高速只跑一辆车。

正确的做法是一次性把整个Batch扔进去:

# 高效做法:批量矩阵运算
outputs = np.dot(batch, W) + b  # batch shape: [N, d]

这就是向量化的威力。我记得有一次帮团队优化推理服务,把for循环改成批量矩阵运算后,吞吐量直接翻了8倍。你想想看,代码改动就一行,效果天差地别。

核心要点:Batch推理的数学本质,就是把单样本的向量运算扩展成矩阵运算。矩阵运算的并行特性,是Batch提升效率的根本原因。

2.2 数据并行与模型并行:两种并行策略

当模型太大或者数据太多时,单卡搞不定怎么办?这时候就需要并行策略上场了。我把它分成两类:数据并行和模型并行。

数据并行,说白了就是「数据分家,模型共享」。每张卡上都放一份完整的模型副本,然后把Batch数据切成几块,每张卡算一块。最后把梯度或结果汇总一下。

举个例子,假设你有4张GPU,Batch Size=64。数据并行就是每张卡分到16条样本,各自独立做前向推理。最后把4份结果拼起来,得到完整的64条输出。

我曾经在训练一个BERT模型时用过数据并行。当时单卡显存刚好够放下模型,但训练速度太慢。加了数据并行后,4卡同时算,训练时间缩短到原来的1/3左右。注意,不是1/4,因为通信同步有开销。

模型并行则相反,是「模型分家,数据共享」。当模型大到单卡放不下时,比如GPT-3这种1750亿参数的怪兽,就得把模型的不同层或不同参数切分到多张卡上。每张卡只负责模型的一部分,数据要依次流过所有卡。

我建议你记住这个对比:

维度 数据并行 模型并行
适用场景 数据量大,模型可放入单卡 模型超大,单卡放不下
通信开销 梯度同步(AllReduce) 中间结果传递(P2P)
实现难度 较低,框架原生支持 较高,需手动切分
典型框架 PyTorch DDP, Horovod Megatron-LM, DeepSpeed

我的经验:能上数据并行就别上模型并行。数据并行实现简单,通信模式也规整。模型并行虽然能解决显存问题,但卡间通信频繁,容易成为瓶颈。我曾经踩过一个坑——模型并行时卡间带宽不够,推理速度比单卡还慢。

2.3 Batch Size对计算效率的影响

Batch Size这个参数,看着简单,实际上门道很多。我把它对效率的影响总结为三个层面:计算利用率、内存带宽和延迟。

计算利用率:GPU的矩阵运算单元喜欢「大口吃肉」。Batch Size太小,比如Batch=1,矩阵运算的维度太小,计算单元吃不饱,利用率可能只有20%-30%。随着Batch Size增大,计算单元逐渐被填满,利用率能冲到80%以上。

但注意,这个提升不是线性的。我实测过,Batch从1增加到32,吞吐量能翻4-5倍。但从32增加到128,可能只提升50%。再往上,提升就微乎其微了。为什么会这样?因为计算单元已经饱和了,瓶颈转移到了别处。

内存带宽:这是很多人忽略的点。每次推理都要把模型参数从显存搬到计算单元。Batch Size越大,每个参数被重复利用的次数越多,单位数据搬运成本就越低。说白了,Batch=1时,你搬一次参数只算一条样本;Batch=64时,搬一次参数算64条样本,划算多了。

延迟:这里有个权衡。Batch Size越大,单次推理的延迟越高(因为要等所有样本算完)。但吞吐量(每秒处理的样本数)是上升的。在线服务通常要求低延迟,所以Batch Size不能太大。离线批处理则追求高吞吐,Batch Size可以设大一些。

避坑指南:我曾经在一个在线推理服务上把Batch Size设到128,结果延迟从10ms飙到了80ms,用户体验直线下降。后来我改成动态Batch——请求少时用小Batch保证低延迟,请求多时用大Batch提升吞吐。嗯,效果好了很多。

最后,我画了一张图帮你理解Batch Size与效率的关系:

Batch Size对计算效率的影响 Batch Size 1 16 64 256 1024 0% 50% 100% 计算利用率 计算利用率 吞吐量 延迟 计算利用率趋于饱和 吞吐量持续上升

从图上你能看到:Batch Size较小时,计算利用率和吞吐量增长很快;到一定程度后,利用率趋于饱和,但吞吐量还在缓慢上升;而延迟则是一直在增加。所以,选Batch Size就是在吞吐和延迟之间找平衡点。

我的建议:离线批处理,Batch Size可以设到128-512,追求最大吞吐。在线服务,建议从Batch=1开始,逐步增大,直到延迟达到你的SLA上限。我个人习惯在延迟容忍范围内,尽量把Batch Size设大一些。

好了,这一章的内容就到这里。Batch推理的数学原理其实不复杂,核心就是矩阵运算的并行化。理解了这些,后面讲工程实现时你就能明白为什么有些优化手段有效,有些则不行。