AI Specialist · Full-code practice · Edition 03

전체 코드에서
빈칸을 복원합니다.

제공한 예제의 왼쪽 변수·주석·입출력을 읽고,
표현식과 인덱스를 이유까지 연결합니다.

기출형 최종 정리 v3 · 제공 예제 3개/13빈칸 · 기초 56빈칸 유지

EXAM FORMAT · 전체 코드를 읽고 복원합니다.

왼쪽 변수와 주석을 보고,
빈칸에 필요한 표현을 넣습니다.

이번 버전은 사용자가 제공한 전체코드 예제 3개를 중심으로 정리했습니다. 단편 빈칸 문제는 기초 연습으로 유지했습니다.

예제 1·2·3의 코드 구조와 주석은 제공한 내용을 유지했습니다. 번호는 화면에서 추가한 학습 표시이며 Python 문법이 아닙니다. self.???(x)처럼 일부만 가려졌다면 그 조각만 채웁니다.

예제이번에 복원할 부분학습 구분
MultiHeadAttentionhead 축 변경 3개 · mask 인자 · head 재결합 = 5개1일차 Attention 설명과 연결
GPTModel임베딩 2개 · dropout · 블록·출력 호출 = 5개1일차 설명 · 다음날 실습 연결
PreferenceDataset + collate전체 문장 토큰화 2개 · prompt mask = 3개후속 DPO 예제 · Day1 범위로 확대하지 않음
GPTDatasetV1입력·정답·샘플 수·조회 = 4개우리가 추가로 선정한 전체코드 연습

표현 대조는 참고용입니다. Python 실행·정답 판정이 아닙니다. 문자열 안의 공백과 \n을 보존해 대조하며, 동작이 같은 다른 구현은 원본·해설과 직접 비교합니다.

RECALLED TOPICS · 회고 목록 점검

기출 회고는 “확인할 목록”으로 남깁니다.

아래 항목은 사용자가 전달한 회고 목록입니다. 전체 코드·주석·모델·버전이 없는 부분은 정답을 확정하지 않았습니다. 이번 Day1 강사가 확정한 시험 범위와도 구분합니다.

회고 항목지금 확인할 내용정답 상태
DPO회고: model/tokenizer, bfloat16, chosen/rejected, DPOTrainer. 위 예제의 선호 쌍·패딩·mask와 연결합니다. <end_of_turn>·<eos> 조합은 해당 코드와 모델 템플릿을 따로 확인해야 합니다.현재 저장소의 3개 빈칸만 원본 대조. 다른 DPO 회고의 API·포맷은 미확정.
CRAG회고: retriever, kg, combined, answer, llm_input. 전체 함수·주석·출력 계약과 각 변수에 할당되는 값의 역할을 확인합니다. 약어의 의미와 구체 API는 회고만으로 단정하지 않습니다.원본 코드 미제공 · 답 확정 불가
시계열회고: RNNRNN.forward의 h·out·input, nn.MSELoss(reduction="mean"). 이는 전달받은 기억이며 실제 빈칸의 답은 원문에서 확인합니다. 입력·정답 길이·텐서 축·예측 대상을 함께 봅니다. 모델 클래스나 데이터 주기를 임의로 지정하지 않습니다.원본 코드 미제공 · 답 확정 불가
DETR회고: MHA.forward의 logits, Criterion.loss_boxes의 loss_bbox, GIoU. 객체 검출 코드의 입력·출력과 지시 주석을 확인합니다. DETR의 logits가 어떤 점수인지 함수 문맥에서 확인합니다. 같은 Attention 원리라도 GPT와 mask·shape·정답식이 같다고 단정하지 않습니다.원본 코드 미제공 · 모델·API 확정 불가
Pruning회고: kernel pruning의 num_pruned_kernels, importance. 어떤 대상·축·기준으로 제거하는지 원문이 필요합니다. L1/L2, 제거 비율, 임계값을 회고만 보고 정답으로 고정하지 않습니다.원본 코드 미제공 · 기준·비율 확정 불가
이번 최종 정리의 사용 순서
  1. 실전 코드에서 예제 전체를 읽고 13개 빈칸을 복원합니다.
  2. 이해가 막히면 이해 노트의 Dataset·Attention·GPT 설명으로 돌아갑니다.
  3. 기초 빈칸에서 API 이름·인덱스·shape를 반복합니다.
  4. 함수 복원에서 더 큰 조각을 직접 써 봅니다.
  5. 원본 없는 회고 항목은 후속 자료와 대조할 목록으로 유지합니다.
수업 순서로 이동

00 · 시작하기

시험은 “왜 이 코드인가”까지 준비합니다.

1일차는 모델 이해가 중심입니다. 실습에서는 코드의 빈칸과 핵심 함수의 동작을 함께 익힙니다.

