본문 바로가기
Inference

샘플링(Sampling)과 디코딩(Decoding)

by AtoN 2022. 11. 23.

샘플링과 디코딩

모델은 매 스텝 어휘 전체에 대한 확률분포를 냅니다. 그 분포에서 토큰 하나를 어떻게 고를지가 샘플링입니다.

temperature, top_k, top_p 를 감으로 만지는 경우가 많습니다. 온도를 올렸는데 다양성이 안 늘거나, 값을 바꿨는데 출력이 그대로인 경험이 여기서 나옵니다.

셋이 각자 무엇을 하는지, 그리고 적용 순서가 왜 결과를 바꾸는지를 봅니다.


1장. 디코딩이 푸는 문제

어휘 전체의 확률분포. 모델의 출력은 토큰 하나가 아니라 어휘 전체에 대한 점수 벡터입니다.

어휘가 15 만이면 15 만 개의 logit 이 나온다
   softmax 를 거쳐 확률분포가 된다

분포에서 토큰을 뽑는 방식은 크게 셋입니다.

방식 이름
가장 높은 걸 고른다 greedy
확률대로 뽑는다 sampling
후보를 여러 개 유지한다 beam search

greedy가 항상 답은 아닌 이유. greedy는 결정적이라 재현이 쉽고, 짧은 분류형 출력이나 평가처럼 변동을 줄이고 싶은 작업에는 유용합니다. 다만 Holtzman et al.의 The Curious Case of Neural Text Degeneration(2019)은 긴 자유 생성에서 최대우도 중심 디코딩이 반복적이거나 부자연스러운 텍스트로 퇴화할 수 있음을 분석했고, 확률 질량의 핵심 부분만 남기는 nucleus sampling(top-p)을 제안했습니다.

즉 greedy가 나쁜 것이 아니라 자유 생성에서 항상 최고 확률 토큰만 고르는 정책이 언어의 다양성을 지나치게 줄일 수 있다는 문제입니다.


2장. temperature

분포 모양 조절. logit을 temperature로 나눈 뒤 softmax를 겁니다.

temperature 분포 변화
T < 1 분포가 뾰족해진다. 높은 확률이 더 높아진다
T = 1 원래 분포 그대로
T > 1 분포가 평평해진다. 낮은 확률도 기회를 얻는다

양 극단에서의 동작. T를 0으로 보내면 greedy와 같아져 최고 확률 토큰만 뽑히고, 반대로 크게 올리면 거의 균등분포가 되어 아무 토큰이나 나옵니다.

llama.cpp의 정의에서 0 이하는 greedy로 처리합니다.

temp = 0.80f    <= 0.0 to sample greedily

값 자체보다 모델 제작자가 권장하는 generation config가 우선입니다. 아래는 성격을 잡기 위한 출발점이지 보편적인 정답이 아닙니다.

값 대체로 나타나는 성격
0 또는 매우 낮음 변동을 줄이는 방향. 평가·정형 작업에서 자주 사용
0.1 ~ 0.3 분포를 강하게 좁히는 보수적 설정
0.7 ~ 0.8 비교적 넓은 샘플링. llama.cpp의 현재 기본 temperature는 0.8
1.0 이상 낮은 확률 후보의 기회를 크게 늘림. 모델에 따라 품질 저하 가능

흔한 오해. temperature를 올리면 창의적이 된다는 말이 돌지만, 정확히는 덜 그럴듯한 토큰이 뽑힐 확률이 오르는 것입니다. 창의성이 아니라 무작위성이고, 너무 올리면 문법이 무너집니다.

동적 temperature. 분포의 엔트로피에 따라 temperature를 자동으로 조절하는 옵션입니다. 모델이 확신할 때는 낮추고 애매할 때는 올립니다.

dynatemp_range     0.00  기본 비활성
dynatemp_exponent  1.00

3장. top-k

상위 k개만 남기는 절단. 확률 상위 k개만 남기고 나머지를 버린 뒤, 그 안에서 확률대로 뽑습니다.

