이번 버전은 사용자가 제공한 전체코드 예제 3개를 중심으로 정리했습니다. 단편 빈칸 문제는 기초 연습으로 유지했습니다.
예제 1·2·3의 코드 구조와 주석은 제공한 내용을 유지했습니다. 번호는 화면에서 추가한 학습 표시이며 Python 문법이 아닙니다. self.???(x)처럼 일부만 가려졌다면 그 조각만 채웁니다.
예제
이번에 복원할 부분
학습 구분
MultiHeadAttention
head 축 변경 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, 제거 비율, 임계값을 회고만 보고 정답으로 고정하지 않습니다.
원본 코드 미제공 · 기준·비율 확정 불가
이번 최종 정리의 사용 순서
실전 코드에서 예제 전체를 읽고 13개 빈칸을 복원합니다.
이해가 막히면 이해 노트의 Dataset·Attention·GPT 설명으로 돌아갑니다.
기초 빈칸에서 API 이름·인덱스·shape를 반복합니다.
함수 복원에서 더 큰 조각을 직접 써 봅니다.
원본 없는 회고 항목은 후속 자료와 대조할 목록으로 유지합니다.
현재 실전 코드와 회고 목록에서 검색 결과가 없습니다. 다른 예제를 선택하거나 검색어를 지워 주세요.
1일차는 모델 이해가 중심입니다. 실습에서는 코드의 빈칸과 핵심 함수의 동작을 함께 익힙니다.
구분
이 자료의 표시
학습에 사용하는 방법
시험 안내
원문 확인
강사 또는 운영자가 말한 범위·유형만 표시합니다. 개별 문제의 출제를 보장하지 않습니다.
강조한 내용
강사 강조
“중요하다”, “흐름을 이해하라”라고 한 개념을 자세히 설명합니다.
예상 문제
AI 구성
실제 코드의 ???·주석·핵심 동작으로 만든 연습 문제입니다. 실제 시험 문제가 아닙니다.
먼저 공부할 순서
Dataset에서 입력과 정답을 한 칸 이동시키는 이유를 이해합니다.
Attention에서 관련성 점수 → 반영 비율 → V의 가중합 흐름을 이해합니다.
GPT 전체 흐름에서 임베딩 → 블록 → 출력 → 다음 토큰 연결을 확인합니다.
원본 빈칸을 풀고, 마지막으로 짧은 함수를 직접 복원합니다.
근거 · 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 GPT
1일차에 코드 흐름을 설명했습니다. 실습 시간은 다음날로 안내했습니다. 시험 제외라고 말한 것은 아닙니다. Day1-8 33:30, 46:05.
01 · AI 모델의 역사와 미래
모델 이름보다 해결한 문제를 연결합니다.
이 세션은 뒤의 코드를 이해하기 위한 배경입니다. 아래 항목은 이해용 요약이며, 이론 암기 문제의 출제를 뜻하지 않습니다.
모델·기술
쉽게 이해하기
다음 수업과의 연결
AlexNet
이미지에서 작은 패턴을 여러 층으로 조합합니다.
여러 층을 통과하며 표현을 바꾼다는 딥러닝의 기본입니다.
ResNet
새로 계산한 값에 원래 값을 더해 깊은 신경망의 학습을 돕습니다.
GPT의 residual connection인 x + shortcut과 연결됩니다.
Word2Vec
단어를 숫자 하나가 아니라 여러 숫자의 벡터로 표현합니다.
토큰 ID와 임베딩 벡터를 구분하는 출발점입니다.
AlphaGo
신경망의 판단과 탐색·학습을 결합했습니다.
“모델이 예측한다”와 “학습으로 값을 바꾼다”를 구분합니다.
Transformer
Attention으로 토큰 사이의 관계를 계산합니다.
오늘 실습의 중심입니다.
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 → 입력·정답 쌍 → 배치 → 임베딩입니다. 각 단계가 서로 다른 일을 합니다.
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
Wrapper
head 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개
encode와 decode의 방향을 구분합니다.
슬라이스 끝값은 포함하지 않습니다.
target의 시작과 끝을 모두 한 칸 옮깁니다.
max_length와 stride, batch_size를 구분합니다.
Q·K 점수의 마지막 축은 참고할 Key 토큰입니다.
미래 score에는 -torch.inf를 넣습니다.
입력 임베딩 차원과 출력 사전 차원을 구분합니다.
주석이 요구하는 결과와 실제 shape를 먼저 확인합니다.
근거 · Day1 마무리 04:17–07:08
검색 결과가 없습니다. 다른 단어로 검색하거나 검색어를 지워 주세요.
PRACTICE · 정답을 먼저 보지 않습니다.
기초 조각을 익힌 뒤 전체코드로 돌아갑니다.
이 탭은 기존 API·인덱스 연습을 유지한 보조 문제은행입니다. 사용자 제공 예제의 전체코드 형식은 실전 코드 탭에 따로 있습니다.
각 빈칸에는 코드의 해당 조각만 입력합니다. 원본의 정답을 직접 말하는 힌트는 문제에서 숨겼습니다.
모든 문제는 AI가 구성한 예상 문제
간이 채점은 제시한 빈칸 조각의 일치만 확인합니다. 같은 동작의 다른 Python 표현은 정답·해설과 직접 비교하세요. Python 코드를 실제 실행하지는 않습니다.
FUNCTION · 함수 일부 구현 대비
주석과 출력으로 핵심을 복원합니다.
이 탭은 함수 일부를 직접 쓰는 보조 연습입니다. 제공한 기출형 전체코드의 지정 빈칸 복원과 구분합니다.
마무리 안내의 두 번째 유형에 맞춘 연습입니다. 입력한 코드는 이 HTML에서 실행하지 않습니다. 정답 예시와 체크리스트로 비교한 뒤 실습 환경에서 실행해 보세요.
AI 구성 · 실제 출제 문제 아님
근거 · Day1 마무리 05:21–06:29
정답 코드 전체 흐름 참고
필요한 셀만 펼쳐서 순서를 대조하세요. 정답 브랜치의 코드에서 정답을 직접 유도하는 TODO·힌트 표시는 정리했습니다.
Chapter 2 · 셀 1 · 정답 코드 보기
import importlib
import tiktoken
print("tiktoken version:", importlib.metadata.version("tiktoken"))
tokenizer = tiktoken.get_encoding("gpt2")
text = (
"Hello, do you like tea? <|endoftext|> In the sunlit terraces"
"of someunknownPlace."
)
integers = tokenizer.encode(text, allowed_special={"<|endoftext|>"})
print(integers)
strings = tokenizer.decode(integers)
print(strings)
Chapter 2 · 셀 2 · 정답 코드 보기
import os
import requests
os.makedirs("datas", exist_ok=True)
if not os.path.exists("datas/the-verdict.txt"):
url = (
"https://raw.githubusercontent.com/rasbt/"
"LLMs-from-scratch/main/ch02/01_main-chapter-code/"
"the-verdict.txt"
)
file_path = "datas/the-verdict.txt"
response = requests.get(url, timeout=30)
response.raise_for_status()
with open(file_path, "wb") as f:
f.write(response.content)
with open("datas/the-verdict.txt", "r", encoding="utf-8") as f:
raw_text = f.read()
enc_text = tokenizer.encode(raw_text)
print(len(enc_text))
enc_sample = enc_text[50:]
context_size = 4
x = enc_sample[:context_size]
y = enc_sample[1:context_size+1]
print(f"x: {x}")
print(f"y: {y}")
Chapter 2 · 셀 3 · 정답 코드 보기
### 중요: 이 코드는 GPT 모델 학습을 위한 데이터셋 클래스를 정의합니다
import torch
from torch.utils.data import Dataset, DataLoader
class GPTDatasetV1(Dataset):
def __init__(self, txt, tokenizer, max_length, stride):
"""
Args:
txt (str): 학습할 전체 텍스트 데이터
tokenizer: 텍스트를 토큰 ID로 변환해주는 토크나이저 (예: tiktoken)
max_length (int): 모델이 한 번에 볼 수 있는 윈도우 크기 (입력 시퀀스 길이)
stride (int): 윈도우를 이동시킬 간격 (데이터 중복 정도를 결정)
"""
self.input_ids = []
self.target_ids = []
# 1. 전체 텍스트 토큰화
# 텍스트를 정수 리스트(token_ids)로 변환합니다.
# <|endoftext|> 같은 특수 토큰도 허용하여 인코딩합니다.
token_ids = tokenizer.encode(txt, allowed_special={"<|endoftext|>"})
# 데이터가 너무 짧으면 학습할 수 없으므로 최소 길이를 확인합니다.
assert len(token_ids) > max_length, "토큰화된 입력의 개수는 적어도 max_length+1과 같아야 합니다."
# 2. 슬라이딩 윈도우(Sliding Window)로 데이터 생성
# 전체 토큰 리스트를 훑으며 max_length 길이만큼 잘라냅니다.
# stride만큼 건너뛰며 반복합니다.
for i in range(0, len(token_ids) - max_length, stride):
# 입력 청크: 현재 위치(i)부터 max_length만큼 가져옵니다.
input_chunk = token_ids[i : i + max_length]
# 타겟 청크: 입력보다 1칸 뒤의 위치(i+1)부터 가져옵니다.
# GPT는 '다음 단어'를 맞추는 모델이므로, 정답은 입력보다 한 칸씩 뒤로 밀려있어야 합니다.
target_chunk = token_ids[i + 1 : i + max_length + 1]
# 추출한 데이터를 텐서(Tensor)로 변환하여 리스트에 저장합니다.
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):
# DataLoader가 데이터를 요청할 때 호출됩니다.
# 해당 인덱스(idx)의 입력과 정답 쌍을 반환합니다.
return self.input_ids[idx], self.target_ids[idx]
### 중요: 이 코드는 인과적 어텐션 메커니즘을 구현한 PyTorch 모듈입니다.
import torch
import torch.nn as nn
class CausalAttention(nn.Module):
def __init__(self, d_in, d_out, context_length, dropout, qkv_bias=False):
"""
Args:
d_in: 입력 벡터의 차원 크기
d_out: 출력(및 쿼리/키/밸류) 벡터의 차원 크기
context_length: 모델이 한 번에 처리할 수 있는 최대 문맥 길이 (토큰 수)
dropout: 드롭아웃 확률
qkv_bias: 선형 레이어에 편향(bias)을 사용할지 여부
"""
super().__init__()
# 1. 쿼리(Query), 키(Key), 밸류(Value)를 만들기 위한 선형 투영 레이어 정의
# 입력 벡터(d_in)를 각각의 목적에 맞는 벡터(d_out)로 변환합니다.
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)
# 2. 과적합(Overfitting) 방지를 위한 드롭아웃 설정
self.dropout = nn.Dropout(dropout)
# 3. 인과적 마스크(Causal Mask) 생성 및 버퍼 등록
# 'register_buffer'를 사용하면 역전파(학습) 대상은 아니지만, 모델의 상태(state_dict)로 저장됩니다.
# torch.triu(..., diagonal=1): 대각선(0) 위쪽 삼각형 부분만 1로 채웁니다.
# 즉, '미래의 정보' 위치에 1을 표시하여 나중에 가릴(masking) 준비를 합니다.
self.register_buffer(
'mask',
torch.triu(torch.ones(context_length, context_length), diagonal=1)
)
def forward(self, x):
# x.shape: [배치 크기(b), 토큰 개수(num_tokens), 입력 차원(d_in)]
b, num_tokens, d_in = x.shape
# 입력 x를 통과시켜 현재 시점의 관심사(Query), 검색 대상(Key), 정보 내용(Value)을 추출합니다.
keys = self.W_key(x) # Shape: [b, num_tokens, d_out]
queries = self.W_query(x) # Shape: [b, num_tokens, d_out]
values = self.W_value(x) # Shape: [b, num_tokens, d_out]
# Query와 Key의 내적(Dot Product)을 통해 각 토큰 간의 관련성을 구합니다.
# keys.transpose(1, 2): 행렬 곱을 위해 차원을 뒤집습니다. (d_out 차원끼리 곱해짐)
attn_scores = queries @ keys.transpose(1, 2)
# mask가 1인 위치(미래 시점의 토큰들)를 -무한대(-inf)로 채웁니다.
# 이렇게 하면 나중에 Softmax를 거칠 때 확률이 0이 되어, 미래 정보를 참조하지 못하게 됩니다.
# [:num_tokens, :num_tokens]: 입력 길이가 context_length보다 짧을 때를 대비해 크기를 맞춥니다.
attn_scores.masked_fill_(
self.mask.bool()[:num_tokens, :num_tokens],
-torch.inf
)
# 스케일링(/ keys.shape[-1]**0.5): 차원이 커질수록 내적 값이 커져 기울기 소실이 오는 것을 방지합니다.
# Softmax: 점수를 확률(0~1 사이, 합은 1)로 변환합니다.
attn_weights = torch.softmax(
attn_scores / keys.shape[-1]**0.5, dim=-1
)
# 계산된 가중치 중 일부를 무작위로 0으로 만들어 모델이 특정 토큰에만 의존하는 것을 막습니다.
attn_weights = self.dropout(attn_weights)
# 어텐션 가중치(확률)를 기반으로 Value(정보)들을 가중 합산합니다.
# 결과적으로 "현재 토큰과 관련이 깊은 과거 토큰들의 정보"가 진하게 섞인 벡터가 됩니다.
context_vec = attn_weights @ values
return context_vec
Chapter 3 · 셀 2 · 정답 코드 보기
import torch
inputs = torch.tensor(
[[0.43, 0.15, 0.89], # Your (x^1)
[0.55, 0.87, 0.66], # journey (x^2)
[0.57, 0.85, 0.64], # starts (x^3)
[0.22, 0.58, 0.33], # with (x^4)
[0.77, 0.25, 0.10], # one (x^5)
[0.05, 0.80, 0.55]] # step (x^6)
)
d_in = inputs.shape[1] # 입력 차원 (d=3)
d_out = 2 # Q, K, V의 출력 차원 (d=2)
batch = torch.stack((inputs, inputs), dim=0)
# --- 실행 예시 ---
torch.manual_seed(123)
# 가정: batch 변수가 이미 정의되어 있다고 가정 (예: b=2, num_tokens=6, d_in=...)
# context_length는 모델이 허용하는 최대 길이이므로, 현재 배치의 길이와 같거나 더 길게 설정합니다.
context_length = batch.shape[1]
print("context_length:", context_length)
ca = CausalAttention(d_in, d_out, context_length, 0.0)
context_vecs = ca(batch)
print(context_vecs)
print("context_vecs.shape:", context_vecs.shape)
Chapter 3 · 셀 3 · 정답 코드 보기
class MultiHeadAttentionWrapper(nn.Module):
def __init__(self, d_in, d_out, context_length, dropout, num_heads, qkv_bias=False):
super().__init__()
# 1. d_out이 num_heads로 나누어떨어지는지 확인
assert d_out % num_heads == 0, "d_out must be divisible by num_heads"
self.head_dim = d_out // num_heads # 각 헤드가 담당할 차원 (2 / 2 = 1)
# 2. 각 헤드의 출력 차원을 head_dim으로 설정
self.heads = nn.ModuleList(
[CausalAttention(d_in, self.head_dim, context_length, dropout, qkv_bias)
for _ in range(num_heads)]
)
# 3. 최종 출력 차원은 d_out과 일치하도록 설정
self.out_proj = nn.Linear(d_out, d_out)
def forward(self, x):
# 각 헤드의 결과(2, 6, 1)를 합치면 (2, 6, 2)가 됨
context_vec = torch.cat([head(x) for head in self.heads], dim=-1)
return self.out_proj(context_vec)
torch.manual_seed(123)
context_length = batch.shape[1] # This is the number of tokens
d_in, d_out = 3, 2
mha = MultiHeadAttentionWrapper(
d_in, d_out, context_length, 0.0, num_heads=2
)
context_vecs = mha(batch)
print(context_vecs)
print("context_vecs.shape:", context_vecs.shape)
Chapter 3 · 셀 4 · 정답 코드 보기
class MultiHeadAttention(nn.Module):
def __init__(self, d_in, d_out, context_length, dropout, num_heads, qkv_bias=False):
super().__init__()
assert (d_out % num_heads == 0), \
"d_out must be divisible by num_heads"
self.d_out = d_out
self.num_heads = num_heads
self.head_dim = d_out // num_heads # Reduce the projection dim to match desired output dim
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)
self.out_proj = nn.Linear(d_out, d_out) # Linear layer to combine head outputs
self.dropout = nn.Dropout(dropout)
self.register_buffer(
"mask",
torch.triu(torch.ones(context_length, context_length),
diagonal=1)
)
def forward(self, x):
b, num_tokens, d_in = x.shape
# As in `CausalAttention`, for inputs where `num_tokens` exceeds `context_length`,
# this will result in errors in the mask creation further below.
# In practice, this is not a problem since the LLM (chapters 4-7) ensures that inputs
# do not exceed `context_length` before reaching this forward method.
keys = self.W_key(x) # Shape: (b, num_tokens, d_out)
queries = self.W_query(x)
values = self.W_value(x)
# We implicitly split the matrix by adding a `num_heads` dimension
# Unroll last dim: (b, num_tokens, d_out) -> (b, num_tokens, num_heads, head_dim)
keys = keys.view(b, num_tokens, self.num_heads, self.head_dim)
values = values.view(b, num_tokens, self.num_heads, self.head_dim)
queries = queries.view(b, num_tokens, self.num_heads, self.head_dim)
# Transpose: (b, num_tokens, num_heads, head_dim) -> (b, num_heads, num_tokens, head_dim)
keys = keys.transpose(1, 2)
queries = queries.transpose(1, 2)
values = values.transpose(1, 2)
# Compute scaled dot-product attention (aka self-attention) with a causal mask
attn_scores = queries @ keys.transpose(2, 3) # Dot product for each head
# Original mask truncated to the number of tokens and converted to boolean
mask_bool = self.mask.bool()[:num_tokens, :num_tokens]
# Use the mask to fill attention scores
attn_scores.masked_fill_(mask_bool, -torch.inf)
attn_weights = torch.softmax(attn_scores / keys.shape[-1]**0.5, dim=-1)
attn_weights = self.dropout(attn_weights)
# Shape: (b, num_tokens, num_heads, head_dim)
context_vec = (attn_weights @ values).transpose(1, 2)
# Combine heads, where self.d_out = self.num_heads * self.head_dim
context_vec = context_vec.contiguous().view(b, num_tokens, self.d_out)
context_vec = self.out_proj(context_vec) # optional projection
return context_vec
torch.manual_seed(123)
batch_size, context_length, d_in = batch.shape
d_out = 2
mha = MultiHeadAttention(d_in, d_out, context_length, 0.0, num_heads=2)
context_vecs = mha(batch)
print(context_vecs)
print("context_vecs.shape:", context_vecs.shape)
Chapter 4 · 셀 3 · 정답 코드 보기
#####################################
# Chapter 4: GPT 아키텍처 구성 요소
#####################################
class LayerNorm(nn.Module):
"""
층 정규화 (Layer Normalization): 학습 안정성을 높임
"""
def __init__(self, emb_dim):
super().__init__()
self.eps = 1e-5
self.scale = nn.Parameter(torch.ones(emb_dim)) # 학습 가능한 스케일 파라미터 (Gamma)
self.shift = nn.Parameter(torch.zeros(emb_dim)) # 학습 가능한 시프트 파라미터 (Beta)
def forward(self, x):
mean = x.mean(dim=-1, keepdim=True)
var = x.var(dim=-1, keepdim=True, unbiased=False)
norm_x = (x - mean) / torch.sqrt(var + self.eps)
return self.scale * norm_x + self.shift
class GELU(nn.Module):
"""
GELU 활성화 함수: GPT 계열에서 주로 사용하는 비선형 함수
"""
def __init__(self):
super().__init__()
def forward(self, x):
return 0.5 * x * (1 + torch.tanh(
torch.sqrt(torch.tensor(2.0 / torch.pi)) *
(x + 0.044715 * torch.pow(x, 3))
))
class FeedForward(nn.Module):
"""
피드 포워드 네트워크 (Feed-Forward Network)
어텐션이 모은 정보를 각 토큰별로 개별적으로 가공하는 역할
보통 임베딩 차원을 4배로 늘렸다가 다시 줄임
"""
def __init__(self, cfg):
super().__init__()
self.layers = nn.Sequential(
nn.Linear(cfg["emb_dim"], 4 * cfg["emb_dim"]),
GELU(),
nn.Linear(4 * cfg["emb_dim"], cfg["emb_dim"]),
)
def forward(self, x):
return self.layers(x)
Chapter 4 · 셀 4 · 정답 코드 보기
##중요 : 아래는 GPT 모델 전체 구조를 정의하는 코드입니다
#####################################
# Chapter 4: GPT 아키텍처 조립
#####################################
class TransformerBlock(nn.Module):
"""
표준 트랜스포머 블록 (Decoder Block)
구조: LayerNorm -> Attention -> Add(Residual) -> LayerNorm -> FeedForward -> Add(Residual)
"""
def __init__(self, cfg):
super().__init__()
self.att = MultiHeadAttention(
d_in=cfg["emb_dim"],
d_out=cfg["emb_dim"],
context_length=cfg["context_length"],
num_heads=cfg["n_heads"],
dropout=cfg["drop_rate"],
qkv_bias=cfg["qkv_bias"])
self.ff = FeedForward(cfg)
self.norm1 = LayerNorm(cfg["emb_dim"])
self.norm2 = LayerNorm(cfg["emb_dim"])
self.drop_shortcut = nn.Dropout(cfg["drop_rate"])
def forward(self, x):
shortcut = x
x = self.norm1(x) # Pre-LayerNorm 방식
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 # 원본 입력 다시 더함
return x
class GPTModel(nn.Module):
"""
전체 GPT 모델 구조 정의
Embedding -> Transformer Blocks -> Final Norm -> Output Head
"""
def __init__(self, cfg):
super().__init__()
# 토큰 임베딩 (단어 -> 벡터)
self.tok_emb = nn.Embedding(cfg["vocab_size"], cfg["emb_dim"])
# 위치 임베딩 (위치 정보 -> 벡터)
self.pos_emb = nn.Embedding(cfg["context_length"], cfg["emb_dim"])
self.drop_emb = nn.Dropout(cfg["drop_rate"])
# 트랜스포머 블록 쌓기 (n_layers 만큼)
self.trf_blocks = nn.Sequential(
*[TransformerBlock(cfg) for _ in range(cfg["n_layers"])])
# 최종 정규화 및 출력 헤드
self.final_norm = LayerNorm(cfg["emb_dim"])
self.out_head = nn.Linear(cfg["emb_dim"], cfg["vocab_size"], bias=False)
def forward(self, in_idx):
batch_size, seq_len = in_idx.shape
tok_embeds = self.tok_emb(in_idx)
pos_embeds = self.pos_emb(torch.arange(seq_len, device=in_idx.device))
x = tok_embeds + pos_embeds
x = self.drop_emb(x)
x = self.trf_blocks(x)
x = self.final_norm(x)
logits = self.out_head(x) # 각 단어에 대한 예측 점수 (Logits)
return logits
Chapter 4 · 셀 5 · 정답 코드 보기
def generate_text_simple(model, idx, max_new_tokens, context_size):
"""
간단한 텍스트 생성 루프
현재 문맥을 넣어 다음 토큰을 예측하고, 이를 다시 문맥에 추가하여 반복함
"""
# idx: 현재 문맥의 토큰 인덱스들 (Batch, Time)
for _ in range(max_new_tokens):
# 모델이 지원하는 최대 길이(context_size)를 넘지 않도록 자름
idx_cond = idx[:, -context_size:]
# 모델 예측 (기울기 계산 불필요)
with torch.no_grad():
logits = model(idx_cond)
# 마지막 타임스텝의 예측값만 가져옴 (다음 단어 예측이므로)
# (batch, n_token, vocab_size) -> (batch, vocab_size)
logits = logits[:, -1, :]
# 가장 확률(로짓값)이 높은 토큰 선택 (Greedy Decoding)
idx_next = torch.argmax(logits, dim=-1, keepdim=True)
# 예측된 토큰을 현재 시퀀스 뒤에 이어 붙임
idx = torch.cat((idx, idx_next), dim=1)
return idx
EVIDENCE · 새 버전의 작성 기준
무엇을 근거로 만들었나요?
본문은 새 전사문 10개를 수업 순서대로 재구성했습니다. 오디오를 새로 전사한 결과가 아니라, 제공된 전사문을 실제 코드·슬라이드와 대조한 학습 노트입니다.
새 제목
기존 제목
확인 결과
Day1-1 AI 모델의 역사 및 미래
1교시
SHA256 본문 일치
Day1-2 AI 기본 수학
2교시 수학
SHA256 본문 일치
Day1-4 실습 및 언어 모델 입력
1일차 오후실습1
SHA256 본문 일치
Day1-6 Attention
3번 이론
SHA256 본문 일치
Day1-7 Attention 실습
오후 3 실습
SHA256 본문 일치
Day1-8 gpt 모델 이해
1일차 마지막
SHA256 본문 일치
Day1 오티 / Day1-3 언어 모델의 발전 / Day1-5 간단 질의응답 / Day1 마무리
기존 6종과 별도
이번 버전에 추가 반영한 세션 4개
출제 포인트 사진과 연결한 내용
제공한 출제 포인트의 LLM 항목은 ①주요 layer의 의미·동작·구성·활용 ②입력부터 출력까지 Tensor의 타입·값 흐름 ③모델이 무엇을 학습하는지 ④학습·평가 의도에 맞는 데이터 구성입니다. 이 자료에서는 이를 Attention·FFN 역할, shape 표, 다음 토큰 목표, Dataset의 입력·정답 구성으로 연결했습니다.
근거 · 첨부 Scan_1793591851138.jpg의 LLM 항목. 다른 분야의 출제 항목을 Day1 범위로 확대하지 않았습니다.
근거를 읽는 방법
타임스탬프는 각 파일 안의 상대 시간입니다. PDF의 p.번호는 첨부 PDF의 물리 페이지입니다. 셀 번호는 노트북의 첫 셀을 1로 센 번호이며 마크다운 셀도 포함합니다.
출처
사용한 범위
전사문
Day1 오티, Day1-1~8, Day1 마무리 총 10개
강의 PDF
app_sp_slides_20261013(1).pdf의 1일차 내용. 전체 PDF 뒤쪽 내용을 시험 범위로 확대하지 않았습니다.
Chapter 2·3·4의 실행코드 ??? 56개. Ch2 16개, Ch3 21개, Ch4 19개. 같은 뜻의 반복 빈칸도 포함합니다.
제외한 범위
Chapter 1 시험 문제, Ch2 마지막 모델별 Tokenizer 비교, 별도 Attention 맵 시각화 노트북의 시험 문제
코드 대조 시점: main f5c80b0dd5083cf8491a7a0a9ff16d846e356e09 / exercise_answer 635727242e26cd15143f4ec1ed8eab27c301671e. 연결한 브랜치는 이후 갱신될 수 있습니다.
v3에서 추가로 대조한 내용
사용자 제공 MultiHeadAttention·GPTModel·PreferenceDataset/custom_collate_fn 전체코드를 보존한 실전형 예제 3개를 추가했습니다. 총 13개 빈칸입니다. GPTDatasetV1 전체코드의 4개 빈칸은 우리가 선정한 추가 연습입니다.
PreferenceDataset은 Chapter 7 DPO 정답 브랜치의 템플릿·마스크와 뒤의 compute_logprobs를 함께 대조했습니다. 이 추가 자료가 1일차 강사의 확정 시험 범위라는 뜻은 아닙니다.
기출 회고 목록에는 원본이 없는 항목이 있습니다. 이 목록의 CRAG·시계열·DETR·Pruning 및 다른 DPO 예제는 API·모델명·손실식·비율·템플릿의 정답을 새로 만들어 넣지 않았습니다.
실제 코드를 기준으로 보완한 부분
전사의 “코사인/코젤 어텐션”은 이 코드에서는 CausalAttention입니다.
QKV, softmax, dropout, register_buffer 등 음성 인식 오타를 코드의 이름으로 통일했습니다.
인과 마스크는 대각선 위쪽입니다. 자기 자신과 과거를 남깁니다.
softmax 행 합 1과 dropout 이후의 행 합을 구분했습니다.
LayerNorm은 표준편차로 나눕니다. 분산으로 바로 나누지 않습니다.
eval()은 그 자체로 파라미터나 기울기 기록을 동결하지 않습니다.
head의 병렬 관점, 블록의 깊이, 입력 토큰의 시간축을 구분했습니다.
단순 내적에서 자기 자신과의 내적이 항상 다른 모든 벡터와의 내적보다 큰 것은 아닙니다. 이 자료는 이를 일반 법칙으로 외우지 않도록 설명했습니다.
작성일: 2026-11-02 · 새 버전 v3 · 설명용 숫자와 AI 예상 문제는 실제 시험 문제를 뜻하지 않습니다.