Jev 在 25 行 Python 中——一个极简本地分类器的工作原理
快速要点
一个 25 行的 Python 脚本可以通过加载一个 GGUF LLM、向其提供带有标签选项的提示,再将模型的最终标记 logits 转换为概率,从而模拟 Jev 的核心功能——一个快速、本地的决策模型。
脚本的功能
它加载一个 GGUF 模型,将带有标签选项的提示格式化,执行前向传播,提取用于选项标记的最后一个标记 logits,并将其归一化为概率。
# /// script
# requires-python = ">=3.12"
# dependencies = ["huggingface-hub", "llama-cpp-python", "numpy"]
# ///
import numpy
from llama_cpp import Llama
model = Llama.from_pretrained(
repo_id="Qwen/Qwen3-0.6B-GGUF",
filename="Qwen3-0.6B-Q8_0.gguf",
n_ctx=512,
logits_all=True,
verbose=False,
)
labels = ["A", "B", "C"]
choices = ["Legitimate", "Spam", "Phishing"]
email = "Payroll asks for your password on a non-company sign-in page."
options = "\n".join(
f"{l}. {c}" for l, c in zip(labels, choices, strict=True)
)
prompt = f"""system
Choose one option.
user
Email: {email}\n\n{options}
assistant
\n\n"""
model.eval(tokens=model.tokenize(text=prompt.encode(), add_bos=False, special=True))
logits = model.scores[model.n_tokens - 1]
token_ids = [model.tokenize(text=l.encode(), add_bos=False)[0] for l in labels]
choice_logits = numpy.asarray([logits[t] for t in token_ids])
logprobs = choice_logits - numpy.logaddexp.reduce(choice_logits)
probabilities = numpy.exp(logprobs)
for name, scores in (
("Logits", choice_logits),
("Log probabilities", logprobs),
("Probabilities", probabilities),
):
values = numpy.round(scores.astype(float), 3).tolist()
print(f"{name}:", dict(zip(choices, values, strict=True)))
该脚本会打印三个字典,例如:
Logits: {"Legitimate": 26.254, "Spam": 27.262, "Phishing": 29.614}
Log probabilities: {"Legitimate": -3.482, "Spam": -2.474, "Phishing": -0.122}
Probabilities: {"Legitimate": 0.031, "Spam": 0.084, "Phishing": 0.885}
为何这很重要
它表明 Jev 的核心行为——将带有离散选项的自然语言提示转化为校准后的概率——并不需要专有 API、合成数据或基于强化学习的后训练。 整个流程在本地运行,不会产生网络延迟,并且可以使用任何兼容 GGUF 的模型进行复现。
社区见解
日志概率的注意事项
"直接使用 logprobs 总是有点棘手,尤其是当你使用聊天模型作为基础时,因为它们被训练成生成散文作为输出。……你应该添加明确的系统指令或使用结构化输出,以避免模型偏离主题。" – sigmoid10
该评论警告,如果模型在选项标记之前生成额外的散文,标记级别的概率可能会失真。添加明确的系统提示或限制助手的输出格式可以降低此风险。
提示顺序的影响
"由于掩码注意力机制,如果你把选项放在正文之前,Transformer 会提前知道它需要寻找什么,从而可以为该任务分配更多标记。" – antirez
将选项列表放在提示的前面可以提高模型对分类任务的关注度。
结构化输出的替代方案
"与其让模型只生成 "A", "B", 或 "C" 并查看概率,不如让它直接生成 "Legitimate", "Spam", 或 "Phishing"……你也可以让它用文字或数字分配概率。" – sigmoid10
使用 JSON 或纯文本模式作为助手的响应可以简化下游解析,并减少对标记级别 logits 的依赖。
校准方面的顾虑
"这些概率并不总是准确的;校准决策通常需要基于强化学习的微调(RLCD)。" – 原始帖子
许多评论者指出,原始 logits 并不能保证具有良好的校准性。温度缩放、Brier 损失微调或事后校准曲线等技术可以提高可靠性。
速度与准确性的权衡
"为什么你不想在分类器中加入‘推理’?速度和成本显然是原因,但这难道不是一个权衡吗?" – brap
该脚本为了降低延迟而牺牲了任何链式思维推理。对于高风险决策,具有明确推理的较慢模型可能带来更高的准确性。
实际考虑
模型选择
示例使用了 Qwen/Qwen3-0.6B-Q8_0.gguf,这是一个 0.6B 参数的模型,可在普通硬件上运行。使用量化或更小的模型可以实现更快的推理,但不同领域中的准确性可能有所不同。
延迟测量
该帖子未提供基准数据。社区成员请求具体的延迟和错误率数据(例如“45 个问题 <200 毫秒”)。在将该方法用于生产环境之前,必须在目标硬件上测量端到端时间。
错误处理
如果模型输出意外标记,解析可能会失败。添加严格的输出模式(例如带有 choice 字段的 JSON)和重试逻辑可以减少无效响应。
可扩展性
相同模式适用于任何多类分类任务:将 labels、choices 和 email 替换为适当的领域数据,并调整提示以反映新上下文。
替代方案与开源实现
- OpenJev – 更完整的开源参考实现。
- openjev-sglang – 与
sglang运行时集成,以实现更高吞吐量。 - OpenJev on DiffusionGemma – 使用不同主干模型演示该方法。
- Laya – 一个明确针对 System-One 风格决策优化的开源权重模型(参见 SylonZero 的评论)。
总结
25 行脚本证明,Jev 的核心思想——基于提示的分类与概率提取——可以使用通用 LLM、无需专有训练和极少代码来复现。然而,实践者应意识到校准限制、提示设计细节以及在生产中依赖此类轻量级管道前需要具备稳健的输出解析能力。
Sources
相关
- Dispatch
- 项目
- 项目
- Dispatch
- 项目