
Qwen 3.6 27B에서 llama.cpp의 모든 추측 디코딩 (Speculative Decoding) 방법 테스트: MTP ~2.7x
요약
llama.cpp 환경에서 Qwen 3.6 27B 모델을 대상으로 다양한 추측 디코딩(Speculative Decoding) 기법을 테스트한 결과입니다. n-gram 룩업 드래프터를 DFlash와 결합했을 때, 반복적인 코딩 작업에서 성능이 최대 6배까지 향상됨을 확인했습니다.
핵심 포인트
- n-gram 룩업 드래프터는 반복적인 코딩 작업에서 탁월한 성능을 보임
- DFlash와 n-gram 기법 결합 시 성능이 3.7x에서 6x까지 향상 가능
- VRAM 소모가 거의 없으며, 그리디 디코딩 시 출력 손실이 없는 lossless 방식
- ngram-mod와 ngram-map-k4v 등 다양한 드래프터의 스택 구조 검증
여러분 안녕하세요, 지난주에 제가 이곳에 DFlash 벤치마크 결과(36K 컨텍스트에서 4.44x)를 게시했습니다 https://www.reddit.com/r/LocalLLaMA/comments/1uq0h4o/i_tested_freshly_merged_dflash_in_llamacpp_on/ . u/exact_constraint 님의 댓글 중 하나에서 n-gram 룩업 드래프터 (n-gram lookup drafters: ngram-mod, ngram-map-k4v)도 있다는 언급이 있었습니다. 확인 결과 이들이 DFlash 위에 스택(stack) 형태로 쌓인다는 것을 알게 되어, 전체 스택을 벤치마킹했습니다. 요약하자면: 다양한 단일 프롬프트에서는 아무런 효과가 없지만, 반복적인 코딩 작업에서는 DFlash의 성능을 3.7x에서 6x로 끌어올리며, VRAM 소모는 사실상 0 MiB입니다. 또한, aipref가 실패하는 것을 발견하여 지난 포스트의 숫자 두 개를 수정해야 합니다. 그것이 5번 항목입니다. n-gram 드래프팅 (n-gram drafting)이란 무엇인가: 일종의 복사기입니다. 모델이 작성한 마지막 N개의 토큰을 가져와서, 기존 컨텍스트에서 해당 토큰 패턴과 정확히 일치하는 것을 검색한 뒤, 그 뒤에 오는 내용을 그대로 초안(draft)으로 제안합니다. ngram-mod는 24-토큰 룩업 키 (lookup key)를 사용하며, 한 번에 하나의 기억된 토큰씩 48~64개 토큰의 드래프트를 체이닝합니다. ngram-map-k4v는 12-토큰 키를 사용하며, 키당 최대 4개의 연속된 내용을 기억하고, 하나의 연속된 내용이 지배적일 때(나머지 합계의 최소 2배 이상일 때)만 드래프팅을 수행합니다. 이 빌드의 드래프트 라우터 (draft router)는 k4v를 먼저 시도하고, 그다음 mod, 그다음 DFlash로 폴백 (fallback)하며, 비어 있지 않은 첫 번째 드래프트가 해당 라운드에서 승리합니다. 타겟 모델 (target model)은 여전히 모든 드래프트된 토큰을 검증하므로, 그리디 (greedy) 디코딩 시 출력 손실이 없습니다 (output-lossless). 드래프트 가중치 (draft weights)는 없으며, 룩업 테이블 (lookup tables)은 호스트 RAM에 상주합니다.
설정:
타겟 (Target): llama.cpp 서버 (Docker)를 통한 unsloth/Qwen3.6-27B-GGUF (UD-Q4_K_XL), 이미지 server-cuda13, 빌드 b9859
드래프트 (Draft): Alittlehammmer/Qwen3.6-27B-DFlash-GGUF-llama.cpp (Q8_0, ~1.9GB), 단일 GPU, 타겟과 드래프트 모두 카드에 탑재, 플래시 어텐션 (flash attention) 활성화, f16 KV 캐시 (KV cache), 속도 테스트를 위한 256k 컨텍스트 (ctx)
사용된 플래그 (Flags): --spec-type draft-dflash,ngram-mod,ngram-map-k4v 및 --spec-draft-n-max 15 (이 빌드의 최대치).
각 Drafter(초안 생성기) 튜닝은 빌드 기본값으로 유지됨: --spec-ngram-mod-n-match 24, n-min 48, n-max 64; --spec-ngram-map-k4v-size-n 12, size-m 48, min-hits 1 Greedy (temperature 0, top-k 1), 동시성(concurrency) 1, 추론(reasoning) 비활성화(대부분의 경우), 한 번에 하나의 서버만 GPU에서 실행.
실행 내용: 반복적 코딩 벤치마크 (저장소 내 benchmark/bench_ngram.py): 18개의 고정된 프롬프트를 하나의 누적된 대화(ONE cumulative conversation)로 전송. 19회차는 Gradio 채팅 앱을 기능별로 구축하며, 1018회차는 완성된 코드에 대한 유지보수(전체 재출력, docstring 작업, 이름 변경, 버그 수정, 클래스로 리팩토링, pytest 테스트, 검토 및 수정)를 수행함. 공개된 벤치마크 중 n-gram이 목표로 하는 '모델이 자신의 코드를 수정하는' 멀티턴(multi-turn) 워크로드를 다루는 것이 없었기에 이 벤치마크를 직접 작성함. llama.cpp 자체의 타이밍 블록으로부터 한 번에 하나의 스택씩 측정함.
실제 프롬프트 A/B 테스트: 동일한 100개의 LiveCodeBench 프롬프트를 aiperf를 통해 DFlash 대 DFlash+ngram 방식으로 동일한 순서로 재생, 자연스러운 EOS(End of Sentence), 시드(seed) 42.
합성 스윕(Synthetic sweep): aiperf ISL = OSL (512 / 4K / 12K / 36K), ignore_eos + min_tokens 고정, 30 / 10 / 5 / 3개의 요청, 웜업(warm-ups) 제외.
무손실성(Losslessness): 전체 MATH-50 0 (이번에는 500개 문제 전체) 및 공식 LiveCodeBench 하네스 (58개 문제, 2024년 8월 기준).
VRAM: 부하(load), 3개의 프롬프트, 그리고 정리(teardown) 과정 동안 4Hz로 샘플링된 nvidia-smi compute memory.
하드웨어: AMD Ryzen 9 9950X | NVIDIA RTX PRO 6000 Blackwell | 96GB VRAM | CUDA 13 | Ubuntu
최고 결과: 18회차 코딩 세션에서 321.5 vs 53.5 tok/s 디코딩 = 전체 스택 사용 시 6.01배. 유지보수 회차(기존 코드를 수정하는 일상적인 시나리오)에서만 385 tok/s로 작동하며, 이는 베이스라인 대비 7.5배임.
요약
n-gram의 이점은 세션이 진행됨에 따라 커지는 반면, 베이스라인은 감소함.
대화가 KV 캐시(KV cache)를 채워감에 따라 베이스라인 디코딩은 62.8 -> 48.9 tok/s로 떨어짐. 반면 n-gram 스택은 동일한 회차 동안 166 -> 325 tok/s로 가속화되는데, 이는 컨텍스트(context)가 많아질수록 복사할 내용이 더 많아지기 때문임.
전체 절제 실험 (Full ablation, 디코딩 tok/s): 베이스라인(baseline) 53.5, DFlash 단독 198.7 (3.71x), DFlash + map-k4v 200.6 (3.75x), DFlash + ngram-mod 304.3 (5.68x), 전체 스택(full stack) 321.5 (6.01x). 즉, ngram-mod가 n-gram 작업의 거의 대부분을 수행하며, 일반 DFlash 대비 +53%의 성능을 보여줌; map-k4v는 단독으로는 약 1%를, mod 위에 추가되었을 때는 약 6%를 더함. 결과적으로 출력 토큰의 92%가 수락된 초안(accepted drafts)에서 생성되었으며, 출력 토큰당 오버헤드는 약 1.8개의 초안 토큰이었음. 다양한 원샷 프롬프트(one-shot prompts)에서는 n-gram이 아무런 이득을 주지 못함. 동일한 순서의 LiveCodeBench 프롬프트 100개를 사용했을 때: 일반 DFlash는 사용자당 176.1 tok/s인 반면, 전체 스택은 170.3 tok/s였으며, 초안 수락률(draft acceptance)은 47% 대 42%였고, 두 번째 토큰까지의 시간(time to second token)은 30.5 ms 대 69.1 ms였음. 이는 n-gram 캐시가 빈 상태로 시작되어 무언가를 복사하기 전에 컨텍스트 내에 토큰이 필요하기 때문임. 만약 작업 부하가 다양한 원샷 프롬프트라면, 일반 DFlash가 더 나은 기본값임. 어느 쪽이든 플래그 하나로 설정 가능하며, 전환 비용은 들지 않음. https://preview.redd.it/27vpsnhetndh1.png?width=2039&format=png&auto=webp&s=0deee6f50082007290b9addfd4e0ac564b223b0d n-gram 초안 생성기(drafters)는 전체 라이프사이클 동안 GPU 상에서 말 그대로 비용이 들지 않음 (nvidia-smi 기준): 베이스 33,256 MiB, DFlash 38,810 MiB (+5,554 MiB, 따라서 지난번 nvtop으로 눈대중으로 확인했던 약 5GB가 맞았음), DFlash + ngram 38,810 MiB. MiB 수치가 동일함. 로드 시간 비용도 없는데, 이는 룩업 테이블(lookup tables)이 호스트 RAM에 위치하기 때문임. https://preview.redd.it/o284760itndh1.png?width=1360&format=png&auto=webp&s=8447c9b32c04cef68136fef457b000af458c6d5b 여전히 손실이 없으며(lossless), 이제 전체 MATH-500 및 실제 LiveCodeBench에서 측정됨 https://preview.redd.it/vn8jm2fntndh1.png?width=1700&format=png&auto=webp&s=c816e6920a0ee2330ac08c013c85e80ee85c96dd 지난번에 당신이 100문제 이상을 요구한 것은 정당했음. 전체 500문제 결과: 베이스 440/500 (88.0%) 대 DFlash 435/500 (87.0%)로, 모든 과목에서 차이가 2문제 이내였고, 양측 모두 파싱되지 않은 답변(unparsed answers)은 zero였으며, DFlash는 측정되는 동안 254.7 tok/s (3.49x)로 생성됨. 500문제를 푸는 데 걸린 실제 시간(Wall clock): 베이스 2시간 17분 대 DFlash 44.7분.
2024년 8월 윈도우 기준 공식 LCB (LiveCodeBench) 하네스 결과: 베이스 모델 58문제 중 11문제 vs DFlash 58문제 중 12문제로, 이번에는 DFlash가 한 문제 차이로 앞섰습니다. 두 격차 모두 배치 검증 (batched verification) 과정에서 경계선에 있는 토큰이 뒤집히며 발생한 부동 소수점 노이즈(floating point noise)일 뿐, 실제 정확도(accuracy)에 미치는 영향은 아닙니다. 절대적인 LCB 점수에 대한 주의사항: lcb_runner는 max_tokens 2000에서 조용히 제한(cap)되며, 이 모델은 추론(reasoning) 기능이 꺼져 있더라도 코드를 작성하기 전에 일반 산문(plain prose)으로 추론을 수행하기 때문에, 47/58 및 46/58 생성 건은 코드 블록이 나오기 전에 잘려나가 자동 실패 처리된 반면, 제한 범위 내에 들어온 모든 답변은 통과했습니다. 지난 포스트의 수정 사항: 36K에서의 DFlash는 4.44x가 아니라 4.71x (289.3 tok/s)입니다. 이전 실행에서는 LLAMA_CTX=40960을 사용했는데, 윈도우가 약 8.2k 출력 토큰에서 조용히 가득 차면서 응답이 중간에 잘렸습니다. 전체 36,864 토큰 출력을 포함하여 compose 기본 컨텍스트(ctx)인 256k로 다시 스윕(sweep)한 결과 수치가 올라갔습니다. "이 구성에서 q8_0 KV 캐시(KV cache)는 15-17% 더 느리며 (중앙값 824 -> 682 tok/s), 초안 수락률(draft acceptance)을 89%에서 79%로 떨어뜨립니다." 따라서 f16이 기본값으로 유지됩니다. 합성 데이터 스윕(synthetic sweeps)을 통해 n-gram 벤치마킹을 주의 깊게 수행하지 않았다면 거의 쓰레기 같은 데이터를 게시할 뻔했습니다. 전체 스윕 결과에 따르면, n-gram은 36K에서 520.9 tok/s를 기록하며, 완전히 대칭적인 실행(fully symmetric run)에서 베이스 대비 8.47x, 일반 DFlash 대비 1.80x의 성능을 보입니다. 놀랍게 들립니다. 하지만 모델이 실제로 무엇을 생성했는지 조사해 보았습니다. ignore_eos를 통해 자연스러운 정지 지점을 강제로 지나치게 하자, 탐욕적(greedy) 36k 출력은 진정한 'Love's Labour's Lost'의 연속으로 시작되더니 약 1,514번 반복되는 두 줄짜리 ARMADO/MOTH 루프에 갇혀버렸습니다. 출력 12-gram의 98.3%가 중복되었고, 초안 수락률은 92% (35,898/38,908), 재생(replay) 시 서버 측 디코딩은 748 tok/s였습니다. 이는 일반 DFlash 수치도 부풀립니다. 8.47x는 '길고 퇴보적인 출력(long-degenerate-output)'의 상한선으로 취급하십시오. 정직한 멀티 턴(multi-turn) 수치는 앞서 언급한 6x입니다. 더 짧은 크기에서는 무작위 합성 산문(random synthetic prose)이 실제로 n-gram의 최악의 사례(수락률 9-14%)이며, 스택 방식은 여전히 4K와 12K에서 앞섰습니다: 일반 DFlash의 191.5 및 234.0 tok/s 대비 231.4 및 328.0 tok/s를 기록했습니다.
https://preview.redd.it/0kpvmc8bundh1.png?width=1700&format=png&auto=webp&s=c6bb32dfc05a35f55325724b80a865bb19da8417 📦 리소스 (Resources): GitHub, 단일 명령 Docker 배포, 18회 벤치마크 (18-turn bench), 모든 스윕 (sweeps), CSV 및 차트: https://github.com/lukaLLM/DFlash_Qwen3.6_27B_LlamaCPP
mod 및 k4v가 실제로 초안을 작성 (draft)하는 방식에 대한 시각적 워크스루 (visual walkthroughs)를 포함한 전체 영상 및 라이브 실행 영상:
추신 (PS): 이 모든 것을 정리하는 데 AI를 남용했습니다.
기본값인 24/48/64와 다른 키/체인 크기 (key/chain sizes)로 ngram-mod를 실행 중인 분이 계신가요? 어떻게 활용하거나 설정하시는지 궁금합니다.
submitted by /u/FantasticNature7590 [link] [comments]
AI 자동 생성 콘텐츠
본 콘텐츠는 r/LocalLLaMA의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기