구분이 자료의 표시학습에 사용하는 방법
시험 안내원문 확인강사 또는 운영자가 말한 범위·유형만 표시합니다. 개별 문제의 출제를 보장하지 않습니다.
강조한 내용강사 강조“중요하다”, “흐름을 이해하라”라고 한 개념을 자세히 설명합니다.
예상 문제AI 구성실제 코드의 ???·주석·핵심 동작으로 만든 연습 문제입니다. 실제 시험 문제가 아닙니다.

먼저 공부할 순서

  1. Dataset에서 입력과 정답을 한 칸 이동시키는 이유를 이해합니다.
  2. Attention에서 관련성 점수 → 반영 비율 → V의 가중합 흐름을 이해합니다.
  3. GPT 전체 흐름에서 임베딩 → 블록 → 출력 → 다음 토큰 연결을 확인합니다.
  4. 원본 빈칸을 풀고, 마지막으로 짧은 함수를 직접 복원합니다.

근거 · Day1 오티 17:24–19:03 · Day1 마무리 00:40–02:39, 05:21–07:08 · PDF p.2

범위에서 빼도 되는 부분은 무엇인가요?
내용원문에서 확인한 안내
Chapter 1 Vector Space시험 범위 제외. 벡터가 의미를 표현하는 원리를 이해합니다. Day1-4 23:53.
Chapter 2 마지막 Tokenizer 비교임베딩 합산까지 시험 범위라고 말한 뒤, Qwen·EXAONE 비교는 제외했습니다. Day1-4 01:05:02–01:05:21.
Chapter 3 별도 Attention 맵 시각화 노트북레이어·헤드별 차이를 보여 주는 시각화는 시험 범위 제외입니다. Day1-8 33:30–34:18.
Chapter 4 GPT1일차에 코드 흐름을 설명했습니다. 실습 시간은 다음날로 안내했습니다. 시험 제외라고 말한 것은 아닙니다. Day1-8 33:30, 46:05.

01 · AI 모델의 역사와 미래

모델 이름보다 해결한 문제를 연결합니다.

이 세션은 뒤의 코드를 이해하기 위한 배경입니다. 아래 항목은 이해용 요약이며, 이론 암기 문제의 출제를 뜻하지 않습니다.

모델·기술쉽게 이해하기다음 수업과의 연결
AlexNet이미지에서 작은 패턴을 여러 층으로 조합합니다.여러 층을 통과하며 표현을 바꾼다는 딥러닝의 기본입니다.
ResNet새로 계산한 값에 원래 값을 더해 깊은 신경망의 학습을 돕습니다.GPT의 residual connection인 x + shortcut과 연결됩니다.
Word2Vec단어를 숫자 하나가 아니라 여러 숫자의 벡터로 표현합니다.토큰 ID와 임베딩 벡터를 구분하는 출발점입니다.
AlphaGo신경망의 판단과 탐색·학습을 결합했습니다.“모델이 예측한다”와 “학습으로 값을 바꾼다”를 구분합니다.
TransformerAttention으로 토큰 사이의 관계를 계산합니다.오늘 실습의 중심입니다.
GPT-1·2·3사전학습한 생성 모델을 크게 확장하며 사용 방식을 넓혔습니다.모델 구조·데이터·학습 규모를 함께 봅니다.
GAN·Diffusion새 데이터를 만드는 서로 다른 생성 방식입니다.생성형 AI가 텍스트에만 한정되지 않음을 이해합니다.
AlphaFold아미노산 사이의 관계를 이용해 단백질 구조를 예측합니다.관계를 계산하는 방식이 다른 데이터에도 적용됩니다.
형식 증명·미래 응용Lean 같은 도구는 증명 규칙에 맞는지 검사합니다.그럴듯한 답과 검증된 답의 차이를 봅니다.

근거 · Day1-1 AI 모델의 역사 및 미래 전사문 전체의 주제 순서 · PDF pp.3–32

Chapter 1 벡터 공간 실습은 무엇을 확인하나요?

목적: 비슷한 의미를 가진 단어가 벡터 공간에서 가깝게 배치될 수 있음을 확인합니다. cat과 dog의 유사도, computer 주변 단어, 과일 목록에 섞인 car를 살펴봅니다.

이해할 점: 토큰 ID의 크기로 의미의 유사도를 비교하지 않습니다. 학습된 벡터로 관계를 계산합니다. 예제 실행만으로 새 모델을 학습하는 것은 아닙니다.

근거 · Day1-4 23:53 · PDF pp.82–84 · Chapter_1_Exercise_Vector Space.ipynb

02 · AI 기본 수학

Attention에 필요한 수학부터 묶어서 봅니다.

벡터의 관계를 점수로 만들고, 점수를 반영 비율로 바꾸며, 틀린 정도에 따라 모델을 바꾸는 과정입니다.

