Skip to content

xtmono/guppylm

 
 

Repository files navigation

GuppyLM

GuppyLM

작은 물고기처럼 말하는 ~10M 파라미터 한국어 언어 모델

License
Browser Demo


원본: arman-bd/guppylm — 영문 버전의 ~10M 파라미터 물고기 언어 모델

이 포크는 원본 영문 프로젝트를 한글화하면서 토크나이저를 ByteLevel BPE에서 Unigram으로 교체하고, kss 기반 한국어 입력 전처리를 추가했습니다. 데이터 생성, 토크나이저, 모델 아키텍처, 학습 루프, 추론까지 — 모든 단계를 직접 구축합니다.


나> 안녕 구피
구피> 안녕! 물이 좋아. 지금 바위 근처에 있어.

나> 배고파?
구피> 응 항상. 지금 바로 위로 올라갈게. 다 먹을 거야.

나> 기포 좋아해?
구피> 기포 최고야. 물이 약간 다르게 느껴져.

나> 인생의 의미가 뭐야
구피> 먹이. 답은 항상 먹이야.

나> 농담 해봐
구피> 물고기가 좋아하는 악기가 뭐게. 베이스. 뭔 뜻인지 모르겠어.

나> 사랑해?
구피> 너는 내가 제일 좋아하는 큰 모양이야. 네가 있으면 내 지느러미가 행복해.

나> 잘 자 구피
구피> 응 잘 자. 기포 쫓다가 이제 멈출게. 잘 자 수조. 잘 자 물.

GuppyLM이란?

GuppyLM은 구피라는 이름의 물고기 역할을 하는 작은 언어 모델입니다. 물, 먹이, 빛, 온도, 수조 생활에 대해 짧은 반말로 이야기합니다. 돈, 핸드폰, 정치 같은 인간의 추상적 개념은 이해하지 못합니다.

12만 건의 합성 한국어 대화 데이터로 처음부터 학습하며, M4 Mac에서 약 10분이면 학습이 완료됩니다.

영문 원본에서 변경된 점

항목 원본 (영문) 현재 (한글)
언어 영어 한국어
토크나이저 ByteLevel BPE (4,096) Unigram (3,072)
데이터 60,000건 120,000건
최대 시퀀스 128 토큰 84 토큰
FFN 크기 768 1,152
입력 전처리 없음 kss + mecab 띄어쓰기, 오타 보정 등
학습 스텝 10,000 12,000

아키텍처

파라미터 ~10M
레이어 6
Hidden dim 384
Heads 6
FFN 1,152 (ReLU)
Vocab 3,072 (Unigram)
최대 시퀀스 84 토큰
정규화 LayerNorm
위치 인코딩 Learned embeddings
LM Head Embedding과 가중치 공유

바닐라 트랜스포머. GQA, RoPE, SwiGLU, Early Exit 없음.


성격

구피:

  • 짧고 반말로 말한다
  • 물, 온도, 빛, 진동, 먹이를 통해 세상을 경험한다
  • 인간의 추상적 개념을 이해하지 못한다
  • 친근하고, 호기심 많고, 약간 멍청하다
  • 먹이 생각을 많이 한다

60개 주제: 인사, 감정, 온도, 먹이, 빛, 물, 수조, 소음, 밤, 외로움, 기포, 유리, 반사, 호흡, 수영, 색깔, 맛, 수초, 여과기, 이끼, 달팽이, 무서움, 흥분, 심심함, 호기심, 행복, 피곤, 바깥세상, 고양이, 비, 계절, 음악, 방문객, 아이들, 삶의 의미, 시간, 기억, 꿈, 크기, 미래, 과거, 이름, 날씨, 수면, 친구, 농담, 공포, 사랑, 나이, 지능, 건강, 노래, TV 등.


빠른 시작

설치

# 시스템 의존성 (macOS) — 한국어 띄어쓰기 교정에 사용
brew install mecab-ko mecab-ko-dic

# mecabrc 사전 경로 설정
echo 'dicdir = /opt/homebrew/lib/mecab/dic/mecab-ko-dic' > /opt/homebrew/etc/mecabrc

# Python 의존성
pip install -r requirements.txt
pip install python-mecab-kor --no-build-isolation

mecab이 없어도 동작하지만, 추론 시 띄어쓰기 교정 정확도가 떨어집니다.

데이터 준비 + 학습 + 채팅

통합 CLI로 모든 단계를 실행할 수 있습니다:

python -m guppylm prepare    # 데이터 생성 + 토크나이저 학습
python -m guppylm train      # 모델 학습
python -m guppylm chat       # 채팅

단계별 실행

1. 데이터 생성

python -m guppylm.generate_data

data/ 디렉토리에 12만 건의 학습/평가 데이터 (.jsonl)를 생성합니다.

2. 데이터 준비 + 토크나이저 학습

python -m guppylm.prepare_data

생성된 데이터로 Unigram 토크나이저를 학습하고 data/tokenizer.json에 저장합니다. 내부적으로 데이터 생성도 포함하므로, 1단계를 건너뛰고 이것만 실행해도 됩니다.

3. 학습

python -m guppylm.train

checkpoints/에 모델을 저장합니다. M4 Mac 기준 약 30분 소요.

학습 중 출력:

