Skip to content
Published on

RTX 5090 한 장으로 작은 모델들 직접 굴려보기 — microGPT·OCR·음악 생성

공유하기
Authors

들어가며 — 큰 모델 말고, 작은 모델을 손으로

요즘 이야기는 죄다 700B 파라미터, 수천 장 GPU입니다. 하지만 컴퓨터공학을 배우기에는 정반대가 낫습니다 — 손바닥만 한 모델을 GPU 한 장에서 직접 학습시키고, 뜯어보고, 한계를 만나는 것. 마침 RTX 5090(Blackwell, 32GB) 한 장이 SSH 너머에 있어서, 작은 모델 셋을 직접 굴려봤습니다: 밑바닥부터 학습하는 char-level GPT, 전용 OCR vs 소형 VLM 대결, 그리고 텍스트-투-뮤직. 모든 수치는 실측입니다. 그리고 그 과정에서 만난 정직한 함정들이, 사실 결과보다 더 좋은 교재였습니다.

0부 — 첫 번째 교훈: 워밍업 없는 벤치마크는 38배 거짓말을 한다

모델을 돌리기 전, GPU가 제대로 도는지 bf16 행렬곱으로 확인했습니다. 첫 측정값은 6.1 TFLOP/s. 5090이 이럴 리가 없는데... 하고 워밍업 루프를 넣고 다시 쟀습니다.

                 처음(워밍업 없음)     제대로 측정
bf16 matmul       6.1 TFLOP/s          231.8 TFLOP/s     ← 38배 차이

범인은 첫 호출에 cuBLAS 초기화·커널 오토튜닝이 통째로 들어간 것입니다. GPU 벤치마크의 철칙 — 몇 번 예열(warmup)하고, torch.cuda.synchronize()로 커널이 실제로 끝나기를 기다린 뒤에 재야 합니다. 231.8 TFLOP/s는 5090의 bf16 dense 스펙(~210 TFLOPS)과도 맞아떨어집니다. 이 글의 모든 지연(latency) 측정에는 이 워밍업이 들어가 있습니다.

참고로 이 서버를 쓰기 위한 사전 삽질도 있었습니다: 5090은 Blackwell sm_120 아키텍처라, 구버전 PyTorch는 "CUDA 사용 가능"이라 말해 놓고 커널이 없어 조용히 실패합니다. torch 2.10.0+cu128(CUDA 12.8 빌드)에서야 sm_120이 아키텍처 목록에 잡혔습니다. 최신 GPU를 쓸 때 절반은 이 환경 싸움입니다.

1부 — microGPT: GPT를 밑바닥부터, 28초에

먼저 Karpathy의 nanoGPT 계보를 따라, char-level GPT를 순수 PyTorch로 밑바닥부터 학습시켰습니다. 셰익스피어 텍스트 1MB를 글자 단위(vocab 65개)로 먹이고, 6층·6헤드·384차원 트랜스포머(약 10.75M 파라미터)를 2000스텝 돌렸습니다.

iter     1 | train 3.7921 | val 3.8103 |   10.0k tok/s
iter   500 | train 1.5798 | val 1.7738 | 1006.2k tok/s
iter  1000 | train 1.2769 | val 1.5302 | 1119.1k tok/s
iter  1750 | train 1.1221 | val 1.4425 | 1174.8k tok/s   ← val 최저점
iter  2000 | train 1.1150 | val 1.4552 | 1184.7k tok/s   ← val 반등(과적합 시작)

trained 2000 iters in 28.0s | peak VRAM 1.73 GB | 1.17M tokens/s

28초, VRAM 1.73GB. loss는 3.79에서 1.12로 떨어졌고, 생성 샘플은 이런 셰익스피어풍이 나옵니다:

The people, could be speak'd of this course,
Who shall be at bold well away and a chief.

Second Citizen:
This in the morniest hour of Turningh; down princ

문법은 엉성해도 대사 형식·인물 이름·고어투를 글자 단위로 배운 게 보입니다. 그리고 여기 숨은 교훈 하나 — val loss가 1750스텝의 1.4425에서 2000스텝에 1.4552로 반등했습니다. train loss는 계속 떨어지는데 val이 오르는 것, 교과서적인 과적합의 시작입니다. 1MB 데이터에 10M 파라미터면 금세 외우기 시작하죠. 조기 종료(early stopping)를 실물로 보는 순간입니다. 트랜스포머 구조를 더 큰 그림에서 보고 싶다면 AI 모델 개발 라이프사이클 편과 짝지어 읽어 보세요.

핵심 코드는 요즘 PyTorch답게 단출합니다 — 어텐션은 손으로 마스킹하지 않고 flash attention 커널에 맡깁니다:

# 인과적 셀프 어텐션: is_causal=True 가 삼각 마스크를 대신한다
y = F.scaled_dot_product_attention(q, k, v, is_causal=True)

2부 — OCR 대결: 전용 모델 vs 범용 VLM

가장 재미있는 실험입니다. 같은 이미지에 전용 OCR(Microsoft TrOCR)소형 비전-언어 모델(Qwen2-VL-2B) 을 붙여 맞대결시켰습니다. 지표는 CER(Character Error Rate, 낮을수록 좋음). 테스트 이미지는 영어 한 줄, 한글 한 줄, 그리고 한·영·숫자가 섞인 영수증입니다.

모델                  이미지     지연      CER      읽은 결과
────────────────────  ────────  ───────  ──────  ──────────────────────────────
TrOCR-printed         영어      69ms     0.791   THE QUICK BROWN FOX ... LAY DOG
TrOCR-printed         한글      40ms     1.429   CHANGE @ SUBJECT EXCLIP
TrOCR-handwritten     영어      44ms     0.000   The quick brown fox ... lazy dog
Qwen2-VL-2B (VLM)     영어      90ms     0.000   The quick brown fox ... lazy dog
Qwen2-VL-2B (VLM)     한글     143ms     0.071   다람쥐 헌 챗바퀴에 타고파
Qwen2-VL-2B (VLM)     영수증   453ms      —      (한·영·숫자 다중 라인 거의 완벽)

