2025년 11월 업데이트 nanoGPT에는 nanochat라는 새롭고 개선된 사촌 모델이 있습니다. 아마도 nanochat을 사용하거나 찾으려 하셨을 가능성이 매우 높습니다. nanoGPT (이 저장소)는 이제 매우 오래되었고 더 이상 권장되지 않지만(deprecated), 기록을 위해 남겨둡니다.
중간 크기의 GPT를 훈련/미세 조정 (finetuning)하기 위한 가장 단순하고 빠른 저장소입니다. 이는 교육보다는 실용성 (teeth)을 우선시하여 minGPT를 재작성한 것입니다. 여전히 활발히 개발 중이지만, 현재 train.py 파일은
단일 8XA100 40GB 노드에서 약 4일간의 훈련을 통해 OpenWebText 상에서 GPT-2 (124M)를 재현합니다. 코드 자체는 단순하고 읽기 쉽습니다: train.py는
약 300줄 규모의 보일러플레이트 (boilerplate) 훈련 루프이며, model.py는
약 300줄 규모의 GPT 모델 정의로, 선택적으로 OpenAI의 GPT-2 가중치를 로드할 수 있습니다. 그게 전부입니다.
코드가 매우 단순하기 때문에, 사용자의 필요에 맞게 해킹하거나, 처음부터 새로운 모델을 훈련하거나, 사전 훈련된 체크포인트 (pretrained checkpoints)를 미세 조정하기가 매우 쉽습니다 (예: 현재 시작점으로 사용할 수 있는 가장 큰 모델은 OpenAI의 GPT-2 1.3B 모델입니다).
pip install torch numpy transformers datasets tiktoken wandb tqdm
의존성 (Dependencies):
- pytorch <3
- numpy <3
transformers (huggingface transformers <3, GPT-2 체크포인트를 로드하기 위함)
datasets (huggingface datasets <3, OpenWebText를 다운로드 및 전처리하려는 경우)
tiktoken (OpenAI의 fast BPE 코드 <3)
wandb (선택적인 로깅 <3)
tqdm (진행 표시줄 <3)
만약 당신이 딥러닝 전문가가 아니며, 그저 마법 같은 경험을 하고 입문해보고 싶다면, 가장 빠르게 시작하는 방법은 셰익스피어의 작품들로 문자 수준 (character-level) GPT를 훈련하는 것입니다. 먼저, 이를 단일 (1MB) 파일로 다운로드하고 원문 텍스트를 하나의 커다란 정수 스트림 (stream of integers)으로 변환합니다:
python data/shakespeare_char/prepare.py
이 과정은 해당 데이터 디렉토리에 train.bin과 val.bin을 생성합니다. 이제 당신의 GPT를 훈련할 시간입니다. 모델의 크기는 시스템의 계산 자원 (computational resources)에 크게 따라 달라집니다:
GPU가 있습니다. 좋습니다, config/train_shakespeare_char.py 설정 파일에 제공된 설정으로 작은 GPT (baby GPT)를 빠르게 훈련할 수 있습니다:
python train.py config/train_shakespeare_char.py
파일 내부를 살펴보면, 최대 256자의 컨텍스트 크기 (context size), 384개의 피처 채널 (feature channels)을 가진 GPT를 훈련하며, 각 레이어에 6개의 헤드 (heads)를 가진 6개 레이어의 트랜스포머 (Transformer) 모델임을 알 수 있습니다. 하나의 A100 GPU에서 이 훈련 실행은 약 3분이 소요되며, 최적의 검증 손실 (validation loss)은 1.4697입니다. 설정에 따라 모델 체크포인트 (model checkpoints)는 --out_dir 디렉토리인 out-shakespeare-char 에 기록됩니다. 따라서 훈련이 완료되면 샘플링 스크립트가 이 디렉토리를 가리키도록 하여 최적의 모델로부터 샘플을 추출할 수 있습니다:
python sample.py --out_dir=out-shakespeare-char
그러면 다음과 같은 몇 가지 샘플이 생성됩니다:
ANGELO:
And cowards it be strawn to my bed,
And thrust the gates of my threats,
...
lol ¯\_(ツ)_/¯
GPU에서 3분 동안 훈련한 문자 수준 (character-level) 모델치고는 나쁘지 않습니다. 대신 이 데이터셋에 대해 사전 훈련된 (pretrained) GPT-2 모델을 미세 조정 (finetuning)하면 훨씬 더 나은 결과를 얻을 가능성이 매우 높습니다 (나중에 미세 조정 섹션을 참조하세요).
MacBook만 있습니다 (또는 다른 저렴한 컴퓨터). 걱정 마세요, 여전히 GPT를 훈련할 수 있지만 설정을 한 단계 낮춰야 합니다. 현재 코드를 더 효율적으로 만들어 줄 가능성이 매우 높은 최신 PyTorch nightly 버전을 사용하는 것을 권장합니다 (설치 시 여기서 선택하세요). 하지만 이를 사용하지 않더라도, 간단한 훈련 실행은 다음과 같이 구성될 수 있습니다:
python train.py config/train_shakespeare_char.py --device=cpu --compile=False --eval_iters=20 --log_interval=1 --block_size=64 --batch_size=12 --n_layer=4 --n_head=4 --n_embd=128 --max_iters=2000 --lr_decay_iters=2000 --dropout=0.0
여기서 GPU 대신 CPU에서 실행하므로 --device=cpu를 설정해야 하며, --compile=False를 통해 PyTorch 2.0 컴파일 (compile) 기능을 꺼야 합니다. 그런 다음 평가할 때는 조금 더 노이즈가 있지만 더 빠른 추정치를 얻게 됩니다 (--eval_iters=20
, 200에서 감소), 컨텍스트 크기 (context size)는 256 대신 64자뿐이며, 배치 크기 (batch size)는 반복당 64가 아닌 12개의 예시뿐입니다. 또한 훨씬 더 작은 Transformer (4개 레이어, 4개 헤드, 128 임베딩 크기)를 사용하고, 반복 횟수를 2000회로 줄입니다 (그리고 이에 따라 보통 --lr_decay_iters를 사용하여 학습률 (learning rate)을 max_iters 근처까지 감쇠시킵니다). 네트워크가 매우 작기 때문에 규제 (regularization)도 완화합니다 (--dropout=0.0). 이 방식은 여전히 약 3분 정도 소요되지만, 손실 (loss) 값은 1.88에 불과하여 결과 샘플도 더 좋지 않지만, 여전히 재미있습니다:
python sample.py --out_dir=out-shakespeare-char --device=cpu
다음과 같은 샘플을 생성합니다:
GLEORKEN VINGHARD III:
Whell's the couse, the came light gacks,
And the for mought you in Aut fries the not high shee
...
CPU에서 약 3분 만에 올바른 캐릭터 게슈탈트 (character gestalt)의 기미를 보여준다는 점에서 나쁘지 않습니다. 더 오래 기다릴 의향이 있다면, 하이퍼파라미터 (hyperparameters)를 조정하거나, 네트워크 크기, 컨텍스트 길이 (--block_size), 학습 기간 등을 늘려보세요.
마지막으로, Apple Silicon Macbook과 최신 PyTorch 버전을 사용하는 경우 --device=mps ("Metal Performance Shaders"의 약자)를 반드시 추가하세요. 그러면 PyTorch가 온칩 (on-chip) GPU를 사용하여 훈련을 상당히 가속화(2-3배)할 수 있으며 더 큰 네트워크를 사용할 수 있게 해줍니다. 자세한 내용은 Issue 28을 참조하세요.
더 진지한 딥러닝 전문가라면 GPT-2 결과를 재현하는 데 더 관심이 있을 수 있습니다. 그럼 시작해 보겠습니다. 먼저 데이터셋을 토큰화 (tokenize)하는데, 이 경우에는 OpenAI의 (비공개) WebText를 공개적으로 재현한 OpenWebText를 사용합니다:
python data/openwebtext/prepare.py
이 명령은 OpenWebText 데이터셋을 다운로드하고 토큰화합니다. 그러면 GPT2 BPE 토큰 ID를 하나의 시퀀스로 담고 있는 train.bin과 val.bin 파일을 생성하며, 이는 원시 uint16 바이트로 저장됩니다. 이제 훈련을 시작할 준비가 되었습니다. GPT-2 (124M)를 재현하려면 최소 8X A100 40GB 노드가 필요하며 다음을 실행합니다:
torchrun --standalone --nproc_per_node=8 train.py config/train_gpt2.py
이 과정은 PyTorch Distributed Data Parallel (DDP)를 사용하여 약 4일 동안 실행되며, 손실(loss) 값은 약 2.85까지 내려갑니다. 현재 OWT에서 막 평가된 GPT-2 모델은 약 3.11의 검증 손실(val loss)을 기록하지만, 이를 미세 조정(finetune)하면 (명백한 도메인 차이로 인해) 약 2.85 영역까지 내려가게 되어 두 모델이 거의 비슷해집니다.
만약 클러스터 환경에 있고 여러 개의 GPU 노드를 사용할 수 있는 축복을 받았다면, 예를 들어 다음과 같이 2개의 노드에 걸쳐 GPU를 풀가동(go brrrr)할 수 있습니다:
# 예시 IP 123.456.123.456를 사용하는 첫 번째 (마스터) 노드에서 실행:
torchrun --nproc_per_node=8 --nnodes=2 --node_rank=0 --master_addr=123.456.123.456 --master_port=1234 train.py
# 워커(worker) 노드에서 실행:
...
상호 연결(interconnect) 성능을 벤치마킹(예: iperf3)해 보는 것이 좋습니다. 특히, Infiniband가 없다면 위의 실행 명령 앞에 NCCL_IB_DISABLE=1을 추가하십시오.
멀티 노드 학습이 작동은 하겠지만, 아마도 매우 느릿느릿하게(crawl) 진행될 것입니다. 기본적으로 체크포인트(checkpoints)는 주기적으로 --out_dir에 저장됩니다. 단순히 python sample.py를 실행하여 모델로부터 샘플을 추출할 수 있습니다.
마지막으로, 단일 GPU에서 학습하려면 단순히 python train.py 스크립트를 실행하면 됩니다. 스크립트의 모든 인자(args)를 살펴보세요. 이 스크립트는 매우 읽기 쉽고, 해킹(hackable) 가능하며, 투명하게 설계되었습니다. 여러분의 필요에 따라 이러한 변수 중 다수를 튜닝하고 싶을 것입니다.
OpenAI GPT-2 체크포인트를 사용하면 openwebtext에 대한 몇 가지 베이스라인(baselines)을 설정할 수 있습니다. 다음과 같이 수치를 확인할 수 있습니다:
$ python train.py config/eval_gpt2.py
$ python train.py config/eval_gpt2_medium.py
$ python train.py config/eval_gpt2_large.py
...
그리고 학습(train) 및 검증(val)에 대해 다음과 같은 손실(losses)을 관찰할 수 있습니다:
| model | params | train loss | val loss |
|---|
| gpt2 | 124M | 3.11 | 3.12 |
| ... | | | |
하지만 GPT-2는 (비공개되어 공개된 적 없는) WebText로 학습된 반면, OpenWebText는 이 데이터셋을 최대한 비슷하게 재현하려고 노력한 오픈 데이터셋이라는 점에 유의해야 합니다. 이는 데이터셋 도메인 차이 (dataset domain gap)가 존재함을 의미합니다. 실제로 GPT-2 (124M) 체크포인트 (checkpoint)를 가져와 OWT에서 직접 일정 시간 미세 조정 (finetuning)을 수행하면 손실 (loss) 값이 약 2.85까지 도달합니다. 따라서 이것이 재현 (reproduction) 관점에서는 더 적절한 베이스라인 (baseline)이 됩니다.
미세 조정 (Finetuning)은 학습 (training)과 다르지 않으며, 단지 사전 학습된 (pretrained) 모델로부터 초기화하고 더 작은 학습률 (learning rate)로 학습하도록 할 뿐입니다. 새로운 텍스트로 GPT를 미세 조정하는 방법에 대한 예시는 data/shakespeare로 이동하여
prepare.py를 실행하면 됩니다.
이를 통해 작은 Shakespeare 데이터셋을 다운로드하고, GPT-2의 OpenAI BPE 토크나이저 (tokenizer)를 사용하여 이를 train.bin 및 val.bin으로 렌더링합니다.
OpenWebText와 달리 이 과정은 몇 초 안에 완료됩니다. 미세 조정은 매우 짧은 시간이 걸릴 수 있으며, 예를 들어 단일 GPU에서 단 몇 분이면 충분합니다. 다음과 같이 미세 조정 예시를 실행해 보세요:
python train.py config/finetune_shakespeare.py
이렇게 하면 config/finetune_shakespeare.py에 있는 설정 (config) 파라미터 재정의 (overrides) 사항들이 로드됩니다 (저는 많이 튜닝하지는 않았습니다).
기본적으로 init_from을 사용하여 GPT2 체크포인트로부터 초기화한 다음, 평소보다 짧은 기간 동안 작은 학습률로 일반적인 학습을 진행합니다. 만약 메모리 (memory)가 부족하다면 모델 크기를 줄이거나 ({'gpt2', 'gpt2-medium', 'gpt2-large', 'gpt2-xl'}), 혹은 block_size (문맥 길이, context length)를 줄여보시기 바랍니다. 가장 좋은 체크포인트 (가장 낮은 검증 손실, validation loss)는 설정 파일에 따라 기본적으로 out-shakespeare와 같은 out_dir 디렉토리에 저장됩니다. 그 후 sample.py --out_dir=out-shakespeare 코드를 실행할 수 있습니다:
THEODORE:
Thou shalt sell me to the highest bidder: if I die,
I sell thee to the first; if I go mad,
...
와우, GPT가 다소 어두운 영역으로 들어가고 있네요. 설정 파일의 하이퍼파라미터 (hyperparameters)를 크게 튜닝하지는 않았으니, 자유롭게 시도해 보세요!
sample.py 스크립트를 사용하세요.
OpenAI에서 출시한 사전 학습된 (pre-trained) GPT-2 모델들로부터 샘플링하거나, 직접 학습시킨 모델로부터 샘플링할 수 있습니다. 예를 들어, 사용 가능한 가장 큰 gpt2-xl 모델로부터 샘플링하는 방법은 다음과 같습니다:
python sample.py \
--init_from=gpt2-xl \
--start="What is the answer to life, the universe, and everything?" \
...
직접 학습시킨 모델로부터 샘플링하고 싶다면, --out_dir을 사용하여 코드가 적절한 위치를 가리키도록 하세요. 또한 파일에 있는 텍스트로 모델에 프롬프트 (prompt)를 줄 수도 있습니다. 예: python sample.py --start=FILE:prompt.txt.
간단한 모델 벤치마킹 (benchmarking) 및 프로파일링 (profiling)을 위해서는 bench.py가 유용할 수 있습니다. 이는 train.py의 학습 루프 (training loop) 핵심 부분에서 일어나는 일과 동일하지만, 다른 많은 복잡한 과정들은 생략되어 있습니다.
이 코드는 기본적으로 PyTorch 2.0을 사용한다는 점에 유의하세요. 이 글을 쓰는 시점 (2022년 12월 29일) 기준으로, 이는 나이틀리 (nightly) 릴리스에서 torch.compile()을 사용할 수 있게 해줍니다. 단 한 줄의 코드로 얻는 개선 효과는 눈에 띄며, 예를 들어 반복 (iteration) 시간을 iter당 ~250ms에서 135ms로 단축해 줍니다. PyTorch 팀, 멋진 작업입니다!
- DDP 대신 FSDP를 조사하고 추가
- 표준 평가 (standard evals) (예: LAMBADA? HELM? 등)에서 제로샷 퍼플렉서티 (zero-shot perplexities) 평가
- 파인튜닝 (finetuning) 스크립트 개선, 하이퍼파라미터 (hyperparams)가 아주 좋지는 않은 것 같습니다
- 학습 중 선형적인 배치 크기 (batch size) 증가를 위한 스케줄링
- 다른 임베딩 (embeddings) (rotary, alibi) 통합
- 체크포인트 (checkpoints)에서 옵티마이저 (optim) 버퍼를 모델 파라미터 (model params)와 분리
- 네트워크 상태에 관한 추가적인 로깅 (logging) (예: 그래디언트 클리핑 (gradient clip) 이벤트, 크기 등)
- 더 나은 초기화 (init) 등에 관한 몇 가지 추가 조사
이 저장소는 기본적으로 PyTorch 2.0 (torch.compile 포함)을 사용합니다. 이는 상당히 새롭고 실험적인 기능이며, 아직 모든 플랫폼 (예: Windows)에서 사용할 수 없습니다. 관련 에러 메시지가 발생한다면 --compile=False 플래그를 추가하여 이 기능을 비활성화해 보세요. 이렇게 하면 코드는 느려지겠지만, 적어도 실행은 될 것입니다.
이 저장소(repository), GPT, 그리고 언어 모델링 (language modeling)에 대한 맥락을 파악하고 싶다면 저의 Zero To Hero 시리즈를 시청하는 것이 도움이 될 것입니다. 특히, 언어 모델링에 대한 사전 지식이 있다면 GPT 영상이 인기가 많습니다.
더 많은 질문이나 토론을 원하시면 Discord의 #nanoGPT 채널을 자유롭게 방문해 주세요:
모든 nanoGPT 실험은 제가 가장 좋아하는 클라우드 GPU 제공업체인 Lambda Labs의 GPU를 통해 구동됩니다. nanoGPT를 후원해 주신 Lambda Labs에 감사드립니다!