一张显卡上大模型并行处理多个请求的原理
当多个用户请求并行时,大模型的输入层和输出层接收和吐出的内容,可以非常具体地描述如下。
1. 输入层接收的是什么?
输入层通常指词嵌入层。它接收的是一个整数矩阵,形状为:
(batch_size, seq_len)
batch_size:同时处理的请求数量,比如 8。seq_len:每个请求在当前这一步送入模型的 token 数量。在生成阶段,这通常是 1(即每个请求的“当前最新一个 token”);在 prefill 阶段,则是该请求 prompt 的完整长度。
举例,当 8 个请求都在生成第 5 个 token 时,输入层看到的可能是:
[[ 456], // 请求A的当前token ID
[ 1234], // 请求B的当前token ID
[ 78], // 请求C的当前token ID
...
[ 902]] // 请求H的当前token ID
这个矩阵被送入词嵌入层后,会转换为一个浮点数张量:
(batch_size, seq_len, hidden_dim)
比如 (8, 1, 4096)。每一“行”对应一个请求,各行之间没有任何混合计算,仅仅是并行地查表并乘以嵌入矩阵。所以,输入层接收的是多个请求的 token ID 列表,并给出它们各自的向量表示。
2. 输出层吐出的是什么?
输出层通常指模型顶部的线性分类头,也就是 lm_head。它接收的是整个 Transformer 最后一层输出的隐藏状态,形状同样为 (batch_size, seq_len, hidden_dim)。
在生成阶段,我们只关心“下一个 token”,所以通常只取最后一个 token 的隐藏状态,即形状压缩为 (batch_size, hidden_dim)。
这个隐藏状态经过线性层和 softmax 后,输出一个logits 矩阵(或概率矩阵):
(batch_size, vocab_size)
每一行是一个长度为 vocab_size(如 32000)的向量,代表该请求对词表中所有 token 的预测分数。然后对该行进行采样,就得到该请求的下一个 token。
所以,输出层一次性吐出 8 个请求各自的下一个 token 概率分布,每个请求独立占据一行,毫不相干。
3. 整个流程的完整示例
假设现在 batch 中有三个请求,它们各自待生成下一步:
- 请求 A:已经生成了
“我今天很”,当前 token 是“很”(ID 100),要预测下一个。 - 请求 B:已经生成了
“天气”,当前 token 是“气”(ID 2056),要预测下一个。 - 请求 C:刚完成 prompt 处理,准备生成第一个 token。
输入层:
接收的 token ID 矩阵为:
[[100], // 请求A
[2056], // 请求B
[0]] // 请求C(假设用0表示开始生成标记)
嵌入后得到 (3, 1, hidden_dim)。
深层网络计算:
通过注意力层时,每个请求会看到自己的历史 token(通过 KV cache 和掩码实现),但完全看不到其他请求的 token。最终输出隐藏状态 (3, 1, hidden_dim),然后取最后一个 token 的状态得 (3, hidden_dim)。
输出层:
这个 (3, hidden_dim) 进入线性头,产生 (3, vocab_size) 的 logits:
[[0.1, 0.2, ..., 0.05], // 请求A的预测分布
[0.01, 0.3, ..., 0.1], // 请求B的预测分布
[0.15, 0.25, ..., 0.02]] // 请求C的预测分布
然后独立采样出:
- A 的下一个 token:
“开心”(ID 520) - B 的下一个 token:
“不错”(ID 89) - C 的下一个 token:
“你好”(ID 12)
4. 关键澄清:不是“神经元”独立处理,而是张量并行
在“神经元”层面,可以这样理解:
输入层和输出层的每一个单元(神经元)确实在同时参与多个请求的计算,但它们是通过矩阵乘法的批处理维度来实现的。就像一次能同时处理多行的 Excel 公式,不会把不同行的数据加错。
配合LayerNorm和注意力掩码,所有请求在物理上共享同一套权重,逻辑上却像各自运行一个独立模型,输入层接收多个请求的 token,输出层吐出每个请求的专属概率分布。
简而言之:
输入层:同时喂入多个请求的当前 token,吐出它们的向量。
输出层:同时吐出多个请求的下一个 token 概率分布,各取所需。
整个过程,一次前向计算就完成了所有请求的“下一步”预测。
更多推荐

所有评论(0)