제약된 디코딩: LLM을 빠른 의사결정 모델로
제약된 디코딩으로 LLM을 빠른 분류기로 바꿀 수 있습니다. 로짓 마스킹, 보정, 온도 스케일링으로 의사결정 모델을 제대로 쓰는 법을 알아봅니다.

여기 조용히 쓸모 있는 간단한 기법이 하나 있습니다. LLM이 내보낼 첫 번째 토큰만 신경 쓴다면, 객관식 질문을 던지고 단 한 번의 순전파(forward pass)로 답을 얻을 수 있습니다. 제약된 디코딩(어휘에서 받아들이고 싶은 소수의 토큰만 남기고 나머지를 모두 마스킹하는 방식)을 쓰면 생성형 모델이 꽤 분류기처럼 보입니다. JSON 파싱도, 재시도 루프도, 열한 글자를 만들기 위한 열한 번의 자기회귀 단계도 필요 없습니다. 한 번의 순전파, 한 번의 소프트맥스, 그리고 각 선택지에 확률이 붙은 답 하나면 충분합니다.
이 아이디어는 '의사결정 모델'이나 '시스템 1' 추론 같은 이름으로 오랫동안 떠돌았고, 최근에는 Qwen 1.7B 모델로 파이썬 40여 줄 만에 하나를 만드는 방법을 보여준 글이 Hacker News에서 크게 화제가 됐습니다. 댓글의 머신러닝 고수들은 예상대로 열을 냈습니다. 이건 분류기잖아, 퍼셉트론 시절부터 있었던 거라고. 그들 말이 맞고, 요점을 조금 놓친 것이기도 합니다. 새로운 건 개념이 아니라, 아무것도 학습시키지 않고 범용 언어 모델에서 제로샷 분류기를 얻을 수 있다는 점입니다. 흥미로운 엔지니어링 질문은 이 제약 방식이 모델을 그냥 말하게 두는 것보다 언제 나은지, 그리고 언제 과신한 확률로 조용히 거짓말을 하는지입니다.
LLM에서 답을 얻는 두 가지 방법
언어 모델과 대화하는 기본 방식은 생성입니다. 질문을 던지면 모델이 토큰을 하나씩 내보내고, 그 결과를 나중에 파싱합니다. 구조화된 출력이 필요하면 JSON 모드, 문법 제약 샘플링, outlines 방식의 유한 상태 기계 같은 스키마를 덧붙입니다. 이런 방식들은 잘 작동하지만, 모델은 여전히 응답 전체를 토큰 단위로 걸어갑니다. 간단한 객관식 답 하나에도 디코딩 단계가 열한 번 필요할 수 있고, 각 단계마다 수십억 개의 파라미터를 전부 통과하는 순전파 비용이 듭니다.
대안은 아예 걷게 하지 않는 것입니다. 프롬프트를 처리한 뒤 마지막 위치에서 어휘 전체의 로짓을 보고, 선택지에 해당하는 토큰 ID(예를 들어 "A", "B", "C", "D", "E")만 남긴 다음 그 값들에만 소프트맥스를 적용합니다. 최댓값이 예측이고, 소프트맥스 값이 점수가 됩니다. 총비용은 한 번의 순전파이며, 이는 이미 지불하고 있던 프리필 비용과 같습니다. 요즘 떠도는 Qwen 기반 방식을 각색한 핵심 코드는 다음과 같습니다.
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
model_name = "Qwen/Qwen3-1.7B"
options = ["A", "B", "C", "D", "E"]
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(
model_name, torch_dtype="auto", device_map="auto"
)
# First token the model would emit for each option
option_token_ids = [
tokenizer.encode(opt, add_special_tokens=False)[0] for opt in options
]
prompt = (
"What color is the sky?\n"
"A. Red\nB. Blue\nC. Green\nD. Purple\nE. I don't know\nAnswer:"
)
messages = [{"role": "user", "content": prompt}]
text = tokenizer.apply_chat_template(
messages, tokenize=False, add_generation_prompt=True,
enable_thinking=False
)
inputs = tokenizer(text, return_tensors="pt").to(model.device)
with torch.no_grad():
logits = model(**inputs).logits[0, -1]
# Constrained decoding: softmax over only the option tokens
probs = torch.softmax(logits[option_token_ids].float(), dim=-1)
print(options[probs.argmax().item()]) # -> B
이게 엔진의 전부입니다. 어휘는 15만 개가 넘는 토큰인데, 우리는 결정을 다섯 개의 숫자로 줄였습니다. 생성 시점의 샘플링 온도 조작도, 실패할 수 있는 파서도, 목록에 없는 선택지를 모델이 환각으로 만들어낼 여지도 없습니다. 출력 공간이 구조적으로 닫혀 있습니다.
분류기가 맞다, 그래도 괜찮다
회의론자들의 말도 인정해 줍시다. 우리가 만든 것은 고정된 레이블 집합에 대한 판별적 분류기입니다. 1950년대 로젠블랫의 퍼셉트론에서 시작해 로지스틱 회귀를 거쳐, 딥러닝 시대의 소프트맥스 헤드가 달린 모든 신경망까지 이어지는 계보의 일부입니다. 눈을 가늘게 뜨고 보면, 제약된 디코딩은 마지막 은닉 상태 위에 올린 선형 판독기일 뿐이고, 이는 분류 헤드가 늘 해온 일입니다. 몇 년째 팀에 그냥 분류기를 학습시키자고 졸라 온 MLE라면, 이것이 '의사결정 모델'이라는 새 이름으로 재포장되는 걸 보며 조금 울컥할 만합니다.
하지만 역사는 새 버전이 왜 중요한지도 보여줍니다. 예전 분류기는 좁았습니다. 레이블된 데이터를 모으고, 학습시키고, 한 가지 작업만 아는 모델을 얻었죠. 정식 분류기를 써야 할 것 같은 작업을 LLM 기반 시스템이 계속 잡아먹는 이유는 제로샷 특성 때문입니다. 기반 모델이 이미 세상을 충분히 흡수했기 때문에, 프롬프트 자체가 학습 데이터 역할을 합니다. 제가 이런 설정을 CommonsenseQA 홀드아웃에 테스트했을 때, 1.7B 모델은 파인튜닝 없이 약 59%의 정확도를 기록했고, 학습 분할로 잠깐 튜닝하자 약 62%까지 올라갔습니다. 최고 성능은 아니지만, 오후 한나절과 제가 직접 가진 레이블 데이터 없이 얻은 결과입니다. 전용 분류기가 더 잘할 수도 있지만, 그러려면 파이프라인, 데이터셋, 그리고 레이블이 바뀔 때마다 필요한 재학습 계획이 따라와야 합니다.
여기서 예전의 생성 대 판별 논쟁에서 빌려올 만한 관점이 있습니다. 생성형 분류기는 전체 분포를 모델링해서 비효율적이지만 유연하고, 판별형 분류기는 결정 경계만 모델링해서 효율적이지만 경직되어 있습니다. 제약된 디코딩은 묘한 혼종입니다. 생성 모델을 추론 시점에 판별 작업에 끌어다 쓰는 것이죠. 판별 판독기의 효율성과 생성형 사전학습의 폭을 함께 얻는 셈입니다. 부품 자체는 오래되었지만, 이런 조합은 이전에는 사실상 불가능했습니다.
제약된 디코딩이 이기는 곳
- 지연 시간. N번의 자기회귀 단계 대신 순전파 한 번입니다. 작은 모델에서는 '요청 경로에 올려도 충분히 빠름'과 '큐가 필요함'의 차이가 됩니다. 브라우저에서 순수 의사결정 모델을 돌리는 실무자들은 200ms 미만의 응답을 보고합니다. 생성형 JSON으로는 어림도 없죠.
- 구조적 정확성. 모델이 허용된 집합 밖의 출력을 문자 그대로 만들 수 없습니다. 깨진 JSON도, 'The answer is probably B because...' 같은 횡설수설도, 형식 위반을 잡아낼 가드레일 계층도 필요 없습니다.
- 처리량. 모든 요청이 같은 모양의 단일 패스이므로 배칭이 간단하고 예측 가능합니다. 길이가 가변적인 답변을 생성하면 배칭 효율이 무너집니다.
- 선택지별 점수. 승자만이 아니라 전체 분포를 얻습니다. 이는 기권(abstention) 로직의 문을 열어 줍니다. 최상위 확률이 임계값보다 낮으면 사람이나 더 큰 모델로 넘기면 됩니다.
기권 지점은 강조할 만합니다. 생성형 답변은 믿거나 말거나 하는 단일 산출물입니다. 선택지에 대한 확률 분포가 있으면 라우팅을 만들 수 있습니다. 확신이 높은 경우는 자동으로 통과시키고, 확신이 낮은 경우는 에스컬레이션하는 방식이죠. 많은 프로덕션 트리아지 시스템의 기반이 이 패턴이며, 고객 지원 티켓 라우팅, 콘텐츠 모더레이션 사전 검수, 의도 탐지 등에서 이 기법이 제값을 합니다. 작업이 자연스럽게 "K개 레이블 중 하나를 고르고, 얼마나 확신하는지도 알려 달라"로 나뉜다면, 제약된 디코딩이 거의 확실히 맞는 도구입니다.
생성형 출력이 이기는 곳
이제 반대편입니다. 작업이 고정된 레이블 집합에 맞지 않는 순간, 제약된 디코딩은 무너집니다. 답이 자유 형식의 개체명, 숫자, 코드 조각, 또는 무엇이든 합성적인 것이라면 생성이 필요합니다. 구조화된 출력 제약을 함께 쓸 수도 있지만, 어쨌든 생성입니다. 더 미묘한 손실도 있습니다. 추론입니다. 모델이 답하기 전에 생각의 사슬을 생성하면, 어려운 질문에서 실질적으로 더 나은 성능을 보이는 경우가 많습니다. 단일 패스 결정 헤드에는 메모장이 없습니다. 시스템 1 사고, 즉 빠르고 직관적인 사고를 요구하는 셈이고, 그에 따른 전형적인 실패까지 그대로 받아들이게 됩니다.
표현 민감성 문제도 있습니다. "A/B/C/D/E"에 대한 제약된 분류기는 사실상 그 위치에 있는 각 선택지의 텍스트에 대한 모델의 선호를 측정하고 있는 것입니다. 선택지 C의 표현을 살짝 바꾸거나, 목록 순서를 바꾸거나, "Answer:"를 "The best answer is"로 바꾸기만 해도 점수가 움직일 수 있습니다. 추론을 포함한 생성형 답변은 모델이 토큰만이 아니라 내용에 전념해야 하므로 표면적 변형에 더 강한 경향이 있습니다. 어느 접근을 평가하든, 프레젠테이션을 흔들어 보고 무엇이 깨지는지 확인하세요. 저렴한 강건성 테스트이면서 동시에 불편한 테스트입니다.

