Jev를 25줄의 파이썬으로 – 최소한의 로컬 분류기의 작동 방식

빠른 요약

25줄의 파이썬 스크립트는 GGUF LLM을 로드하고, 레이블이 붙은 선택지를 포함한 프롬프트를 제공한 후, 모델의 마지막 토큰 로짓을 확률로 변환함으로써 Jev의 핵심 기능을 모방할 수 있습니다.


스크립트의 기능

GGUF 모델을 로드하고, 레이블이 붙은 선택지를 포함한 프롬프트를 포맷한 후, 포워드 패스를 실행하고, 선택 토큰에 대한 마지막 토큰 로짓을 추출하여 확률로 정규화합니다.

# /// 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, 합성 데이터, 또는 RL 기반 사후 훈련이 필요하지 않다는 것을 보여줍니다. 전체 파이프라인은 로컬에서 실행되며, 네트워크 지연이 발생하지 않으며, GGUF 호환 모델이라면 누구나 재현할 수 있습니다.

커뮤니티의 통찰

로그-확률에 대한 주의사항

"채팅 모델을 기반으로 사용할 때 로그-확률을 직접 사용하는 것은 항상 불편합니다. 왜냐하면 모델은 출력으로 논설문을 작성하도록 훈련되었기 때문입니다. … 모델이 벗어나지 않도록 명확한 시스템 지침을 추가하거나 구조화된 출력을 사용해야 합니다." – sigmoid10

이 댓글은 모델이 선택 토큰 전에 추가적인 논설문을 생성할 경우 토큰 수준의 확률이 왜곡될 수 있음을 경고합니다. 명시적인 시스템 프롬프트를 추가하거나 어시스턴트의 출력 형식을 제한함으로써 이 위험을 완화할 수 있습니다.

프롬프트 순서의 영향

"마스크된 어텐션 때문에, 선택지를 본문 앞에 놓으면 트랜스포머는 이미 어떤 것을 찾아야 하는지 알고 있으며, 더 많은 토큰을 해당 작업에 할당할 수 있습니다." – antirez

프롬프트의 앞부분에 선택 목록을 배치하면 모델의 분류 작업에 대한 집중도가 향상될 수 있습니다.

구조화된 출력 대안

"모델이 단지 "A", "B", "C"만 생성하도록 하고 확률을 확인하는 대신, 직접 "Legitimate", "Spam", "Phishing"을 생성하도록 하세요 … 또한 모델이 단어나 숫자로 확률을 할당하도록 할 수도 있습니다." – sigmoid10

어시스턴트의 응답에 JSON 또는 일반 텍스트 스키마를 사용하면 후속 파싱을 간소화하고 토큰 수준의 로짓에 대한 의존도를 줄일 수 있습니다.

보정에 대한 우려

"확률은 항상 정확하지 않습니다; 보정된 결정은 일반적으로 RL 기반 미세조정(RLCD)이 필요합니다." – original post

많은 댓글러들이 원시 로짓이 항상 잘 보정되지 않음을 지적합니다. 온도 스케일링, Brier-손실 미세조정, 또는 사후 보정 곡선과 같은 기법들은 신뢰도를 향상시킬 수 있습니다.

속도와 정확도의 트레이드오프

"왜 분류기에서 "이해"을 원하지 않겠습니까? 속도와 비용은 명백한 이유지만, 이것이 트레이드오프가 아니겠습니까?" – brap

이 스크립트는 지연 시간을 줄이기 위해 사고의 흐름을 포기합니다. 고위험 결정의 경우, 명시적인 사고 과정을 포함한 더 느린 모델이 더 높은 정확도를 제공할 수 있습니다.

실용적인 고려사항

모델 선택

예제는 Qwen/Qwen3-0.6B-Q8_0.gguf를 사용하며, 이는 중간 수준의 하드웨어에 적합한 0.6B 파라미터 모델입니다. 더 빠른 추론은 양자화되거나 더 작은 모델을 사용해 달성할 수 있지만, 도메인에 따라 정확도가 달라질 수 있습니다.

지연 시간 측정

이 게시물은 벤치마크 수치를 제공하지 않습니다. 커뮤니티 구성원들은 구체적인 지연 시간과 오류율 수치(예: "45개 질문에 대해 <200ms")를 요청합니다. 생산 환경에 도입하기 전에 타겟 하드웨어에서 엔드투엔드 시간을 측정하는 것이 필수적입니다.

오류 처리

모델이 예상치 못한 토큰을 생성할 경우 파싱 실패가 발생할 수 있습니다. 엄격한 출력 스키마(예: choice 필드가 있는 JSON)와 재시도 로직을 추가하면 잘못된 응답을 줄일 수 있습니다.

확장성

이와 같은 패턴은 어떤 다중 클래스 분류 작업에도 적용할 수 있습니다: labels, choices, email을 적절한 도메인 데이터로 교체하고, 프롬프트를 새로운 맥락에 맞게 조정하면 됩니다.

대안 및 오픈 소스 구현

  • OpenJev – 더 완전한 오픈소스 참조 구현
  • openjev-sglang – sglang 런타임과 통합되어 더 높은 처리량을 제공
  • OpenJev on DiffusionGemma – 다른 백본 모델을 사용하여 이 접근 방식을 시연
  • Laya – System-One 스타일 결정을 위해 특별히 튜닝된 오픈웨이트 모델 (SylonZero의 댓글 참조)

결론

25줄의 스크립트는 Jev의 핵심 아이디어 – 프롬프트 기반 분류 및 확률 추출 – 가 일반적인 LLM, 특허 훈련 없이, 최소한의 코드로 재현될 수 있음을 입증합니다. 그러나 실무자들은 생산 환경에서 이러한 경량 파이프라인을 신뢰하기 전에 보정 한계, 프롬프트 설계의 세부 사항, 그리고 강력한 출력 파싱의 필요성을 인지해야 합니다.

Sources

관련

  • Dispatch
  • 프로젝트
  • 프로젝트
  • Dispatch
  • 프로젝트