개념한 문장 설명실습 코드와의 연결
벡터하나의 대상을 여러 숫자로 표현합니다.토큰 임베딩, Q·K·V
내적같은 차원의 숫자들을 곱해서 더합니다.Q와 K의 관련성 점수
행렬곱 @여러 내적을 한 번에 계산합니다.queries @ keys.transpose(...)
요소별 곱 *같은 위치의 값끼리 곱합니다.행렬곱 @와 다릅니다.
활성화 함수선형 계산 사이에 비선형성을 넣습니다.FFN 안의 GELU
softmax점수들을 음수가 없는 값으로 바꾸고 합이 1이 되도록 정규화합니다.Attention의 반영 비율
손실 loss모델의 예측이 정답과 얼마나 다른지 수치로 표현합니다.다음 토큰 예측 학습
역전파손실이 각 파라미터에 얼마나 영향을 받는지 기울기를 계산합니다.기울기 계산 후 optimizer가 값을 갱신합니다.

왜 지수와 로그를 쓰나요?

softmax는 점수 z에 exp(z)를 적용합니다. 지수값은 양수이고, 이를 전체 지수값의 합으로 나누면 반영 비율을 만들 수 있습니다.

softmax(z_i) = exp(z_i) / sum_j(exp(z_j))

손실의 대표적인 형태는 정답 토큰의 확률 p에 대한 -log(p)입니다. 정답에 높은 확률을 주면 손실이 작고, 정답에 매우 낮은 확률을 주면 손실이 큽니다.

정답 토큰에 준 확률 p-ln(p)해석
0.9약 0.105정답에 높은 확률을 주었습니다.
0.1약 2.303정답에 낮은 확률을 주었습니다.
0.01약 4.605더 강한 수정 신호를 줍니다.

역전파의 숲: 무엇을 바꾸나요?

예측 = model(입력)        # 순전파
loss = loss_fn(예측, 정답)
loss.backward()           # 각 파라미터의 기울기 계산
optimizer.step()          # 기울기를 사용해 파라미터 갱신

x1, x2는 입력입니다. z는 가중합이고, h나 출력은 활성화 함수를 통과한 값입니다. 역전파는 출력에서 입력 방향으로 연결 규칙을 따라 기울기를 계산합니다. 입력 문장이나 정답을 고치는 과정이 아닙니다.

근거 · Day1-2 06:59, 08:00–12:43, 14:02–24:30 · PDF pp.35–46, pp.47–66

sigmoid와 softmax는 무엇이 다른가요?

sigmoid는 한 점수를 0과 1 사이로 바꿉니다. 여러 항목에 각각 적용하면 항목 간 합이 1이 될 필요가 없습니다. softmax는 함께 비교하는 점수들의 묶음에 적용하며 그 묶음의 합이 1입니다.

Attention에서는 한 Query가 여러 Key 중 어디에 얼마나 주목할지 비율을 나눠야 하므로 softmax를 사용합니다.

03 · 언어 모델의 발전

RNN의 기억에서 Transformer의 관계 계산으로 갑니다.

강사는 Transformer를 볼 때 Attention과 Feed Forward Network(FFN)를 중심으로 보라고 반복했습니다.

구조문맥을 처리하는 방식기억할 한계·특징
RNN이전 hidden state와 현재 입력으로 새 hidden state를 만듭니다.앞의 정보가 뒤까지 전달되는 과정에서 약해질 수 있습니다. 순서 의존성도 있습니다.
RNN + Attention출력을 만들 때 앞의 여러 상태를 다시 참고합니다.한 요약값에만 의존하던 부담을 줄입니다.
Transformer토큰 간 관계를 Attention으로 계산합니다.학습 시 토큰 위치의 계산을 병렬화하기 좋습니다. 생성은 다음 토큰을 순서대로 만듭니다.

hidden state가 왜 중요한가요?

문장을 한 단어씩 읽는다고 가정해 보세요. 지금 단어만 보면 앞에서 무슨 말을 했는지 모릅니다. hidden state는 앞에서 읽은 정보를 다음 단계로 넘기는 중간 기억입니다. Transformer는 이 방식의 순차 전달에만 의존하지 않고 토큰 사이의 관계를 직접 계산합니다.

GPT에서 인코더가 빠진다는 뜻

원래 encoder–decoder Transformer에는 인코더 출력과 디코더 상태를 연결하는 cross-attention이 있습니다. GPT의 decoder-only 구조에는 별도 인코더가 없으므로 이 연결이 필요하지 않습니다. 남는 중심은 causal self-attention + FFN입니다.

모델 계열대표 학습 방식·특징이해 포인트
BERT가린 토큰을 주변 문맥으로 예측합니다.양쪽 문맥을 보는 이해 작업과 연결합니다.
GPT앞의 토큰으로 다음 토큰을 예측합니다.생성 시 미래 정답을 보면 안 됩니다.
T5인코더–디코더로 입력을 출력 텍스트로 바꿉니다.입력 이해와 출력 생성을 연결합니다.

