[기획: AI 트렌드] length_penalty를 바꿔도 같은 답이 나오던 vLLM beam search, 점수의 분모에 프롬프트 길이가 들어 있었다
10월 22일, 설정을 바꿔도 같은 답
10월 22일, vLLM에 PR #62928이 머지됐습니다. vLLM은 AI 모델을 서버에서 여러 사용자에게 빠르게 돌려 주는 오픈소스 엔진입니다. PR은 코드 수정 요청이고, 머지는 그것을 본 코드에 합치는 일입니다. 외부 기여자 4arkeinlvy가 쓰고 DarkLight1337이 머지한 이 PR은 답 후보에 점수를 매기는 계산을 고쳤습니다.
작성자는 590토큰짜리 프롬프트로 length_penalty를 시험했습니다. 프롬프트는 모델에 넣는 입력 글이고, 토큰은 모델이 글을 잘게 자른 조각입니다. length_penalty는 긴 답과 짧은 답 가운데 어느 쪽을 얼마나 우대할지 정하는 설정입니다. 고치기 전 vLLM에서는 이 설정을 0에서 2까지 바꿔도, 가장 높은 점수를 받은 답이 매번 같은 16토큰짜리였습니다. 고친 뒤에는 설정에 따라 16토큰에서 생성 한도인 64토큰까지 갈렸습니다.
작성자가 서버에 보낸 예시 요청에서도, 고치기 전 vLLM의 첫 답은 빈 문자열이었습니다. 고친 뒤에는 글이 정상으로 이어졌습니다.
점수의 분모
모델은 다음 토큰을 하나씩 골라 답을 이어 씁니다. beam search는 답 후보(beam) 여럿을 함께 이어 쓰다가, 한 토큰 나아갈 때마다 점수가 높은 몇 개만 남기는 방식입니다.
점수의 바탕은 로그확률입니다. 모델이 그 토큰을 고를 확률에 로그를 씌운 수치이고, 0 이하이며 대개 음수입니다. 후보의 점수는 생성한 토큰마다 로그확률을 더한 합이라, 토큰마다 음수가 더해지니 답이 길수록 작아집니다. 그대로 겨루면 짧은 답이 유리합니다.
그래서 합을 길이로 나눕니다. 이를 길이 정규화라고 하고, length_penalty는 이때 길이에 거는 거듭제곱입니다. 이 수치가 클수록 긴 답 쪽으로 기웁니다.
고치기 전 vLLM은 이 합을 「프롬프트 길이 + 생성 길이」로 나눴습니다. 틀릴 때마다 감점이 쌓이는 시험에서 평균 감점을 내면서, 각자 푼 문항 수에 시험지 앞 예제 문항 수까지 더해 나눈 셈입니다. 많이 푼 학생은 쌓인 감점만큼 나빠 보이는데, 예제가 많을수록, 즉 프롬프트가 길수록 분모가 그 차이를 거의 덜어 주지 못합니다.
작성자의 590토큰 조건에서 length_penalty가 1이면, 16토큰 답과 64토큰 답의 분모는 606 대 654로 약 1.08배입니다(우리 계산). 생성 길이로만 나누면 두 분모는 4배로 벌어집니다(우리 계산). 분모가 거의 같으면 점수는 합끼리 겨루는 것과 다름없어, 감점이 덜 쌓인 짧은 답이 이깁니다. 작성자도 프롬프트가 길면 length_penalty가 효과를 잃는다고 적었습니다.
답은 「여기서 끝」을 뜻하는 특수 토큰 EOS가 나오면 끝납니다. 작성자는 EOS에 일찍 닿은 beam이 이겨 빈 응답이 자주 나왔다고 적었습니다. 끝 줄바꿈을 뺀 프롬프트 8개에서, 맨 위 beam이 EOS 하나뿐인 빈 답은 고치기 전 8개 모두였고 고친 뒤 1개였습니다. Transformers에서도 1개였습니다.
고친 뒤에도 남은 차이
고친 뒤의 점수는 로그확률 합을 생성 토큰 수(의 거듭제곱)로만 나눕니다. 끝의 EOS도 그 수에 넣습니다. 작성자는 이것이 Transformers의 방식이라고 적었습니다. Transformers는 Hugging Face의 오픈소스 AI 모델 라이브러리입니다.
작성자는 vLLM과 Transformers에 같은 프롬프트 8개를 넣어, beam이 얼마나 같게 나오는지 쟀습니다.
| 조건(끝 줄바꿈 · 토큰) | 맨 위 beam 일치(고치기 전 → 후) | beam 32개 전체 일치(고치기 전 → 후) |
|---|---|---|
| 줄바꿈 포함 · 64 | 6/8 → 8/8 | 24/32 → 30/32 |
| 줄바꿈 포함 · 128 | 6/8 → 8/8 | 21/32 → 29/32 |
| 줄바꿈 제거 · 64 | 1/8 → 7/8 | 2/32 → 22/32 |
| 줄바꿈 제거 · 128 | 1/8 → 5/8 | 2/32 → 11/32 |
출처 f7 · PR 작성자 한 사람의 측정 · 독립 재현 아님 · TinyLlama-1.1B-Chat(fp16) · 프롬프트 8개 × beam 4개 = 32개 · length_penalty 1.0 · 줄바꿈 = 작성자 표의 example.txt 끝 개행
네 조건 모두 일치가 늘었습니다. 그래도 표 마지막 줄은 고친 뒤에도 전체 일치가 절반에 못 미칩니다.
작성자는 남은 차이를 이 PR이 바꾸지 않는 두 가지로 설명했습니다. 하나는 EOS로 끝난 후보를 완성된 답으로 확정하는 기준입니다. vLLM은 beam마다 로그확률 상위 「beam 수의 2배」 안에 든 EOS를 모두, Transformers는 전체 상위 「beam 수」 안에 든 EOS만 확정합니다. 그만큼 vLLM은 짧게 끝난 후보를 완성 답 목록에 더 많이 올리게 됩니다(우리 풀이). 다른 하나는 vLLM에 탐색을 일찍 멈추는 규칙이 없다는 점입니다.
순위가 바뀌는 것은 비교되는 beam끼리 생성 길이가 다를 때뿐이고, 작성자는 그것이 의도라고 적었습니다. 영향 범위는 코드에서 직접 부르는 LLM.beam_search, 서버 API에서 beam search를 켠 요청, 음성을 글로 옮기는 기능에서 beam search를 쓸 때입니다.
PR에는 회귀 테스트도 들어갔습니다. 고친 버그가 다시 생기면 실패하도록 넣는 시험 코드입니다. 작성자 보고로는 고치기 전 코드에서 2건 모두 실패했고, 서버 경로는 빈 목록을, 코드 경로는 EOS 하나를 돌려줬습니다. 고친 코드에서는 2건 모두 통과했습니다.
알려진 버그였다
같은 버그는 Transformers에서 먼저 고쳐졌습니다. 2023년 11월 머지된 PR #27351은, 프롬프트 뒤에 답을 이어 쓰는 모델에서 beam 점수가 프롬프트와 생성 문장을 합친 길이로 정규화되던 문제를 고쳤습니다. PR 본문에는 이 문제가 「짧은 문장을 만드는 쪽으로 예상 밖의 편향」을 일으킨다는 문장이 있습니다.
vLLM에도 보고가 있었습니다. 이슈는 버그를 알리는 공개 게시글입니다. 2024년 1월 physicsrob은 이슈 #2606에, 점수를 계산할 때 길이에 프롬프트 길이를 잘못 넣는 것 같다고 적었습니다. 이번에 고친 원인 그대로입니다. 90일 동안 활동이 없자 2024년 11월 봇이 이 이슈에 stale(오래 묵음) 표시를 붙였고, 12월 「not planned(계획 없음)」로 닫았습니다.
PR #62928의 작성자는 이전 수정 시도 #7007이 vLLM의 옛 V0 코드를 겨냥했고 머지되지 않았다고 적었습니다. Transformers의 수정에서 이번 머지까지는 약 2년 11개월, 이슈가 열린 뒤로는 약 2년 9개월입니다(우리 계산).
Rust 언어로 짠 vLLM 프런트엔드(요청을 받아 엔진에 넘기는 앞단)에 beam search를 붙이는 PR #47705는 아직 머지 전인데, 옛 공식을 그 공식대로 적은 단위 테스트와 함께 담고 있습니다. 단위 테스트는 함수 하나가 정해 둔 결과를 내는지 확인하는 작은 시험 코드입니다. PR #62928의 작성자는 그 PR에도 같은 수정이 필요하다고 적었습니다.
옛 공식을 정답으로 적은 테스트
긴 프롬프트에서 length_penalty는 받아들여지기만 하고 답을 바꾸지 않는 설정이었습니다. 앞선 글에서 우리는 vLLM의 다른 결함이 블록 이음매를 넘는 입력에서만 드러난다고 봤는데8,192칸마다 처음부터 센 동률 개수, vLLM의 빠른 top-p 경로가 후보 토큰을 더 남겼다, 이번 결함은 분모 구조상 프롬프트가 길어야 크게 드러납니다.
앞으로 볼 것은 #47705가 머지되기 전에 같은 수정이 들어가는지입니다.
우리는 이 버그가 오래 남은 이유를 무지에서 찾지 않습니다. 원인은 2024년 이슈로 짚혔고, 같은 수정은 Transformers에 먼저 들어갔습니다. 그런데 이슈는 활동이 없다는 이유로 봇이 닫았고, 수정 시도는 옮겨 가기 전의 옛 코드를 겨냥한 채 머지되지 않았습니다. 원인은 알려져 있었지만 수정이 코드의 이전을 따라가지 못한 셈입니다. 같은 일이 다시 일어날 수 있는 곳도 보입니다. 옛 공식을 정답으로 적은 테스트라면, 공식을 고치는 수정이 그 테스트에서 실패합니다. 테스트의 통과는 코드가 적힌 공식대로 돈다는 뜻이지, 그 공식이 옳다는 뜻이 아닙니다(이 문단은 우리 판단).
유의
"루틴 17회차 · 캐스케이드 ①(GitHub 1차 · 메인 재열람 인용 대조) · 분량 raw 10,286B·마커 제거 약 10,086B(10KB 상한 경계) · 델타 R2 should-fix 1(L23 f6 64/128 조건 미표시 한정 누락) · nit 3(L21 고리 문장 표지 없음 · L17 [f4][f14] 권고 · P5 분류 사유 reviews 이관=완료) 미반영 fail-forward(재판정·스폰 상한 8) · 측정 전부 PR 작성자 1인·TinyLlama-1.1B-Chat 한 모델 자기보고(독립 재현 아님) · 짧은 프롬프트 측정 미확보 · 원문 WebFetch 요약 경유 인용 대조 ·출처
- 기준일 = 2026년 10월 23일 오전(한국 시간).
- 아래 GitHub 페이지는 요약 모델을 거치는 웹 열람 도구(WebFetch)로 우리가 직접 열어, 핵심 문장만 인용으로 대조했습니다. 문단 전문은 대조하지 못했습니다. 페이지에 시각과 시간대가 없어 날짜만 적었습니다. 측정 수치는 모두 PR 작성자 한 사람이 한 모델(TinyLlama-1.1B-Chat)로 잰 자기보고이고 독립 재현이 아닙니다. 짧은 프롬프트에서 잰 결과는 확인하지 못했습니다.
- 이해관계: Refract는 Anthropic의 Claude로 글을 분석하고 씁니다. 이 PR과는 관련이 없습니다.
- beam search 점수 분모 수정 PR(2026-10-22 머지)·증상·측정·남은 차이·연관 PR — vllm-project/vllm PR #62928
- 길이 정규화 이슈(2024-01-25 열림 · 2024-12-05 not planned 종료) — vllm-project/vllm 이슈 #2606
- 같은 버그의 Transformers 수정 PR(2023-11-15 머지) — huggingface/transformers PR #27351
- Rust 프런트엔드 beam search PR(2026-10-23 기준 미머지) — vllm-project/vllm PR #47705