当你问一个大语言模型"法国的首都是哪里?",它回答"巴黎"。这个看似简单的过程背后,模型经历了一系列复杂的计算。在最终的文字出现之前,模型首先要给出一个答案:对于词表中的每一个词,它认为这个词作为下一个输出的可能性有多大。这个"可能性"的原始形式,就是logits。

Logits是神经网络输出的最原始数据,是所有后续处理——概率转换、采样、解码——的起点。理解logits,就是理解神经网络如何"思考"的第一步。

一个被误解的概念

在深度学习社区,“logits"这个词的使用常常让初学者困惑。如果你去翻阅PyTorch的CrossEntropyLoss文档,会发现它明确要求输入是"logits”,而不是经过softmax的概率值。TensorFlow的softmax_cross_entropy_with_logits函数名称更是直接把两个概念绑在了一起。

那么,logits到底是什么?

从最直观的角度理解:logits是神经网络最后一层的原始输出值,它们代表了模型对每个类别的"偏好程度",但还不是概率。

考虑一个简单的三分类问题:猫、狗、鸟。神经网络最后一层可能输出三个数值:[2.3, -0.5, 1.1]。这三个数就是logits。数值越高,代表模型认为这个类别越可能是正确答案。但如果你问"猫的概率是多少",这个2.3并不能直接回答——它不是概率,没有归一化,也不在[0, 1]区间内。

这个原始输出值有两个关键特性:

无界性:logits可以是任意实数,从负无穷到正无穷。一个logit值为-100并不意味着"绝对不可能",同样100也不代表"绝对确定"。

相对性:logits的意义在于它们的相对大小关系。[2.0, 1.0, 0.5][20.0, 10.0, 5.0]代表了相同的偏好顺序,经过softmax后会得到完全相同的概率分布。

从统计学继承的名字

“Logit"这个词并非深度学习的发明,它来自统计学领域,是"log-odds"的缩写形式。

在统计学中,给定一个事件发生的概率$p$,它的odds(几率)定义为:

$$\text{odds} = \frac{p}{1-p}$$

几率表示的是"事件发生"与"事件不发生"的比率。如果下雨的概率是0.75,那么odds就是0.75/(1-0.75) = 3,意味着"下雨的可能性是不下雨的3倍”。

而logit,就是对几率取对数:

$$\text{logit}(p) = \log\left(\frac{p}{1-p}\right)$$

这个变换有一个美妙的性质:它把范围在$[0, 1]$的概率,映射到了整个实数域$(-\infty, +\infty)$。概率为0.5时,logit是0;概率趋近于0时,logit趋向负无穷;概率趋近于1时,logit趋向正无穷。

这个变换在逻辑回归中至关重要。逻辑回归本质上是在线性模型的输出上套一个sigmoid函数,将线性预测值(logits)转换为概率。深度学习继承了这套术语,但含义已经泛化——在现代神经网络语境中,“logits"泛指最后一层线性变换的输出,无论后续是否真的对应于log-odds。

graph LR
    subgraph 概率空间
        A["p = 0.01"] --> B["p = 0.5"]
        B --> C["p = 0.99"]
    end
    
    subgraph Logit空间
        D["logit ≈ -4.6"] --> E["logit = 0"]
        E --> F["logit ≈ 4.6"]
    end
    
    A -.->|logit变换| D
    B -.->|logit变换| E
    C -.->|logit变换| F
    
    style A fill:#ffcccc
    style C fill:#ccffcc
    style D fill:#ffcccc
    style F fill:#ccffcc

神经网络中的Logits:架构视角

理解logits在神经网络中的位置,需要看清整个网络的"数据流”。

graph LR
    A[输入层] --> B[隐藏层]
    B --> C[最后一层隐藏状态]
    C --> D[线性投影层]
    D --> E[Logits向量]
    E --> F[Softmax/Sigmoid]
    F --> G[概率分布]
    G --> H[采样/解码]
    H --> I[最终输出]
    
    style E fill:#ffeb3b
    style G fill:#4caf50

对于一个语言模型,这个过程更加具体:

隐藏状态维度:假设模型最后一层输出的隐藏状态维度是$d$(比如GPT-2 small是768,GPT-3是12288)。