근거 · Day1-3 02:30–06:34, 07:09–11:37, 15:34–17:27 · PDF pp.67–78

04 · 실습 1의 앞부분

문자열을 모델의 숫자 입력으로 바꿉니다.

전체 흐름은 문자열 → 토큰 ID → 입력·정답 쌍 → 배치 → 임베딩입니다. 각 단계가 서로 다른 일을 합니다.

단계입력 → 출력이 단계가 하는 일
Tokenizer문자열 → 정수 ID 목록텍스트를 토큰으로 나누고 각 토큰의 ID를 찾습니다.
DatasetID 목록 → (입력 청크, 정답 청크)다음 토큰 예측을 위한 학습 문제를 준비합니다.
DataLoader여러 쌍 → 배치같은 길이의 여러 샘플을 한 묶음으로 가져옵니다.
Embedding정수 ID → 실수 벡터ID에 해당하는 임베딩 테이블의 행을 가져옵니다.
모델 학습입력·정답 → 갱신된 파라미터예측·손실·역전파·optimizer 단계가 있어야 합니다.

encode와 decode는 서로 반대입니다.

tokenizer = tiktoken.get_encoding("gpt2")
integers = tokenizer.encode(text, allowed_special={"<|endoftext|>"})
strings = tokenizer.decode(integers)

encode는 문자열을 ID 목록으로 바꿉니다. decode는 ID 목록을 문자열로 복원합니다. allowed_special은 지정한 특수 문자열을 제어용 토큰으로 인식하도록 허용합니다.

BPE를 왜 설명했나요?

문장을 항상 단어 하나씩만 자르는 것은 아닙니다. BPE는 자주 나오는 인접한 조각을 병합해 토큰 사전을 구성합니다. 모델마다 사전과 토큰화 규칙이 달라질 수 있으므로 실습에서는 get_encoding("gpt2")로 사용할 토크나이저를 명시합니다.

근거 · Day1-3 17:50–19:58 · Day1-4 35:16–40:00, 48:22–56:10 · PDF pp.80–98 · Ch2 셀 1

실행 결과의 y 앞 빈칸도 인덱스 이동인가요?
print(f"y:      {y}")

여기 공백은 눈으로 배열을 정렬하려고 출력 문자열에 넣은 공백입니다. 데이터가 이동한 이유는 출력 공백이 아니라 enc_sample[1:context_size+1]이라는 슬라이스입니다.

05 · 실습 1의 중심

GPTDatasetV1은 “문제와 정답 묶음”을 만듭니다.

목적은 문장 하나로 다음 토큰 예측 문제를 여러 개 만드는 것입니다. 이 클래스는 모델의 가중치를 학습하지 않습니다.

1. 왜 x와 y가 한 칸 겹치나요?

tokens = [1, 2, 3, 4, 5, 6, 7]
x = tokens[0:4]  # [1, 2, 3, 4]
y = tokens[1:5]  # [2, 3, 4, 5]

각 입력 위치에서 다음 토큰을 정답으로 둡니다. 위치별로 보면 아래처럼 됩니다.

모델이 볼 수 있는 앞부분정답모든 위치를 준비하는 이유
[1]2첫 위치도 다음 토큰 예측을 연습합니다.
[1, 2]3한 샘플 안에 여러 학습 위치가 있습니다.
[1, 2, 3]4실제 미래 차단은 causal mask가 담당합니다.
[1, 2, 3, 4]5마지막 위치는 현재 문맥 뒤의 다음 토큰입니다.

Dataset은 x와 y를 준비합니다. 모델은 학습 때 각 위치의 예측을 y와 비교합니다. 생성할 때는 주로 마지막 위치의 출력을 사용해 새 토큰 하나를 고릅니다.

2. 왜 max_length + 1개가 필요한가요?

입력 길이가 4이면 x에는 4개가 필요합니다. 마지막 입력의 다음 토큰까지 정답에 넣으려면 원문에는 최소 5개가 있어야 합니다. 입력 길이가 6이면 최소 7개입니다.

assert len(token_ids) > max_length, "...max_length+1..."

길이는 정수이므로 len(token_ids) > max_length는 len(token_ids) >= max_length + 1과 같습니다. assert는 조건이 참이면 계속 진행하고, 거짓이면 오류를 냅니다.

3. range와 stride를 코드에 맞춥니다.

for i in range(0, len(token_ids) - max_length, stride):
    input_chunk  = token_ids[i : i + max_length]
    target_chunk = token_ids[i + 1 : i + max_length + 1]

