乐于分享
好东西不私藏

每天学一点AI知识|DAY 10 - 隐状态、Logits 与 Softmax

每天学一点AI知识|DAY 10 - 隐状态、Logits 与 Softmax

每天学一点AI知识|DAY 10 - 隐状态、Logits 与 Softmax

Transformer 内部为每个 Token 位置逐层计算隐状态。最后一层当前位置的隐状态经过输出层映射成与词表等长的 logits,Softmax 再把这些原始分数归一化为总和为 1 的概率分布。

在 DAY 9 中,我们认识了 self-attention、cross-attention 和 causal attention。

Attention 让每个位置根据允许读取的信息更新自己的表示。经过一层又一层 Transformer 以后,模型内部已经形成了融合上下文的向量。

但用户最终看到的不是向量,而是文字。

模型内部的一组浮点数,怎样变成“北京”、“上海”、“。”等整个词表上的候选倾向?为什么模型能发现某个 Token 的概率更高?Logit 与概率又是不是同一个东西?

这条输出链路可以先压缩成四步:

隐状态 ↓输出层映射 ↓Logits ↓ Softmax词表概率分布

隐状态是当前输入经过模型逐层计算形成的内部表示;logits 是模型为词表中每个 Token 给出的原始分数;Softmax 则把整组分数转换成概率分布。

今天,我们就沿着这条链路回答一个核心问题:模型内部的向量怎样变成整个词表上的概率?

一、先把完整输出链路连起来

假设用户输入:

中国的首都是

Tokenizer 先把文本转换成 Token ID,Embedding 再把 ID 转成初始向量。随后,这些向量经过多层 Transformer,不断融合当前上下文中的信息。

到最后一层时,生成位置已经形成一个内部向量。这个向量还不是“北京”,也不是一个词表概率表,而是一组长度固定的连续数值。