词表大小:模型的词表包含$V$个token(比如GPT-2是50257,LLaMA是32000)。

线性投影:最后一层隐藏状态通过一个线性层,将$d$维向量投影到$V$维空间:

$$\text{logits} = h \cdot W_{lm\_head} + b_{lm\_head}$$

其中$W_{lm\_head}$的形状是$[d, V]$,$b_{lm\_head}$的形状是$[V]$。

输出结果:得到的logits向量长度为$V$,每个元素对应词表中一个token的"原始得分"。

这个线性投影层是模型参数量最大的层之一。以LLaMA-70B为例,隐藏状态维度是8192,词表大小是32000,这一层就包含了约2.6亿个参数,占整个模型参数量的相当大比例。

从Logits到概率:Softmax的数学

有了logits,下一步是将其转换为有意义的概率。这正是softmax函数的工作。

Softmax的定义:

$$\text{softmax}(z_i) = \frac{e^{z_i}}{\sum_{j=1}^{V} e^{z_j}}$$

这个公式做了三件事:

指数化:$e^{z_i}$将可能为负的logits转换为严格正值。这也是为什么叫"soft"max——它在取最大值的同时,保留了非最大值的信息。

归一化:除以总和$\sum e^{z_j}$确保所有概率之和为1。

放大差异:指数函数具有放大效果。如果两个logits相差1,它们的概率比值约为$e \approx 2.7$倍;相差2,比值约为$e^2 \approx 7.4$倍。

graph TB
    subgraph Logits空间
        L1["Token A: 2.0"]
        L2["Token B: 1.0"]
        L3["Token C: 0.5"]
    end
    
    subgraph 指数化后
        E1["e^2.0 = 7.39"]
        E2["e^1.0 = 2.72"]
        E3["e^0.5 = 1.65"]
    end
    
    subgraph 概率空间
        P1["P = 0.628"]
        P2["P = 0.231"]
        P3["P = 0.141"]
    end
    
    L1 -->|"exp()"| E1
    L2 -->|"exp()"| E2
    L3 -->|"exp()"| E3
    
    E1 -->|归一化| P1
    E2 -->|归一化| P2
    E3 -->|归一化| P3

考虑一个具体例子:

Logits:     [2.0, 1.0, 0.5]
exp:        [7.39, 2.72, 1.65]
Sum:        11.76
Probabilities: [0.628, 0.231, 0.141]

第一个token的logit比第二个高1.0,但概率却是后者的2.7倍。这就是softmax的"赢家通吃"效应——微小的logit差异会被放大成显著的概率差异。

二分类的特殊情况:Sigmoid

对于二分类问题,softmax可以简化为sigmoid函数:

$$\sigma(z) = \frac{1}{1 + e^{-z}}$$

在二分类中,通常只需要一个logit值,表示"正类"的得分。概率计算如下:

  • $P(\text{正类}) = \sigma(z) = \frac{1}{1+e^{-z}}$
  • $P(\text{负类}) = 1 - \sigma(z) = \frac{e^{-z}}{1+e^{-z}}$

这种简化在某些场景下很有用,比如多标签分类,每个标签的判断都是独立的二分类问题。

温度参数:控制Logits的"性格"

直接对logits应用softmax会产生一个"自信"的分布——最高的概率会非常高,其他选项被压制。但在某些场景下,我们希望模型更"开放",给其他选项更多机会。

温度参数$T$的引入改变了softmax的计算方式:

$$\text{softmax}(z_i, T) = \frac{e^{z_i/T}}{\sum_{j=1}^{V} e^{z_j/T}}$$

温度的效果:

$T = 1$:标准softmax,保持原始分布。

$T \rightarrow 0$:分布变得极端尖锐,接近于取最大值(argmax)。模型变得非常"确定"和"保守"。

$T \rightarrow \infty$:分布变得非常平坦,接近于均匀分布。模型变得非常"随机"和"创造"。