i는 이번 청크의 시작 위치입니다. max_length는 청크의 길이이고, stride는 다음 시작 위치까지 이동하는 간격입니다. 슬라이스와 range 모두 끝값을 포함하지 않습니다.

토큰 7개, 길이 4시작 위치 i생성되는 쌍
stride=10, 1, 2([1,2,3,4], [2,3,4,5])
([2,3,4,5], [3,4,5,6])
([3,4,5,6], [4,5,6,7])
stride=20, 2첫 번째와 세 번째 쌍을 만듭니다.
stride=40다음 i=4는 stop=3보다 크므로 멈춥니다.

직접 움직여 보기

원문 ID는 [1, 2, 3, 4, 5, 6, 7]로 고정합니다.

4. 저장과 조회를 분리합니다.

self.input_ids.append(torch.tensor(input_chunk))
self.target_ids.append(torch.tensor(target_chunk))

def __len__(self):
    return len(self.input_ids)

def __getitem__(self, idx):
    return self.input_ids[idx], self.target_ids[idx]

__init__에서 쌍을 미리 만듭니다. __len__은 토큰 수가 아니라 만들어진 샘플 수를 반환합니다. __getitem__은 같은 번호의 입력과 정답을 함께 반환합니다.

흔한 오답왜 틀리나요?
x와 y를 똑같이 만든다.다음 토큰이 아니라 현재 토큰을 복사하는 목표가 됩니다.
target의 시작만 +1 하고 끝은 그대로 둔다.target이 한 개 짧아집니다. 시작과 끝을 모두 한 칸 옮겨야 합니다.
stride가 입력 길이다.길이는 max_length입니다. stride는 시작 위치의 이동 간격입니다.
Dataset을 실행했으니 학습이 끝났다.예측·손실·역전파·파라미터 갱신이 없습니다.

근거 · Day1-4 51:50–52:10, 56:50–01:01:48 · PDF pp.95–101 · Ch2 셀 2–3

06 · 실습 1의 뒷부분

배치와 임베딩의 shape를 따라갑니다.

DataLoader는 준비된 샘플을 묶고, Embedding은 정수 ID를 실수 벡터로 바꿉니다.

1. DataLoader는 무엇을 받나요?

dataset = GPTDatasetV1(txt, tokenizer, max_length, stride)
dataloader = DataLoader(
    dataset, batch_size=batch_size, shuffle=shuffle,
    drop_last=drop_last, num_workers=num_workers
)
인자정확한 역할
dataset가져올 샘플들이 들어 있는 Dataset 객체입니다.
batch_size한 묶음에 몇 개의 샘플을 넣을지 정합니다. 토큰 길이가 아닙니다.
shuffle샘플 순서를 섞을지 정합니다. 샘플 내부의 토큰 순서를 섞는 옵션이 아닙니다.
drop_last마지막에 배치 크기보다 적게 남은 샘플 묶음을 버릴지 정합니다.
num_workers데이터 로딩 작업에 사용할 하위 프로세스 수입니다.

입력 길이 4, 배치 크기 8이면 inputs와 targets는 각각 [8, 4]입니다. “4개짜리 문맥 8개”를 한 번에 가져온 것입니다.

2. Embedding의 두 인자는 무엇인가요?

input_ids = torch.tensor([2, 3, 5, 1])
embedding_layer = torch.nn.Embedding(num_embeddings=6, embedding_dim=3)
# 원본은 위치 인자를 사용합니다: torch.nn.Embedding(6, 3)
token_embeddings = embedding_layer(input_ids)

위의 num_embeddings는 테이블 행 수, embedding_dim은 벡터 차원입니다. 원본은 같은 값을 위치 인자로 torch.nn.Embedding(6, 3)처럼 전달합니다.

값shape·의미
weight[6, 3]: ID 0~5의 벡터 6개를 보관합니다.
input_ids[4]: 조회할 ID가 4개입니다.
token_embeddings[4, 3]: 4개의 ID마다 3차원 벡터를 가져옵니다.

Embedding은 매번 새 랜덤 벡터를 만드는 함수가 아닙니다. 초기화한 테이블에서 ID의 행을 조회합니다. 학습이 진행되면 이 테이블의 파라미터도 바뀔 수 있습니다.

3. 위치 임베딩을 왜 더하나요?

token_embeddings = token_embedding_layer(inputs)   # [8, 4, 256]
pos_embedding_layer = torch.nn.Embedding(4, 256)
pos_embeddings = pos_embedding_layer(torch.arange(4))  # [4, 256]
input_embeddings = token_embeddings + pos_embeddings  # [8, 4, 256]

torch.arange(4)는 위치 ID [0, 1, 2, 3]을 만듭니다. 위치 임베딩의 벡터 차원은 토큰 임베딩과 같아야 더할 수 있습니다. [4, 256]은 배치 8개에 공통으로 적용됩니다. 이를 broadcasting이라고 합니다.

