Jev 스타일의 토큰 로그확률을 사용한 LLM 및 비전 모델용 파이썬 래퍼

TL;DR – 래퍼가 하는 일과 그 중요성

작성자는 어떤 채팅 완성 엔드포인트에도 Jev 스타일의 요청(상태, 질문, 선택적 이미지 첨부)을 보내는 최소한의 파이썬 래퍼를 만들었다. 이 요청은 모델이 한 토큰(선택된 옵션을 나타내는 문자)만 출력하도록 하고, 모든 후보 토큰에 대한 로그확률(log-probabilities)을 읽어온다. 이 기법은 텍스트 전용 모델뿐 아니라 비전 보강 모델까지도 사용할 수 있는 저비용, 저지연 분류기로 생성형 LLM을 전환한다.


핵심 아이디어 – 로그확률을 사용해 생성을 분류로 전환

  • 프롬프트 형식 – 래퍼는 상태, 질문, [A], [B], …와 같은 레이블이 붙은 옵션 목록을 포함한 프롬프트를 구성하고, 마지막에 “Answer with the letter of the best option only.” (가장 좋은 선택지의 문자만 응답하십시오.)라는 지시를 추가한다. 예시:
    State:
    이 웹캠 프레임을 검사하세요. 보이는 것만 판단하세요.
    
    Question: 사람이 보이나요?
    Options:
    [A] true
    [B] false
    
    Answer with the letter of the best option only.
    
  • API 파라미터 – 요청에는 다음이 포함된다:
    {
      "max_completion_tokens": 1,
      "logprobs": true,
      "top_logprobs": 20,
      "temperature": 0
    }
    
    max_completion_tokens를 1로 설정하면 모델이 단일 토큰만 출력하도록 강제되어 응답 크기가 작고 지연이 낮아진다.
  • 로그확률 추출 – API는 상위 N개 토큰 후보와 그 로그확률을 반환한다. 래퍼는 각 옵션 문자를 토큰에 매핑하여 로그확률을 옵션에 대한 정규화된 확률 분포로 변환한다.
  • 결과 해석 – 이진 질문(noul 유형)의 경우 래퍼는 true의 확률을 반환한다. 다중 선택 질문의 경우 가장 가능성 높은 옵션과 전체 확률 표를 반환한다. 순서형 점수의 경우 기대값을 계산한다.

Jev를 비전으로 확장 – attachments 필드

  • 원래 Jev 사양은 텍스트/JSON state만 지원한다. 작성자는 파일 경로 또는 base64 인코딩된 데이터 URL을 포함할 수 있는 attachments 배열을 추가했다.
  • 요청이 OpenAI 엔드포인트로 전송될 때 각 이미지는 type: "input_image" 요소로 추가되고, llama.cpp의 경우 type: "image_url"로 추가된다.
  • 예시는 OpenCV로 실시간 웹캠 프레임을 캡처하고, JPEG로 인코딩한 후 데이터 URL로 감싸 attachments에 넣어 각 요청 전에 전송한다.

엔드투엔드 파이썬 예제 (약 150줄)

스크립트는 루프 내에서 세 단계를 수행한다:

  1. 캡처 – /dev/video0에서 프레임을 캡처한다.
  2. 인코딩 – 프레임을 base64 JPEG로 인코딩하고 data["attachments"]를 설정한다.
  3. 제출 – 백그라운드 스레드를 사용해 선택한 백엔드(로컬 llama.cpp 서버 또는 OpenAI)에 요청을 보낸다.
  4. 출력 – 최신 답변과 측정된 FPS를 포함한 테이블을 출력한다.

핵심 함수:

  • score(data, url, model) – 각 질문에 대한 프롬프트를 구성하고 요청을 전송하며, 로그확률을 정규화하고 구조화된 답변 사전을 반환한다.
  • main – CLI 인수(url 및 model)를 파싱하고 웹캠을 시작하며 비동기 스코어링을 조율한다.

스크립트는 의도적으로 자가 포함되어 있으며, 웹캠 접근을 위해 opencv-python 외에는 외부 종속성이 없다. 기타 모든 라이브러리는 파이썬 표준 라이브러리에 포함되어 있다.


작성자가 보고한 성능 수치

백엔드 모델 하드웨어 FPS (프레임/초)
llama.cpp (로컬) Gemma‑4‑12B‑QAT (GGUF) RTX 3090 ≈ 1 fps (프레임당 세 질문)
OpenAI gpt‑6‑luna 클라우드 ≈ 0.2 fps

작성자는 OpenAI 실행이 더 느린 이유 중 하나가 프레임당 각 질문마다 새 HTTP 연결을 생성하기 때문이라고 지적하며, 이는 최적화될 수 있다고 언급했다.


로컬 llama.cpp 서버 설정 지침

# 1. 7GB의 Gemma‑4‑12B 모델과 다중 모달 프로젝터(~175MB) 다운로드
mkdir -p ~/models/gemma-4-12b/
cd ~/models/gemma-4-12b/
curl -fL -C - -o gemma-4-12b-it-qat-q4_0.gguf \
  https://huggingface.co/google/gemma-4-12B-it-qat-q4_0-gguf/resolve/main/gemma-4-12b-it-qat-q4_0.gguf
