메뉴
HN
Hacker News 40일 전

1MHz 코모도어 64에서 구동되는 실제 트랜스포머

IMP
7/10
핵심 요약

1980년대 1MHz 프로세서를 탑재한 레트로 컴퓨터 '코모도어 64(C64)'에서 ChatGPT와 동일한 트랜스포머 아키텍처 기반의 LLM이 구동되는 프로젝트가 공개되었습니다. 약 2만 5천개의 Int8 파라미터를 사용하며 소프트맥스(Softmax) 연산을 최적화하여 제한된 하드웨어에서도 실질적인 어텐션(Attention) 가중치를 계산해 내는 것이 핵심 기술입니다. 사용자가 직접 데이터를 학습시켜 3.5인치 플로피 디스크로 빌드하여 실제 하드웨어나 에뮬레이터에서 실행해 볼 수 있는 오픈소스 프로젝트입니다.

번역된 본문

Soul Player C64: 1MHz 코모도어 64에서 구동되는 진정한 트랜스포머

.-------. | O O | | V | |..|---|..|

SOUL PLAYER C64

2만 5천 개 파라미터. 2개 레이어. 진정한 트랜스포머. 플로피 디스크로 로드 가능.

YOU> hey C64> HELLO! RE SOUNDS ME. MEFUL!

이것은 ChatGPT, Claude, Gemini의 근간이 되는 것과 동일한 아키텍처인 2 레이어 디코더 전용(Decoder-only) 트랜스포머입니다. 이 모델은 수작업으로 작성된 6502/6510 어셈블리 언어로 구현되었으며, 개조되지 않은 코모도어 64에서 실행됩니다.

약 25,000개의 int8 파라미터를 사용합니다. 실제 멀티 헤드 인과적(Causal) 셀프 어텐션, 실제 소프트맥스, 실제 RMSNorm이 적용되었습니다. 토큰당 약 60초가 소요됩니다. 프로그램 전체는 플로피 디스크에 넉넉히 들어갈 만큼 가볍습니다.

아키텍처

  • 2개 레이어, 4개 어텐션 헤드 × 8 차원, 32차원 임베딩, 64개 FFN 은닉 유닛
  • 텐서별 시프트 스케일링(Shift Scaling)이 적용되어 int8로 양자화된 약 25,000개의 파라미터

핵심적인 기술적 돌파구는 소프트맥스 점수 정규화를 수정한 것입니다. 어텐션 점수를 17비트가 아닌 14비트로 시프트하면, 128개 항목으로 이루어진 exp 룩업 테이블(Lookup Table)이 의미 있는 어텐션 가중치를 생성할 수 있는 충분한 동적 범위(Dynamic Range)를 확보하게 됩니다. 이 수정이 없었다면 정수형 어텐션 연산이 모든 위치에서 균일해졌을 것이며, 결국 아키텍처나 학습과 무관하게 모델이 앞을 보지 못하는 상태가 되었을 것입니다.

빠른 시작 - 미리 빌드된 Soul 실행하기

  1. disk/soulplayer.d64 파일을 다운로드하여 임의의 C64 에뮬레이터(VICE 권장)에 로드합니다: LOAD"SOULPLAYER",8,1 RUN
  2. 소문자로 짧은 메시지를 입력한 뒤 RETURN 키를 누르고 기다립니다.
  3. 모델이 연산하는 동안 테두리가 깜빡입니다. 각 토큰이 생성될 때마다 SID 블립 사운드가 납니다. 응답 하나가 완성되는 데는 몇 분 정도 걸립니다. 종료하려면 q를 입력하세요.