여기서 세 가지 이야기가 나옵니다.

첫째, 지표를 곧이곧대로 믿지 마세요. TrOCR-printed의 영어 CER이 0.791이라 "형편없다"고 결론 내리면 틀립니다. 실제 출력은 THE QUICK BROWN FOX JUMPS OVER THE LAY DOG — 거의 다 맞게 읽고 전부 대문자로 냈을 뿐입니다(그리고 lazy의 z 하나를 놓쳤죠). CER은 대소문자를 다른 글자로 세기 때문에, 대문자화 하나가 점수를 0.79까지 뻥튀기한 겁니다. 대소문자를 무시하면 CER은 0.05 수준입니다. 지표가 나쁘다고 모델이 나쁜 게 아니라, 지표가 무엇을 세는지를 알아야 합니다.

둘째, 전용 모델은 좁습니다. TrOCR은 라틴 문자로만 학습돼서, 한글 이미지엔 CHANGE @ SUBJECT EXCLIP 같은 완전한 헛것을 냅니다(CER 1.429 — 삽입 오류 탓에 1을 넘김). 모델이 "모른다"고 말하지 못하고 아는 문자로 우겨넣는 것, 도메인 밖 입력의 전형적인 실패입니다.

셋째, 재미있는 반전 — "손글씨용" TrOCR-handwritten이 인쇄체 영어를 CER 0.000으로 완벽하게, 심지어 대소문자까지 맞게 읽었습니다. "인쇄체용" 모델이 대문자로 뭉갠 걸 "손글씨용"이 정확히 맞춘 것. 이름표(모델 이름)가 성능을 보장하지 않습니다 — 실제 데이터로 재보기 전까지는 모릅니다.

그리고 범용 VLM의 압승: Qwen2-VL-2B는 영어를 완벽히, 한글은 한 글자(챗/쳇)만 틀리고, 무엇보다 한·영·숫자가 섞인 영수증을 줄바꿈까지 살려 거의 그대로 옮겼습니다. 게다가 "이 영수증을 그대로 옮겨줘"라는 자연어 지시를 알아듣습니다 — 전용 OCR은 못 하는 일이죠.

정리하면 전용 vs 범용의 고전적 트레이드오프입니다:

              전용 OCR (TrOCR)          범용 VLM (Qwen2-VL-2B)
──────────  ───────────────────────  ─────────────────────────────
속도          40~69ms (빠름)            90~453ms (느림)
VRAM          1.39 GB (작음)            4.49 GB (3배)
언어          라틴 전용                 다국어
레이아웃      한 줄만                   다중 라인·표·혼용
지시          불가                      자연어로 지시 가능

한 줄짜리 라틴 문서를 초저지연·저비용으로 대량 처리하면 전용 OCR, 다국어·복잡 레이아웃·유연함이 필요하면 VLM — 감각은 이렇습니다.

3부 — 음악 생성: 8초 음악을 1.9초에

마지막은 텍스트-투-뮤직입니다. Meta의 MusicGen-small(587M)에 프롬프트 두 개를 던졌습니다.

프롬프트                                          생성시간   음악길이   실시간 배율
────────────────────────────────────────────────  ────────  ────────  ──────────
"upbeat lofi hip hop beat, warm piano, relaxing"   1.90s     7.9s       4.19x
"epic orchestral cinematic trailer, drums, brass"  1.85s     7.9s       4.30x

peak VRAM 1.37 GB

8초짜리 음악을 1.9초에 — 실시간의 4.2배 속도로 생성했습니다. MusicGen은 오디오를 EnCodec 토큰(초당 약 50개)으로 바꿔 언어 모델처럼 자기회귀로 뽑아내고, 마지막에 다시 파형으로 디코딩합니다. LLM이 텍스트 토큰을 뽑듯 음악을 "토큰"으로 뽑는 것 — 생성 AI의 문법이 여기서도 그대로입니다. 결과물은 32kHz 모노 WAV로 저장돼, 실제로 재생하면 프롬프트대로 로파이 비트와 오케스트라가 나옵니다.

마치며 — 작은 모델의 시대, GPU 한 장이면 충분하다

세 실험의 공통점: 전부 5GB 미만 VRAM에서, 초 단위로 끝났습니다.

실험            파라미터   VRAM        핵심 수치
────────────  ─────────  ─────────  ────────────────────────
microGPT       10.75M     1.73 GB    2000스텝 학습 28초
TrOCR          334M       1.39 GB    한 줄 읽기 40~69ms
Qwen2-VL-2B    2B         4.49 GB    영수증 전사 453ms
MusicGen       587M       1.37 GB    8초 음악을 1.9초에

큰 모델이 헤드라인을 가져가지만, 배우고 실험하기 좋은 곳은 이 작은 모델들입니다. GPT를 밑바닥부터 학습시키며 과적합을 눈으로 보고, OCR 대결로 "전용 vs 범용"과 "지표의 함정"을 몸으로 익히고, 음악 생성으로 생성 AI의 토큰 문법을 확인하는 것 — 이 모든 게 노트북이 아니라 30초, GPU 한 장이면 됩니다. 멀티 GPU로 넘어가는 다음 단계가 궁금하다면 분산 학습 플랫폼 편이 이어지는 이야기입니다. 그리고 손으로 직접 굴려본 모델만이 진짜 감각으로 남습니다 — 벤치마크 숫자 하나도, 워밍업 없이는 38배 거짓말을 하니까요.

참고 자료