보정 문제: 신뢰도 점수는 거짓말을 한다
이런 것을 만들어 본 사람이라면 누구나 걸리는 함정이 여기 있습니다. 소프트맥스에서 확률을 얻으니 당연히 확률이겠죠? 그렇지 않습니다. 그것은 특정 토큰이 다음에 올 것이라는 모델의 신뢰도이며, 이는 언어에 관한 진술이지 정답 여부에 관한 진술이 아닙니다. "박쥐를 가장 찾기 쉬운 곳은 어디인가?"라고 묻고 선택지에 "동굴"과 "야구 경기장"을 넣으면, 모델은 "동굴"에 0.998 같은 값을 줄 것입니다. 명확히 옳다고 말할 수 없는 모호한 질문에 거의 완벽한 확신으로 답하는 셈입니다.
실제 평가에서 신뢰도 구간별로 나눠 보면 상황은 더 나빠집니다. 한 CommonsenseQA 실험에서 0.9~1.0 구간은 약 70%만 맞았고, 0.8~0.9 구간은 겨우 40%를 넘겼습니다. 잘 보정된 모델이라면 0.9라고 말할 때 약 90%는 맞아야 합니다. 이 모델은 체계적으로 과신합니다. 곰곰이 생각하면 이는 현대 딥넷이 전반적으로 과신한다는 오래된 관찰과 맞아떨어집니다. Guo 등은 2017년에 일반 ResNet의 소프트맥스 출력이 1990년대의 얕은 네트워크에 비해 심각하게 잘못 보정되어 있음을 보였습니다. 오래된 것이 다시 새로운 것이 되었고, 우리는 17억 파라미터 규모에서 같은 문제를 다시 발명했을 뿐입니다.
다행히 해결책도 오래되었고, 민망할 만큼 단순합니다. 바로 온도 스케일링입니다. 소프트맥스 이전에 로짓을 학습된 스칼라 T 하나로 나눕니다.
def scaled_probs(logits, option_ids, temperature):
selected = logits[option_ids].float() / temperature
return torch.softmax(selected, dim=-1)
# Fit T on a validation set by minimizing negative log-likelihood
# of the correct option. For the CommonsenseQA run above, T ~= 3.8
temperature = 3.7973
probs = scaled_probs(logits, option_token_ids, temperature)
T가 1보다 크면 분포가 평평해지고, 1보다 작으면 날카로워집니다. 홀드아웃 세트에 맞춰 숫자 하나를 적합시키니, 그 끔찍한 보정표가 정직한 표로 바뀌었습니다. 0.9~1.0 구간은 이제 약 95% 정확도에 머물고, 0.5~0.6 구간은 약 55%입니다. 정확도는 전혀 바뀌지 않습니다. argmax는 단조 스케일링에 불변이기 때문입니다. 하지만 이제 점수가 당신이 생각하던 의미를 갖습니다. 이 확률로 라우팅할 생각이라면, 먼저 보정하세요. 그렇지 않다면 아예 수집할 필요도 없습니다.
벤치마크에서 온도를 튜닝하는 건 반칙일까?
토론에서 나온 타당한 질문이 있습니다. 벤치마크에서 모델이 보정된 것처럼 보이도록 T를 맞추는 건 약간 p-해킹이 아니냐는 것이죠. 테스트 세트에 맞춰 적합시킨다면 그렇습니다. 제대로 하면, 즉 검증 분할에서 T를 적합시키고 보정 결과는 별도의 테스트 분할에서 보고하면, 그것은 그저 파라미터 하나짜리 회귀일 뿐이며 문헌에서도 바로 이런 이유로 표준적인 관행입니다. 중요한 원칙은 ML의 다른 모든 곳과 같습니다. 분할을 정직하게 유지하고, 적합 과정에 조금이라도 닿은 데이터에서 측정한 보정 주장에는 의심을 품으세요. 배포 도메인이 평가 도메인에서 멀어지면 T도 함께 어긋나므로, 재보정은 다른 모든 것과 함께 유지보수 루프에 들어가야 합니다.
이것은 사실 더 넓은 문제의 한 사례입니다. 시스템이 무엇을 의미하도록 명세되었는지와 실제로 무엇을 계산하는지 사이의 간극인데, 제가 전에도 버그가 숨어 있는 곳이라고 주장한 바 있습니다. "신뢰도"는 정답일 확률로 명세되지만, 구현은 다음 토큰의 그럴듯함을 줍니다. 온도 스케일링은 그 간극을 메우는 패치이지, 간극을 해소하는 것은 아닙니다. 소프트맥스 출력을 알림 임계값에 연결하고 싶어질 때마다 이 차이를 기억하세요.
실용적인 의사결정 절차
요즘 두 모드 중에서 고를 때는 짧은 체크리스트를 돌려 봅니다.
- 출력이 고정된 레이블 집합인가? 그렇다면 제약된 디코딩이 선택지에 오릅니다. 아니라면 생성하세요.
- 허용할 만한 정확도를 위해 추론이 필요한가? 둘 다 프로토타이핑하세요. 단일 패스 헤드가 사고의 사슬보다 감당할 수 없을 만큼 뒤처진다면, 지연 시간이 길어도 생성이 이깁니다.
- 라우팅이나 기권을 위해 선택지별 점수가 필요한가? 그렇다면 제약된 디코딩에 온도 스케일링을 더하는 것은 거의 공짜이며, 생성은 비슷한 것을 전혀 주지 못합니다.
- 레이블 집합은 얼마나 큰가? 선택지 다섯 개는 사소하고, 5,000개라면 검색의 영역입니다. 레이블 공간이 크다면, 레이블을 임베딩하고 벡터 검색으로 후보를 추린 뒤, 그 최종 후보들 사이에서 모델이 고르게 하세요. 추천 시스템 시대부터 표준이 된 분류기 확장 기법입니다.
- 레이블이 자주 바뀔까? 제로샷 특성이 바로 핵심입니다. 레이블이 매주 바뀐다면 재학습되는 분류기는 유지보수 부담이 되지만, 프롬프트 수정은 그렇지 않습니다.
모델의 소프트맥스는 무엇이 다음 토큰인지에 관한 주장이지, 무엇이 참인지에 관한 주장이 아닙니다. 보정하거나, 믿지 마세요.
한 가지 고려 사항이 더 있습니다. 모델 크기는 선택과 상호작용합니다. 작은 모델의 단일 패스 헤드는 모든 요청에서, 심지어 클라이언트 측에서도 돌릴 만큼 저렴합니다. 사람들은 이미 200ms 미만으로 응답하는 의사결정 모델을 브라우저에서 돌리도록 배포하고 있습니다. 쉬운 80%는 작은 제약 모델이, 어려운 나머지 꼬리는 큰 생성 모델이 처리하는 캐스케이드 설계는 비용과 정확도 양쪽에서 어느 극단보다 자주 낫습니다. 이것은 추측 디코딩과 같은 직관인데, 토큰 수준이 아니라 시스템 수준에 적용한 것입니다.
제 결론
제 입장은 이렇습니다. 작업이 진짜로 고정된 선택지의 결정이라면, 즉 라우팅, 트리아지, 의도 파악, 객관식 평가라면, 제약된 결정 헤드를 만들고 뒤돌아보지 마세요. 지연 시간의 이득만으로도 가치가 있고, 구조적 보장은 파싱 실패의 한 부류를 통째로 없애며, 선택지별 점수는 생성이 따라올 수 없는 라우팅 로직을 줍니다. 다만 날것의 소프트맥스는 보정되지 않은 계측기로 취급하세요. 적은 양이라도 데이터를 모을 수 있다면 실제 작업에 맞춰 파인튜닝하고, 깨끗한 검증 분할에서 T를 적합시키고, 적합에 쓰이지 않은 데이터에서 보정을 검증하세요.
생성형 출력은, 필요하다면 구조화된 출력 제약과 함께, 진정으로 합성적이거나 가시적인 추론의 이점을 받는 작업에 남겨 두세요. 그리고 누군가 '의사결정 모델'을 새로운 범주라고 팔려 하면 믿지 마세요. 그것은 LLM의 외투를 걸친 분류기이며, 60년간의 판별 모델링에서 이어진 것입니다. 옛 전문가들이 늘 강조했던 보정 작업을 그 계보를 충분히 존중하며 해낼 때 가장 쓸모 있습니다. 도구는 새롭지만, 원칙은 그대로입니다.