graph LR
    subgraph 原始Logits
        A["[4.0, 2.0, 1.0]"]
    end
    
    subgraph "T = 0.5 低温"
        B["[0.975, 0.022, 0.003]"]
    end
    
    subgraph "T = 1.0 标准"
        C["[0.844, 0.114, 0.042]"]
    end
    
    subgraph "T = 2.0 高温"
        D["[0.628, 0.231, 0.141]"]
    end
    
    subgraph "T = 10.0 极高温"
        E["[0.390, 0.332, 0.278]"]
    end
    
    A --> B
    A --> C
    A --> D
    A --> E
    
    style B fill:#ffcdd2
    style E fill:#c8e6c9

用一个例子说明:

原始Logits: [4.0, 2.0, 1.0]

T = 1.0:  概率 = [0.844, 0.114, 0.042]  # 很确定第一个
T = 0.5:  概率 = [0.975, 0.022, 0.003]  # 几乎只选第一个
T = 2.0:  概率 = [0.628, 0.231, 0.141]  # 第二、三个有更多机会
T = 10.0: 概率 = [0.390, 0.332, 0.278]  # 接近均匀,几乎随机选

温度参数的实际意义:

代码生成、数学推理:通常使用较低温度(0.1-0.3),减少随机性,追求准确。

创意写作、头脑风暴:使用较高温度(0.7-1.0),增加多样性。

科学论文生成:使用中等温度(0.4-0.6),平衡准确性和流畅性。

温度的数学本质是对logits进行整体缩放。logits / T意味着:当T>1时,所有logits被缩小,它们的差异也被缩小,分布变得更平;当T<1时,logits被放大,差异被扩大,分布变得更尖锐。

Logits处理技术

在实际应用中,我们常常需要对logits进行干预,以控制模型的输出行为。

Logit Bias:直接干预概率

Logit bias是一种简单但强大的技术:在softmax之前,给特定token的logit加上一个偏置值。

$$z'_i = z_i + b_i$$

如果$b_i > 0$,token $i$被选中的概率会增加;如果$b_i < 0$,概率会减少。当$b_i$是一个很大的负数(如-100),该token几乎不可能被选中。

graph LR
    A[原始Logits] --> B[应用Logit Bias]
    B --> C[调整后Logits]
    C --> D[Softmax]
    D --> E[采样]
    
    subgraph Logit Bias示例
        F["token: 暴力"] --> G["bias = -100"]
        H["token: 和平"] --> I["bias = +5"]
    end
    
    style B fill:#fff3e0
    style G fill:#ffcdd2
    style I fill:#c8e6c9

应用场景

  • 禁止特定词汇:在内容过滤场景,将敏感词的logit设为极低值。

  • 强制JSON格式:增加{}"等token的logit,减少换行符的logit,引导模型生成结构化输出。

  • 领域适应:在特定领域生成中,增加领域相关词汇的概率。

Top-k和Top-p采样:限制候选空间

即使设置了温度,模型仍然可能选择概率很低但内容不合适的token。Top-k和Top-p采样提供了额外的控制手段。

Top-k采样:只保留概率最高的k个token,将其余的logit设为负无穷。

def top_k_filtering(logits, k):
    # 找到第k大的值
    top_k = torch.topk(logits, k)
    # 将低于第k大的值设为负无穷
    indices_to_remove = logits < top_k.values[-1]
    logits[indices_to_remove] = -float('inf')
    return logits

Top-p(Nucleus)采样:保留累积概率达到p的最小token集合。

def top_p_filtering(logits, p):
    # 按概率降序排列
    sorted_logits, sorted_indices = torch.sort(logits, descending=True)
    sorted_probs = torch.softmax(sorted_logits, dim=-1)
    
    # 计算累积概率
    cumulative_probs = torch.cumsum(sorted_probs, dim=-1)
    
    # 找到累积概率超过p的位置
    sorted_indices_to_remove = cumulative_probs > p
    
    # 保留第一个超过p的token(确保至少有一个候选)
    sorted_indices_to_remove[..., 1:] = sorted_indices_to_remove[..., :-1].clone()
    sorted_indices_to_remove[..., 0] = False
    
    # 将要移除的token设为负无穷
    indices_to_remove = sorted_indices_to_remove.scatter(0, sorted_indices, sorted_indices_to_remove)
    logits[indices_to_remove] = -float('inf')
    return logits