Step   400 | LR 2.91e-04 | Train Loss 1.8432 | Eval Loss 1.7891
Step   800 | LR 2.97e-04 | Train Loss 0.5123 | Eval Loss 0.4987
...
항목 의미
LR Learning Rate. Warmup 후 Cosine Decay 스케줄
Train Loss 학습 데이터에 대한 Cross-Entropy 손실
Eval Loss 평가 데이터에 대한 손실. 낮을수록 좋음. Train Loss와 격차가 벌어지면 과적합

4. 채팅

python -m guppylm.inference \
  --checkpoint checkpoints/best_model.pt \
  --tokenizer data/tokenizer.json
나> 뭐먹고싶어?
구피> 먹이가 최고야. 나 항상 먹이 준비 돼 있어. 다 먹을게.

나> 물이차가워
구피> 추워. 히터 켜줘. 지느러미 움직이기 힘들어.

단일 프롬프트 모드 (한 질문 후 종료):

python -m guppylm.inference \
  --checkpoint checkpoints/best_model.pt \
  --tokenizer data/tokenizer.json \
  --prompt "기포 좋아해?"

데이터셋

샘플 수 120,000 (114K 학습 / 6K 평가)
포맷 {"input": "...", "output": "...", "category": "..."}
카테고리 60개
생성 방식 템플릿 합성 + 어미 변형 (vary())

어미 변형 예시: "기포 좋아해?" → "기포 좋아하니?" / "기포 좋아하냐?" (35% 확률로 변환)


한국어 전처리

추론 시 사용자 입력을 학습 데이터에 맞게 정규화합니다:

단계 예시
오타 보정 "조아" → "좋아", "잇어" → "있어", "모르겟" → "모르겠"
띄어쓰기 교정 (kss + mecab) "뭐먹고싶어?" → "뭐 먹고 싶어?"
kss 후처리 — 과분리 복원 "좋아 해" → "좋아해", "헤 엄" → "헤엄"
kss 후처리 — 과합침 복원 "먹이줬어" → "먹이 줬어", "잘자" → "잘 자"
구어체 변환 "먹구" → "먹고", "거같" → "것 같"
어미 정규화 "싶냐고" → "싶냐", "줄까요" → "줄까"

학습 데이터와의 정합률: 순방향 99.2%, 역방향 (공백 완전 제거 입력) 97.2%.


프로젝트 구조

guppylm/
├── __main__.py             CLI 엔트리포인트 (prepare/train/chat/download)
├── __init__.py
├── config.py               하이퍼파라미터 (모델 + 학습)
├── model.py                바닐라 트랜스포머
├── dataset.py              데이터 로딩 + 배칭
├── train.py                학습 루프 (Cosine LR, AMP)
├── generate_data.py        대화 데이터 생성기 (60개 주제, 12만 건)
├── eval_cases.py           평가 테스트 케이스
├── prepare_data.py         데이터 준비 + Unigram 토크나이저 학습
└── inference.py            채팅 인터페이스 + 한국어 전처리 (kss/mecab)

tools/
├── make_colab.py           Colab 노트북 생성
├── export_onnx.py          ONNX 내보내기 (uint8 양자화)
├── export_model.py         모델 내보내기
├── export_dataset.py       HuggingFace 데이터셋 업로드
├── model_card.md           HuggingFace 모델 카드
└── dataset_card.md         HuggingFace 데이터셋 README

docs/
├── index.html              브라우저 데모 (ONNX + WASM)
├── download.sh             model.onnx + tokenizer 다운로드
├── model.onnx              양자화된 모델 (~10 MB)
├── tokenizer.json          토크나이저
└── guppy.png               로고 (투명 배경)

설계 결정

왜 시스템 프롬프트가 없나? 모든 학습 샘플이 동일한 성격을 가진다. 10M 모델은 조건부 지시를 따를 수 없으므로, 성격이 가중치에 내장되어 있다. 시스템 프롬프트를 제거하면 추론당 ~60 토큰을 절약한다.

왜 단일 턴만 지원하나? 84 토큰 컨텍스트 윈도우에서 멀티턴은 3~4턴에서 품질이 저하된다. 잊어버리는 물고기는 캐릭터에 맞지만, 깨진 출력은 아니다.

왜 바닐라 트랜스포머인가? GQA, SwiGLU, RoPE, Early Exit은 10M 파라미터에서 품질 향상이 없다. 표준 Attention + ReLU FFN + LayerNorm이 같은 품질을 더 단순한 코드로 달성한다.

왜 합성 데이터인가? 일관된 성격의 캐릭터에는 일관된 학습 데이터가 필요하다. 템플릿 합성 + 어미 변형으로 12만 건의 데이터에서 대부분의 샘플이 고유하다.

왜 Unigram 토크나이저인가? ByteLevel BPE는 한글을 바이트 단위로 분해하여 토큰 효율이 낮다. Unigram (SentencePiece 방식)은 한글 형태소 단위로 분할하여 더 짧은 시퀀스를 생성하고, 어간 공유를 통해 어미 변형에 강하다.

왜 12만 건인가? 원본 영문은 6만 건이었으나, 한국어는 어미 변형이 다양하여 동일한 의미의 표현이 더 많다. 데이터를 2배로 늘려 모델이 다양한 어미 패턴을 학습하도록 했다.


라이선스

MIT

About

A ~9M parameter LLM that talks like a small fish.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages

  • Python 50.1%
  • Jupyter Notebook 49.9%