2026/1/15 19:14:23
网站建设
项目流程
做网站需要买服务器么,软件开发培训机构哪家好,wordpress后台改成中文,哈尔滨建设网站公司Decoder-Only架构
Decoder-only 架构摒弃了 Encoder-Decoder 架构中的编码器部分以及与编码器交互的交叉注意力模块。在这种架构下#xff0c;模型仅使用解码器来构建语言模型。这种架构利用“自回归”机制#xff0c;在给定上文的情况下#xff0c;生成流畅且连贯的下文。一…Decoder-Only架构Decoder-only 架构摒弃了 Encoder-Decoder 架构中的编码器部分以及与编码器交互的交叉注意力模块。在这种架构下模型仅使用解码器来构建语言模型。这种架构利用“自回归”机制在给定上文的情况下生成流畅且连贯的下文。一、计算流程1、初始设置该模型由3个Transformer解码器层Block堆叠而成任务设定输入提示词 (Prompt)假设已有2个Token[T1,T2][\text{T}_1, \text{T}_2][T1,T2]。目标自回归地生成接下来的4个TokenT3,T4,T5,T6\text{T}_3, \text{T}_4, \text{T}_5, \text{T}_6T3,T4,T5,T6。关键概念铺垫在Decode-only架构中为了防止“剧透”注意力机制是**因果Causal**的。Tokeniii只能看到它自己和它之前的Token (111到i−1i-1i−1)看不到未来的Token。为了提高效率我们使用KV Cache。当生成第NNN个Token时我们不需要重新计算前N−1N-1N−1个Token的Key (K) 和 Value (V) 向量而是直接从显存中读取之前存好的。2. 详细计算过程推演整个过程分为两个阶段预填充Prefill阶段处理提示词和解码Decoding阶段逐个生成。阶段 0预填充Prefill—— 处理[T1,T2][\text{T}_1, \text{T}_2][T1,T2]在开始生成之前必须先并行处理输入的提示词建立初始的KV Cache。输入T1,T2\text{T}_1, \text{T}_2T1,T2的嵌入向量x1,x2x_1, x_2x1,x2。过程以第 L 层为例L1,2,3计算所有输入Token在当前层的Q,K,VQ, K, VQ,K,V。将K1,K2K_1, K_2K1,K2和V1,V2V_1, V_2V1,V2存入第 L 层的 KV Cache。执行因果注意力计算T1\text{T}_1T1只看自己T2\text{T}_2T2看T1\text{T}_1T1和自己。预填充结束状态模型输出了T3\text{T}_3T3的预测概率分布。我们采样得到T3\text{T}_3T3。显存中建立了3个独立的CacheBlock 1 Cache:[(K1(1),V1(1)),(K2(1),V2(1))][(K_1^{(1)}, V_1^{(1)}), (K_2^{(1)}, V_2^{(1)})][(K1(1),V1(1)),(K2(1),V2(1))]Block 2 Cache:[(K1(2),V1(2)),(K2(2),V2(2))][(K_1^{(2)}, V_1^{(2)}), (K_2^{(2)}, V_2^{(2)})][(K1(2),V1(2)),(K2(2),V2(2))]Block 3 Cache:[(K1(3),V1(3)),(K2(3),V2(3))][(K_1^{(3)}, V_1^{(3)}), (K_2^{(3)}, V_2^{(3)})][(K1(3),V1(3)),(K2(3),V2(3))]阶段 1生成第1个Token (T3→T4\text{T}_3 \to \text{T}_4T3→T4)现在开始正式的Decode过程。当前的输入是刚刚生成的T3\text{T}_3T3。目标是预测T4\text{T}_4T4。当前输入向量h3(0)h_3^{(0)}h3(0)(即T3\text{T}_3T3的嵌入向量)。【第1次注意力计算Block 1】计算当前的Q, K, V使用Block 1的权重矩阵计算T3\text{T}_3T3的向量q3(1)h3(0)⋅WQ(1)q_3^{(1)} h_3^{(0)} \cdot W_Q^{(1)}q3(1)h3(0)⋅WQ(1)k3(1)h3(0)⋅WK(1)k_3^{(1)} h_3^{(0)} \cdot W_K^{(1)}k3(1)h3(0)⋅WK(1)v3(1)h3(0)⋅WV(1)v_3^{(1)} h_3^{(0)} \cdot W_V^{(1)}v3(1)h3(0)⋅WV(1)KV Cache 更新将新的k3(1)k_3^{(1)}k3(1)和v3(1)v_3^{(1)}v3(1)追加到 Block 1 的 Cache 末尾。当前Kcache(1)K_{cache}^{(1)}Kcache(1)变为[K1(1),K2(1),k3(1)][K_1^{(1)}, K_2^{(1)}, \mathbf{k_3^{(1)}}][K1(1),K2(1),k3(1)]当前Vcache(1)V_{cache}^{(1)}Vcache(1)变为[V1(1),V2(1),v3(1)][V_1^{(1)}, V_2^{(1)}, \mathbf{v_3^{(1)}}][V1(1),V2(1),v3(1)]注意力计算 (Attention)T3\text{T}_3T3的查询向量q3(1)q_3^{(1)}q3(1)需要与当前Cache中所有的K包括它自己进行计算。ScoresSoftmax(q3(1)⋅[K1(1),K2(1),k3(1)]Tdk)Scores \text{Softmax}(\frac{q_3^{(1)} \cdot [K_1^{(1)}, K_2^{(1)}, k_3^{(1)}]^T}{\sqrt{d_k}})ScoresSoftmax(dkq3(1)⋅[K1(1),K2(1),k3(1)]T)Output3(1)Scores⋅[V1(1),V2(1),v3(1)]Output_3^{(1)} Scores \cdot [V_1^{(1)}, V_2^{(1)}, v_3^{(1)}]Output3(1)Scores⋅[V1(1),V2(1),v3(1)]后续处理Output3(1)Output_3^{(1)}Output3(1)经过残差连接、层归一化和FFN得到 Block 1 的输出h3(1)h_3^{(1)}h3(1)。【第2次注意力计算Block 2】输入是上一层的输出h3(1)h_3^{(1)}h3(1)。计算当前的Q, K, Vq3(2)h3(1)⋅WQ(2)q_3^{(2)} h_3^{(1)} \cdot W_Q^{(2)}q3(2)h3(1)⋅WQ(2);k3(2)…k_3^{(2)} \dotsk3(2)…;v3(2)…v_3^{(2)} \dotsv3(2)…KV Cache 更新将k3(2),v3(2)k_3^{(2)}, v_3^{(2)}k3(2),v3(2)追加到 Block 2 的 Cache。Block 2KcacheK_{cache}Kcache现在包含T1,T2,T3\text{T}_1, \text{T}_2, \text{T}_3T1,T2,T3在第二层的Key。注意力计算q3(2)q_3^{(2)}q3(2)对 Block 2 的整个KcacheK_{cache}Kcache做注意力计算并取回VcacheV_{cache}Vcache。【第3次注意力计算Block 3】输入是h3(2)h_3^{(2)}h3(2)。重复上述过程更新 Block 3 的 KV Cache并完成注意力计算。【本步结束】Block 3 的输出经过最后的线性层Unembedding和Softmax采样得到下一个TokenT4\text{T}_4T4。阶段 2生成第2个Token (T4→T5\text{T}_4 \to \text{T}_5T4→T5)当前的输入是T4\text{T}_4T4。目标是预测T5\text{T}_5T5。注意观察Cache是如何增长的。当前输入向量h4(0)h_4^{(0)}h4(0)(T4\text{T}_4T4的嵌入向量)。【第1次注意力计算Block 1】计算 Q, K, V计算T4\text{T}_4T4在第一层的q4(1),k4(1),v4(1)q_4^{(1)}, k_4^{(1)}, v_4^{(1)}q4(1),k4(1),v4(1)。KV Cache 更新将k4(1),v4(1)k_4^{(1)}, v_4^{(1)}k4(1),v4(1)追加到 Block 1 Cache。Cache中现有Token[T1,T2,T3,T4][\text{T}_1, \text{T}_2, \text{T}_3, \mathbf{\text{T}_4}][T1,T2,T3,T4]。注意力计算 (Q与K的详细交互)当前的 Queryq4(1)q_4^{(1)}q4(1)需要“关注”之前所有的信息。Attention Scores calculation:Score(T4,T1)q4(1)⋅(K1(1))TScore(\text{T}_4, \text{T}_1) q_4^{(1)} \cdot (K_1^{(1)})^TScore(T4,T1)q4(1)⋅(K1(1))T(关注最早的提示词)Score(T4,T2)q4(1)⋅(K2(1))TScore(\text{T}_4, \text{T}_2) q_4^{(1)} \cdot (K_2^{(1)})^TScore(T4,T2)q4(1)⋅(K2(1))TScore(T4,T3)q4(1)⋅(k3(1))TScore(\text{T}_4, \text{T}_3) q_4^{(1)} \cdot (k_3^{(1)})^TScore(T4,T3)q4(1)⋅(k3(1))T(关注上一步生成的词)Score(T4,T4)q4(1)⋅(k4(1))TScore(\text{T}_4, \text{T}_4) q_4^{(1)} \cdot (k_4^{(1)})^TScore(T4,T4)q4(1)⋅(k4(1))T(关注自己)将这些分数Softmax归一化后对Vcache(1)V_{cache}^{(1)}Vcache(1)进行加权求和。【第2次 第3次注意力计算】数据依次流过 Block 2 和 Block 3。每一层都重复计算当前T4\text{T}_4T4的K/V - 存入对应层的Cache - 用T4\text{T}_4T4的Q去查询该层所有的Cache - 输出。【本步结束】生成TokenT5\text{T}_5T5。阶段 3生成第3个Token (T5→T6\text{T}_5 \to \text{T}_6T5→T6)过程同上。输入是T5\text{T}_5T5。在经过3个Block的运算后每个Block的KV Cache长度都会增加到5个[T1,T2,T3,T4,T5][\text{T}_1, \text{T}_2, \text{T}_3, \text{T}_4, \text{T}_5][T1,T2,T3,T4,T5]。T5\text{T}_5T5的 Query 向量会与这 5 个 Key 向量进行点积计算。本步结束生成TokenT6\text{T}_6T6。阶段 4生成第4个Token (完成任务)虽然题目只需生成4个Token (T3\text{T}_3T3到T6\text{T}_6T6)但通常生成T6\text{T}_6T6后模型还会继续预测下一个。我们演示最后这一步的Cache状态。输入是T6\text{T}_6T6。当T6\text{T}_6T6进入Block 3最后一次注意力计算时计算出q6(3),k6(3),v6(3)q_6^{(3)}, k_6^{(3)}, v_6^{(3)}q6(3),k6(3),v6(3)。将k6(3),v6(3)k_6^{(3)}, v_6^{(3)}k6(3),v6(3)存入 Cache。此时 Block 3 的 KV Cache 中包含了完整的历史上下文[T1,T2,T3,T4,T5,T6][\text{T}_1, \text{T}_2, \text{T}_3, \text{T}_4, \text{T}_5, \text{T}_6][T1,T2,T3,T4,T5,T6]在该层的 K 和 V 状态。q6(3)q_6^{(3)}q6(3)与所有这6个 K 向量进行计算聚焦相关信息最终帮助模型预测出序列的下一个词例如EOS\text{EOS}EOS结束符。3、细节解释1在生成多个Token时假设现在生成完第二个token要生成第三个token该干嘛还需要对第二个token进行词嵌入和位置编码吗生成第三个Token (T3T_3T3) 的详细步骤假设我们现在刚刚拿到了T2T_2T2准备启动生成T3T_3T3的流程。以下是硬件和软件层面发生的确切步骤步骤 1准备新输入Input Preparation—— 你的问题所在这是最关键的一步。模型内部的 Transformer 结构无法直接识别 Token ID如数字7356它只认识高维向量。因此必须把T2T_2T2这个“生肉”加工成模型能消化的“熟食”。词嵌入 (Word Embedding)拿着T2T_2T2的 ID去查模型的词嵌入表Embedding Matrix。取出对应的向量VT2V_{T2}VT2。这个向量蕴含了 “apple” 这个词的语义信息。注意这一步只针对T2T_2T2做之前的T1T_1T1和提示词早就在以前的步骤里做过了。位置编码 (Positional Encoding)模型需要知道T2T_2T2在句子里排老几。假设提示词有 10 个 TokenT1T_1T1是第 11 个那么T2T_2T2就是第 12 个位置。计算第 12 个位置的位置向量P12P_{12}P12把它加到词向量上XT2VT2P12X_{T2} V_{T2} P_{12}XT2VT2P12。结论经过这两步T2T_2T2变成了一个带有语义和位置信息的输入向量XT2X_{T2}XT2准备好进入 Transformer 的第一层了。步骤 2进入 Transformer 层 更新 KV Cache现在向量XT2X_{T2}XT2进入第 1 个 Transformer 块Block。计算当前的 Q, K, VXT2X_{T2}XT2分别乘以三个权重矩阵WQ,WK,WVW_Q, W_K, W_VWQ,WK,WV。得到了针对T2T_2T2的查询向量QT2Q_{T2}QT2、键向量KT2K_{T2}KT2和值向量VT2V_{T2}VT2。更新 KV Cache关键动作这是 Decoding 阶段最核心的操作。我们把刚刚算出来的KT2K_{T2}KT2和VT2V_{T2}VT2存入显存中的 KV Cache 里。此时Cache 里已经躺着提示词和T1T_1T1的 K 和 V 了。现在T2T_2T2的 K 和 V 也加入了队伍。注意我们不需要存QT2Q_{T2}QT2它马上就要被用掉。步骤 3注意力计算 (Attention Calculation)现在开始计算“注意力”也就是T2T_2T2如何看待它之前的历史。拿着 Query 去查询使用当前的查询向量QT2Q_{T2}QT2。回顾所有历史 KeyQT2Q_{T2}QT2必须和 Cache 里所有的 K进行点积计算包括提示词的 K、T1T_1T1的 K以及刚刚存进去的它自己的KT2K_{T2}KT2。这一步计算出了注意力分数Attention Scores表示预测下一个词时历史上的每个词有多重要。加权求和 Value根据分数对 Cache 里所有的 V进行加权求和得到这一层的输出向量。步骤 4层层传递与最终输出第 1 层的输出会进入第 2 层重复步骤 2 和 3计算新的 QKV更新第 2 层的 Cache做注意力计算。直到通过最后一层 Transformer Block。最后的输出向量通过一个线性层Unembedding Layer映射到词表大小计算出下一个 Token 是词表中每个词的概率Logits。采样根据概率选择可能性最大的词这就是T3T_3T3。(2)存到Cache中的K,V是什么为什么称为KV-Cache?为什么不存储Q每一个Token的词向量与Qk,Wk,VkQ_k,W_k,V_kQk,Wk,Vk权重矩阵点积后生成的向量是Q(Query),K(Key),V(Value)Q(Query),K(Key),V(Value)Q(Query),K(Key),V(Value)Q (Query - 查询向量)代表当前这个词比如“坐”正在寻找什么信息。“我在找我的主语和状语。”K (Key - 键向量)代表当前这个词比如“猫”或“垫子”具有什么特征线索用来回答别人的查询。“我是一个名词也是一个潜在的主语。”V (Value - 值向量)代表当前这个词包含的实际语义信息。“包含‘猫科动物’的含义”或“包含‘位置地点’的含义”。Qk,Wk,VkQ_k,W_k,V_kQk,Wk,Vk权重矩阵在每个注意力Block中都是不变的是固定值每一个Token进入一个Block中的时候都会用这个Block的Qk,Wk,VkQ_k,W_k,V_kQk,Wk,Vk去计算出这个Token的Q,K,VQ,K,VQ,K,V。1. 为什么存的是计算出的 K, V 激活值而不是权重值因为权重值本来就在那里不需要存。而 K, V 激活值是“一次性”的不存就没了。权重值 (WQ,WK,WVW_Q, W_K, W_VWQ,WK,WV)是模型的参数是固定的在推理时。它们就像厨师手里的刀具和锅具做第一层蛋糕用这套工具做第 100 层还是用这套工具。它们一直都在显存里不需要额外的 Cache 来存。K, V 激活值是特定的输入 Token经过特定的权重矩阵计算出来的临时结果。比如Ktoken1Inputtoken1×WKK_{token1} \text{Input}_{token1} \times W_KKtoken1Inputtoken1×WK这个Ktoken1K_{token1}Ktoken1只属于第一个 Token。当计算第二个 Token 时如果没把Ktoken1K_{token1}Ktoken1存下来它就丢失了。为了让第二个 Token 能“看到”第一个 Token你必须把这个计算结果存下来。2. 这个激活值存下来干嘛为了在计算未来的 Token 时不用重新计算历史 Token 的注意力信息。在注意力机制中计算 Query (Q) 和 Key (K) 的相似度点积是核心步骤Attention ScoreQ当前×K历史T\text{Attention Score} Q_{\text{当前}} \times K_{\text{历史}}^TAttention ScoreQ当前×K历史T当你要生成第 100 个 Token 时它的Q100Q_{100}Q100必须和前 99 个 Token 的K1,K2,...,K99K_1, K_2, ..., K_{99}K1,K2,...,K99依次做点积。如果不存下来你就得把前 99 个 Token 重新输入模型重新乘一次权重矩阵WKW_KWK才能得到这 99 个 K。把它们存下来下次直接从显存读取做点积节省了巨大的计算量。V 值同理它是用来加权求和的“内容”也需要存下来。3. 为什么不存 Q因为 Q 是“一次性消费品”用完就废了存下来没用。Q (Query)代表的是**“当前正在生成的 Token”的查询需求**。当生成 Token A 时我们计算QAQ_AQA用它去查询历史信息。用完之后QAQ_AQA的使命就完成了。当生成下一个 Token B 时我们需要的是全新的QBQ_BQB去查询包括 A 在内的历史信息。之前的QAQ_AQA对 B 来说毫无意义。K, V (Key, Value)代表的是**“已生成的历史 Token”的信息存档**。Token A 生成后它的KAK_AKA和VAV_AVA就成了历史档案馆的一部分供未来所有的 Token (B, C, D…) 来查询。它们需要长期保存。