상황토큰 벡터위치 벡터합산 결과
같은 토큰·같은 위치·같은 파라미터같음같음같음
같은 토큰·다른 위치같음다름달라질 수 있음
다른 토큰·같은 위치다름같음달라질 수 있음

근거 · Day1-4 01:00:18–01:05:21 · Day1-5 00:00–00:50 · PDF pp.89–101 · Ch2 셀 4–7

07 · Attention 이론

사과의 뜻을 문맥으로 정합니다.

강사는 이 장을 가장 중요한 챕터 중 하나로 설명했습니다. 식을 외우기 전에 각 계산이 하는 일을 연결합니다.

1. 같은 “사과”인데 왜 벡터가 달라져야 하나요?

강의 예시에서는 오렌지와 함께 있는 애플은 과일을, 폰과 함께 있는 애플은 브랜드를 가리킵니다. Attention은 주변 토큰의 정보를 가져와 현재 토큰의 문맥 표현을 만듭니다.

2. Q·K·V는 역할이 다릅니다.

역할사과 예시로 이해하기실제 코드
Q · Query현재 사과가 어떤 주변 정보에 관심을 둘지 표현합니다.queries = self.W_query(x)
K · Key주변 토큰이 어떤 관련성으로 검색될지 표현합니다.keys = self.W_key(x)
V · Value주변 토큰에서 실제로 가져올 정보를 표현합니다.values = self.W_value(x)

Q·K·V는 사람이 직접 적는 질문·답변 문장이 아닙니다. 같은 입력 x를 서로 다른 학습 가능한 선형 레이어에 넣어 얻는 벡터입니다. V도 학습되는 표현입니다.

3. QK → softmax → V를 곱하는 이유

단계질문얻는 값
Q @ Kᵀ각 토큰이 현재 토큰과 얼마나 관련 있나요?관련성 점수 score
√dₖ로 나누기차원이 커져 점수가 과도하게 커지는 것을 줄일까요?스케일을 조절한 점수
미래 마스킹아직 보면 안 되는 토큰은 무엇인가요?미래 위치가 -∞인 점수
softmax각 정보를 몇 비율로 가져올까요?가중치 weight: 행별 합 1
weights @ V그 비율로 어떤 내용을 섞을까요?문맥 벡터 context vector
Attention(Q, K, V) = softmax(mask(Q @ Kᵀ / √dₖ)) @ V

점수만 있으면 “어디를 얼마나 볼지”만 정한 상태입니다. 실제 내용을 가져오려면 그 비율을 V에 곱하고 합해야 합니다. 그래서 마지막에 V가 필요합니다.

강의의 사과 예시를 직접 계산합니다.


PDF p.106의 강의용 벡터입니다. 실제 모델에서 학습한 임베딩이나 Attention 가중치를 뜻하지 않습니다. 슬라이더는 비율 변화 이해용입니다.

4. “합이 1”은 어디에서 성립하나요?

한 Query에 대한 여러 Key의 softmax 결과를 더하면 1입니다. 모든 Query의 행을 통째로 더해 1이 되는 것은 아닙니다. Attention score의 합이 1인 것도 아닙니다.

5. 마스킹에는 왜 -∞를 쓰나요?

exp(-∞)=0이므로 softmax 후 미래 위치의 가중치가 0이 됩니다. 점수를 단순히 0으로 덮으면 exp(0)=1이므로 미래를 확실하게 가리지 못합니다.

현재 토큰 위치볼 수 있는 토큰가리는 위치
1번1번 자신2번, 3번, 4번
2번1번, 2번3번, 4번
3번1번, 2번, 3번4번
4번1번, 2번, 3번, 4번없음

근거 · Day1-6 00:16–03:20, 04:47–07:05, 13:27–16:52, 18:52–21분대 · PDF pp.104–119

08 · 실습 2

CausalAttention의 forward를 다섯 줄로 읽습니다.

목적은 입력 임베딩을 받아 미래를 보지 않는 문맥 벡터를 만드는 것입니다. 이 실습은 이미 학습한 모델의 답을 복원하는 작업과 다릅니다.

1. Q·K·V 레이어를 준비합니다.

self.W_query = nn.Linear(d_in, d_out, bias=qkv_bias)
self.W_key   = nn.Linear(d_in, d_out, bias=qkv_bias)
self.W_value = nn.Linear(d_in, d_out, bias=qkv_bias)

d_in은 실제 입력 벡터 차원과 같아야 합니다. d_out은 Q·K·V와 출력의 차원입니다. 단독 Attention에서는 두 차원이 같을 필요는 없습니다.

2. forward의 계산 순서를 연결합니다.

keys = self.W_key(x)
queries = self.W_query(x)
values = self.W_value(x)