模型需要继续完成两次转换:

    可以想象模型得到一张很长的候选表:

    词表 Token
    原始分数
    Softmax 后概率
    北京
    较高
    较高
    上海
    较低
    较低
    中国
    更低
    更低
    很低
    很低
    其他 Token
    各不相同
    合计占剩余概率

    真实词表可能包含数万甚至更多 Token,所以这张表非常长。模型并不是只给少数候选打分,而是为完整词表中的每个 Token 产生一个 logit。

    解码策略再根据概率或相关分数,从候选中选择实际输出的 Token。

    今天先关注概率是怎样得到的;具体如何根据分布选择 Token,会在后面的解码策略章节继续讨论。

    二、隐状态:每一层、每个 Token 的内部向量

    Transformer 不是只在最后得到一个向量。实际上输入序列经过每一层以后,每个 Token 位置都会形成新的内部表示。

    这些中间表示叫作隐状态,英文是 hidden states。

    如果模型当前处理的序列有 n 个 Token,每个隐状态向量的维度是 d_model,那么第 ℓ 层的全部隐状态可以写成:

    其中:

    • ℓ 是层编号;
    • H⁽ℓ⁾ 表示第 ℓ 层输出的全部隐状态;
    • n 是当前输入序列的 Token 数量;
    • d_model 是每个隐状态向量的维度;
    • ℝ^(n × d_model) 表示这是一个由实数组成的 n 行、d_model 列矩阵。

    矩阵中的每一行,对应一个 Token 位置在当前层的内部向量。

    例如,输入序列有 5 个 Token,模型的隐状态维度是 768,那么每一层输出矩阵的形状就是:

    5 × 768

    可以把它想象成 5 行,每行 768 个数:

    Token 1 → [第 1 个位置的 768 维隐状态]Token 2 → [第 2 个位置的 768 维隐状态]Token 3 → [第 3 个位置的 768 维隐状态]Token 4 → [第 4 个位置的 768 维隐状态]Token 5 → [第 5 个位置的 768 维隐状态]

    当这些表示进入下一层时,模型会使用当前层输出继续计算,形成下一层隐状态。层数不断增加,每个位置的表示也不断被上下文和模型计算更新。

    因此,隐状态中的“隐”并不是神秘或不可知,而是说它属于模型内部的中间表示,不直接等于人类可读文本或最终概率。

    三、同一个 Token,为什么会有不同隐状态

    在 DAY 6 中,我们区分了初始 Embedding 与上下文化后的隐状态。

    如果同一个 Token ID 出现在不同句子中,它从 Embedding 表查到的初始向量可以相同,但经过 Transformer 以后形成的隐状态通常不同。

    比较:

    苹果发布了新手机。我吃了一个苹果。

    假设两处“苹果”对应同一个 Token ID,它们最初取得相同的 Embedding。但第一句话中,“苹果”会与“发布”“新手机”等位置交互;第二句话中,它会与“吃了”“一个”等位置交互。

    经过多层 Attention 和其他计算以后,这两个位置形成不同的内部向量。

    因此,可以把两者的关系概括为:

    初始 Embedding:Token 身份提供的起点表示隐状态:Token 在当前层、当前上下文中的临时表示

    同一个 Token 不仅会因为上下文不同而产生不同隐状态,也会在模型不同层拥有不同隐状态。如果把每一层的输出称为该层的隐状态,那么第 2 层隐状态是在第 1 层输出的基础上,由第 2 层继续计算得到的;第 20 层隐状态则是在第 19 层输出的基础上,由第 20 层继续计算得到的。

    所以,谈论隐状态时要同时说明两个位置:

    1. 它属于序列中的哪个 Token 位置;
    2. 它来自模型的哪一层。

      四、隐状态不是参数、概率或长期记忆

      “模型内部的数字”很容易被统称为参数,但隐状态与参数不是一回事。

      参数:模型训练后保留下来的计算规则

      权重矩阵和偏置等参数在训练过程中学习得到。普通推理时,同一套参数会被反复用于处理不同输入,通常不会因为一轮聊天而自动改变。

      隐状态:本次输入产生的临时计算结果

      隐状态由当前输入与当前参数共同计算得到。输入改变,隐状态通常也会改变;当前前向传播结束后,这些中间表示也不等于已经写入模型的长期知识。

      可以对照理解:

      对比维度
      参数
      隐状态
      来源
      训练过程中学习
      当前输入经过前向传播得到
      普通推理时是否改变
      通常固定
      随输入、位置和层变化
      作用
      规定模型怎样计算
      表示当前计算中的上下文信息
      生命周期
      可被保存并重复加载
      通常属于本次计算的中间结果

      隐状态也不是概率。它的各个维度可以是正数、负数或其他实数,并不要求位于 0 到 1 之间,也不要求所有维度相加等于 1。

      它同样不等于模型的长期记忆。模型在当前上下文中形成某些隐状态,只说明这些信息参与了本轮计算,不表示它们已经永久修改参数。

      五、为什么生成下一个 Token 时关注最后一个可见位置

      在 decoder-only 模型中,提示词和已生成内容位于同一条因果序列中。

      假设当前序列是:

      中国 的 首都 是

      按照这个简化后的 Token 划分,模型经过多层 Transformer 后,会为“中国”“的”“首都”“是”等每个位置产生最后一层隐状态。

      由于因果 self-attention 的限制,最后一个可见位置可以汇总当前序列中允许读取的全部前文信息。因此,当模型要预测紧接在当前序列之后的下一个 Token 时,会使用最后一个可见位置的最终隐状态进行输出映射。

      在这个简化例子中,最后一个 Token 是“是”,所以“最后一个可见位置的隐状态”就是“是”这个位置经过最后一层 Transformer 后得到的隐状态。它不只包含“是”本身的信息,还包含这个位置通过因果 self-attention 从“中国”、“的”、“首都”等前文位置获得的信息。

      需要区分隐状态所属的位置与它用于预测的对象:

      隐状态所属的位置 → 当前序列末尾的“是”它用于预测的对象 → “是”后面的下一个 Token

      模型不会额外创建一个“空白位置”的隐状态,而是将“是”这个位置的最终隐状态映射到词表 logits,用来预测后续可能出现的“北”、“北京”等 Token。实际 tokenizer 不一定按照示例中的汉字和词语切分,这里只用简化后的 Token 划分说明计算过程。

      可以抽象为:

      当前序列所有位置的最终隐状态 ↓取最后一个可见位置的隐状态 ↓映射到下一个 Token 的词表分数

      训练时,模型可能一次为多个位置计算下一 Token 预测;推理时,我们通常关心当前序列末尾位置对应的下一步分布。

      这也解释了为什么模型不是把整张隐状态矩阵直接当成一个概率表。隐状态仍然位于 d_model 维的内部表示空间,而词表概率需要覆盖 |V| 个候选 Token。两者维度和含义都不同,还需要一个输出层连接起来。

      六、Logits:从内部表示映射到词表维度

      假设 Transformer 一共有 L 层,第 i 个位置在最后一层的隐状态记作 hᵢ⁽ᴸ⁾。

      模型通过输出权重矩阵和偏置,把这个 d_model 维向量映射成词表维度:

      其中:

      • L 是 Transformer 的最后一层编号;
      • i 是当前处理的 Token 位置;
      • hᵢ⁽ᴸ⁾ 是第 i 个位置在最后一层的隐状态,长度为 d_model;
      • W_out ∈ ℝ^(d_model × |V|) 是可训练的输出权重矩阵;
      • b_out ∈ ℝ^|V| 是输出层偏置向量;
      • V 是 Tokenizer 的完整词表;
      • |V| 是词表包含的 Token 总数;
      • z 是长度为 |V| 的 logits 向量。

      这次映射完成了一个关键转换:

      d_model 维内部表示 ↓|V| 维词表原始分数

      如果模型的隐状态维度是 4096,词表大小是 50,000,那么输入输出层的是一个 4096 维向量,得到的则是一个包含 50,000 个分数的 logits 向量。

      词表中的每个 Token 恰好对应一个 logit。

      为了避免与位置编号 i 混淆,可以用 zⱼ 表示词表中第 j 个 Token 的 logit。这里的 j 是词表条目编号,不是输入序列的位置编号。

      七、为什么输出层维度必须等于词表大小

      模型下一步可能输出的候选,来自 Tokenizer 定义的词表。

      如果词表有 50,000 个 Token,输出层就必须为这 50,000 个候选分别提供一个分数。少一个维度,就会有一个 Token 没有对应分数;多一个维度,则会产生无法对应到词表条目的输出。

      因此,输出层、Token ID 与 Tokenizer 词表之间存在严格对应:

      输出向量第 0 项 ↔ 词表 ID 0输出向量第 1 项 ↔ 词表 ID 1输出向量第 2 项 ↔ 词表 ID 2……

      这与 DAY 6 介绍的输入链路形成首尾呼应:

      输入:Token ID → Embedding 向量输出:隐状态 → 每个 Token ID 对应的 logit

      模型输入和输出都必须与同一套词表协议匹配。随意更换 Tokenizer,不仅会让输入 ID 与 Embedding 错位,也会让输出 logits 无法按照原来的词表正确解码。

      八、Logit 是原始分数,不是概率

      Logit 表示模型对某个词表 Token 的原始倾向分数。

      它可以是任意实数,例如:

      Token A → 2.4Token B → 0.7Token C → -1.2

      负 logit 并不表示出现了负概率,正 logit 也不表示它已经是合法概率。

      Logits 不需要满足概率的两个基本条件:

        例如 logits 为:

        (2, 1, 0)

        三个数相加等于 3,而且第一个值大于 1,显然不能直接当作概率。

        Logits 的主要意义在于相对大小。其他条件不变时,logit 越高,经过 Softmax 后对应概率通常也越高。

        但模型最终需要一组可以比较、总和为 1 的概率分布,因此还要继续执行 Softmax。

        九、Softmax 怎样把 Logits 变成概率

        对于词表中第 j 个 Token,Softmax 概率写成:

        其中:

        • j 是当前正在计算概率的词表 Token 编号;
        • k 是求和时使用的索引,从 1 遍历到 |V|;
        • zⱼ 是第 j 个 Token 的 logit;
        • e 是自然常数,约等于 2.718;
        • e^(zⱼ) 把当前 Token 的原始分数转换成正数;
        • 分母把词表中所有 Token 的指数分数相加;
        • pⱼ 是第 j 个 Token 的归一化概率。

        Softmax 可以分成两步理解。

        第一步:对每个 logit 取指数

        指数结果一定为正数,所以负 logit 也会得到一个正值。

        第二步:除以所有指数结果之和

        每个候选都除以同一个总和,因此所有输出概率相加恰好等于 1。

        对于 logits:

        (2, 1, 0)

        Softmax 后的概率约为:

        (0.665, 0.245, 0.090)

        这表示第一个候选在当前三项中获得最高概率,但另外两个候选仍然保留一定概率。

        Softmax 不会改变 logits 的相对排序:原始分数最高的候选,归一化以后仍然拥有最高概率。它完成的是把整组相对分数转换到一个统一概率尺度上。

        十、Softmax 为什么要同时查看整组 Logits

        一个 Token 的概率不是只由它自己的 logit 决定,还取决于词表中其他 Token 的 logits。

        假设某个候选的 logit 保持为 2。如果其他候选的 logits 都很低,它可能获得较大概率;如果许多其他候选也有接近或更高的 logits,它分到的概率就会下降。

        原因就在 Softmax 的分母:

        所有候选的指数分数总和

        每个 Token 都在与整个词表共同参与归一化。

        所以,下面两组情况中,Token A 虽然 logit 都是 2,最终概率却可能不同:

        情况一:(2, 0, -2)情况二:(2, 1.9, 1.8)

        第一种情况下,Token A 明显领先其他候选;第二种情况下,多个候选分数接近,概率会更分散。

        这也解释了为什么只查看某一个 logit 的绝对数值,无法单独判断它最终会获得多大概率。概率来自整组 logits 的相对关系。

        十一、从概率分布到实际生成,还需要解码策略

        Softmax 得到概率分布以后,模型仍然没有自动决定最终输出哪个 Token。

        接下来需要由解码策略选择一个候选。

        最简单的做法可以是选择概率最高的 Token;其他策略也可以在多个候选之间进行采样,或者先调整分布再选择。

        因此,完整链路是:

        最后一层当前位置隐状态 ↓输出层映射 ↓整个词表的 logits ↓Softmax 概率分布 ↓解码策略选择实际 Token

        生成出的 Token 会被追加到序列中,模型再基于更新后的上下文进行下一次前向计算。

        这正好关联了 DAY 4 的自回归生成:每一步都重新形成当前位置隐状态、词表 logits 和下一 Token 分布,然后选择一个 Token 继续向前。

        十二、高概率为什么不等于事实正确

        Softmax 做的是数学归一化,不是事实核验。

        它只根据模型已经算出的 logits,决定如何把相对倾向转换成总和为 1 的分布。至于 logits 为什么会是当前这些数值,取决于模型参数、训练数据和当前上下文。

        如果模型学到了错误模式,输入信息不足,或者问题涉及模型不了解的最新事实,错误 Token 仍然可能获得很高 logit,并在 Softmax 后得到很高概率。

        因此,需要区分:

        模型在当前条件下更倾向生成什么现实世界中什么是真的

        Softmax 回答的是前一个问题,不负责回答后一个问题。

        一个回答语气自信、概率集中,只说明模型内部对某条生成路径更确定,不等于这条路径已经通过外部证据验证。

        这与 DAY 4 的结论一致:高概率不是真实性的担保。

        十三、Softmax 不只用于最终输出层

        Softmax 是一种通用的归一化操作,并非语言模型输出层专属。

        在最终输出层中,Softmax 处理的是:

        整个词表上每个 Token 的 logits

        输出是一组词表概率。

        在 Attention 中,Softmax 处理的则是:

        一个 Query 与各个可读取 Key 的匹配分数

        输出是一组注意力权重,用于决定从各个 Value 位置汇总多少信息。

        两处使用的归一化公式相同,但归一化对象不同:

        使用位置
        Softmax 输入
        Softmax 输出
        模型输出层
        词表中各 Token 的 logits
        下一 Token 概率分布
        Attention
        Query 与各 Key 的匹配分数
        对各 Value 位置的注意力权重

        因此,看到 Softmax 时需要了解它的本质是为了方便做归一化处理,而不是“它把结果变成下一个词的概率”,需要根据实际使用场景来说明Softmax的目标

        十四、隐状态、Logits 与概率到底怎样区分

        这三个对象都由数字组成,但维度、含义和用途不同。

        对象
        典型维度
        表示什么
        是否概率
        隐状态
        d_model
        某个 Token 位置在某一层的内部表示
        Logits
        词表大小
        词表中每个 Token 的原始分数
        Softmax 概率
        词表大小
        词表中每个 Token 的归一化概率

        可以用一句话分别记忆:

        隐状态:模型现在怎样表示这个位置Logits:模型给每个词表候选打了多少原始分概率:这些分数归一化后各自占多少

        隐状态到 logits 需要输出层映射;logits 到概率需要 Softmax。

        十五、几个常见误解

        误解一:隐状态就是模型参数

        隐状态是当前输入经过模型计算得到的临时结果;参数是训练中学习并保存的权重和偏置。同一套参数处理不同输入,会产生不同隐状态。

        误解二:隐状态中的每一维都有清楚的人工含义

        隐状态通常是分布式表示。模型利用许多维度共同编码信息,不能简单规定某一维永远表示“是否是动物”,另一维永远表示“情绪是否积极”。

        误解三:Logit 大于 1,所以它是超过 100% 的概率

        Logit 不是概率,可以是任意实数。只有经过 Softmax 后,结果才位于 0 到 1 之间并且总和为 1。

        误解四:Softmax 会检查哪个答案是真的

        Softmax 只做归一化。错误答案也可能因为 logit 很高而获得高概率。

        误解五:Softmax 只出现在模型最终输出

        Attention 中也会使用 Softmax,把匹配分数归一化成注意力权重。公式相同,处理对象不同。

        十六、今天真正需要记住什么

        今天最重要的,是把 Transformer 内部表示与最终词表概率之间的通路连起来:

        每层、每个 Token 的隐状态 ↓最后一层当前位置隐状态 ↓ 输出层整个词表的 logits ↓ Softmax整个词表的概率分布 ↓ 解码策略实际生成的下一个 Token

        核心结论可以压缩成八句话:

        第一,隐状态是模型为每个 Token 位置在每一层计算出的内部向量。

        第二,隐状态属于当前前向传播的中间结果,不是模型参数、概率或长期记忆。

        第三,同一个 Token 的初始 Embedding 可以相同,但经过不同上下文处理后会形成不同隐状态。

        第四,输出层把最后一层当前位置的 d_model 维隐状态映射到 |V| 维词表空间。

        第五,词表中的每个 Token 都对应一个 logit;logit 是原始分数,不是概率。

        第六,Softmax 对整组 logits 做指数化和归一化,使所有 Token 概率为正且总和为 1。

        第七,一个 Token 的概率取决于它与整个词表其他 logits 的相对关系,不只取决于自己的分数。

        第八,Softmax 只整理模型的相对倾向,不验证事实真假;同一个公式也可以用于 Attention 分数归一化。

        下一篇,我们会回到 Attention 内部,进一步理解 Query、Key、Value 分别怎样产生,以及因果掩码怎样在分数矩阵中真正遮住未来位置。


        自检

        1. 隐状态与模型参数在来源、作用和生命周期上有什么区别?
        2. 为什么同一个 Token 在不同句子中会形成不同隐状态?
        3. 为什么输出层的维度必须等于 Tokenizer 的词表大小?
        4. Logit 为什么不能直接当作概率?Softmax 做了哪两步转换?
        5. 为什么某个 Token 获得很高 Softmax 概率,仍然不能证明它表达的事实正确?