오늘 끝나면
나만의 미니 LLM 만들기
- ✓나만의 미니 LLM 만들기의 핵심 문제를 한 문장으로 설명한다
- ✓오른쪽 실습에서 나만의이 어떻게 움직이는지 관찰한다
- ✓다음 강의와 이어지는 한계를 말할 수 있다
실습 미션
nanoGPT 스타일 — 셰익스피어로 직접 학습시킨다 이 문장이 실제로 무슨 뜻인지 실습에서 한 번 손으로 확인한다.
성공 조건
- □실습의 기본값을 먼저 관찰
- □입력값이나 모드를 한 번 이상 바꿔 결과 비교
- □왜 결과가 바뀌었는지 한 문장으로 설명
AI · Day 12 / 지도학습 · 생성 DL
나만의
미니 LLM
12일의 종착점이다. GPT를 한 줄씩 직접 짜고, 학습시키고, 문장을 뽑는다.나만의 작은 AI를 노트북에서 만든다. 300줄짜리 nanoGPT면 충분하다.
왜 굳이 직접 만드나
API를 1만 번 호출해도 "왜 이렇게 답하지?"는 풀리지 않는다. 소비만 해서는 내부가 보이지 않는다.
논문만 읽어도 부족하다. loss.backward()를 직접 돌려보지 않으면 "학습"이 감이 오지 않는다.
학습률을 1e-3에서 1e-4로 낮추면 loss 곡선이 어떻게 휘는지,block_size를 늘리면 메모리가 왜 O(n²)로 터지는지. 이건 만들어본 사람만 아는 감각이다.
Karpathy의 말처럼 "LLM을 이해하는 유일한 길은 직접 만들어보는 것"이다. 오늘은 그의 영상 "Let's build GPT from scratch"를 한국어로 졸업하는 버전이다.
직접 만들어야 꼭대기 칸에 오른다. 진짜 직관이 생긴다
토큰은 그냥 숫자다
토크나이저는 거창한 게 아니다.문자열을 정수 수열로 바꾸는 딕셔너리 하나일 뿐이다.
셰익스피어 텍스트 1.1MB를 쓴다. 등장한 문자를 모으니 딱 65개다.
stoi는 문자를 숫자로 바꾸고, itos는 숫자를 문자로 되돌린다. 그게 전부다. character-level이라 한국어 소설을 넣어도 코드를 고치지 않고 그대로 돌아간다.
파이썬 코드 보기
chars = sorted(set(text)) # 65개 문자
stoi = {ch: i for i, ch in enumerate(chars)}
itos = {i: ch for i, ch in enumerate(chars)}
def encode(s): return [stoi[c] for c in s]
def decode(ids): return "".join(itos[i] for i in ids)
print(encode("Hi")) # [20, 47]
print(decode([20, 47])) # "Hi"GPT-4의 cl100k_base도 본질은 똑같다. vocab이 65개냐 10만 개냐의 차이뿐이다. 숫자에서 의미를 배우는 건 다음 단계 Embedding의 몫이다.
문자와 정수의 양방향 매핑이 토크나이저의 본질이다
GPT는 블록 N개의 반복이다
GPT의 정체는 두 문장이면 끝난다. Attention은 사실 행렬 곱 한 번이고, GPT는 똑같은 블록의 반복이다.
입력 토큰에 Token Embedding과 Positional Embedding을 더한다. 블록 하나는 LayerNorm, Causal Attention, Residual을 거치고, 이어서 다시 LayerNorm, FeedForward, Residual로 이어진다. 이 블록을 N번 쌓고, 마지막에 Linear Head로 vocab 크기의 logits을 뽑으면 끝난다.
핵심은 causal mask다. 하삼각 행렬로 미래 토큰을 -inf로 처리한다. 각 위치가 자기 이전만 보게 된다. 이게 "다음 글자 맞히기"라는 언어모델 정의 그 자체다.
파이썬 코드 보기
wei = q @ k.transpose(-2, -1) * C ** -0.5
wei = wei.masked_fill(tril == 0, float("-inf"))
wei = softmax(wei, dim=-1) # 미래는 가린 채
out = wei @ v # 과거를 가중 평균n_layer=6이면 nanoGPT이고, n_layer=96이면 GPT-3다. 아키텍처는 같다. 확장의 문제일 뿐이다.
파란 블록 하나를 N번 쌓으면 GPT가 된다. n_layer=96이면 GPT-3다
학습은 loss가 떨어지는 현상
학습은 결국 loss라는 숫자가 떨어지는 현상이다.
매 스텝마다 get_batch로 임의 구간을 뽑는다. 입력 x와 한 칸 민 정답 y를 만들고, 예측과 정답의 cross-entropy를 잰다.loss.backward()로 기울기를 흘리고, optimizer.step()으로 가중치를 살짝 고친다. 이 반복이 전부다.
파이썬 코드 보기
for step in range(max_steps):
xb, yb = get_batch("train")
_, loss = model(xb, yb)
optimizer.zero_grad()
loss.backward() # 기울기
optimizer.step() # 한 걸음warmup으로 학습률을 완만히 올렸다가 cosine으로 부드럽게 내린다. 그러면 train·val loss가 4.2에서 2.1, 1.5로 우하향한다.
loss가 4.2, 2.1, 1.5로 우하향한다. 이게 "학습"의 전부다
Temperature는 모델의 성격
학습이 끝나면 모델이 처음으로 "뭔가"를 말한다. Temperature 하나 바꿨을 뿐인데 성격이 달라진다.
같은 모델에 같은 프롬프트 ROMEO:를 넣어도 디코딩 전략이 결과를 바꾼다.
Greedy는 항상 argmax라 지루하다.Temperature는 logits을 T로 나눈다. T가 낮으면 엄격해지고 높으면 자유로워진다.Top-k는 상위 k개만 남기고, Top-p는 누적확률 p까지만 두고 샘플링한다.
여기까지가 12일의 결론이다. 사내 문서 전용 소형 LLM, 엣지용 경량 모델, 데이터가 밖으로 못 나가는 on-premise 보안 LLM, 브랜드 보이스 페르소나 LLM까지 모두 출발점은 오늘 손으로 만든 이 구조다. 차이는 파라미터와 데이터, 컴퓨트뿐이고 근본은 같다.