attn_scores = queries @ keys.transpose(1, 2)
attn_scores.masked_fill_(
    self.mask.bool()[:num_tokens, :num_tokens], -torch.inf
)
attn_weights = torch.softmax(
    attn_scores / keys.shape[-1]**0.5, dim=-1
)
attn_weights = self.dropout(attn_weights)
context_vec = attn_weights @ values
코드shape·의미빈칸에서 헷갈릴 점
Q, K, V 레이어 호출[B,T,d_out]벡터 이름과 레이어 이름을 맞춥니다. keys에는 W_key입니다.
keys.transpose(1,2)[B,d_out,T][T,d_out] @ [d_out,T]를 만들어 점수 [T,T]를 얻습니다.
attn_scores[B,T,T]행은 질문 위치 Q, 열은 참고 대상 K입니다.
keys.shape[-1]**0.5√d_out-1은 마지막 차원입니다. **0.5는 제곱근입니다.
dim=-1각 Query의 Key 방향마지막 축의 여러 Key 점수를 확률분포로 바꿉니다.
attn_weights @ values[B,T,T] @ [B,T,d_out]V를 반영 비율로 섞어 [B,T,d_out]을 만듭니다.

3. 마스크는 상삼각입니다.

torch.triu(torch.ones(context_length, context_length), diagonal=1)

diagonal=1은 대각선 바로 위부터 표시합니다. 현재 토큰 자신은 볼 수 있고, 오른쪽의 미래 토큰은 볼 수 없습니다. [:T, :T]는 실제 입력 길이에 맞게 두 축을 자릅니다.

4. 모듈을 호출하면 forward가 실행됩니다.

batch = torch.stack((inputs, inputs), dim=0)  # [2,6,3]
ca = CausalAttention(3, 2, context_length=6, dropout=0.0)
context_vecs = ca(batch)                       # [2,6,2]

ca(batch)는 위에서 정의한 모듈의 forward 흐름을 실행합니다. 입력 1개를 사용할 때도 배치 차원은 유지합니다.

근거 · Day1-7 00:03–04:54, 07:28 · PDF p.124 · Ch3 셀 1–2

5. 여러 관점은 head, 깊이는 block입니다.

단순 wrapper는 여러 CausalAttention의 결과를 마지막 차원으로 이어 붙입니다. 최적화한 MultiHeadAttention은 큰 Q·K·V를 만든 다음 head 차원으로 나누어 계산합니다.

구조원본 예제최종 shape
Wrapperhead 2개 × 각 출력 2차원[2,6,4]
분할 방식 MultiHeadAttention전체 d_out=2, head 2개 → 각 head_dim=1[2,6,2]
head_dim = d_out // num_heads
# [B,T,d_out] → [B,T,H,head_dim] → [B,H,T,head_dim]
keys = keys.view(B, T, H, head_dim).transpose(1, 2)
# 각 head의 점수: [B,H,T,T]
attn_scores = queries @ keys.transpose(2, 3)
# 계산 후 H와 head_dim을 다시 합쳐 d_out으로 복원합니다.

강의에서 가로를 관점, 세로를 시간에 비유했습니다. 코드에서 head는 같은 블록 안의 병렬 관점이고, block/layer는 표현을 단계적으로 바꾸는 깊이입니다. 이 깊이를 입력 토큰의 시간축 T와 같은 축으로 해석하면 안 됩니다.

근거 · Day1-6 24:43–27:24 · Day1-7 04:54–07:00 · PDF pp.120–123 · Ch3 셀 3–4

09 · GPT 모델 이해

Attention과 FFN을 쌓아 다음 토큰 점수를 만듭니다.

1일차에는 Chapter 4 코드 흐름까지 설명했습니다. 강사는 실습 시간을 다음날 다시 주겠다고 안내했습니다. 아래 문제는 이어서 대비할 수 있게 따로 분리했습니다.

1. 전체 입력과 출력

지점shape하는 일
토큰 ID[B,T]정수 ID를 받습니다.
토큰+위치 임베딩[B,T,D]각 토큰을 D차원 벡터로 표현합니다.
Transformer 블록[B,T,D]Attention과 FFN으로 표현을 바꿉니다.
출력 head[B,T,V]각 위치에서 사전의 V개 토큰에 점수를 줍니다.
마지막 위치 선택[B,V]다음 토큰을 고를 위치만 남깁니다.
argmax + keepdim[B,1]배치별로 다음 토큰 ID 하나를 고릅니다.

2. 블록 안의 역할