graph TB
    subgraph Top-k采样 k=3
        A1["原始概率分布"]
        A2["保留前3个"]
        A3["其余设为-inf"]
        A1 --> A2 --> A3
    end
    
    subgraph Top-p采样 p=0.9
        B1["原始概率分布"]
        B2["按概率排序"]
        B3["累积概率达到0.9"]
        B4["动态保留候选"]
        B1 --> B2 --> B3 --> B4
    end
    
    style A3 fill:#c8e6c9
    style B4 fill:#c8e6c9

Top-p的优势在于自适应性:如果分布很集中(一个token概率0.9),只需要很少的token就达到p;如果分布很分散,会保留更多候选。

重复惩罚

模型有时会陷入重复生成相同内容的模式。重复惩罚通过动态调整logits来缓解这个问题:

$$z'_i = z_i - \alpha \cdot \text{count}(i)$$

其中$\alpha$是惩罚系数,$\text{count}(i)$是token $i$在已生成文本中出现的次数。

Logits与训练:交叉熵损失

在训练阶段,logits是损失函数的直接输入。交叉熵损失是最常用的选择:

$$\mathcal{L} = -\sum_{i=1}^{V} y_i \log(p_i) = -\log(p_{\text{target}})$$

其中$y$是真实标签的one-hot编码,$p$是softmax后的概率。

为什么用logits而不是概率?

PyTorch的CrossEntropyLoss文档明确要求输入是logits,内部会自动应用log_softmax。这是因为:

  1. 数值稳定性:分开计算softmax和log会导致数值问题。当某个logit非常大时,$e^{z_i}$会溢出。log_softmax使用LogSumExp技巧避免这个问题:
$$\log\sum_i e^{z_i} = a + \log\sum_i e^{z_i - a}$$

其中$a = \max_i z_i$。

  1. 梯度计算简化:softmax + 交叉熵的组合有一个优雅的梯度公式:
$$\frac{\partial \mathcal{L}}{\partial z_i} = p_i - y_i$$

这个公式极其简洁:梯度就是预测概率与真实标签的差值。这大大简化了反向传播的实现。

graph LR
    subgraph 正确方式
        A1[Logits] --> B1["CrossEntropyLoss"]
        B1 --> C1[Loss]
        D1["内部: log_softmax + nll_loss"]
    end
    
    subgraph 错误方式
        A2[Logits] --> B2[Softmax]
        B2 --> C2[Log]
        C2 --> D2[手动计算损失]
        D2 --> E2["数值不稳定!"]
    end
    
    style B1 fill:#c8e6c9
    style E2 fill:#ffcdd2
# 错误方式:分开计算
probs = softmax(logits)  # 可能有数值问题
loss = cross_entropy(log(probs), labels)

# 正确方式:合并计算
loss = cross_entropy_with_logits(logits, labels)  # 内部使用log_softmax

数值稳定性:一个被忽视的关键问题

Logits的计算看起来简单,但在实际工程中,数值稳定性是必须面对的挑战。

溢出与下溢

当logits范围很大时,指数运算会产生问题:

logits = torch.tensor([1000.0, 100.0, 10.0])
exp_logits = torch.exp(logits)  # 溢出!变成 [inf, inf, 22026.5]

Max-Trick解决方案

标准的解决方案是在指数化之前,减去logits的最大值:

def stable_softmax(logits):
    max_logit = logits.max()
    shifted = logits - max_logit
    exp_shifted = torch.exp(shifted)
    return exp_shifted / exp_shifted.sum()

这个变换不改变softmax的结果(因为分子分母同时除以了$e^{\max}$),但避免了溢出问题。

混合精度训练中的Logits

在FP16混合精度训练中,logits的范围可能超出FP16的表示范围(约±65504)。常见的解决方案:

  1. Loss Scaling:在计算损失前对logits进行缩放,反向传播后再反缩放。

  2. FP32 Master Weights:在输出层保持FP32精度的权重副本。

  3. BF16替代:BF16虽然精度更低,但范围与FP32相同,避免了溢出问题。

Logits的实际应用场景

知识蒸馏:暗知识的载体

知识蒸馏的核心思想是:让小模型(学生)学习大模型(教师)的"暗知识"——不仅仅是最终分类,还有各类别之间的相对关系。