llama.cpp 기본값

   top_k = 40    <= 0 to use vocab size

단순한 구현과 확실한 꼬리 배제. 구현이 단순하고 빠르며, 꼬리의 이상한 토큰을 확실히 배제합니다.

고정된 후보 수의 한계. 분포 모양을 무시한다는 것이 약점입니다.

확신이 강한 상황
   1 위가 0.95 인데도 40 개를 남긴다
   불필요하게 넓다

애매한 상황
   상위 100 개가 고르게 퍼져 있는데 40 개로 자른다
   너무 좁다

항상 k개라는 점이 문제입니다.


4장. top-p (nucleus sampling)

누적 확률 p까지의 절단. 확률을 높은 순으로 더해 가며 누적이 p를 넘는 지점까지만 남깁니다.

llama.cpp 기본값

   top_p = 0.95    1.0 = disabled

top-k와의 차이. top-k는 개수로 잘라 후보 수가 항상 같고, top-p는 확률 질량으로 잘라 후보 수가 상황마다 달라집니다.

확신이 강하면   1 위가 0.96 이면 후보가 1 개
애매하면       고르게 퍼져 있으면 후보가 수백 개

분포 모양에 적응한다는 것이 top-p의 이점입니다.

용도별로 쓰는 값입니다.

값 성격
0.9 ~ 0.95 일반적
낮추면 보수적. 안전하지만 단조로움
1.0 비활성

평평한 꼬리에서의 과잉 후보. 누적 확률이 기준이라 꼬리가 길고 평평하면 여전히 많이 남습니다. 1위가 0.5여도 나머지 0.45를 채우려고 낮은 확률 토큰이 대거 들어옵니다.


5장. min-p

1위 확률 대비 상대 기준. 1위 확률 대비 몇 퍼센트 이상인 토큰만 남깁니다.

llama.cpp 기본값

   min_p = 0.05    0.0 = disabled

top-p와의 차이. top-p는 누적 확률이라는 절대 기준을 쓰고, min-p는 최고 확률 대비 상대 기준을 씁니다.

1 위가 0.9 이면    0.045 이상만 남는다. 매우 좁다
1 위가 0.1 이면    0.005 이상. 넓게 열린다

모델의 확신 정도에 자동으로 맞춰집니다.

llama.cpp의 기본값 선택. 셋이 동시에 켜져 있고 체인으로 차례로 걸러집니다.

top_k = 40
top_p = 0.95
min_p = 0.05

min_p 가 기본 활성이라는 점이 최근 흐름을 보여줍니다. top-p의 꼬리 문제를 보완하는 자리입니다.


6장. 샘플러 체인의 순서

기본 체인. llama.cpp 소스의 기본값입니다.

penalties  ──►  dry  ──►  top_n_sigma  ──►  top_k
           ──►  typical_p  ──►  top_p  ──►  min_p
           ──►  xtc  ──►  temperature

체인 마지막에 놓인 temperature. 후보를 다 걸러낸 뒤에 온도를 겁니다. 그래서 temperature는 남은 후보들 사이의 상대 확률만 조절합니다.

top_k 로 40 개를 남긴 뒤 T=1.5 를 걸면
   그 40 개 안에서 평평해질 뿐
   버려진 토큰이 되살아나지는 않는다

이것을 모르면 temperature를 올렸는데 왜 다양성이 안 느는지에서 막힙니다. 상한이 top-k와 top-p로 이미 정해져 있습니다.

--samplers로 순서 변경. --samplers 옵션으로 체인을 직접 지정할 수 있습니다. temperature를 앞으로 옮기면 온도로 평평해진 분포에서 top-p를 자르게 되므로 결과가 달라집니다.

후보 하한, min_keep. 필터가 너무 공격적이어서 후보가 0개가 되는 것을 막는 하한입니다.

min_keep = 0    0 = disabled

7장. 반복 억제 네 가지

반복 억제가 필요한 자리. 샘플링만으로는 같은 구절을 되풀이하는 것을 막지 못합니다.

