LLM의 'Let me double-check'를 1 토큰으로 압축하는 방법: Shorthand for Thought 실습
요약
본 글은 LLM의 사고 과정에서 반복되는 정형 구문('Let me double-check' 등)을 'supertoken'으로 압축하는 방법을 다룹니다. 이 기법은 토큰 낭비를 줄여 모델 추론 길이를 단축할 수 있으며, 논문의 알고리즘을 작은 자작 데이터로 구현하여 개념 증명(PoC) 과정을 공유합니다.
핵심 포인트
- LLM의 정형 구문 압축으로 토큰 효율성 개선 가능
- Supertoken 학습은 BPE 기반 병합 테이블 구축 필요
- 공식 파이프라인 대신 소규모 코퍼스로 PoC 진행
- 실제 논문의 수치 재현보다는 구현 과정 공유에 초점
이 글에 대하여
매일 해외 AI 관련 소재를 하나씩 직접 검증하고 발신하는 개인적인 도전의 Day 001입니다. 이번에 선택한 것은 COLM 2026에 채택된 논문 'Shorthand for Thought: Compressing LLM Reasoning with Learned Supertokens'와 그 공개 코드 Writer/shorthand-for-thought(Apache-2.0)입니다.
이 논문은 "LLM의 사고 과정(reasoning)에는 Wait,, So,, Let me double-check 같은 정형 구문이 대량으로 포함되어 있어, 이 부분에서 토큰을 낭비하고 있다. 만약 이런 정형 구문들을 BPE로 하나의 'supertoken'으로 묶어 모델에 학습시킨다면, 정확도를 떨어뜨리지 않으면서 사고 과정의 길이를 평균 8.1% 단축할 수 있을 것이다"라는 내용입니다. 이 방법은 3개 모델 패밀리와 5가지 수학 벤치마크에서 검증되었으며, 모델 비종속적이라고 합니다.
필자는 이 논문을 통째로 재현한 것은 아닙니다. 실제로 직접 만져보면서 알게 된 '할 수 있었던 것'과 '할 수 없었던 것'을 모두 솔직하게 쓰는 것이 이 연재의 규칙이므로, 먼저 그 경계 설정부터 작성하겠습니다.
진행한 것 및 하지 않은 것
공식 파이프라인은 다음 4단계로 구성되어 있습니다(README 참고):
- 추론 트레이스를 토큰화하여 n-gram 빈도를 계산한다.
- BPE를 사용하여 병합 테이블(=supertoken 후보)을 구축한다.
- 자체 완결형 토크나이저 아티팩트를 만든다.
- supertoken으로 인코딩한다.
이 중 1~4의 알고리즘 부분(reastok.ngrams.count_ngrams, scripts/bpe_from_ngrams.run_bpe, reastok.merge_apply.SuperPostTokenizer)은 공식 코드를 그대로 호출했습니다. 직접 재구현한 것은 아닙니다.
반면, 공식 파이프라인이 전제하는 다음 요소들은-
- 데이터셋(
open-thoughts/OpenThoughts3-1.2M등) - - 베이스 토크나이저(
Qwen/QwQ-32B등)
두 가지 모두 Hugging Face에 존재하지만, 이번 실행 환경에서는 네트워크 정책상 huggingface.co로의 접근이 차단되었습니다(curl https://huggingface.co → 403). GitHub 접근은 가능했기 때문에 리포지토리 클론은 했지만, 데이터셋과 토크나이저 다운로드는 할 수 없었습니다.
그래서 규모를 크게 줄인 개념 증명으로 전환했습니다.
- 코퍼스: 필자가 작성한, 논문에서 다루는 수학계 CoT(chain-of-thought) 스타일의 해설문을 16건(훈련용) + 4건(검증용)을 직접 제작했습니다. 실제 LLM 출력이 아닙니다.
- 베이스 토크나이저:
tokenizers라이브러리의BpeTrainer를 사용하여, 이 코퍼스 위에서 어휘 크기 3000의 작은 byte-level BPE 토크나이저를 자체적으로 학습시켰습니다(Qwen/QwQ-32B 대체).
즉, "공식 알고리즘을 작은 자작 데이터로 돌려보면 어떻게 될까"라는 실험이었으며, 논문의 8.1% 수치를 재현한 것은 아닙니다. 이 점은 오해하지 말아 주십시오.
실행 환경
- Python 3.11.15, CPU 전용, GPU 불필요, 외부 API 키 불필요
- 의존성:
tokenizers==0.23.2,
numpy==2.4.6,
safetensors==0.8.0
절차
python3 -m venv venv
./venv/bin/pip install tokenizers numpy safetensors
git clone https://github.com/Writer/shorthand-for-thought.git shorthand-for-thought
...
스크립트 내용은 대략적으로 다음과 같습니다(전체는 experiments/day-001/run_experiment.py):
from reastok.ngrams import count_ngrams
from reastok.merge_apply import SuperPostTokenizer
from bpe_from_ngrams import run_bpe # 공식 스크립트의 함수를 그대로 import
...
결과
실행 로그의 원본 출력 및 상세한 고찰은 results.md에 그대로 있습니다. 여기서는 요점만 다룹니다.
토큰 수 감소율
held-out(훈련에 사용하지 않은 4개) 데이터에서, 병합 수를 늘릴수록 감소율이 증가했습니다.
| 병합 수 | 베이스 토큰 수 | supertoken 적용 후 | 감소율 |
|---|---|---|---|
| 25 | 744 | 710 | 4.57% |
| ... | |||
| 논문의 헤드라인 수치(평균 8.1%)와 자릿수 및 방향성이 일치하는 결과를 얻었습니다. 다만 앞서 언급했듯이 코퍼스 규모가 완전히 다르기 때문에, 이 일치는 '알고리즘이 제대로 작동하고 있다'는 확인일 뿐, 수치 자체의 재현은 아닙니다. |
참고로 훈련 데이터(in-sample)에서는 최대 27.24%까지 감소했으며, held-out과의 차이(10.75% vs 27.24%)는 'BPE 병합이 코퍼스의 빈출 패턴에 최적화되므로, 미지 데이터에서는 효과가 약해진다'는, 어느 의미에서 예상 가능한 과적합적 거동이었습니다.
실제로 어떤 supertoken이 발견되었는지
발견된 상위 병합(빈도순)을 보면, 의도대로 '구조적인 정형 구문'이 1 토큰화된 것을 알 수 있습니다.
| freq | tok1 | tok2 | merged |
|---|---|---|---|
| 23 | . | ||
So | |||
. So | |||
| 16 | - | ||
check | |||
-check | |||
| 15 | double | ||
-check | |||
double-check | |||
| 14 | . Let | ||
me | |||
. Let me | |||
| 14 | me | ||
double-check | |||
me double-check | |||
| 11 | . | ||
Let me double-check | |||
. Let me double-check |
마지막 행은, 병합이 연쇄적으로 일어나 . Let me double-check라는 문장 전체가 통째로 하나의 토큰 ID가 된 예시입니다. 실제로 held-out의 한 문장에서 토큰열을 비교해 보면:
- 베이스 토크나이저만 사용 시:
['Let', ' me', ' think', ' about', ' this', ' step', ' by', ' step', '.', ' What', ...]
(초반 9단어로 10토큰) - supertoken 적용 후:
[' Let me', ' think about this', ' step by step.', ' What', ...]
(같은 9단어로 4토큰)
AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기