教师的logits包含了丰富的类别间关系信息。考虑一个图像分类任务:

输入:一张狗的照片

教师的logits:[猫: 2.1, 狗: 8.5, 汽车: -5.0, 卡车: -4.2]
学生的logits:[猫: 0.5, 狗: 5.0, 汽车: 2.0, 卡车: 1.8]

即使两个模型都正确预测"狗",教师知道"猫比汽车更相似"(logit值2.1 vs -5.0),而学生没有学到这一点。

graph LR
    subgraph 教师模型
        A[输入] --> B[大模型]
        B --> C["软标签 Logits"]
    end
    
    subgraph 学生模型
        D[输入] --> E[小模型]
        E --> F["预测 Logits"]
    end
    
    C --> G["蒸馏损失<br/>KL散度"]
    F --> G
    H[真实标签] --> I["硬标签损失"]
    F --> I
    
    G --> J["总损失"]
    I --> J
    
    style C fill:#ffeb3b
    style F fill:#ffeb3b

蒸馏损失使用温度缩放:

$$\mathcal{L}_{KD} = -\sum_i p_i^T \log q_i^T$$

其中$p^T$是教师的高温softmax输出,$q^T$是学生的高温softmax输出。高温使分布更平滑,保留更多"暗知识"。

模型校准:让置信度可信

神经网络的softmax概率往往不能真实反映预测的置信度——模型可能以99%的概率给出错误答案。这就是校准问题。

Expected Calibration Error (ECE) 是常用的校准度量:

$$\text{ECE} = \sum_{m=1}^{M} \frac{|B_m|}{n} |\text{acc}(B_m) - \text{conf}(B_m)|$$

其中$B_m$是置信度落在第$m$个区间的样本集合。

graph TB
    A[原始模型] --> B["计算验证集Logits"]
    B --> C[应用温度缩放]
    C --> D["优化温度参数T"]
    D --> E[校准后模型]
    
    subgraph 校准前
        F["置信度 0.9<br/>准确率 0.6"]
    end
    
    subgraph 校准后
        G["置信度 0.65<br/>准确率 0.63"]
    end
    
    style F fill:#ffcdd2
    style G fill:#c8e6c9

温度缩放校准是一个简单有效的方法:

  1. 在验证集上,固定模型参数。
  2. 学习一个标量温度$T$,使得校准误差最小。
  3. 推理时使用这个温度。

这个方法之所以有效,是因为温度参数只改变概率分布的"尖锐度",不改变预测类别。

困惑度:语言模型的"惊讶程度"

困惑度是语言模型评估的核心指标,直接从logits计算:

