LLM 기초 For Dummies - forward, logits, attention, sampling, 학습과 추론
이 문서는 토크나이저, forward, 생성 루프를 읽기 위한 기초 용어 설명입니다.
여기까지 읽으셨다면 다음 두 방향으로 나뉩니다. 개념을 더 파고 싶으시면 1주차 part 2 - LLM과 트랜스포머 기본의 트랜스포머 절로 가시면 됩니다. 위치 인코딩, 멀티헤드, 잔차 연결 같은 나머지 조각이 거기 있습니다. 코드가 급하시면 위 링크의 워커 분석으로 바로 가셔도 됩니다.
전체 흐름부터 보기
LLM이 다음 토큰을 만드는 과정은 다음과 같습니다.
사람이 입력한 문자열
↓
토크나이저가 토큰 ID로 변환
↓
모델이 forward 실행
↓
각 토큰 후보의 점수인 logits 출력
↓
확률로 변환
↓
다음 토큰 하나 선택
↓
토크나이저가 문자열로 복원
예를 들면 다음과 같습니다.
입력: "나는 오늘"
모델 실행
다음 토큰: " 학교에"
입력: "나는 오늘 학교에"
모델 실행
다음 토큰: " 갔다"
이 과정을 반복하면 문장이 만들어집니다.
1. 모델의 forward 한 번이란?
쉬운 정의
입력을 모델의 처음부터 끝까지 한 번 통과시켜 결과를 계산하는 것입니다.
코드에서는 다음 한 줄입니다.
outputs = self.model(input_ids)
이 한 줄 안에서 다음 과정이 실행됩니다.
토큰 ID
↓
임베딩
↓
트랜스포머 블록 1
↓
트랜스포머 블록 2
↓
...
↓
마지막 트랜스포머 블록
↓
LM Head
↓
logits
따라서 forward 한 번은 트랜스포머 블록 하나만 실행하는 것이 아닙니다. 모델에 포함된 모든 트랜스포머 블록과 출력 계층을 한 차례 통과하는 것입니다.
forward 한 번으로 문장이 완성되나?
아닙니다.
텍스트 생성 모델은 일반적으로 forward 한 번으로 다음 토큰 후보의 점수를 계산합니다.
입력: "나는 오늘"
forward 1회
다음 토큰: "학교에"
문장을 계속 생성하려면 방금 만든 토큰을 입력 뒤에 붙여 다시 forward해야 합니다.
"나는 오늘"
↓ forward
"학교에" 생성
"나는 오늘 학교에"
↓ forward
"갔다" 생성
이 반복을 자기회귀 생성이라고 합니다.
블록 사이를 흐르는 것은 무엇인가
블록을 지날 때 오가는 값을 hidden state라고 부릅니다. 각 토큰에 대해 모델이 지금까지 이해한 내용을 담은 벡터입니다.
여기서 자주 헷갈리는 점이 하나 있습니다. 블록 사이를 logits가 오가는 것이 아닙니다. logits는 맨 끝 LM Head에서 딱 한 번 생깁니다. 그 전까지 흐르는 것은 전부 hidden state입니다.
길이는 끝까지 그대로입니다. GPT-2라면 768로 고정입니다. 바뀌는 것은 안에 든 값뿐입니다. 임베딩 직후에는 사전에서 찾은 뜻만 들어 있다가, 블록을 지날 때마다 어텐션이 앞 토큰의 정보를 섞어 넣습니다. 열두 번 반복하면 "그는" 벡터 안에 "철수"라는 맥락이 배어 있게 됩니다.
그래서 마지막 위치의 hidden state 하나가 문장 전체의 요약본 노릇을 합니다. LM Head는 그 요약본을 받아 "그렇다면 다음은 무엇인가"로 번역합니다.
| hidden state | logits | |
|---|---|---|
| 모양 | [B, T, 768] |
[B, T, 50272] |
| 언제 | 블록마다 계속 | 맨 끝에 한 번 |
| 뜻 | 토큰에 대한 이해 | 다음 토큰 후보의 점수 |
hidden size는 흐르는 데이터의 폭이고, 파라미터는 모델이 들고 있는 가중치의 개수입니다. opt-125m에서 hidden은 768이고 파라미터는 1억 2천 5백만 개입니다. hidden은 요청이 들어올 때마다 새로 만들어졌다 사라지고, 파라미터는 학습 때 정해져 추론 내내 고정입니다.
블록 안 행렬이 대부분 hidden × hidden이라서, hidden을 두 배로 키우면 파라미터는 네 배가 됩니다.
2. logit이란?
쉬운 정의
logit은 모델이 다음 토큰 후보마다 매긴 원점수입니다.
예를 들어 모델의 어휘에 다음 세 토큰만 있다고 가정해보겠습니다.
"학교에" → 2.0점
"밥을" → 1.0점
"우주선" → 0.0점
여기서 2.0, 1.0, 0.0이 logits입니다.
logit은 확률이 아니다
logit에는 다음과 같은 특징이 있습니다.
- 음수일 수도 있습니다.
- 모든 값을 더해도 1이 되지 않습니다.
- 값 자체보다 후보 사이의 차이가 중요합니다.
logits를 softmax에 넣으면 확률처럼 사용할 수 있는 값으로 바뀝니다.
logits
학교에: 2.0
밥을: 1.0
우주선: 0.0
↓ softmax
확률
학교에: 66.5%
밥을: 24.5%
우주선: 9.0%
정리하면 다음과 같습니다.
logit
= 모델이 토큰 후보에 매긴 원점수
softmax
= 원점수를 합이 1인 확률 분포로 바꾸는 함수
3. 모델은 왜 logits를 많이 출력할까?
모델의 출력은 보통 다음 shape을 가집니다.
[B, T, V]
각 문자는 다음 뜻입니다.
| 문자 | 의미 |
|---|---|
B |
한 번에 처리한 문장의 개수 |
T |
각 문장의 토큰 길이 |
V |
모델이 알고 있는 전체 토큰의 개수 |
예를 들어 다음 shape이 있다고 해보겠습니다.
[4, 7, 50272]
이는 다음 뜻입니다.
문장 4개를 한 번에 처리했고
각 문장의 길이는 7토큰이며
각 위치마다 50,272개 토큰의 점수를 계산했다
다음 코드는 그중 마지막 위치의 점수만 가져옵니다.
next_token_logits = outputs.logits[:, -1, :]
왜 마지막 위치만 필요할까요?
입력: "The quick brown"
"The" 위치의 예측
→ "The" 다음에는 무엇이 올까?
"quick" 위치의 예측
→ "The quick" 다음에는 무엇이 올까?
"brown" 위치의 예측
→ "The quick brown" 다음에는 무엇이 올까?
현재 문장 전체 다음에 올 토큰이 필요하므로 마지막 위치의 logits를 사용합니다.
4. 행렬곱과 어텐션은 같은 것인가?
같은 것은 아닙니다.
행렬곱은 계산 도구이고, 어텐션은 행렬곱을 이용해 만든 메커니즘입니다.
비유하면 다음과 같습니다.
행렬곱 = 벽돌
어텐션 = 벽돌로 만든 건물
행렬곱은 어텐션뿐 아니라 모델 곳곳에서 사용됩니다.
임베딩 계산
Q, K, V 생성
어텐션 점수 계산
FFN 계산
LM Head 계산
어텐션은 무엇을 하는가?
어텐션은 각 토큰이 이전 토큰 중 무엇을 얼마나 참고할지 계산합니다.
예를 들어 다음 문장이 있다고 해보겠습니다.
"철수는 사과를 먹었다. 그는 배가 고팠다."
모델이 그는을 처리할 때 앞의 철수를 중요하게 참고해야 합니다. 어텐션은 이런 관계를 계산합니다.
각 토큰을 Q, K, V로 변환
↓
Q와 K를 비교
↓
어떤 토큰을 얼마나 참고할지 계산
↓
그 비율로 V를 섞음
어텐션의 핵심 계산은 다음과 같습니다.
Attention(Q, K, V)
= softmax(QKᵀ / √d) V
수식 자체를 외울 필요는 없습니다. 다음 정도로 이해하면 충분합니다.
Q와 K의 행렬곱
→ 토큰 사이의 관련성 점수 계산
softmax
→ 관련성 점수를 참고 비율로 변환
V와 결합
→ 참고한 정보를 현재 토큰에 반영
5. 샘플링은 언제 하는가?
샘플링은 어텐션을 포함한 모델의 forward가 모두 끝난 다음에 합니다.
트랜스포머 블록들
├─ 어텐션
└─ FFN
↓
마지막 hidden state
↓
LM Head
↓
logits
↓
softmax
↓
샘플링
↓
다음 토큰
따라서 샘플링은 어텐션에 포함된 기능이 아닙니다.
역할을 나누면 다음과 같습니다.
모델
→ 다음 토큰 후보의 logits를 계산
샘플링 로직
→ logits 중 실제로 사용할 토큰 하나를 선택
코드에서는 다음 부분입니다.
next_token_logits = outputs.logits[:, -1, :]
probabilities = torch.softmax(
next_token_logits / 0.7,
dim=-1,
)
next_token = torch.multinomial(
probabilities,
num_samples=1,
)
각 단계의 의미는 다음과 같습니다.
next_token_logits / 0.7
→ 후보 사이의 점수 차이 조절
softmax
→ 점수를 확률로 변환
multinomial
→ 확률에 따라 토큰 하나 선택
모양이 줄어드는 길
여기까지를 한 줄로 늘어놓으면 이렇습니다. 어디까지가 모델이고 어디부터가 생성 코드인지도 이 그림에서 갈립니다.
[4]에 담긴 값은 어휘 전체가 아니라 골라낸 토큰 번호 네 개입니다. 예를 들어 [6219, 11475, 48659, 31414]처럼요. 각 값은 0부터 50271 사이의 정수 하나이고, 요청 하나에 토큰 하나씩 대응합니다.
어휘가 5만 개뿐인데 어떻게 온갖 문장을 만드느냐면 조합이기 때문입니다. 5만 개 중 하나를 고르는 일을 20번 반복하면 경우의 수가 50000^20입니다. 자모 24개로 모든 한국어 문장을 적는 것과 같습니다. 어휘가 크지 않아도 이어붙이면 끝이 없습니다.
softmax가 두 번 나와서 헷갈린다면
softmax는 어텐션 안에서도 나오고, 토큰 선택 과정에서도 나옵니다. 하지만 용도가 다릅니다.
| 위치 | softmax의 역할 |
|---|---|
| 어텐션 내부 | 과거 토큰 중 무엇을 얼마나 참고할지 결정 |
| logits 뒤 | 다음 토큰 후보 중 무엇을 선택할지 확률 계산 |
횟수도 다릅니다. 어텐션 쪽은 블록마다, 그리고 헤드마다 돕니다. opt-125m은 블록 12개에 헤드가 12개씩이라 forward 한 번에 144번 계산합니다. 생성 코드 쪽 softmax는 맨 끝에 딱 한 번입니다.
같은 함수를 다른 목적으로 사용하는 것입니다.
6. 학습과 추론은 무엇이 다른가?
학습
모델이 정답을 더 잘 맞히도록 가중치를 수정하는 과정입니다.
학습은 다음 순서로 진행됩니다.
입력
↓
forward
↓
모델의 예측
↓
정답과 비교
↓
loss 계산
↓
backward
↓
가중치 수정
예를 들어 다음 학습 데이터가 있다고 해보겠습니다.
입력: "대한민국의 수도는"
정답: "서울"
모델이 다음처럼 예측했다고 가정합니다.
부산: 60%
서울: 30%
대전: 10%
정답은 서울인데 부산의 확률이 더 높습니다. 학습 과정에서는 다음 작업을 합니다.
- 예측과 정답의 차이인 loss를 계산합니다.
- backward로 어떤 가중치를 어떻게 바꿔야 하는지 계산합니다.
- optimizer가 가중치를 조금 수정합니다.
- 이 과정을 수많은 데이터로 반복합니다.
따라서 학습은 계산이 잘 돌아가는지 확인하는 과정이 아닙니다.
모델의 예측이 정답에 가까워지도록 내부 가중치를 조정하는 과정입니다.
계산이 잘되는지 또는 처음 보는 데이터에도 잘 맞히는지를 확인하는 것은 별도의 검증과 평가 과정에 더 가깝습니다.
추론
학습이 끝난 모델의 가중치를 고정하고 결과만 계산하는 과정입니다.
입력
↓
forward
↓
logits
↓
토큰 선택
↓
결과 반환
추론에서는 다음 작업을 하지 않습니다.
- 정답과 비교
- loss 계산
- backward
- gradient 계산
- 가중치 수정
그래서 다음 코드를 사용합니다.
with torch.no_grad():
outputs = self.model(...)
torch.no_grad()는 다음 뜻입니다.
가중치를 수정할 필요가 없으므로 역전파를 위한 계산 기록을 만들지 말고 결과만 계산하라.
학습과 추론 비교
| 구분 | 학습 | 추론 |
|---|---|---|
| 목적 | 모델을 개선 | 결과를 생성 |
| forward | 사용 | 사용 |
| 정답 데이터 | 필요 | 보통 불필요 |
| loss 계산 | 사용 | 보통 사용하지 않음 |
| backward | 사용 | 사용하지 않음 |
| 가중치 수정 | 수정함 | 고정함 |
torch.no_grad() |
보통 사용하지 않음 | 사용 |
한 문장으로 정리하면 다음과 같습니다.
학습은
forward → 오답 측정 → backward → 가중치 수정이고, 추론은forward → 결과 사용입니다.
최종 요약
forward
= 입력을 모델 처음부터 끝까지 한 번 통과시키는 것
logit
= 모델이 다음 토큰 후보마다 매긴 원점수
행렬곱
= 어텐션을 포함해 모델 전체에서 사용하는 기본 계산 도구
어텐션
= 어떤 과거 토큰을 얼마나 참고할지 계산하는 메커니즘
샘플링
= 모델 계산이 끝난 뒤 logits에서 실제 다음 토큰을 선택하는 과정
학습
= 예측과 정답을 비교하고 가중치를 수정하는 과정
추론
= 학습된 가중치를 고정하고 결과만 계산하는 과정