curl -fL -C - -o mmproj-gemma-4-12b-it-qat-q4_0.gguf \
  https://huggingface.co/google/gemma-4-12B-it-qat-q4_0-gguf/resolve/main/mmproj-gemma-4-12b-it-qat-q4_0.gguf

# 2. CUDA-86 llama.cpp 바이너리 설치
curl -fL -o llama.zst \
  https://huggingface.co/buckets/ggml-org/install.sh/resolve/b11160/x86_64/linux/cuda/86/llama-app.zst
mkdir -p ~/bin/
zstd -d llama.zst -o ~/bin/llama
chmod +x ~/bin/llama

# 3. 포트 8060에서 서버 시작
~/bin/llama serve --models-dir ~/models/ --port 8060

서버가 실행된 후 래퍼를 실행한다:

uv run webcam.py http://localhost:8060/v1 gemma-4-12b
# 또는 OpenAI 사용 (OPENAI_API_KEY를 미리 설정)
uv run webcam.py https://api.openai.com/v1 gpt-6-luna

커뮤니티 반응 (선택된 HN 댓글)

@TeMPOraL – “이것이 스타트렉 스타일의 주변 인식을 얻는 방법이 될 수 있다: 다중 모달 컨텍스트에서 의도를 추론하고 자동으로 행동한다.” 댓글은 단순한 분류를 넘어서 실시간 의도 탐지에 이러한 래퍼를 사용할 수 있는 더 넓은 비전을 강조한다.

@prathje – “이건 JSON 응답을 가지는 문법 기반 디코딩일 뿐인가요? 그렇다면 Jev는 사실상 그 위에 캐싱 레이어일 뿐입니다.” 작성자의 구현은 결정적인 프롬프트와 토큰 수준의 로짓에 의존하지만, 반복되는 상태 접두사에 대해 가벼운 캐싱 전략을 추가한다.

@frabcus – “RLHF로 훈련된 일반적인 LLM은 네트워크 내에서 더 일찍 결정을 내릴 수 있으므로, Jev 스타일의 로그확률 래퍼는 특정한 보정된 결정을 위해 훈련된 모델보다 정확도가 낮을 수 있습니다.” 이는 일반 모델의 확률 보정이 하위 결정에 대해 최적화되지 않을 수 있다는 잠재적 한계를 지적한다.

@arcticbull – “Jev처럼 보이지만, 수십 배 더 비싸고 느리다.” 이 댓글은 일반적인 LLM의 유연성과 전용 비전 분류기의 효율성 사이의 트레이드오프를 반영한다.

@CROON_tv – “꼬리 지연 시간 수치를 보고 싶다. 우리의 실시간 음성 엔드포인트에서는 Jev가 동일한 프롬프트를 가진 일반 LLM보다 더 빠르고 더 망설이지 않았다.” 지연 시간은 상호작용 애플리케이션에 중요한 지표이며, 래퍼의 단일 토큰 접근 방식이 꼬리 지연 시간을 낮추는 데 도움이 된다.

@czl_my – “나는 어떤 OpenAI 호환 엔드포인트와도 작동하는 Jev 래퍼를 만들었다: https://github.com/zhulinchng/jevper.” 외부 구현은 이 아이디어가 점점 인기를 얻고 있음을 보여준다.


한계와 미해결 질문

  • 확률 보정 – 일반 모델에서 나오는 원시 로그확률은 특히 드문 토큰에 대해 잘 보정되지 않을 수 있다. 사용자는 온도 스케일링 또는 사후 보정이 필요할 수 있다.
  • 누락된 토큰 처리 – 래퍼는 상위 N 목록에 없는 옵션의 토큰을 0 확률로 간주하지만, 누락된 질량이 1e-6를 초과하면 오류를 발생시킨다. 이 안전 검사는 침묵된 잘못된 순위를 방지한다.
  • 확장성 – 프레임당 세 개의 질문을 처리하는 예제처럼 각 질문마다 별도의 요청을 보내는 것은 네트워크 오버헤드를 증가시킨다. 여러 질문을 하나의 프롬프트에 묶어 배치 처리하면 처리량을 향상시킬 수 있다.
  • 비전 모델 선택 – Gemma-4-12B는 작동하지만, 전용 다중 모달 모델(예: CLIP, Florence)은 더 높은 FPS와 더 나은 시각 정확도를 달성할 가능성이 높다.

결론

제출된 래퍼는 토큰 수준의 로그확률을 활용하여 어떤 채팅 완성 API(비전 보강 백엔드 포함)를 빠르고 결정적인 분류기로 전환하는 실용적이고 언어 모델에 관계없는 방법을 보여준다. 단일 파이썬 함수로 간단하고 이미지 첨부 기능을 추가할 수 있어 실시간 다중 모달 결정 시스템의 유용한 빌딩 블록이지만, 확률 보정과 질문당 요청 오버헤드에 대한 일반적인 주의사항은 여전히 존재한다.

Sources