구성역할코드 핵심
LayerNorm각 토큰의 벡터 스케일을 안정화합니다.(x-mean) / sqrt(var+eps), 이후 scale·shift
Causal Attention관련 있는 과거·현재 토큰 정보를 모읍니다.미래 차단 → 가중합
FFN각 토큰의 표현을 가공합니다.D → 4D → GELU → D
Residual입력을 결과에 더해 정보·기울기의 전달을 돕습니다.x = x + shortcut
Dropout학습 중 일부 값을 무작위로 0으로 만들어 과적합을 줄입니다.인과 마스크와 목적·위치가 다릅니다.
shortcut = x
x = self.norm1(x)
x = self.att(x)
x = self.drop_shortcut(x)
x = x + shortcut

shortcut = x
x = self.norm2(x)
x = self.ff(x)
x = self.drop_shortcut(x)
x = x + shortcut

원본은 Pre-LayerNorm 구조입니다. Attention 전후와 FFN 전후에도 D차원을 유지하므로 원래 입력을 더하고 블록을 반복해서 쌓을 수 있습니다. 마지막 출력 head에서만 D를 V로 바꿉니다.

근거 · Day1-8 03:18–10:22, 33:30–40:55 · PDF pp.125–139, pp.144–146 · Ch4 셀 3–4

3. 다음 토큰 생성 루프

for _ in range(max_new_tokens):
    idx_cond = idx[:, -context_size:]
    with torch.no_grad():
        logits = model(idx_cond)
    logits = logits[:, -1, :]
    idx_next = torch.argmax(logits, dim=-1, keepdim=True)
    idx = torch.cat((idx, idx_next), dim=1)

현재 문맥의 마지막 부분을 입력합니다. 마지막 위치에서 점수가 가장 높은 ID를 고릅니다. 고른 ID를 문맥 뒤에 붙입니다. 이 과정을 반복해 한 토큰씩 문장을 늘립니다.

4. model.eval()과 no_grad()는 다릅니다.

기능하는 일하지 않는 일
model.eval()Dropout 등 학습·평가 동작이 다른 모듈을 평가 모드로 바꿉니다.그 자체로 기울기 기록이나 파라미터 갱신을 막지 않습니다.
torch.no_grad()이 구간의 자동미분 그래프 기록을 끕니다.Dropout을 평가 모드로 바꾸지 않습니다.
LayerNorm학습·평가 모두 현재 입력의 통계로 정규화합니다.eval()이 scale·shift를 별도로 동결하는 것은 아닙니다.

unsqueeze(0)는 토큰 목록에 배치 차원을 붙여 [T]를 [1,T]로 만듭니다. 출력의 squeeze(0)은 그 배치 차원을 제거하고, ID 목록을 decode하면 텍스트가 됩니다.

근거 · Day1-8 40:55–46:05 · PDF pp.145–149 · Ch4 셀 5–6

학습하지 않은 GPTModel에서도 글이 나오나요?

코드를 실행하면 ID와 문자열이 출력될 수는 있습니다. 하지만 원본의 GPTModel(...) 생성만으로 사전학습 가중치를 가져온 것은 아닙니다. 무작위로 초기화한 모델의 출력을 의미 있는 언어 능력으로 해석하면 안 됩니다.

Temperature와 Top-K는 지금 argmax와 어떤 관계인가요?

현재 코드는 greedy argmax입니다. Temperature는 확률을 만들기 전에 logits를 T로 나눠 분포의 날카로움을 바꿉니다. Top-K는 점수가 높은 K개 후보에서 샘플링하도록 제한합니다. 이 둘은 “항상 가장 높은 ID 하나 선택”과 구분합니다.

근거 · PDF pp.148–149 · Day1-8 출력 생성 설명

10 · 마무리 복습

손으로 세 흐름을 복원하면 준비가 구체적입니다.

암기한 API 이름을 넣는 데서 끝내지 않고, 입력·출력·shape가 왜 맞는지 확인합니다.

흐름말로 설명하기코드로 복원하기
Dataset입력과 정답의 길이는 같고, 정답은 원문에서 한 칸 뒤입니다.range → input slice → target slice → tensor → 같은 idx 쌍
Attention관련성 점수를 반영 비율로 바꾼 뒤 V를 섞습니다.Q/K/V → QKᵀ → -∞ mask → scale/softmax → V
GPT 생성마지막 위치의 다음 ID를 문맥 뒤에 붙입니다.문맥 자르기 → no_grad → 마지막 logits → argmax → cat

시험 직전에 확인할 8개

  1. encode와 decode의 방향을 구분합니다.
  2. 슬라이스 끝값은 포함하지 않습니다.
  3. target의 시작과 끝을 모두 한 칸 옮깁니다.
  4. max_length와 stride, batch_size를 구분합니다.
  5. Q·K 점수의 마지막 축은 참고할 Key 토큰입니다.
  6. 미래 score에는 -torch.inf를 넣습니다.
  7. 입력 임베딩 차원과 출력 사전 차원을 구분합니다.
  8. 주석이 요구하는 결과와 실제 shape를 먼저 확인합니다.

근거 · Day1 마무리 04:17–07:08

↑ 위로