并行处理与上下文长度:AI 的架构是如何处理输入的 → 超强多线程

#2025/12/29 #ai

目录

1. 并行 → 并行处理,同时开工

以前的老模型(如 RNN)处理文本像写 for 循环,必须处理完第 1 个词,才能处理第 2 个,再处理第 3 个……效率很低。

{%}|672

  • 直观理解:想象一个巨大的工厂流水线。
    • 输入:“Write an email to Sarah”(5 个词元)。
    • 模型会瞬间开启 5 条平行的传送带 → 并行计算
    • 每个词元跳上自己的传送带,同时开始向前跑,经过层层神经网络的处理。

又比如: “Write an email” → 后面有备注

  • 流水线 1 处理 "Write"
  • 流水线 2 处理 "Write an"
  • 流水线 3 处理 "Write an email"

虽然每个词元有自己的通道,但在"注意力"步骤时,它们会互相交流信息(比如 “it” 会去问 “Sarah” 指的是谁)。

1.1. 一些疑问❓

① 流水线 2 处理 "Write an" ,其实不对,而是每次都拿到了一个词,但因为注意力掩码机制 , 虽然每次拿到一个词,但可以回头看,所以 效果 和 流水线 2 处理 "Write an" 是一样的

② 但即使是并行的,也得等到所有并行都 resolve ,统一集合后才能进入下一层

流水线 2 可以回头看流水线 1,是否意味着它依赖于流水线 1

  • 是“数据依赖”,但不是“执行等待”
  • 在下一层时,上一层完全的 Promise.all 了,已经都存档到,去读就好了
  • 所以是: 它们是在“读存档”,而不是“等实时计算”

2. 上下文长度 = 内存缓冲区大小 (MAX_BUFFER_SIZE)

你可能经常听说某个模型支持 “4K” 或 “128K” 上下文。这在代码里其实就是一个最大数组长度缓冲区限制

  • 定义
    • 模型的上下文长度(Context Length),就是它同时能开启多少条“计算跑道”
    • 上下文长度 (Context Length) 就是模型一次性能并行处理的最大 Token 数量。
  • 硬件限制
    • 如果模型是 4K(4096)上下文,意味着它有 4096 条跑道。
    • 如果你输入 4000 个词,它能全部并行处理。
    • 如果你输入 5000 个词,多出来的 1000 个就没有跑道可用,模型就会“报错”或必须截断输入。

[!info]
注意:这个“总 token 数”通常指 输入 + 已经生成的输出 一起算在内,举个例子

  • 第 1 轮:模型看 ["Write", "an", "email"] (3个词),算出第 4 个词。
  • 第 10 轮:模型看 ["Write", "an", "email", ... + 9个新词] (12个词),算出第 13 个词。
  • 第 N 轮:模型必须看着 (原本的输入 + 之前所有生成出来的词),才能保证写出的文章前后通顺。
  • 所以,占用的内存(Token 数)= 你给它的 + 它刚写出来的

3. 每一条“跑道”(计算流)里发生了什么?

  1. 起点(Input):词元不是光秃秃进去的,它携带了两个信息打包成的向量
    • 身份信息(我是什么词,比如 “Cat”)
    • 位置信息(我是第几个词,比如 index=5)
    • 注:这一步叫“嵌入 + 位置编码”。
  2. 过程(Process)
    • 数据流经神经网络层 → 有很多很多层
    • 虽然大家各跑各的道,但在中间的某些站点(称为注意力层),
    • 不同的跑道之间会“交换情报”(比如“it”这个词的跑道会去问“dog”那个跑道的含义),但总体架构是独立的流。
  3. 终点(Output)
    • 每条跑道的尽头,都会输出一个新的向量,代表处理后的结果
    • 最后得到一个"处理过的向量",包含了上下文信息,用于预测下一个词元。

另一个向量作为模型处理的结果出现,如下图

{%}|824

图 3-9:每条处理流接收一个向量作为输入,并生成一个大小相同的最终结果向量

不太明白,请看后文 每个词元的旅程

4. 关键逻辑:虽然都算了,但只用最后一个