repeat penalty. 최근 등장한 토큰의 logit을 억제합니다. 구현에서는 logit의 부호를 고려해 곱하거나 나누는 식으로 처리할 수 있으므로 단순히 '확률을 나눈다'고 이해하기보다, 최근 토큰을 다시 선택하기 어렵게 만드는 페널티라고 보는 편이 정확합니다. 등장 횟수 자체를 선형으로 누적하는 frequency penalty와는 다릅니다.

penalty_repeat  = 1.00    1.0 = disabled
penalty_last_n  = 64      최근 몇 토큰을 볼지

frequency penalty. 등장 횟수에 비례해 깎습니다. 많이 나온 토큰일수록 더 강하게 억제됩니다.

penalty_freq = 0.00    0.0 = disabled

presence penalty. 한 번이라도 나왔으면 횟수와 무관하게 고정 크기로 깎습니다.

penalty_present = 0.00    0.0 = disabled

세 페널티를 나란히 놓으면 이렇습니다.

페널티 적용 방식
repeat 나눗셈으로. 횟수 무관
frequency 횟수에 비례. 많이 나올수록 강하게
presence 등장 여부만. 새 주제를 유도

같은 단어의 반복을 막고 싶으면 frequency를, 새로운 주제로 넘어가게 하고 싶으면 presence를 씁니다.

DRY. 토큰 단위가 아니라 반복 시퀀스를 봅니다.

dry_multiplier     0.0    0.0 = disabled
dry_base           1.75
dry_allowed_length 2      이보다 긴 반복에 페널티
dry_penalty_last_n 64

소스 주석에 적힌 페널티 식은 다음과 같습니다.

multiplier * base ^ (length - allowed_length)

반복 길이가 길수록 페널티가 지수적으로 커집니다. 단어 하나가 아니라 같은 문구를 통째로 반복하는 것을 막습니다.

부작용. 반복 페널티는 정당한 반복까지 막습니다. 코드에서 변수명을 여러 번 쓰거나 목록에서 같은 형식을 반복하는 경우가 그렇습니다. 그래서 코드 생성에서는 대개 낮추거나 끕니다.


8장. 그 외 샘플러

typical-p. Locally Typical Sampling(2022)에서 나왔습니다. 확률이 높은 것도 낮은 것도 아닌, 정보량이 전형적인 토큰을 남깁니다. 사람의 언어가 정보량 면에서 일정한 수준을 유지한다는 관찰에 기댑니다.

typ_p = 1.00    1.0 = disabled

top-n-sigma. logit 분포의 표준편차를 기준으로 잘라, 최고값에서 n 시그마 안쪽만 남깁니다.

top_n_sigma = -1.00    -1.0 = disabled

XTC. Exclude Top Choices의 약자로, 확률적으로 상위 후보를 오히려 제외합니다. 창작에서 뻔한 전개를 피하려는 목적입니다.

xtc_probability = 0.00    0.0 = disabled
xtc_threshold   = 0.10    > 0.5 disables XTC

Mirostat. 목표 surprisal/entropy 수준을 정해 두고 생성 중 그 수준에 맞도록 샘플링을 적응적으로 조절합니다. llama.cpp에서는 Mirostat 경로가 일반적인 top-k/top-p 체인과 다르게 구성되지만, 다른 구현까지 '켜면 top-k와 top-p가 항상 무시된다'고 일반화하면 안 됩니다.

mirostat     = 0     0 = disabled, 1 = Mirostat, 2 = Mirostat 2.0
mirostat_tau = 5.00  target entropy
mirostat_eta = 0.10  learning rate

9장. 실무 설정

용도별 출발점입니다.

코드, 구조화 출력, 평가
   temperature 0
   반복 페널티 끔

사실 질의응답
   temperature 0.1 ~ 0.3
   top_p 0.9

일반 대화
   temperature 0.7 ~ 0.8
   top_p 0.95, min_p 0.05

창작
   temperature 1.0 이상
   min_p 로 하한만 잡음

모델 카드의 권장값. 모델마다 권장 설정이 다릅니다. Ling-3.0-tiny는 다음을 권합니다.