$$\text{PPL} = \exp\left(-\frac{1}{N}\sum_{t=1}^{N} \log p(x_t|x_{困惑度可以理解为:模型对下一个token预测的"平均分支因子"。困惑度越低,模型越"确定"。

从logits的角度理解:

  1. 计算真实token的logit值$z_t$。
  2. 计算softmax,得到概率$p_t = e^{z_t} / \sum e^{z_j}$。
  3. 取负对数:$-\log p_t$。
  4. 对所有token取平均,再指数化。

不确定性量化:Logits作为置信信号

在需要高可靠性的应用中,估计模型预测的不确定性至关重要。Logits提供了多种不确定性信号:

最大概率:最简单的置信度估计。

$$\text{confidence} = \max_i p_i = \max_i \frac{e^{z_i}}{\sum_j e^{z_j}}$$

Logit Gap:最高和次高logit的差距。

$$\text{gap} = z_{\text{max}} - z_{\text{second\_max}}$$

较大的gap表示模型更确定。这个度量不依赖于softmax,直接使用logits。

:概率分布的不确定性。

$$H = -\sum_i p_i \log p_i$$

熵越高,模型越不确定。

Logits与Logprobs:两个密切相关但不同的概念

在实际工程中,经常会遇到"logprobs"这个概念,它和logits容易混淆。

Logits:线性层的原始输出,未归一化的分数。

Logprobs:概率的对数值,$\log p_i$。

两者的关系:

$$\text{logprob}_i = \log p_i = \log \frac{e^{z_i}}{\sum_j e^{z_j}} = z_i - \log\sum_j e^{z_j}$$
graph LR
    subgraph Logits
        A["原始输出<br/>范围: (-∞, +∞)"]
        B["未归一化"]
        C["相对大小有意义"]
    end
    
    subgraph Logprobs
        D["概率的对数<br/>范围: (-∞, 0]"]
        E["可用于累加"]
        F["数值稳定"]
    end
    
    A -->|"softmax + log"| D
    B -.-> E
    C -.-> F
    
    style A fill:#ffeb3b
    style D fill:#bbdefb

这是一个关键洞察:logprob等于logit减去一个常数(LogSumExp)。这意味着:

  • 所有logprob都是负数(因为概率小于1,对数小于0)。
  • Logprob最大的token就是logit最大的token。
  • 但logprob的和没有特殊含义,而概率和必须为1。

为什么要用logprobs?

  1. 数值稳定性:直接存储和计算概率可能导致下溢(概率很小时)。log空间避免了这个问题。

  2. 累乘变累加:计算序列概率时,$P(x_1, x_2, ..., x_n) = \prod P(x_i|x_{

$$\log P(x_1, ..., x_n) = \sum_{i=1}^{n} \log P(x_i|x_{
  • 困惑度计算:直接使用logprobs的和。
  • # 计算困惑度
    log_probs = model(input_ids).log_probs  # [batch, seq_len, vocab]
    target_log_probs = log_probs.gather(-1, target_ids.unsqueeze(-1))
    perplexity = torch.exp(-target_log_probs.mean())
    

    工程实践:vLLM中的Logits处理

    vLLM是当前最流行的大模型推理引擎之一,它对logits的处理体现了工业级实现的考量。

    Logits Processors架构

    vLLM提供了一个可扩展的logits处理器框架:

    class LogitsProcessor:
        def __call__(self, token_ids: List[int], logits: torch.Tensor) -> torch.Tensor:
            # 修改logits
            return modified_logits
    

    处理器按顺序应用:

    graph LR
        A[原始Logits] --> B[LogitBiasProcessor]
        B --> C[RepetitionPenaltyProcessor]
        C --> D[TopKProcessor]
        D --> E[TopPProcessor]
        E --> F[TemperatureProcessor]
        F --> G[采样]
        
        style A fill:#ffeb3b
        style G fill:#c8e6c9
    

    高效Logit Bias实现

    对于大规模词表(如32000),逐token遍历设置bias会很慢。vLLM使用向量化实现:

    def apply_logit_bias(logits: torch.Tensor, bias: Dict[int, float]) -> torch.Tensor:
        if not bias:
            return logits
        
        token_ids = torch.tensor(list(bias.keys()), device=logits.device)
        bias_values = torch.tensor(list(bias.values()), device=logits.device)
        
        logits[:, token_ids] += bias_values
        return logits
    

    批量处理优化

    在批量推理中,不同请求可能需要不同的logits处理。vLLM使用padding和mask来高效处理:

    # 假设batch_size=4, vocab_size=32000
    # 每个请求的logit_bias不同
    # 使用scatter操作批量应用
    bias_matrix = torch.zeros(batch_size, vocab_size)
    for i, bias_dict in enumerate(batch_logit_bias):
        for token_id, bias_value in bias_dict.items():
            bias_matrix[i, token_id] = bias_value
    
    logits += bias_matrix
    

    调试与分析:窥视模型的"思考过程"

    Logits提供了一个窗口,让我们能够观察模型的决策过程。

    Logit Lens技术

    “Logit Lens"是一种可解释性技术,它将模型中间层的隐藏状态映射到词表空间:

    def logit_lens(hidden_states, lm_head):
        """
        hidden_states: [layers, batch, seq_len, hidden_dim]
        lm_head: 线性投影层
        """
        layer_logits = []
        for layer_hidden in hidden_states:
            logits = lm_head(layer_hidden)  # [batch, seq_len, vocab]
            layer_logits.append(logits)
        return layer_logits
    

    通过观察每一层的logits,可以了解模型在哪个阶段开始"形成"答案。研究发现,某些能力(如事实回忆)在较早的层就表现出来,而复杂的推理能力需要更深的层。

    Top-k Logits可视化

    对于调试生成问题,查看top-k logits是非常有用的:

    def print_top_k_logits(logits, tokenizer, k=10):
        top_k = torch.topk(logits, k)
        for i in range(k):
            token = tokenizer.decode([top_k.indices[i].item()])
            logit = top_k.values[i].item()
            prob = torch.softmax(logits, dim=-1)[top_k.indices[i]].item()
            print(f"{token:15s} | logit: {logit:7.2f} | prob: {prob:.4f}")
    

    这可以帮助诊断:

    • 为什么模型选择了错误的token?是分数太接近还是确实学错了?
    • 模型是否在"犹豫”?(多个token概率接近)
    • 是否有"异常高"的token?(可能是训练数据问题)

    Logits作为安全信号

    在安全敏感的应用中,logits可以帮助检测异常:

    置信度异常:如果模型对某个输出过于自信(某个token概率>99.9%),可能是在"背诵"训练数据,而非真正推理。

    分布异常:正常情况下,top-1和top-2的logit差距应该有一定的分布。如果差距过大或过小,可能是异常输入或攻击。

    OOD检测:将logits的熵与阈值比较,识别分布外输入:

    def is_out_of_distribution(logits, threshold=3.0):
        probs = torch.softmax(logits, dim=-1)
        entropy = -torch.sum(probs * torch.log(probs + 1e-10))
        max_entropy = torch.log(torch.tensor(logits.size(-1)))
        normalized_entropy = entropy / max_entropy
        return normalized_entropy > threshold
    

    总结

    Logits是神经网络输出最原始的形式,是连接模型"思考"与最终行为的桥梁。理解logits,意味着理解:

    数学本质:它们是未归一化的分数,可以取任意实数值,通过softmax(或sigmoid)转换为概率。

    工程角色:它们是训练时损失函数的输入,推理时采样的基础,各种干预手段(logit bias、temperature、top-k/p)的作用对象。

    信息载体:它们编码了模型对所有类别的偏好,包含比最终预测更丰富的信息(“暗知识”)。

    实用工具:通过分析logits,可以评估模型置信度、检测异常、理解模型决策过程。

    在大模型时代,logits的重要性更加凸显。每一次与AI的对话,背后都是数万个token的logits在流动;每一个温度参数的调整,都在重塑这些logits的分布;每一次logit bias的应用,都在精确控制模型的输出边界。

    Logits不是什么神秘的内部状态,它只是模型对世界的一个最朴素的表达:在所有可能的输出中,我对每一个有多大的"偏好"。这个简单的数字,承载着神经网络对任务的理解,也为我们提供了干预和理解的接口。

    下次当你调整一个模型的温度参数,或者设置某个词的惩罚系数时,你知道你正在做什么——你正在重塑那一个个logits,改变模型的"思考"方向。这就是理解的开始。


    参考文献

    1. Goodfellow, I., Bengio, Y., & Courville, A. (2016). Deep Learning. MIT Press.

    2. Hinton, G., Vinyals, O., & Dean, J. (2015). Distilling the Knowledge in a Neural Network. arXiv preprint arXiv:1503.02531.

    3. Guo, C., Pleiss, G., Sun, Y., & Weinberger, K. Q. (2017). On Calibration of Modern Neural Networks. ICML 2017.

    4. Holtzman, A., Buys, J., Du, L., Forbes, M., & Choi, Y. (2020). The Curious Case of Neural Text Degeneration. ICLR 2020.

    5. Radford, A., et al. (2019). Language Models are Unsupervised Multitask Learners. OpenAI Technical Report.

    6. Brown, T., et al. (2020). Language Models are Few-Shot Learners. NeurIPS 2020.

    7. Touvron, H., et al. (2023). LLaMA: Open and Efficient Foundation Language Models. arXiv preprint arXiv:2302.13971.

    8. Kwon, W., et al. (2023). Efficient Memory Management for Large Language Model Serving with PagedAttention. SOSP 2023.

    9. Naeini, M. P., Cooper, G., & Hauskrecht, M. (2015). Obtaining Well Calibrated Probabilities Using Bayesian Binning. AAAI 2015.

    10. Xiao, Y., & Wang, W. Y. (2019). Quantifying Uncertainties in Natural Language Processing Tasks. AAAI 2019.