팁: 이 모델은 소문자, 공백 및 구두점( . , ! ? ' : ; - )을 이해합니다. 대문자는 알 수 없는 토큰(Unknown Token)으로 처리됩니다.

나만의 Soul 학습시키기 이 부분이 가장 재미있는 과정입니다. 말뭉치(Corpus)를 작성하고 모델을 학습시킨 뒤 플로피 디스크를 빌드해 보세요.

의존성 설치 pip install numpy torch

말뭉치 작성하기 입력응답 형식을 사용하여 한 줄에 하나의 대화 내용이 들어간 텍스트 파일을 생성합니다: hellohey! nice to see you! i'm sadi hear you. i care about you. tell me a jokewhy did the bit flip? it was tired!

대화 내용은 짧게 유지하세요. 이 모델의 컨텍스트 윈도우(Context Window)는 20개 토큰입니다. 기본 예시는 data/example_corpus.txt 파일을 참고하세요.

학습 python train.py data/example_corpus.txt

이 코드는 BPE 토크나이저(128개 토큰)를 학습시키고, QAT(양자화 인식 학습) 트랜스포머를 학습시킨 후 models/soul.bin 및 models/tokenizer.json 파일로 내보냅니다. GPU 환경에서는 몇 분 정도 걸립니다.

매 500 에포크마다 실수(float) 및 int8 추론 결과가 나란히 출력되어, 모델이 학습한 내용과 C64가 실제로 생성할 내용을 비교해 볼 수 있습니다. 최적의 체크포인트는 실수 손실(Float Loss)이 아닌 int8 품질을 기준으로 저장됩니다. 모든 체크포인트는 모델 중 최적을 선택할 수 있도록 models/checkpoints/ 디렉터리에 저장됩니다.

옵션: python train.py data/my_corpus.txt --epochs 30000 --output models/ python train.py # 내장된 감정적 지원(Emotional Support) 말뭉치를 사용합니다

이전 실행에서 체크포인트가 존재하는 경우 학습이 자동으로 이어서 진행됩니다.

C64 바이너리 빌드 python build.py

이 코드는 모든 6502/6510 루틴을 어셈블하고, 학습된 가중치를 포함하여 disk/soulplayer.prg 및 disk/soulplayer.d64 파일을 생성합니다.

실행하기 x64 disk/soulplayer.d64 # VICE 에뮬레이터 사용 또는 실제 하드웨어용으로 .d64 파일을 실제 1541 플로피 디스크에 플래시합니다.

로컬에서 Soul과 채팅하기 python soulchat.py # models/soul.bin 사용 python soulchat.py models/soul.bin # 커스텀 Soul 사용 C64와 동일한 정수 연산을 사용하지만 훨씬 더 빠르게 실행됩니다.

테스트 실행 python test.py # 전체 테스트 스위트 (~90개 테스트, 약 30초 소요) python test.py --quick # 6502/6510 어셈블리 테스트 건너뛰기

테스트는 전체 체인을 검증합니다: 실수(Float) 레퍼런스 → 정수(Integer) 레퍼런스 → 메모리 정확(Memory-faithful) 섀도우 → 6502/6510 어셈블리 루틴 → 빌드 라운드 트립.

리포지토리 내용물 soulplayer-c64/ ├── train.py - 모델 학습 및 가중치 내보내기 ├── build.py - C64 바이너리 어셈블 ├── test.py - 모든 테스트 실행 ├── soulchat.py - 터미널에서 채팅 │ ├── data/ │ └── example_corpus.txt ├── models/ │ ├── soul.bin - 사전 학습된 가중치 (25KB, int8) │ ├── tokenizer.json - BPE 토크나이저 (128개 토큰) │ └── checkpoints/ - 저장된 모든 학습 체크포인트 ├── disk/ │ ├── meful.d64 - 오리지널 릴리즈, 디스크 이미지 │ └── meful.prg - 오리지널 릴리즈, RAW PRG 파일 │ │ ├── soulplayer.d64 - 실행 가능한 디스크 이미지

원문 보기
원문 보기 (영어)
Soul Player C64 A real transformer running on a 1 MHz Commodore 64. .-------. | O O | | V | |..|---|..| # SOUL PLAYER C64 25K PARAMETERS. 2 LAYERS. REAL TRANSFORMER. LOADED OFF A FLOPPY DISK. YOU> hey C64> HELLO! RE SOUNDS ME. MEFUL! A 2-layer decoder-only transformer - the same architecture behind ChatGPT, Claude, and Gemini - implemented in hand-written 6502/6510 assembly and running on an unmodified Commodore 64. ~25,000 int8 parameters. Real multi-head causal self-attention, real softmax, real RMSNorm. About 60 seconds per token. The whole thing fits on a floppy disk with room to spare. Architecture 2 layers, 4 attention heads × 8 dims, 32-dimensional embeddings, 64 FFN hidden units. ~25,000 parameters quantized to int8 with per-tensor shift scaling. The key breakthrough was fixing the softmax score normalization - shifting attention scores by 14 bits instead of 17 gives the 128-entry exp lookup table enough dynamic range to produce meaningful attention weights. Without this fix, the integer attention was essentially uniform across all positions, making the model blind regardless of architecture or training. Quick start - run the pre-built soul Grab disk/soulplayer.d64 and load it in any C64 emulator ( VICE recommended): LOAD"SOULPLAYER",8,1 RUN Type a short message in lowercase, press RETURN, wait. The border flashes while it thinks. Each token gets a SID blip. A full response takes a few minutes. Type q to quit. Tip: The model understands lowercase letters, spaces, and punctuation ( . , ! ? ' : ; - ). Capital letters become unknown tokens. Train your own soul This is the fun part. Write a corpus, train a model, build a floppy. Install dependencies pip install numpy torch Write a corpus Create a text file with one exchange per line in <SEP>input<SEP>response<SEP> format: <SEP>hello<SEP>hey! nice to see you!<SEP> <SEP>i'm sad<SEP>i hear you. i care about you.<SEP> <SEP>tell me a joke<SEP>why did the bit flip? it was tired!<SEP> Keep exchanges short - the model has a 20-token context window. See data/example_corpus.txt for a starter. Train python train.py data/example_corpus.txt This trains a BPE tokenizer (128 tokens), trains the QAT transformer, exports models/soul.bin and models/tokenizer.json . Takes a few minutes on GPU. Every 500 epochs, you'll see both the float and int8 inference output side by side - what the model learned vs what the C64 will actually produce. The best checkpoint is saved based on int8 quality, not float loss. All checkpoints are saved to models/checkpoints/ for cherry-picking. Options: python train.py data/my_corpus.txt --epochs 30000 --output models/ python train.py # uses built-in emotional support corpus Training resumes automatically if checkpoints exist from a previous run. Build the C64 binary python build.py This assembles all 6502/6510 routines, embeds your trained weights, and writes disk/soulplayer.prg and disk/soulplayer.d64 . Run it x64 disk/soulplayer.d64 # VICE emulator Or flash the .d64 to a real 1541 floppy for hardware. Chat with the soul locally python soulchat.py # uses models/soul.bin python soulchat.py models/soul.bin # custom soul Runs the same integer arithmetic as the C64, just faster. Run the tests python test.py # full suite (~90 tests, ~30 seconds) python test.py --quick # skip 6502/6510 assembly tests Tests verify the entire chain: float reference → integer reference → memory-faithful shadow → 6502/6510 assembly routines → build round-trip. What's in the repo soulplayer-c64/ ├── train.py - train a model + export weights ├── build.py - assemble the C64 binary ├── test.py - run all tests ├── soulchat.py - chat in your terminal │ ├── data/ │ └── example_corpus.txt ├── models/ │ ├── soul.bin - pre-trained weights (25KB, int8) │ ├── tokenizer.json - BPE tokenizer (128 tokens) │ └── checkpoints/ - all saved training checkpoints ├── disk/ │ ├── meful.d64 - original release, disk image │ └── meful.prg - original release, raw PRG │ ├── soulplayer.d64 - ready-to-run disk image │ └── soulplayer.prg - raw PRG └── src/ - the engine ├── numerics.py - ground truth: fixed-point math + forward pass ├── soul_io.py - .bin weight file format ├── shadow.py - memory-faithful Python shadow of the 6502/6510 ├── assembler.py - mini 6502 assembler (labels, patches, far branches) ├── cpu6502.py - minimal 6502 interpreter for testing ├── asm_matvec.py - 6502 matrix-vector multiply ├── asm_rms_norm.py - 6502 RMSNorm (integer sqrt + divide) ├── asm_attn_head.py - 6502 attention head (LUT softmax) ├── asm_simple.py - 6502 embed, residual, relu, argmax └── build.py - PRG + D64 assembler Specs Vocab 128 tokens (4 special + 34 chars/punct + 90 BPE merges) Embedding 32 dimensions Layers 2 Attention 4 heads × 8 dims per head FFN 64 hidden units Context 20 tokens Parameters ~25,000 (all int8) Weight size 25 KB Decoding Greedy (argmax) Each layer: RMSNorm → multi-head causal self-attention → residual → RMSNorm → ReLU MLP → residual. Final RMSNorm → output projection → argmax. All activations are Q8.8 fixed-point (int16). Weights are int8 with per-tensor power-of-2 shifts. Biases are int16 pre-scaled to the matmul accumulator. Softmax uses a 128-entry exp lookup table with >>14 score normalization. The 6502 has no multiply instruction - everything is shift-and-add. Memory map $0801-$20FF code + tokenizer tables (~6 KB) $2100-$85A0 weights (25.3 KB) $8600-$9D00 activation buffers (5.8 KB) $C000-$C3FF token buffer, input, scratch $D000- VIC-II, SID, CIA (I/O) How training works The model uses quantization-aware training (QAT). During training, weights pass through FakeQuantI8 - fake-quantized with continuous float scaling and straight-through gradient estimation. The deliberate mismatch between training's continuous scale and export's power-of-2 shift grid acts as implicit noise, forcing the model to learn weights with wider logit margins that survive the quantization gap. Biases are fake-quantized with simple fq() . Every matmul gets a × 0.5 post-shift simulating the 6502's >> 1 . Label smoothing (0.15) prevents the model from sharpening logit distributions beyond what int8 arithmetic can reliably distinguish. The training loop evaluates the actual integer forward pass ( numerics.forward() ) every 500 epochs and saves the best checkpoint by int8 argmax accuracy, not float loss. The training output shows float and int8 inference side by side - what the model learned vs what the C64 will produce. Caveats It's not smart. 25K parameters is about 70 million times smaller than GPT-4. It will produce broken sentences. That's the point - the architecture works at this scale. It's slow contemplative. About 60 seconds per token on real hardware. A full response takes several minutes. Capitals become <UNK> . Stick to lowercase. Small vocabulary. 128 tokens and 20-token context - keep training exchanges short. Credits Code, training: gizmo64k Debugging, unit tests, rubber duck: Claude (Opus 4.6) by Anthropic Lucky soul: The Commodore 64 by Commodore Business Machines, 1982 License GNU General Public License v3. See LICENSE . The future came back for the past. And now it has a soul.