temperature 1.0
top_p 0.95
top_k 20

temperature 1.0을 권하는 모델도 있는 셈입니다. 학습 방식에 따라 최적점이 달라집니다.

조정 방법. 파라미터가 열 개가 넘어서 여러 개를 동시에 바꾸면 무엇이 효과인지 알 수 없습니다. 기본값에서 하나씩 움직이고, 같은 프롬프트 세트로 여러 설정을 돌려 지표로 비교합니다.

구조화 출력에는 Constrained Decoding. temperature를 0으로 내려도 JSON 문법이나 스키마가 보장되지는 않습니다. 형식이 중요하면 grammar/constrained decoding, 제공자의 native structured output, tool schema처럼 허용 가능한 토큰이나 출력 구조 자체를 제한하는 수단을 우선합니다. 라이브러리의 with_structured_output 같은 상위 API도 내부적으로 어떤 방식으로 보장하는지 모델·제공자별로 확인해야 합니다.


10장. 정리

top-k는 개수로, top-p는 누적 확률로, min-p는 최고 확률 대비 상대 기준으로 후보를 줄입니다. temperature는 분포의 상대적인 모양을 바꿉니다. 현재 llama.cpp 기본 체인에서는 temperature가 뒤쪽에 있기 때문에 이미 앞 단계에서 제거된 후보는 되살아나지 않습니다. 다만 이 순서는 llama.cpp의 구현 선택이며 모든 API가 같은 순서를 쓰는 것은 아닙니다.

기본값을 한 표로 모으면 이렇습니다.

파라미터 llama.cpp 기본값 비활성 값
temp 0.80 0 이하면 greedy
top_k 40 0 이하면 어휘 전체
top_p 0.95 1.0
min_p 0.05 0.0
typ_p 1.00 1.0
penalty_repeat 1.00 1.0
penalty_freq 0.00 0.0
penalty_present 0.00 0.0
dry_multiplier 0.0 0.0
mirostat 0 0

temperature를 올려도 앞선 필터가 이미 제거한 토큰은 다시 후보가 되지 않습니다. top-p와 min-p는 각각 누적 확률 질량과 최고 확률 대비 비율이라는 다른 기준을 사용합니다. 반복 페널티는 코드나 JSON처럼 정상적인 반복이 필요한 작업에서는 오히려 품질을 떨어뜨릴 수 있으므로 작업별로 따로 검증합니다.

형식을 강제하는 축은 Constrained Decoding 쪽이고, 샘플링을 유지한 채 속도를 올리는 축은 Speculative Decoding 쪽입니다.


용어 정리

용어 한 줄 뜻
logit softmax 전 단계의 원시 점수
greedy 디코딩 매 스텝 최고 확률 토큰만 고르는 방식
샘플링 확률분포에서 무작위로 토큰을 뽑는 방식
temperature logit을 나눠 분포를 평평하게 하거나 뾰족하게 하는 값
top_k 확률 상위 k개만 후보로 남기는 필터
top_p (nucleus) 누적 확률이 p가 될 때까지만 남기는 필터
min_p 최고 확률 대비 일정 비율 이상만 남기는 필터
typical_p 정보량이 전형적인 토큰만 남기는 필터
샘플러 체인 필터들이 적용되는 순서
min_keep 필터 후 최소한 남길 후보 수
repeat penalty 최근 등장 토큰의 확률을 나눗셈으로 깎는 억제
frequency penalty 등장 횟수에 비례해 깎는 억제
presence penalty 한 번이라도 나왔으면 고정 크기로 깎는 억제
DRY 반복되는 시퀀스 길이에 지수적으로 페널티를 주는 억제
XTC 확률적으로 상위 후보를 제외하는 샘플러
Mirostat 목표 엔트로피를 유지하도록 자동 조절하는 샘플링
동적 temperature 분포 엔트로피에 따라 온도를 자동 조절하는 방식

참고자료

'Inference' 카테고리의 다른 글

Continuous Batching과 PagedAttention  (0) 2024.01.03

댓글