llama.cpp를 위한 Particle Scattering Sampler
요약
llama.cpp에 실험적인 'Particle Scattering Sampler'가 추가되었습니다. 이 샘플러는 전체 확률 분포를 무작위로 만드는 온도(Temperature) 방식과 달리, 상위 후보군 내에서만 확률을 국소적으로 재조정하여 생성의 경직성을 완화합니다.
핵심 포인트
- 상위 후보군(top candidates) 내에서 확률 분포를 부드럽게 조정
- 온도 조절 방식과 달리 낮은 확률의 '꼬리' 토큰을 끌어들이지 않음
- 토큰 순위(rank) 간의 확률 질량을 교환하는 로컬 확산 방식 사용
- top-k, top-p 등 기존 샘플러와 결합하여 사용 권장
llama.cpp에 scatter라고 불리는 실험적인 샘플러(sampler)를 추가했습니다.
요약하자면: 이미 선택된 상위 후보들(top candidates) 내부에서 모델의 다음 토큰 확률 분포(next-token probability distribution)를 약간 부드럽게 만듭니다. 이는 일반적인 "온도(temperature)를 높여 쓰레기 같은 꼬리 부분(garbage tail)을 깨우는" 방식 없이, 생성 과정을 덜 경직되게 만들기 위해 고안되었습니다.
빛의 산란(light-scattering) 비유를 사용하지만, 구현 방식이 실제 물리학은 아닙니다. 실제 작동 방식은 훨씬 간단합니다: 토큰 순위(token rank)에 대해 저렴한 로컬 확산(local diffusion) / 이동 평균(moving-average) 단계를 거칩니다.
모델의 다음 토큰 분포를 하나의 광선(beam)이라고 생각하십시오. 가장 강력한 후보는 순위 1(rank 1)이고, 그다음으로 강력한 후보는 순위 2(rank 2)이며, 이런 식으로 이어집니다. scatter는 인접한 순위들이 약간의 확률 질량(probability mass)을 교환할 수 있게 합니다. 순위 1은 순위 2, 3, 4 등에게 일부 질량을 잃을 수 있습니다. 순위 5는 인접한 순위들과 교환할 수 있습니다. 하지만 무작위로 깊은 꼬리(deep-tail)에 있는 토큰들을 끌어들이지는 않습니다.
그것이 핵심입니다:
깊은 꼬리 쪽으로 확률이 새어나가지 않게 하면서 분포의 머리(head) 부분을 평탄화(flatten)하는 것입니다.
상태
llama.cpp를 위한 실험적 샘플러로 구현 및 테스트되었습니다.
현재 구현된 사항:
네이티브 샘플러 API (Native sampler API):
llama_sampler_init_scatter(...)
llama_sampler_init_scatter_ext(...)
샘플러 체인 이름 (Sampler-chain name): scatter
샘플러 체인 문자 (Sampler-chain character): r
xtc와 temperature 사이의 기본 샘플러 체인에 포함됨
기본적으로 비활성화됨: 기본 파라미터 사용 시 아무 동작도 하지 않는(noop) 샘플러를 반환함
고정된 산란 강도 (Fixed scattering strength)
엔트로피 피드백을 사용하는 선택적 적응형 강도 (Optional adaptive strength using entropy feedback)
선택적 반복 토큰 흡수 (Optional repeated-token absorption)
선택적 충돌 / 평균 자유 행로 게이팅 (Optional collision / mean-free-path gating)
tests/test-sampling.cpp 내의 불변 테스트 (Invariant tests)
어떤 문제를 해결하려고 하나요?
온도(Temperature)는 투박합니다.
온도를 높이면 전체 분포가 평탄해집니다. 이는 모델을 더 창의적으로 만들 수 있지만, 약한 꼬리 토큰들에게도 더 많은 확률을 부여합니다. 때로는 이것이 괜찮을 수도 있지만, 때로는 이상한 단어 선택, 깨진 형식, 잘못된 식별자 또는 일관성 없는 도약을 유발합니다.
scatter는 더 국소적(local)입니다.
이 샘플러는 이전 샘플러들이 이미 분포를 필터링한 후, 선택된 top-K 후보군(candidate set) 내부에서만 작동합니다. 따라서 top-k, top-p, min-p, XTC 등이 이미 좋지 않은 후보들을 제거했다면, scatter는 살아남은 것들의 형태만을 재조정합니다.
즉, 다음과 같이 작동하는 대신:
"꼬리(tail)를 포함하여 모든 것을 더 무작위로 만들어라."
다음과 같이 작동합니다:
"살아남은 가장 강력한 후보들을 가져와서, 인접한 순위(rank) 간의 차이를 국소적으로 완화하라."
작동 방식
이 샘플러는 이전 샘플러들이 후보들의 "매질(medium)"을 이미 정의한 후에 실행됩니다.
권장 순서:
penalties -> dry -> top_n_sigma -> top_k -> typ_p -> top_p -> min_p -> xtc -> scatter -> temperature -> dist
따라서 scatter가 실행될 때쯤이면, 후보 목록은 이미 일반적인 샘플러들에 의해 필터링된 상태입니다.
생성된 토큰당 다음과 같은 과정을 거칩니다.
- 충돌 게이트 (Collision gate)
먼저, scatter는 이번 토큰에 대해 아무것도 하지 않기로 결정할 수 있습니다.
플래그는 다음과 같습니다:
--scatter-collision N
기본값:
--scatter-collision 1.0
collision = 1.0이면, 매 토큰마다 scattering이 실행됩니다.
collision = 0.25이면, scattering은 약 25%의 확률로만 발생합니다. 이는 대략 4개 토큰마다 한 번씩 발생하는 평균 자유 행로(mean free path)를 가집니다.
만약 scattering이 발생하지 않으면, 샘플러는 후보들을 완전히 건드리지 않은 채로 둡:
정렬 없음 (no sorting)
절단 없음 (no truncation)
재정규화 없음 (no renormalization)
쓰기 작업 없음 (no write-back)
이는 지속적인 약한 평활화(smoothing) 대신, 가끔씩 발생하는 더 강력한 "굴절(deflections)"을 원할 때 유용합니다.
- 순위 공간 확산 (Rank-space diffusion)
샘플러는 살아남은 상위 k개의 후보를 가져와 로짓(logit) 순으로 정렬하고, 이를 소프트맥스(softmax)하여 확률로 변환합니다.
그 다음, 순위 거리(rank distance)에 대해 로컬 가우시안 평활화 커널(local Gaussian smoothing kernel)을 적용합니다:
K_ij = exp(-((i - j)^2) / (2 * radius^2)) q_i = sum_j K_ij * p_j / sum_j K_ij
이를 쉽게 설명하면 다음과 같습니다:
- 순위(rank)가 가까운 토큰들은 더 많은 확률을 공유합니다.
- 순위가 멀리 떨어진 토큰들은 확률을 거의 또는 전혀 공유하지 않습니다.
- 토큰의 의미가 아닌 순위 거리가 중요합니다.
- 커널은 행 정규화(row-normalized)되므로 확률 질량(probability mass)이 제어된 상태로 유지됩니다.
그 다음, 직접 분포(direct distribution)와 산란 분포(scattered distribution)를 혼합합니다:
p_i = normalize((1 - strength) * p_i + strength * q_i)
즉:
- strength = 0.0 이면 산란(scattering)이 일어나지 않습니다.
- strength = 0.1 이면 대부분 원래 분포를 유지하며 약간만 평활화(smoothed)됩니다.
- strength = 0.3 이면 더 강력한 로컬 평탄화(local flattening)가 일어납니다.
커널은 순위 거리에만 의존하므로, 한 번 계산되어 캐시(cached)됩니다.
샘플러는 이 확산(diffusion) 단계를 여러 번 반복할 수도 있습니다:
--scatter-steps N
대략적으로 말하면, 여러 단계(steps)를 거치는 것은 더 넓은 블러(blur) 효과와 유사하게 동작합니다:
n steps ≈ radius * sqrt(n) 인 한 번의 단계
따라서 일반적으로 steps 값은 낮게 유지해야 합니다.
3. 적응형 강도 (Adaptive strength)
선택적인 적응형 모드도 있습니다:
--scatter-adaptive
이는 현재 상위 K(top-K) 분포의 정규화된 엔트로피(normalized entropy)를 사용합니다:
H_norm = -sum_i p_i * log(p_i) / log(K)
해석:
H_norm = 0.0 -> 매우 날카로운(sharp) 분포, H_norm = 1.0 -> 매우 확산된(diffuse) 분포
그 다음 샘플러는 해당 엔트로피를 목표값과 비교합니다:
--scatter-entropy-target 0.55
분포가 목표보다 더 날카로우면, 샘플러는 유효 산란 강도(effective scattering strength)를 높입니다.
분포가 이미 확산되어 있다면, 유효 산란 강도를 낮춥니다.
개념적 설명:
H_norm < target: 날카로운 빔(sharp beam) -> 더 밀도 높은 매질(denser medium) -> 더 강한 산란(stronger scattering)
H_norm > target: 확산(diffuse) -> 더 얇은 매질(thinner medium) -> 더 약한 산란(weaker scattering)
적응형 강도(adaptive strength)는 다음 범위 내로 제한됩니다:
--scatter-strength-min N --scatter-strength-max N
예시:
--scatter-adaptive \ --scatter-strength 0.14 \ --scatter-strength-min 0.02 \ --scatter-strength-max 0.30 \ --scatter-entropy-target 0.55
중요한 주의사항: 적응형 모드(adaptive mode)는 모델이 확신할 때 강도를 높입니다. 이는 의도된 설계이며, XTC와 유사한 취지를 가지고 있지만, 확신이 있는 분포는 종종 타당한 이유가 있는 경우가 많습니다. 이는 창의적인 산문(creative prose)에는 좋을 수 있으나, 코드(code), 수학(math), JSON, 정확한 이름(exact names), 그리고 엄격한 지시 이행(strict instruction following)에는 위험할 수 있습니다.
4. 선택적 반복 토큰 흡수 (Optional repeated-token absorption)
다음과 같은 선택적인 반복 관련 기능이 있습니다:
--scatter-absorption N --scatter-absorption-last-n N
만약 특정 토큰이 최근에 나타났다면, 산란된 질량(scattered mass)의 일부를 잃을 수 있습니다:
p_i *= exp(-absorption * repeat_count_i)
이것은 일반적인 반복 페널티(repetition penalty)나 DRY를 대체하기 위한 것이 아닙니다. 이는 top-K 산란 매질(scattering medium) 내로 제한된 부드러운 추가 효과입니다.
이를 현재 매질 내에 있는 후보들에게만 적용되는 작은 빈도 페널티(frequency penalty)라고 생각할 수 있습니다.
주의해서 사용하십시오. 강한 흡수(strong absorption)는 다음을 손상시킬 수 있습니다:
이름(names)
스타일 마커(style markers)
대화 구두점(dialogue punctuation)
의도적인 반복(intentional repetition)
반복되는 구조적 토큰(repeated structural tokens)
정확한 포맷팅(exact formatting)
낮은 값이 더 안전합니다:
--scatter-absorption 0.06 --scatter-absorption-last-n 64
5. 쓰기-백 (Write-back)
산란 후, 최종 확률은 다시 로짓(logits)으로 변환됩니다:
log(max(p_i, eps))
그 다음 후보 목록은 top-K 매질로 잘립니다(truncated).
따라서 산란이 실행될 때, 상위 --scatter-k 후보들만 남게 됩니다.
중요한 동작: 순서 안정성 (order stability)
산란은 순서 안정적(order-stable)이도록 설계되었습니다.
행 정규화된 가우시안 커널(row-normalized Gaussian kernel)은 부동 소수점 노이즈(floating-point noise)를 제외하면 정렬된 분포의 순위(rank ordering)를 보존합니다. 따라서 산란 전 최상위 토큰이 순위 1위였다면, 산란 후에도 여전히 순위 1위여야 합니다.
이는 다음을 의미합니다:
argmax가 보존됨
greedy decoding (탐욕적 디코딩)에 영향을 주지 않음
샘플러가 후보군을 공격적으로 재정렬하지 않음
주로 인접한 순위(rank) 사이의 확률 격차를 완화함
의도적인 예외는 absorption (흡수)입니다. 만약 repeated-token absorption (반복 토큰 흡수)이 활성화되어 있다면, 반복되는 토큰의 순위가 낮아질 수 있습니다.
이는 temperature (온도)와의 주요 차이점 중 하나입니다.
Temperature는 꼬리 부분의 깊은 토큰(deep-tail tokens)을 끌어올릴 수 있습니다.
반면 scatter는 선택된 top-K 매질(medium) 내부에서만 국소적으로 확률을 이동시킵니다.
테스트
tests/test-sampling.cpp는 다음을 확인합니다:
순위 안정성 (order stability)
argmax 보존
정규화 (normalization)
절단 (truncation)
collision-0 noop (무작위 동작 없음) 동작
합성 분포에서의 absorption 동작
CLI 레퍼런스
| 플래그 | 기본값 | 의미 |
|---|---|---|
| --scatter-k N | 64 | Top-K scattering 매질. 산란에 참여하는 후보들입니다. 산란이 발생할 때 이 크기로 절단됩니다. |
| --scatter-strength N | 0.0 | 원래 분포와 산란된 분포 사이의 혼합(blend). 0.0은 비활성화를 의미합니다. |
| --scatter-radius N | 2.5 | 가우시안 순위 반경 (Gaussian rank radius). 값이 높을수록 확률이 더 넓은 순위 이웃(rank neighborhood)을 가로질러 이동합니다. |
| --scatter-steps N | 1 | 반복되는 확산 패스(diffusion passes)의 횟수. |
| --scatter-adaptive | off | entropy-feedback (엔트로피 피드백) 강도 활성화. |
| --scatter-strength-min N | 0.02 | adaptive 강도의 하한선. |
| --scatter-strength-max N | 0.30 | adaptive 강도의 상한선. |
| --scatter-entropy-target N | 0.55 | adaptive 모드의 목표 정규화 엔트로피. |
| --scatter-absorption N | 0.0 | 반복 토큰 댐핑 (Repeated-token damping). 0.0은 비활성화를 의미합니다. |
| --scatter-absorption-last-n N | 64 | absorption을 위한 히스토리 윈도우. |
| --scatter-collision N | 1.0 | 산란이 발생할 토큰당 확률. 평균 자유 행로(Mean free path)는 대략 1 / collision 토큰입니다. |
샘플러는 다음 중 하나라도 참인 경우에만 활성화됩니다:
--scatter-strength > 0, --scatter-adaptive가 활성화됨, 또는 --scatter-absorption > 0
또한 다음 조건들도 유효해야 합니다:
k > 1, radius > 0, steps > 0, collision > 0
그렇지 않으면 noop (동작 없음) 상태가 됩니다.
이 샘플러는 이미 기본 샘플러 체인에 포함되어 있습니다.
완전히 제거하려면 --samplers에서 scatter를 제거하거나 --sampler-seq에서 r을 제거하세요.
모든 scatter 플래그를 기본값으로 남겨두는 것만으로도 비용 없이 비활성화할 수 있습니다.
권장 샘플러 순서
penalties -> dry -> top_n_sigma -> top_k -> typ_p -> top_p -> min_p -> xtc -> scatter -> temperature -> dist
왜 이 순서인가요?
Penalties와 DRY는 반복되는 경로에 압력을 가합니다.
Top-k, top-p, min-p, 그리고 XTC는 후보군(candidate medium)을 정의합니다.
scatter는 오직 해당 후보군 내부에서만 확률을 재분배합니다.
Temperature와 최종 샘플링(sampling)은 그 이후에 발생합니다.
이것이 기본 체인 순서입니다.
프리셋 (Presets)
미세하게 고정된 산란 (Subtle fixed scattering)
가벼운 효과를 원하는 경우에 좋습니다.
--scatter-strength 0.12 \ --scatter-radius 2.0 \ --scatter-k 64
창의적 글쓰기 시작점 (Creative-writing starting point)
산문(prose) 또는 역할극(RP)을 위한, 더 강력하지만 여전히 합리적인 시작점입니다.
--scatter-strength 0.18 \ --scatter-radius 2.5 \ --scatter-k 64
적응형 후보군 (Adaptive medium)
엔트로피(entropy)가 산란의 강도를 결정하도록 합니다.
--scatter-adaptive \ --scatter-strength 0.14 \ --scatter-strength-min 0.02 \ --scatter-strength-max 0.30 \ --scatter-entropy-target 0.55 \ --scatter-radius 2.5 \ --scatter-k 64
충돌 게이트 산란 (Collision-gated scattering)
평균 자유 행로(Mean free path)는 약 4개 토큰입니다:
--scatter-collision 0.25 \ --scatter-strength 0.30 \ --scatter-radius 2.5 \ --scatter-k 64
이는 산란이 덜 자주 발생하지만, 발생할 때는 더 강력하게 작용함을 의미합니다.
테스트 결과, 동일한 평균 강도에서 지속적인 약한 블러(blur)를 사용하는 것보다 가끔 발생하는 강력한 편향(deflection)이 국소적 일관성(local coherence)을 더 잘 유지하는 경향이 있습니다.
참고: 충돌 게이트(collision gate)가 공통 RNG 시드(seed)를 사용하기 때문에, 이는 최종 dist 샘플러 이전 단계에서도 생성을 확률적(stochastic)으로 만듭니다.
적응형 + 약한 흡수 (Adaptive + light absorption)
--scatter-adaptive \ --scatter-strength 0.14 \ --scatter-strength-min 0.02 \ --scatter-strength-max 0.30 \ --scatter-entropy-target 0.55 \ --scatter-absorption 0.06 \ --scatter-absorption-last-n 64 \ --scatter-radius 2.5 \ --scatter-k 64
흡수(absorption)를 낮게 유지하세요.
흡수(absorption)가 너무 높으면 반복되는 이름, 문장 부호, 서식 및 의도적인 스타일 패턴을 손상시킬 수 있습니다.
전체 예시 명령
./build/bin/llama-cli \ -m model.gguf \ --samplers "penalties;dry;top_n_sigma;top_k;typ_p;top_p;min_p;xtc;scatter;temperature" \ --temp 0.8 \ --top-k 40 \ --top-p 0.95 \ --min-p 0.05 \ --scatter-adaptive \ --scatter-strength 0.14 \ --scatter-strength-min 0.02 \ --scatter-strength-max 0.30 \ --scatter-entropy-target 0.55 \ --scatter-radius 2.5 \ --scatter-k 64
압축된 샘플러 시퀀스(Compact sampler sequence):
--sampler-seq edskypmxrt
각각의 의미:
e = penalties d = dry s = top_n_sigma k = top_k y = typ_p p = top_p m = min_p x = xtc r = scatter t = temperature
이것이 유용할 것으로 예상되는 용도
다음과 같은 경우에 유용할 가능성이 높습니다:
창의적 글쓰기 (creative writing)
RP 산문 (RP prose)
덜 경직된 단어 선택 (less rigid word choice)
부드러운 탐색 (soft exploration)
고온(high-temperature)의 혼란 방지
모델이 최상위 토큰(top token)에 덜 "고착"되도록 함
위험할 수 있는 용도:
코드 (code)
수학 (math)
엄격한 지시 사항 준수 (strict instruction following)
JSON
문법 제약이 있는 출력 (grammar-constrained output)
희귀하고 정확한 이름 (rare exact names)
식별자 (identifiers)
최상위 토큰이 이유가 있어 보통 정확한 작업들
적응형 모드(Adaptive mode)는 모델이 확신할 때 강도를 높이기 때문에 엄격한 작업에서는 특히 위험합니다.
네이티브 API (Native API)
고정 확산 모드 (Fixed diffusion mode):
/// Fixed diffusion mode. strength <= 0, k <= 1, radius <= 0, or steps <= 0 -> noop. LLAMA_API struct llama_sampler * llama_sampler_init_scatter( int32_t k, float strength, float radius, int32_t steps);
확장 모드 (Extended mode):
/// Extended: adaptive medium, repeated-token absorption, collision gating. LLAMA_API struct llama_sampler * llama_sampler_init_scatter_ext( int32_t k, float strength, float radius, int32_t steps, bool adaptive, float strength_min, float strength_max, float entropy_target, fl
AI 자동 생성 콘텐츠
본 콘텐츠는 r/LocalLLaMA의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기