notpoem
마르코프 체인으로 시를 "생성"한다. 진짜 시는 아니다 — 직전 단어(들) 다음에
뭐가 자주 왔는지 통계만 보고 잇는 거라, 의미는 없이 리듬과 어휘만 원본을 닮는다.
GPT류 언어모델의 아주 먼 조상뻘 되는 방식을 손으로 만들어본다.
코어 아이디어
- 코퍼스(기존 시들)를 토큰화해서 "이 단어(들) 다음에 뭐가 왔는가" 표를 만든다
- 아무 지점에서 시작해 그 표를 따라 랜덤하게 다음 단어를 골라가며 문장을 잇는다
- 차수(직전 몇 개를 볼지)를 올리면 문법은 자연스러워지지만 원본을 그대로
베낄 확률도 올라간다 — 이 트레이드오프를 코드로 직접 겪어보는 게 목적
로드맵
| 단계 |
내용 |
배우는 것 |
| 0 |
설계 결정 (단위: 단어/음절, 코퍼스 입력 방식) |
- |
| 1 |
1차 마르코프: 전이표 만들기 |
HashMap, 토큰화 |
| 2 |
랜덤 워크로 생성 |
rand 크레이트 |
| 3 |
N차로 일반화 + zero-copy 토큰 |
lifetime, Vec<&str> 키 |
| 4 |
백오프(고차 문맥 없으면 저차로) |
재귀/반복, Option 체이닝 |
| 5(선택) |
음절 단위 모드 |
한글 문자 단위 처리 |
| 6(선택) |
crates.io 배포, README 데모 |
- |
알고리즘 심화 (선택, 코어 끝난 뒤)
| 확장 |
내용 |
| 명시적 가중치 샘플링 |
Vec 중복 대신 HashMap<String, u32> + WeightedIndex |
| top-k / nucleus 샘플링 |
확률 상위 k개(또는 상위 누적 확률)만 후보로 — GPT 디코딩과 동일한 기법 |
| 루프 방지 |
최근 N단어 재방문 금지 |
| 다중 차수 보간 |
백오프를 정교화 — 여러 차수 확률을 가중 평균 |
| 양방향 체인 |
강한 단어를 중심으로 앞뒤 동시 확장 |
| 줄바꿈 휴리스틱 |
원본 코퍼스의 평균 줄 길이/구두점 기반으로 줄 끊기 |
한국어 특화 (선택, 코어 끝난 뒤)
| 확장 |
내용 |
| 다중 코퍼스 블렌딩 |
여러 시인 코퍼스를 비율로 섞기 (--corpus a.txt --corpus b.txt --weight 0.7,0.3) |
| 세로쓰기 출력 |
--vertical로 동아시아 전통 세로쓰기 방향 출력 |
| 의성어/의태어 강조 |
목록 기반으로 해당 단어 가중치 상향 |
| 형태소 단위 체인 |
어절이 아니라 어간+조사로 분리해서 더 유연한 조합 (외부 형태소 분석기 필요, 스코프 큼) |
| 한자어/고유어 톤 조절 |
단어 사전 기반으로 격식/고전 ↔ 부드러움 톤 조절 |
구조
핵심 로직(코퍼스 처리, 마르코프 체인)과 CLI를 분리해뒀다. 나중에 웹/GUI 껍데기를
씌우고 싶어져도 notpoem-core는 그대로 재사용하면 된다.
notpoem-core/ 순수 로직 (라이브러리) — CLI도 터미널도 모른다
notpoem-cli/ clap 기반 CLI — notpoem-core를 불러써서 조립만 한다
개발
cargo run -p notpoem-cli -- --corpus poems.txt --order 2 --lines 4