这是最容易让程序员困惑的地方:

  • 现象:
    • 当你输入 “Write an email”(假设 3 个 Token)让模型写信时
    • 模型其实并行输出了 3 个结果向量。
  • 取舍:
    • 但是,为了预测下一个字是什么
    • 我们只关心最后一条流(对应 “email” 那条)的输出向量。
  • LM Head 的工作:
    • 这个最后的向量会被送入 LM Head(语言建模头),转换成概率,猜出下一个词是 “to”。

既然只用最后一个,为什么前面的一起算了?是浪费算力吗?

  • 不是浪费:
    • 虽然我们不直接用前面流的输出结果,但最后一条流在计算时,需要通过
      • 注意力机制(Attention)去“回头看”前面的流。

5. 每个词元的“旅程“

5.1. 什么是“一个向量作为输入“?

当一个词元(比如 “Write”)进入 Transformer 时:

# 假设模型的隐藏维度是 3072  
输入词元: "Write"  
       ↓  
首先转换成向量(嵌入向量):  
[0.23, -0.45, 0.78, ..., 1.23]  # 一共 3072 个数字  

这个向量包含

  • 词元本身的嵌入(这个词的基本含义)
  • 位置信息(它在句子中的位置)

5.2. 什么是“最终结果向量“?

这个词元的向量会穿过所有的 Transformer 层(比如 32 层),每一层都会对它加工处理:

输入向量 [3072 个数字]  
    ↓ 穿过 Transformer 块 1(注意力 + 前馈网络)  
中间向量 [3072 个数字]  # 融合了其他词元的信息  
    ↓ 穿过 Transformer 块 2  
中间向量 [3072 个数字]  
    ↓ ...  
    ↓ 穿过 Transformer 块 32  
输出向量 [3072 个数字]  # 这就是"最终结果向量"  

关键点

  • 大小相同:输入是 3072 个数字,输出也是 3072 个数字
  • 内容完全不同
    • 虽然数量一样,但经过处理后的值已经大变样了

5.3. 为什么说“大小相同“?

# 以 Phi-3 模型为例  
输入矩阵形状:  [1, 5, 3072]  
              批次 词元数 隐藏维度  
              
输出矩阵形状:  [1, 5, 3072]  
              批次 词元数 隐藏维度(大小没变!)  

每个词元位置:

  • 输入:一个 3072 维的向量
  • 输出:还是一个 3072 维的向量

这就像

  • 你把一张 A4 纸(输入向量)放进复印机
  • 出来的还是一张 A4 纸(输出向量)
  • 但纸上的内容已经改变了(融合了上下文信息)

5.4. 这个“最终结果向量“有什么用? → 只用最后一个输出向量 来做预测

对于生成任务,只有最后一个词元输出向量会被送到“语言建模头“去预测下一个词:

句子: "Write an email"  
         ↓          ↓       ↓  
      输出向量1  输出向量2  输出向量3  
                            ↓  
                    只有这个被用来预测下一个词!  
                            ↓  
                    语言建模头  
                            ↓  
                    预测结果: "to" (概率 40%)  

但是! 前面词元的输出向量虽然不直接用于预测,但它们在注意力步骤中被后面的词元“回头看“,从而影响最终的预测。

5.5. 用代码理解

# 实际的数据流  
input_text = "Write an email"  
tokens = tokenizer(input_text)  # 分词: ["Write", "an", "email"]  

# 每个词元变成一个向量(3072 维)  
input_vectors = embed(tokens)  
# 形状: [3, 3072]  
#   3 个词元  
#   每个词元 3072 个数字  

# 穿过 32 层 Transformer  
output_vectors = transformer_layers(input_vectors)  
# 形状还是: [3, 3072]  ← 大小相同!  
#   还是 3 个词元  
#   每个还是 3072 个数字  
#   但具体的数值已经完全改变了  

# 只用最后一个词元的输出向量预测下一个词  
next_token_logits = language_head(output_vectors[2])  # 取第 3 个  
# 输出: [32064] ← 词表中每个词元的分数  

6. 总结

概念通俗解释
输入向量词元进入模型时的数字表示(包含词义 + 位置信息)
最终结果向量词元经过所有 Transformer 层处理后的数字表示
“大小相同”输入是 N 个数字,输出也是 N 个数字(比如都是 3072 个)
“作为模型处理的结果”这个向量融合了上下文信息,变得更“聪明“了
只用最后一个虽然每个词元都有输出向量,但只有最后一个直接用于预测下一个词