본문으로 건너뛰기
5분 AI.
GITHUB TRENDING JUPYTER NOTEBOOK DAILY #3 딥다이브 · 2026-10-23 분석

happy-llm 딥다이브
— 토큰 하나의 예측에서 도구를 배우는 AI까지

문서·실습자료

빠른 판단

무엇
토큰화·Transformer 구현·학습·RAG·에이전트·강화학습을 연결한 중국어 교재와 Python 실습 모음
이럴 때
작은 LLM의 데이터·손실·학습 원리를 익힌 뒤 검색·도구 호출·보상 학습까지 순서대로 확장할 때
LLM에게 질문하는 법에서 한 걸음 더 들어가, 모델이 문장을 배우고 답을 만드는 과정을 따라가는 한국어 공부자료입니다. Datawhale의 happy-llm은 NLP 기초부터 Transformer 직접 구현, 사전학습·미세조정, RAG·Agent, 강화학습까지 이어지는 8개 장의 교육 저장소입니다. Python 49개 파일 약 11,446줄을 중심으로 실제 데이터와 손실의 흐름을 읽습니다. 저장소 ⭐34,523 · 포크 3,267 · 주 언어 Jupyter Notebook · 2026-10-23 GitHub 기준. 코드 설명은 커밋 1e0b3a1 기준입니다.
목차
  1. 프로젝트 한줄 요약
  2. 왜 주목받는가
  3. 기술 스택 전체 지도
  4. 아키텍처 심화 분석
  5. 디렉토리 구조 해부
  6. 학습 포인트 (기술별)
  7. 하드웨어 / 시스템 요구사항
  8. 직접 해볼 수 있는 실습 과제
  9. 관련 기술 심화 학습 로드맵
  10. 핵심 키워드 사전
  11. 참고 링크

1프로젝트 한줄 요약

문서와 작은 구현을 함께 읽는 LLM 제작 교재

happy-llm은 텍스트를 숫자로 바꾸는 순간부터, 그 숫자로 다음 단어를 예측하고, 학습한 모델에 검색과 도구를 붙이는 순간까지 연결하는 공개 교육 자료입니다. 중국어 원문 교재와 Python 실습 코드가 함께 있으며, 영어 README도 있습니다.

이 저장소의 학습 목표는 모델을 호출하는 데서 멈추지 않고, 어떤 데이터가 어떤 손실을 통해 모델을 바꾸는지 설명하는 것입니다.

5장의 k_model.py에는 Attention, RoPE, RMSNorm, MLP, 생성 루프가 직접 구현되어 있습니다. 6장에서는 같은 작업을 Trainer와 DeepSpeed에 맡깁니다. 두 구현을 비교하면 학습 프레임워크가 대신 해 주는 일을 구체적으로 이해할 수 있습니다.

용어
언어 모델과 다음 토큰 예측
문장을 작은 조각인 토큰으로 나눈 뒤, 앞부분을 보고 다음 조각의 확률을 계산하는 모델입니다. 정답 토큰에 낮은 확률을 줬다면 손실이 커지고, 그 손실을 줄이는 방향으로 내부 숫자인 파라미터를 수정합니다.
전체를 관통하는 비유

자동차 운전법에서 출발해, 엔진과 정비 기록까지 들여다보는 수업

API 사용은 운전대를 잡는 일과 닮았습니다. 모델 구현은 엔진을 분해하는 일이고, 학습 데이터와 손실은 엔진을 어떻게 조정할지 정하는 측정 장치입니다.

RAG는 필요한 지도를 찾아 조수석에 펼치는 일, Agent는 내비게이션이나 계산기 같은 도구를 직접 쓰는 일입니다. 강화학습은 여러 주행 결과를 비교해 더 나은 선택을 익히는 과정에 해당합니다.

이 글을 읽고 답할 수 있어야 할 질문

읽기 목표
처음부터 215M 모델 전체를 훈련할 필요는 없습니다

먼저 토큰 ID 몇 개를 종이에 적고 입력·정답·마스크를 만들어 보세요. 그다음 한 개의 Attention 블록, 마지막으로 학습 루프를 읽으면 됩니다. 작은 배열에서 설명한 규칙이 큰 모델에서도 유지되는지 확인하는 것이 첫 성과입니다.

주요 출발점: 5장 모델 구현, 5장 데이터셋 구현.

2왜 주목받는가

원리·학습·응용을 같은 학습 경로에서 연결한다

2026-10-23 GitHub Trending의 Jupyter Notebook 일간 3위로 선정되었고, GitHub 기준 스타는 34,523개입니다. 이 순위는 관심의 신호이며, 특정 날짜의 상승 원인이나 교육 효과까지 입증하는 수치는 아닙니다. 관심을 끌 만한 이유는 교육 내용과 소스의 연결에서 찾을 수 있습니다.

2-1. 공식을 읽고 바로 구현을 찾을 수 있다

Attention을 공부했다면 2장의 docs/chapter2/code/transformer.py로, LLaMA 계열의 Decoder를 공부했다면 5장의 k_model.py로 이동할 수 있습니다. 이름만 소개하는 대신 정규화, 위치 정보, 가중치 공유와 같은 부품을 실제 코드에서 만납니다.

README는 LLaMA2를 직접 구현한다고 소개합니다. 소스에서는 모델 이름이 Tiny-K인 교육용 구현을 확인할 수 있습니다. 공식 LLaMA2 가중치를 재현했다는 뜻으로 읽기보다, LLaMA 계열의 핵심 구조를 작은 모델로 학습한다고 이해하는 편이 정확합니다.

2-2. 모델을 만든 다음에 무엇을 하는지도 다룬다

5장은 모델과 학습 루프, 6장은 프레임워크를 사용하는 학습, 7장은 RAG와 도구 호출, 8장은 GRPO·OPD·Search-R1·ReTool을 다룹니다. 특히 8장은 도구를 사용하는 행동이 어떻게 학습 데이터가 되는지까지 연결합니다.

각 장의 구현을 비교하면, 가중치를 바꾸는 학습과 입력에 정보를 더하는 응용의 차이가 선명해집니다.

비교 대상주된 학습 초점happy-llm의 상대적 장점과 대가
nanoGPTGPT 구현과 간결한 학습 루프NLP 기초부터 RAG·Agent·강화학습까지 넓게 연결합니다. 대신 한 가지 학습 코드만 집중해서 읽기에는 범위가 큽니다.
Transformers 문서모델·토크나이저·Trainer의 API와 사용법직접 구현과 프레임워크 사용을 장별로 대조할 수 있습니다. API의 정확한 호환성은 해당 버전의 공식 문서로 보완해야 합니다.
self-llmREADME가 소개하는 기존 모델 활용 학습 경로happy-llm은 그 다음 단계인 구조와 학습 원리 이해를 목표로 내세웁니다. 이 관계는 happy-llm README의 설명 기준입니다.
API 중심 입문 예제프롬프트와 응답 처리데이터셋·손실·가중치 갱신까지 배웁니다. 텐서와 미분의 기초 공부가 추가로 필요합니다.

비교 참고 자료: nanoGPT 공식 저장소, Transformers Trainer 공식 안내, happy-llm README.

2-3. 넓은 교재와 완제품은 평가 기준이 다르다

기대 조정
별이 많다고 모든 예제가 바로 실행되는 것은 아닙니다

중앙 패키지와 통합 실행 명령이 없고, 장별 의존성이 서로 다릅니다. 일부 경로와 모델명은 예시 값이며, 6장에는 본문과 스크립트를 대조해야 할 부분도 있습니다. 이 저장소의 강점은 읽고 수정하며 배우는 재료가 풍부하다는 데 있습니다.

3기술 스택 전체 지도

백엔드 역할의 Python, 작은 웹 UI, 장별 실행 환경

일반 웹 서비스의 백엔드·프론트엔드 분리와는 구성이 다릅니다. 계산과 데이터 처리는 Python 스크립트가 맡고, 교재는 정적 문서로 제공됩니다. 사용자와 대화하는 웹 화면은 7장 Agent의 Streamlit 예제에 한정됩니다.

3-1. 백엔드·모델·데이터 처리

위치실제 의존성 선언담당하는 일
2장torch==2.7.0
transformers==4.52.4
tokenizers==0.21.1
Transformer 기초 구현과 텐서 연산. numpy==2.2.6도 고정합니다.
5장torch==2.4.0
transformers==4.44.0
datasets==2.16.1
직접 만든 모델, Tokenizer, 사전학습·SFT. numpy==1.26.4, SwanLab은 버전 미지정입니다.
6장torchdata==0.9.0
transformers, torch, deepspeed
Trainer와 분산 학습. 세 패키지는 버전 미지정이며 datasets·swanlab도 마찬가지입니다.
7장 RAGopenai==1.88.0
numpy==2.3.0
tiktoken==0.9.0
호환 API 호출, 코사인 유사도, 문서 길이 계산. python-dotenv==1.1.0으로 환경변수를 읽습니다.
8장pytrio==0.2.6
swanlab==0.9.2
torch>=2.9.1
원격 LoRA 학습과 실험 기록. datasets>=5.0.0, numpy>=2.5.1은 하한만 지정합니다.

출처는 각각 docs/chapter2/code/requirements.txt, docs/chapter5/code/requirements.txt, docs/chapter6/code/requirements.txt, docs/chapter7/RAG/requirements.txt, docs/chapter8/requirements.txt입니다. 표의 숫자는 설치 결과가 아니라 커밋 1e0b3a1에 기록된 의존성 선언입니다.

용어
정확한 버전 고정과 하한 지정
==는 특정 버전을 요구하고, >=는 그 버전 이상을 허용합니다. 이름만 적은 항목은 버전 선택을 설치 시점에 맡깁니다. 모든 간접 의존성까지 고정하는 잠금 파일과는 재현성 수준이 다릅니다.

3-2. 프론트엔드·교재 화면

구성실제 파일·버전읽을 지점
Agent 웹 화면docs/chapter7/Agent/web_demo.py
streamlit==1.46.0
st.chat_input, st.chat_message, st.session_state로 대화 UI를 만듭니다.
교재 웹사이트docs/index.html
Docsify @latest
Markdown을 브라우저에서 읽는 문서 사이트입니다. Node 프론트엔드 빌드가 중심인 프로젝트는 아닙니다.
수식·그림 보조Mermaid 8.0.0-rc.8
KaTeX @latest
문서의 다이어그램·수식 표시를 돕습니다. 사이트 의존성 일부도 움직이는 버전을 참조합니다.

7장 Agent 의존성에는 RAG와 같은 openai==1.88.0, numpy==2.3.0 외에 wikipedia==1.4.0이 있습니다. React·Vue 앱이나 FastAPI 서비스의 구성으로 해석하면 실제 구조와 어긋납니다.

3-3. 인프라·실행·배포 구성

공통 환경 하나를 만드는 것보다, 배우려는 장의 의존성과 실행 전제를 먼저 확인하는 것이 재현의 출발점입니다.

환경 안내는 2~7장에 Python 3.10·3.11을 권장하고, 8장에는 Python 3.13을 사용한다고 명시합니다. 6장 본문이 설명하는 peft는 6장 의존성 파일에 없습니다. 본문의 LoRA 예제를 따라갈 때 추가 의존성 확인이 필요합니다.

근거: 장별 문서·의존성, 문서 사이트 구성.

4아키텍처 심화 분석

하나의 서버 구조보다, 데이터와 학습 신호가 이동하는 경로를 본다

각 장은 서로 다른 실행 예제입니다. 5장에서 만든 체크포인트가 7·8장에 자동으로 연결되는 통합 제품은 아닙니다. 아래 구조도에서 실선은 각 예제 안의 흐름이고, 장 사이의 연결은 공부 순서를 뜻합니다.

[1~4장: NLP / Attention / 모델 계열 / 학습 원리]
                         |
                         v
[5장: 직접 구현]     JSONL 문장 / 대화
                         |
                 Tokenizer + Dataset
                         |
               X / Y / loss_mask 생성
                         |
               Transformer.forward()
                         |
           토큰별 손실 --> 역전파 --> Adam(사전학습) / AdamW(SFT)
                         |
                    .pth 체크포인트

[6장: 프레임워크]    데이터 --> Trainer --> DeepSpeed ZeRO-2

[7장: 응용 예제]     문서 --> 임베딩 --> 검색 --> LLM 답변
                    질문 --> 도구 선택 --> Python 함수 --> 답변

[8장: Agentic RL]    로컬: 문제 / 도구 환경 / 보상 / 궤적 정렬
                         |                       ^
                         v                       |
                    PyTRIO 원격 학습·샘플링 서비스
                    LoRA / forward_backward / optim_step

4-1. 설계 ① 작은 모듈 조합과 설정 분리

docs/chapter5/code/k_model.py는 ModelConfig → Transformer → DecoderLayer → Attention·MLP 구조입니다. ModelConfig는 Transformers의 PretrainedConfig를, Transformer는 PreTrainedModel을 상속합니다. 직접 구현한 내부 연산을 기존 생태계의 설정·출력 형식에 연결하는 설계입니다.

설정ModelConfig 기본값5장 학습 스크립트의 실제 선택
모델 폭dim=768dim=1024로 변경
블록 수n_layers=12n_layers=18로 변경
Attention 머리n_heads=16
n_kv_heads=8
기본값 유지. 16개의 Q 머리가 8개 K/V 머리를 공유
어휘·문맥vocab_size=6144
max_seq_len=512
기본값 유지. Dataset의 X·Y 길이는 한 칸 이동 후 511
기타dropout=0.0
norm_eps=1e-5
기본값 유지. 학습률 등은 별도 argparse 설정
입력 ID [B, T]
    |
    v
Embedding [B, T, 1024]
    |
    +-- 18개 DecoderLayer -----------------------+
    | RMSNorm --> Attention --> 입력과 더하기       |
    | RMSNorm --> 게이트형 MLP --> 다시 더하기      |
    +-------------------------------------------+
    |
최종 RMSNorm --> 출력 투영 --> logits [B, T, 6144]
                                       학습 시

RMSNorm과 잔차 연결: DecoderLayer.forward()는 정규화한 입력을 Attention에 넣은 다음 원래 입력과 더합니다. 이어 MLP에도 같은 구조를 적용합니다. 더하기 경로가 정보를 보존하고, 정규화가 각 블록의 입력 크기를 조절합니다.

# DecoderLayer.forward()의 흐름을 간추린 표현
h = x + attention(attention_norm(x), freqs_cos, freqs_sin)
out = h + feed_forward(ffn_norm(h))

RoPE와 GQA: precompute_freqs_cis()가 회전에 사용할 값을 만들고, apply_rotary_emb()가 Q와 K에 적용합니다. repeat_kv()는 K/V 머리를 Q 머리 수에 맞춥니다. 학습 설정에서 머리 하나의 폭은 1024 / 16 = 64, 반복 수는 16 / 8 = 2입니다.

게이트형 MLP: MLP.forward()는 F.silu(w1(x)) * w3(x)를 w2에 넣습니다. 한 갈래가 변환한 정보에 다른 갈래가 문을 여닫는 형태입니다. multiple_of=64에 맞춘 중간 폭은 이 설정에서 2,752입니다.

가중치 공유: self.tok_embeddings.weight = self.output.weight로 입력 임베딩과 출력층이 같은 파라미터를 사용합니다. 블록과 공유 임베딩의 크기를 합산하면 학습 구성은 215,127,040개, 약 215M 파라미터입니다. 이는 소스의 행렬 크기로 계산한 값입니다.

용어
텐서의 모양: B·T·D
B는 한 번에 넣는 샘플 수, T는 토큰 위치 수, D는 토큰 하나의 표현 폭입니다. [2, 8, 1024]는 문장 2개를 각각 8개 위치, 위치당 숫자 1,024개로 나타낸다는 뜻입니다. 모양을 적어 가며 읽으면 행렬 연산을 추적하기 쉽습니다.

Attention은 PyTorch에 scaled_dot_product_attention이 있으면 그 API를 사용하고, 없으면 점수 행렬·미래 가림·softmax를 직접 계산합니다. 소스의 self.flash는 API 존재 여부로 결정되며 ModelConfig.flash_attn 값을 읽지 않습니다.

따라서 flash_attn=True라는 설정만으로 특정 FlashAttention 커널 사용을 확정할 수 없습니다. 실제 백엔드는 장치와 자료형 등의 조건에도 좌우됩니다. 참고: PyTorch 2.7 SDPA 공식 문서.

생성 쪽도 경계가 명확합니다. Transformer.generate()는 마지막 위치의 점수로 다음 토큰을 선택해 입력 뒤에 붙이고 반복합니다. 모델에 넣는 문맥은 최근 max_seq_len 토큰으로 제한하며, K/V cache는 구현하지 않습니다. 학습용으로 읽기 쉬운 대신 빠른 추론 서버의 최적화는 별도 주제입니다.

소스: k_model.py, ddp_pretrain.py.

4-2. 설계 ② 입력과 학습 책임을 마스크로 분리

PretrainDataset와 SFTDataset은 JSONL 파일의 줄별 바이트 위치를 미리 저장합니다. 샘플을 요청하면 seek()로 해당 줄을 읽습니다. 전체 텍스트를 메모리에 보관하지 않는 대신 초기 전체 스캔과 줄 수에 비례하는 위치 목록은 필요합니다.

두 Dataset 모두 토큰 배열을 만든 다음 X=input_id[:-1], Y=input_id[1:]로 나눕니다. 모델이 현재 위치에서 다음 토큰을 예측하도록 정답을 맞추는 과정입니다.

# 설명용 토큰 ID. 실제 문장에 대한 분절 결과는 아닙니다.
전체 토큰     [3, 21, 52, 9, 4]
X: 입력       [3, 21, 52, 9]
Y: 다음 정답  [21, 52, 9, 4]
                 ^
# X의 첫 위치를 읽고 Y의 첫 위치를 맞힙니다.

SFTDataset.generate_loss_mask()는 <|im_start|>assistant\n의 토큰열 뒤에서 첫 EOS를 찾으면, 답변 시작부터 그 EOS까지 1을 표시합니다. 사용자 질문은 모델이 볼 수 있지만, 질문 토큰은 손실 계산에서 제외합니다.

대화          [사용자 머리말 | 질문 | 답변 머리말 | 답변 | EOS | PAD]
입력에 포함   [     O      |  O   |      O      |  O   |  O  |  O ]
이동 전 mask  [     0      |  0   |      0      |  1   |  1  |  0 ]

주의: 위 표는 한 칸 이동 전입니다. loss_mask도 Y와 함께 한 칸 이동합니다.
      Dataset의 loss_mask와 모델 내부 ignore_index는 별개입니다.

Transformer.forward()는 reduction='none'으로 토큰별 교차 엔트로피를 반환하고, train_epoch()가 마스크를 곱해 평균을 냅니다. 모델 계산과 어떤 위치를 채점할지 정하는 정책이 분리되어 있습니다.

# ddp_pretrain.py / ddp_sft_full.py의 핵심 식
out = model(X, Y)
loss = out.last_loss / args.accumulation_steps
loss_mask = loss_mask.view(-1)
loss = torch.sum(loss * loss_mask) / loss_mask.sum()
비유

시험지의 문제와 학생 답안을 모두 읽되, 점수는 답안에만 매기는 방식입니다. attention_mask는 무엇을 참고할 수 있는지, loss_mask는 무엇을 채점할지를 정합니다. 서로 같은 마스크가 아닙니다.

다만 5장 학습 루프의 호출은 model(X, Y)이며 attention_mask를 넘기지 않습니다. 오른쪽 패딩과 미래 토큰 가림 때문에 유효 위치는 뒤의 패딩을 보지 않지만, 일반적인 양방향 모델·왼쪽 패딩까지 같은 논리를 적용하면 안 됩니다.

코드에서 발견하는 학습 문제
PAD와 EOS가 같은 ID일 때 손실 계산을 점검하세요

동봉된 Tokenizer는 PAD와 EOS를 모두 <|im_end|>에 연결합니다. 모델은 기본적으로 pad_token_id를 ignore_index로 사용합니다. 따라서 Dataset이 EOS 위치의 마스크를 1로 만들어도 모델 내부에서 그 토큰의 손실이 0이 될 수 있습니다. 종료를 배우는 신호와 평균의 분모를 함께 점검할 좋은 사례입니다.

소스: dataset.py, Tokenizer 설정.

4-3. 설계 ③ 검색·모델·도구를 인터페이스로 연결

7장 RAG의 VectorStore는 EmbeddingModel 객체를 받아 벡터를 만들고 질문을 검색합니다. BaseEmbeddings.get_embedding()라는 경계가 있으므로, 임베딩 공급자를 바꿔도 검색 흐름을 유지하는 방식으로 확장할 수 있습니다.

docs = ReadFiles('./data').get_content(600, 150)
        | 문서 조각
        v
vector = VectorStore(docs)
embedding = OpenAIEmbedding()
vector.get_vector(EmbeddingModel=embedding)
        | 문서 벡터 + 원문
        v
vector.persist() --> storage/doecment.json / storage/vectors.json
        |
질문 --> vector.query(question, embedding, k=1)
        --> 검색된 원문 --> OpenAIChat 인스턴스의 chat()

VectorStore.query()는 모든 벡터와 코사인 유사도를 구한 뒤 NumPy로 정렬합니다. 임베딩 차원을 D, 문서 수를 N이라 하면 유사도 계산은 대략 O(ND), 전체 정렬은 O(N log N)입니다. HNSW 같은 근사 검색 인덱스나 전용 벡터 DB를 사용하지 않습니다.

persist()의 파일명은 실제로 doecment.json입니다. 철자가 어색해도 load_vector()가 같은 이름을 사용하므로, 한쪽만 이름을 바꾸면 기존 저장물을 읽지 못합니다.

예제 임베딩 모델은 BAAI/bge-m3, 응답 모델은 Qwen/Qwen2.5-32B-Instruct입니다. OpenAI 클래스는 API 형식의 호환 클라이언트로 쓰이며, 환경변수 OPENAI_BASE_URL이 실제 서비스 주소를 정합니다.

Agent는 도구를 별도 Python 함수로 등록합니다. function_to_json()은 함수 서명과 타입 힌트를 JSON 도구 스키마로 바꾸고, Agent.handle_tool_call()은 함수명을 tool_map에서 찾아 호출합니다. 모델이 함수의 실행 결과를 꾸며 내는 대신, 실제 도구 결과를 메시지에 넣는 구조입니다.

Agent.get_completion()은 첫 응답의 도구 요청들을 실행한 뒤 모델을 한 번 더 호출합니다. 반복 루프는 없으므로 두 번째 응답이 또 도구를 요청하는 복잡한 다단계 작업은 추가 구현이 필요합니다.

소스: VectorBase.py, Agent 핵심 구현, 도구 스키마 변환.

4-4. 설계 ④ 원격 정책 학습과 로컬 환경의 분리

8장에서는 로컬 Python 코드가 문제·보상·도구 결과를 관리하고, PyTRIO 클라이언트로 원격 샘플링과 LoRA 업데이트를 요청합니다. ServiceClient.create_lora_training_client(), forward_backward(), optim_step()이 두 영역의 경계입니다. 서비스 이용 방법은 PyTRIO 공식 빠른 시작을 참고하세요.

GRPO 동기 예제의 run_rollout_group()는 같은 문제에 답을 여러 개 생성합니다. grade_answer()는 마지막 \boxed{...}에서 꺼낸 답과 기준 답을 간단히 정규화한 뒤 문자열이 같으면 1, 아니면 0을 줍니다. 이 코드의 advantage는 보상에서 그룹 평균을 뺀 값입니다. 표준편차로 나누는 형태는 여기에 구현되어 있지 않습니다.

# 이 예제의 그룹 상대 신호를 손으로 계산
보상        = [1, 0, 1, 0]
그룹 평균   = 0.5
advantage   = [0.5, -0.5, 0.5, -0.5]

# 모두 맞거나 모두 틀리면 상대적 차이가 없습니다.
보상        = [1, 1, 1, 1]
advantage   = [0, 0, 0, 0]  # 이 그룹은 학습 데이터에서 건너뜀

OPD는 다른 신호를 만듭니다. completion_teacher_logprobs()가 학생이 실제 생성한 토큰열을 교사에게 채점하게 하고, student_logprob - teacher_logprob의 음수에 계수를 곱해 advantage로 사용합니다. 정답 문자열을 베끼는 학습과 토큰별 확률을 비교하는 학습의 차이를 볼 수 있습니다.

Search-R1과 ReTool은 이를 여러 파일로 나눕니다. protocol.py가 도구 호출 형식, rollout.py가 행동 궤적, reward.py가 채점, train.py가 업데이트를 맡습니다. 검색·코드 실행 같은 환경만 바꾸면서 같은 큰 구조를 유지합니다.

ReTool의 build_datum()는 여러 턴을 이어 붙일 때 이전 궤적이 다음 입력의 접두부인지 검사합니다. 도구가 준 관측 토큰은 입력에 남기되 advantage를 0으로 둡니다. 모델이 직접 생성한 토큰에 학습 책임을 주기 위한 장치입니다.

모델이 참고한 정보와 모델이 직접 선택한 행동을 구분하는 일이, SFT와 Agent 강화학습을 이어 주는 핵심입니다.

구체적으로 ReTool은 훈련 입력 길이 최대 8192토큰, micro-batch당 최대 샘플 32개, 패딩 포함 토큰 예산 64,000을 둡니다. PPO 비율 제한은 0.8과 1.28입니다. 알고리즘뿐 아니라 길이가 다른 궤적을 묶는 비용도 학습할 수 있습니다.

소스: GRPO 동기 예제, OPD 동기 예제, ReTool 학습 데이터 구성.

5디렉토리 구조 해부

문서를 읽을 때와 코드를 읽을 때의 진입점을 구분한다
happy-llm/
|-- README.md / README_en.md     소개와 장별 이동 경로
|-- LICENSE.txt                 CC BY-NC-SA 4.0 본문
|-- docs/
|   |-- index.html              Docsify 문서 사이트
|   |-- _sidebar.md             교재 탐색 메뉴
|   |-- chapter1/               NLP 기초
|   |-- chapter2/code/          transformer.py
|   |-- chapter3/               사전학습 모델의 계열
|   |-- chapter4/               LLM과 학습 전략
|   |-- chapter5/code/
|   |   |-- k_model.py          직접 만든 Decoder 언어 모델
|   |   |-- dataset.py          사전학습·SFT 샘플과 마스크
|   |   |-- train_tokenizer.py  BPE 학습
|   |   |-- tokenizer_k/        어휘·특수 토큰·대화 형식
|   |   |-- ddp_pretrain.py     사전학습 루프
|   |   |-- ddp_sft_full.py     전체 파라미터 SFT
|   |   |-- model_sample.py     생성 예제
|   |   `-- export_model.py     모델 내보내기
|   |-- chapter6/code/          Trainer / DeepSpeed / Notebook
|   |-- chapter7/
|   |   |-- RAG/                문서 분할·벡터·검색·생성
|   |   `-- Agent/              도구 등록·호출·Streamlit UI
|   |-- chapter8/
|   |   |-- grpo/               동기·비동기 그룹 상대 학습
|   |   |-- opd/                동기·비동기 정책 증류
|   |   |-- search-r1/          검색 환경을 사용하는 RL
|   |   `-- retool/             코드 실행 환경을 사용하는 RL
|   `-- images/                 장별 교재 그림
|-- Extra-Chapter/              생성·작은 모델·멀티모달 등 보충 글
|-- images/                     저장소 소개 이미지
`-- .github/ISSUE_TEMPLATE/     오류 제보 양식

5-1. 처음에는 세 파일만 연결해 읽는다

  1. docs/chapter5/code/dataset.py에서 반환값 X, Y, loss_mask를 확인합니다.
  2. docs/chapter5/code/k_model.py의 Transformer.forward()에서 그 값이 무엇을 예측하는지 읽습니다.
  3. docs/chapter5/code/ddp_pretrain.py의 train_epoch()에서 손실이 어떻게 역전파되는지 연결합니다.

파일을 위에서 아래로 모두 읽는 것보다, 한 샘플이 이 세 파일 사이를 어떻게 이동하는지 따라가면 함수의 존재 이유를 이해하기 쉽습니다. 그다음 Attention과 MLP의 내부로 들어가세요.

5-2. 코드 규모를 읽는 올바른 단위

종류파일 수·분량해석
Python49개 · 11,446줄빈 줄·주석을 포함한 개행 수. 핵심 실행 예제의 대부분입니다.
Notebook5개 · 코드 셀 67개코드 셀 원문 약 1,068줄. 출력·메타데이터를 제외한 값입니다.
Markdown28개 · 15,258줄교재 본문·README·보충 글. 설명 자료의 비중이 큽니다.
Shell4개 · 109줄데이터 다운로드와 DeepSpeed 실행 예제입니다.
HTML1개 · 239줄교재 사이트. JavaScript·CSS가 이 파일 안에도 포함됩니다.

집계 범위는 커밋 1e0b3a1의 파일이며 .git은 제외합니다. 전체 214개 파일은 약 31.2MB이고 이미지·Tokenizer 데이터도 포함합니다. Python 줄 수와 Notebook 코드 셀 줄 수는 서로 다른 집계 방식이므로 엄밀한 생산 코드량 비교에 쓰지는 마세요.

8장 Python은 22개 파일·7,537줄로 전체 Python 분량의 약 66%입니다. GitHub의 주 언어 표시는 Jupyter Notebook이지만, 현재 학습 경로의 상당 부분은 독립 Python 파일을 읽는 작업입니다.

5장은 Python 8개·1,937줄, 6장은 Python 4개·543줄입니다. 코드가 짧은 5장으로 학습 루프를 익히고, 파일이 더 잘게 나뉜 8장으로 확장하면 구조를 놓치기 어렵습니다.

6학습 포인트 (기술별)

라이브러리 이름보다, 직접 설명할 수 있는 동작을 늘린다

6-1. Python과 데이터 입출력: 큰 파일을 조금씩 다루기

read_texts_from_jsonl()는 한 줄씩 JSON을 해석하고 text 필드를 yield합니다. 깨진 JSON이나 누락 필드는 보고한 뒤 건너뜁니다. Dataset은 줄 위치를 미리 모은 뒤 샘플 하나씩 읽습니다. 모두 입력 전체를 한 번에 리스트로 들고 있는 방식과 비교하기 좋습니다.

실습 아이디어: JSONL 5줄 중 1줄을 잘못된 JSON으로, 다른 1줄을 text 없는 객체로 만들어 처리 정책을 설계하세요. 잘못된 데이터를 건너뛰면 원래 줄 번호와 학습 샘플 번호가 달라진다는 점도 기록합니다.

6-2. Tokenizer: 텍스트 표현 자체도 설계 대상이다

train_tokenizer.py는 NFKC 정규화, ByteLevel 분절·복원, BPE 학습을 조합합니다. 함수의 기본 vocab_size는 8,192지만, main()은 6,144를 명시해 호출합니다. 동봉된 tokenizer_k/tokenizer.json의 어휘 수도 6,144입니다. 기본 인자와 실제 호출 설정을 구별해야 하는 사례입니다.

실습 아이디어: 한국어 조사, 영문 약어, 숫자, 이모지가 섞인 문장 10개를 준비하고 토큰 수·복원 결과·특수 토큰을 비교하세요. 어휘 수를 줄이면 출력층은 작아지지만 같은 문장의 토큰 수는 늘 수 있습니다.

용어
BPE와 어휘 크기의 절충
BPE는 자주 함께 나오는 작은 조각을 합쳐 토큰 사전을 만듭니다. 사전이 크면 한 토큰이 더 긴 표현을 담을 수 있지만, 임베딩과 출력층의 파라미터가 늘어납니다. 문장 길이와 모델 크기를 함께 결정하는 선택입니다.

6-3. PyTorch: 수식보다 먼저 모양을 보존하기

Attention.forward()에서 Q는 [B,T,16,64], K/V는 [B,T,8,64]가 됩니다. K/V를 반복하고 머리 축을 앞으로 옮겨 Attention을 계산한 뒤 다시 합칩니다. view·transpose·contiguous가 왜 필요한지 한 흐름에서 배울 수 있습니다.

실습 아이디어: B=2, T=4인 경우의 각 중간 모양을 표에 적으세요. 머리 수를 바꿀 때 dim의 나눗셈과 RoPE의 짝수 차원 조건을 함께 확인합니다. 단순히 전체 파라미터 수만 맞춘다고 유효한 설정이 되는 것은 아닙니다.

6-4. 학습 루프: 작은 배치 여러 번을 묶는 이유

5장의 기본값은 batch size 64, 누적 8회, 학습률 2e-4, gradient clipping 1.0입니다. 완전한 배치 기준으로 optimizer 한 번 갱신 전 샘플 512개를 처리합니다. DataParallel에서는 64가 장치마다 별도로 생기는 배치가 아니라 분할되는 전체 배치입니다.

get_lr()는 warmup과 cosine decay를 구현하지만 기본 warmup_iters=0입니다. 기능이 있다고 기본 실행에서도 활성화되는 것은 아닙니다. 또한 epoch 마지막에 누적 횟수를 채우지 못한 나머지를 처리하는 별도 업데이트 분기는 보이지 않습니다.

실습 아이디어: 10개 micro-batch를 4회씩 누적한다고 가정하고 optimizer 갱신 시점을 표시하세요. 끝의 2개를 버릴지, 따로 평균을 조정해 갱신할지 정책을 정해 봅니다.

6-5. Trainer·DeepSpeed: 프레임워크가 대신 관리하는 것

6장 docs/chapter6/code/pretrain.py의 main()은 인자를 dataclass로 파싱하고, 데이터를 토큰화·분할한 뒤 Trainer에 전달합니다. config_name이면 새 가중치를 초기화하고, model_name_or_path이면 기존 가중치를 읽습니다. 같은 모델 구조라도 출발점이 달라지는 분기입니다.

ds_config_zero2.json의 zero_optimization.stage=2는 optimizer 상태와 gradient를 분할하는 단계입니다. 가중치까지 분할하는 ZeRO-3와 다릅니다. 이 설정의 offload_optimizer.device는 none이므로 CPU offload가 켜졌다고 읽으면 안 됩니다. 단계의 의미는 DeepSpeed 공식 ZeRO 안내로 보완하세요.

실습 아이디어: 5장에서 수동으로 하는 작업을 6장에서 누가 맡는지 대응표를 만드세요. 데이터 배치, 역전파, 누적, 저장, 재시작을 각각 찾아보면 추상화가 무엇을 감추는지 보입니다.

6-6. RAG: 검색 결과를 먼저 평가하기

RAG 예제는 ReadFiles.get_content(max_token_len=600, cover_content=150)으로 문서를 나누고, query(k=1) 결과 한 조각을 답변에 넣습니다. 문서 분할은 cl100k_base로 길이를 세지만, 실제 임베딩 모델은 bge-m3입니다. 길이 측정용 Tokenizer와 모델 Tokenizer가 다를 수 있음을 기억해야 합니다.

또한 겹치는 내용을 가져오는 일부 분기는 문자열 뒤쪽을 슬라이스합니다. cover_content=150을 모든 경우에 정확한 150토큰 겹침이라고 단정하지 말고, 반환되는 조각을 확인하세요.

실습 아이디어: 정답 근거 문서를 미리 정한 질문 10개를 만들고, LLM 답변 전에 검색 상위 결과에 근거가 있는지 표시하세요. 검색 실패와 답변 생성 실패를 따로 세면 개선할 부품을 찾기 쉽습니다.

6-7. Agent·강화학습: 도구 실행과 행동 학습을 구분하기

7장의 Agent는 이미 학습된 모델에 도구 목록을 주고 결과를 돌려줍니다. 8장은 생성 궤적을 보상으로 평가해 파라미터를 갱신합니다. 함수 호출이 존재한다는 사실만으로 강화학습을 하고 있다고 볼 수는 없습니다.

실습 아이디어: assistant → tool → assistant 기록을 만들고, 어느 토큰이 모델의 선택이고 어느 토큰이 외부 결과인지 색으로 나눠 보세요. retool/train.py의 build_datum()가 그 경계를 숫자 배열로 보존합니다.

새 기술을 배울 때마다 입력 형식, 출력 형식, 실패 조건을 한 장에 정리하면 라이브러리가 바뀌어도 이해가 남습니다.

7하드웨어 / 시스템 요구사항

읽기·작은 실험·전체 학습의 비용을 따로 계산한다

학습 가능 여부는 모델 가중치 크기만으로 결정되지 않으며, 배치·문맥·gradient·optimizer 상태까지 함께 계산해야 합니다.

학습 범위장치·시스템 전제추가로 필요한 것
교재·소스 읽기일반 PC와 브라우저·편집기모델 다운로드나 GPU 없이 구조를 공부할 수 있습니다.
2장·축소한 5장환경 안내 기준 CPU 또는 GPU작은 차원·배치·문맥으로 시작합니다. CPU는 전체 학습보다 모양과 손실 확인에 적합합니다.
5장 215M 학습환경 안내는 단일 GPU 권장5장 본문은 사전학습 batch 4에서 약 7GB 사용을 보고합니다. 최소 사양 보장은 아니며, 기본 batch 64의 필요량과는 다릅니다.
6장 프레임워크 학습CUDA GPU·DeepSpeed 환경예시 실행 파일은 GPU 2개를 의도한 설정입니다. 경로·GPU 지정·메모리 설정을 조정해야 합니다.
7장 RAG·Agent로컬 CPU와 네트워크임베딩·대화 API 접근, 모델 접근 권한, 문서 보관 공간이 필요합니다.
8장 강화학습환경 안내 기준 로컬 CPU·Python 3.13TRIO 계정·사용 가능 모델·원격 연산 이용량, Search-R1의 검색 연결이 필요합니다.
8장 ReTool 환경POSIX 계열 실행 기능resource·os.killpg를 사용합니다. Windows 기본 환경과 동일하게 취급할 수 없습니다.

7-1. 215M 모델의 메모리를 손으로 추정하기

# 십진 GB 기준의 대략적인 크기. 실행 시 최대 사용량은 아닙니다.
파라미터 수               215,127,040
FP32 가중치               파라미터 수 × 4 bytes ≈ 0.86 GB
BF16 가중치               파라미터 수 × 2 bytes ≈ 0.43 GB
FP32 가중치 + gradient
  + Adam의 두 상태        파라미터 수 × 16 bytes ≈ 3.44 GB

여기에 활성값, 임시 연산 버퍼, CUDA 비용 등이 더해집니다.

5장 학습 스크립트는 기본 FP32 파라미터를 만들고 GPU에서 autocast를 사용합니다. --dtype bfloat16은 docs/chapter5/code/ddp_sft_full.py에서는 autocast 자료형에 반영되지만, docs/chapter5/code/ddp_pretrain.py의 autocast()에는 자료형이 전달되지 않습니다. 이 인자만으로 모든 학습 메모리가 숫자당 2바이트가 된다고 계산하면 안 됩니다. GPU의 BF16 지원 여부도 확인해야 합니다.

메모리가 부족하면 먼저 배치와 문맥 길이를 줄여 원인을 나누세요. gradient accumulation은 작은 배치를 여러 번 처리하는 방법이지만, 파라미터와 optimizer 상태 자체를 작게 만들지는 않습니다.

7-2. 저장 공간과 네트워크

저장소 파일 자체는 약 31.2MB이지만, 학습 데이터와 모델 가중치는 별도입니다. 다운로드·토큰화 캐시·체크포인트가 쌓이면 필요한 디스크는 크게 늘어납니다. 5장의 기본 예시는 seq_monkey_datawhale.jsonl과 BelleGroup_sft.jsonl을 가리킵니다.

RAG의 순수 벡터 저장량은 대략 문서 수 × 차원 × 숫자당 바이트 수입니다. 예를 들어 10,000조각·1,024차원·FP32라고 가정하면 약 41MB입니다. 예제처럼 JSON과 Python 리스트를 사용하면 문자열·객체 비용이 추가되므로 실제 메모리는 이보다 큽니다.

7장 RAG는 OPENAI_API_KEY와 OPENAI_BASE_URL을 읽습니다. CPU에서 화면을 띄운다는 것과 모델 추론도 로컬에서 한다는 것은 다른 조건입니다. 8장은 실험 기록을 꺼도 원격 학습 호출은 남습니다.

7-3. 운영체제와 예시 설정의 경계

5장 장치 선택은 CUDA가 있으면 cuda:0, 없으면 cpu를 택합니다. Apple Silicon의 mps를 자동 선택하는 분기는 없습니다. Mac GPU 활용은 장치 선택과 연산 호환성을 별도로 조정해야 합니다.

6장 Shell 파일은 CUDA_VISIBLE_DEVICES=0,1을 단독 대입하고 다음 명령을 실행합니다. 셸 환경에 따라 이 값이 자식 프로세스에 전달되지 않을 수 있으므로, export 또는 명령 앞의 환경변수 지정으로 의도를 명확하게 만드는 점검이 필요합니다.

ReTool 실행 전제
시간 제한이 있는 자식 프로세스도 강한 보안 격리는 아닙니다

LocalPythonSandbox 클래스의 기본값은 시간 제한 30초, 동시 실행 8개입니다. 긴 표준 출력·오류 출력은 각각 마지막 4,096자를 남기고 잘림 안내를 덧붙이며, CPU 시간도 제한합니다. 그러나 환경변수를 상속하고 파일·네트워크 접근을 차단하는 경계는 없습니다. 모델 생성 코드를 실행하는 실습에는 비밀정보가 없는 일회성 컨테이너·저권한 가상머신 같은 별도 격리가 필요합니다.

근거: ReTool 코드 실행기, 6장 실행 예제. 하드웨어 표는 저장소 환경 안내와 소스의 전제를 정리한 것으로, 장치별 속도나 최소 VRAM을 보증하는 측정표가 아닙니다.

8직접 해볼 수 있는 실습 과제

정답 기준이 있는 다섯 가지 과제

첫 세 과제는 모델 API나 대규모 데이터 없이 시작할 수 있습니다. 아래 코드는 동작 원리를 확인하기 위한 작은 예제입니다. 전체 학습은 장별 환경과 데이터 형식을 먼저 이해한 뒤 진행하세요.

과제 01 · 데이터의 의미

한 칸 이동을 손으로 검증하기 입문

읽을 파일: docs/chapter5/code/dataset.py의 PretrainDataset.__getitem__().

설명용 ID 배열 [3, 11, 22, 4, 4]을 입력·정답으로 나누세요. 마지막 4만 패딩이라고 가정하고, 이동 전 마스크 [1, 1, 1, 1, 0]를 정답과 함께 이동합니다.

tokens = [3, 11, 22, 4, 4]
valid = [1, 1, 1, 1, 0]
x, y = tokens[:-1], tokens[1:]
mask = valid[1:]
print(list(zip(x, y, mask)))
# 기대: [(3, 11, 1), (11, 22, 1), (22, 4, 1), (4, 4, 0)]

제출물: 입력·정답·마스크 대응표와 각 행의 의미 한 문장씩.

완료 기준: 마지막 토큰 ID가 같아도, 한쪽은 실제 EOS이고 다른 쪽은 패딩일 수 있음을 설명합니다. ID만 보고 유효 위치를 구별할 수 있는지도 답하세요.

과제 02 · 텐서 읽기

Attention의 모양 지도를 그리기 입문

읽을 파일: k_model.py의 Attention.forward()와 repeat_kv().

B=2, T=4, dim=128, n_heads=4, n_kv_heads=2인 작은 설정을 가정하세요. 코드 실행 없이 Q·K·V 투영 직후, 머리 분리 후, K/V 반복 후, 전치 후, 출력 합치기 후의 모양을 적습니다.

제출물: 단계별 모양 표와 head_dim=32, n_rep=2를 구한 계산식.

완료 기준: Attention 점수 행렬이 [2, 4, 4, 4]가 되는 이유를 설명하고, 토큰 수가 두 배일 때 점수 행렬 원소 수가 네 배가 됨을 계산합니다. 같은 숫자 4가 머리 수와 토큰 수 양쪽에 쓰였다는 점에 주의하세요.

과제 03 · 검색의 최소 구현

API 없이 벡터 검색 순위 만들기 중급

읽을 파일: docs/chapter7/RAG/VectorBase.py와 Embeddings.py.

문서 3개에 설명용 벡터 [1,0], [0.8,0.2], [0,1]을 대응시키고 질문 벡터 [1,0]과의 코사인 유사도를 계산하세요. 이 벡터들은 실제 임베딩 결과가 아닙니다.

from math import sqrt

def cosine(a, b):
    dot = sum(x * y for x, y in zip(a, b))
    norm = sqrt(sum(x * x for x in a)) * sqrt(sum(y * y for y in b))
    return dot / norm if norm else 0.0

query = [1, 0]
vectors = [[1, 0], [0.8, 0.2], [0, 1]]
ranked = sorted(enumerate(vectors), key=lambda p: cosine(query, p[1]), reverse=True)
print([index for index, _ in ranked])
# 기대: [0, 1, 2]

제출물: 세 점수와 정렬 결과, 0벡터 입력을 처리하는 규칙.

완료 기준: 검색 순위를 만드는 작업과 검색 결과를 읽어 답을 생성하는 작업이 별개의 단계임을 설명합니다. VectorStore.query()에서 두 단계의 경계를 찾으세요.

과제 04 · 손실을 설계하기

EOS까지 제대로 채점하는 SFT 만들기 중급

읽을 파일: dataset.py의 generate_loss_mask(), k_model.py의 forward(), ddp_sft_full.py의 train_epoch().

PAD=EOS 상황에서 위치 기반으로 유효 정답을 판정하고, 채점하지 않을 위치만 -100으로 바꾸는 변형을 설계하세요. 답변 마지막 EOS의 정답 ID는 보존합니다. 이미 이동된 X·Y를 다시 이동하지 않도록 인터페이스도 적으세요.

제출물: 답변 있는 샘플, 답변이 잘린 샘플, 전부 패딩인 샘플의 기대 마스크와 분모 정책.

완료 기준: 질문·패딩은 손실에 기여하지 않고 EOS는 기여해야 합니다. 유효 토큰이 0개인 배치에서 0으로 나누지 않으며, 평균 분모가 실제 채점 위치 수와 일치해야 합니다.

확장: 6장 finetune.py의 IGNORE_TOKEN_ID = tokenizer.pad_token_id와 비교하세요. 사용하는 모델이 실제로 어떤 ID를 손실에서 제외하는지까지 확인해야 같은 의도가 유지됩니다.

과제 05 · 강화학습 데이터 계약

두 턴짜리 도구 궤적을 훈련 배열로 바꾸기 고급

읽을 파일: docs/chapter8/retool/train.py의 build_datum()와 rollout.py의 AssistantTurn·Trajectory.

실제 도구를 실행하는 대신 고정 문자열을 사용합니다. 모델의 첫 도구 요청, 고정된 도구 응답, 모델의 최종 답변을 하나의 토큰열로 연결한 뒤 입력·정답·old logprob·advantage를 정렬하세요.

제출물: 각 토큰의 출처와 네 배열의 대응표. 정상 궤적과, 다음 턴 입력의 접두부를 일부 바꾼 잘못된 궤적을 각각 만듭니다.

완료 기준: 모든 배열 길이가 같고, 도구 관측 토큰은 입력에 남지만 advantage는 0입니다. 접두부가 맞지 않으면 학습 전에 오류로 처리해야 합니다. 그룹 보상이 모두 같을 때 데이터가 어떻게 처리되는지도 설명하세요.

확장: 원격 학습을 붙이는 단계에서는 먼저 한 step과 작은 그룹으로 제한합니다. GRPO 동기 예제는 --steps, --batch-size, --group-size, --max-tokens를 받습니다. --swanlab-mode disabled는 기록만 끄며 원격 연산 비용을 없애지 않습니다.

실습의 완료 기준은 명령이 종료되는 것이 아니라, 입력·정답·마스크가 의도한 학습 문제를 표현한다고 설명할 수 있는 것입니다.

9관련 기술 심화 학습 로드맵

주차마다 설명 한 장과 확인 가능한 결과물 하나를 남긴다

Python의 함수·클래스·리스트에 익숙한 초보자를 위한 8주 학습 예시입니다. 주당 학습 시간과 수학 배경에 따라 늘려도 됩니다. 원격 학습은 마지막 선택 과제로 두고, 앞부분은 작은 데이터와 코드 읽기로 진행할 수 있습니다.

주차공부할 기술·자료주간 결과물
1주1장 NLP, 토큰·벡터·확률. Python 파일·JSONL·generator 복습문장 → 토큰 ID → 벡터의 차이를 설명하는 그림. 정상·오류 JSONL 처리 규칙
2주2장 Transformer, 내적·행렬곱·softmax·미래 가림Q/K/V 모양 표와 작은 Attention 점수표. 과제 02 완료
3주3~4장 모델 계열, 5장 RMSNorm·RoPE·GQA·게이트형 MLPEncoder와 Decoder의 용도 비교. DecoderLayer 한 블록 설명
4주5장 Tokenizer·Dataset·교차 엔트로피·역전파·AdamW한 샘플의 X/Y/loss_mask 추적. 과제 01과 EOS 손실 점검
5주6장 Trainer·체크포인트·DeepSpeed·LoRA/QLoRA 본문직접 학습 루프와 Trainer의 역할 대응표. 설정·가중치·optimizer 저장물 구분
6주7장 RAG, 문서 분할·임베딩·코사인 유사도·검색 평가질문 10개와 근거 문서 목록. 검색 성공·답변 성공 분리 평가
7주7장 Agent, JSON Schema·도구 결과·상태 관리함수 등록부터 결과 메시지까지 추적. 도구 오류·반복 제한 정책 설계
8주8장 GRPO → OPD → Search-R1·ReTool보상·advantage 손계산과 과제 05. 선택적으로 원격 한 step의 입력·출력 대조

9-1. 막힐 때 돌아갈 기초

9-2. 목표에 따른 후속 경로

모델 연구: 5장의 폭·깊이·어휘 크기를 각각 바꾸는 실험 설계를 해 보세요. 비교할 때 데이터·토큰 수·평가 질문을 고정해야 무엇이 효과를 냈는지 알 수 있습니다.

응용 개발: 7장에서 검색 품질, 도구 입력 검증, 사용자별 대화 상태를 보완하세요. 특히 web_demo.py는 Agent를 st.cache_resource로 캐시하지만 Agent 내부에 메시지 목록이 있으므로, 여러 사용자 환경에서는 세션별 상태 분리를 검토해야 합니다.

학습 인프라: 6장의 ZeRO 설정과 8장의 micro-batch 묶기를 비교하세요. 하나는 여러 장치의 학습 상태를, 다른 하나는 길이가 다른 샘플의 실행 비용을 다룹니다.

다음 주제로 넘어가는 기준은 읽은 페이지 수보다, 현재 단계의 데이터 계약과 실패 조건을 설명할 수 있는지에 두세요.

10핵심 키워드 사전

코드에서 만나는 이름을 쉬운 말로 다시 읽기
용어쉬운 설명
토큰모델이 읽고 예측하는 작은 텍스트 조각입니다. 단어 하나와 반드시 같지는 않습니다.
Tokenizer텍스트와 토큰 ID를 서로 바꾸는 부품입니다. 어휘와 특수 토큰, 대화 형식도 함께 맞춰야 합니다.
BPE자주 붙어 나오는 작은 조각을 합쳐 토큰 사전을 만드는 방법입니다.
Embedding토큰이나 문장을 숫자 벡터로 표현하는 방법입니다. 모델 입력 임베딩과 RAG의 문서 임베딩은 사용 목적이 다릅니다.
파라미터학습하면서 수정하는 숫자입니다. 행렬의 원소와 정규화의 학습 가능한 계수 등이 여기에 속합니다.
logits확률로 바꾸기 전의 후보별 점수입니다. 언어 모델의 출력층은 어휘의 각 토큰에 점수를 줍니다.
softmax후보별 점수를 합계 1인 확률 분포로 바꾸는 연산입니다.
Q·K·V어디를 참고할지 묻는 Query, 참고 대상을 찾는 Key, 가져올 정보인 Value입니다.
인과적 Attention현재 토큰이 자기 뒤의 정답을 미리 보지 못하게 하는 Attention입니다.
RoPEQ·K를 위치에 따라 회전시켜 순서 정보를 담는 방식입니다.
GQA여러 Query 머리가 더 적은 Key·Value 머리를 공유하는 방식입니다.
RMSNorm입력 값의 제곱 평균을 이용해 규모를 정돈하고, 학습 가능한 계수로 조절하는 정규화입니다.
잔차 연결변환 결과에 원래 입력을 더하는 경로입니다. 5장 Decoder 블록에 두 번 등장합니다.
교차 엔트로피정답 토큰에 낮은 확률을 줄수록 커지는 손실입니다.
역전파손실에 각 파라미터가 어떻게 영향을 주는지 미분으로 계산하는 과정입니다.
gradient accumulation작은 배치 여러 번의 gradient를 모은 뒤 가중치를 갱신하는 방법입니다.
사전학습넓은 텍스트에서 언어의 패턴을 배우는 단계입니다. 5장에서는 다음 토큰 예측으로 구성합니다.
SFT질문·답변 같은 지도 데이터로 원하는 응답 방식을 학습시키는 미세조정입니다.
PAD·EOSPAD는 길이를 맞추는 빈자리, EOS는 끝을 알리는 토큰입니다. 같은 ID여도 위치의 역할은 다릅니다.
attention_mask어떤 위치를 참고할 수 있는지 정하는 표시입니다. 손실을 계산할 위치와 구분해야 합니다.
loss_mask어떤 정답 위치를 채점할지 정하는 표시입니다. 5장에서는 토큰별 손실에 곱합니다.
LoRA큰 가중치를 고정하고 작은 보조 행렬을 학습하는 미세조정 방식입니다.
QLoRA양자화한 기본 모델과 LoRA를 조합해 학습 메모리를 줄이는 접근입니다.
ZeRO-2optimizer 상태와 gradient를 여러 장치에 나눠 보관하는 DeepSpeed의 최적화 단계입니다.
체크포인트모델이나 학습 상태를 저장한 중간 결과입니다. 정확한 재개에는 가중치 외의 상태도 필요합니다.
RAG질문과 관련된 외부 자료를 찾아 모델의 입력에 넣는 방식입니다. 검색 자체가 가중치를 수정하지는 않습니다.
코사인 유사도두 벡터 방향의 가까움을 나타내는 점수입니다. 7장 RAG의 검색 순위에 사용합니다.
도구 호출모델이 함수명과 인자를 제안하고, 프로그램이 실제 함수를 실행해 결과를 돌려주는 과정입니다.
rollout·궤적문제에서 출발해 모델 출력과 환경 응답을 거쳐 결과에 도달한 기록입니다.
reward·보상결과를 얼마나 좋게 평가했는지 나타내는 점수입니다. 보상 설계가 학습 목표를 결정합니다.
advantage어떤 행동이 비교 기준보다 얼마나 나았는지 나타내는 학습 신호입니다.
GRPO같은 문제의 여러 답변을 비교해 상대적 학습 신호를 만드는 접근입니다. 이 예제는 보상에서 그룹 평균을 뺍니다.
OPD학생 모델이 생성한 궤적을 교사 모델의 확률로 평가해 학습하는 정책 증류입니다.
old logprob답변을 생성했던 시점의 정책이 각 토큰에 부여한 로그 확률입니다. 업데이트 전후 정책 비교에 사용합니다.
PPO새 정책이 기존 정책에서 한 번에 너무 크게 바뀌지 않도록 확률 비율 등을 제한하는 학습 방법입니다.
K/V cache생성 중 이미 계산한 Key·Value를 재사용하는 최적화입니다. 5장 생성 구현에는 없습니다.

용어를 외우기보다 dataset.py의 반환값, forward()의 출력, build_datum()의 배열 중 어디에 해당하는지 연결해 보세요.

11참고 링크

공식 자료와 커밋에 고정한 소스 탐색 경로

11-1. 프로젝트와 직접 읽을 소스

11-2. 관련 기술의 공식 문서

현재 상태 (2026-10-23 확인)

항목상태와 해석
프로젝트datawhalechina/happy-llm · Datawhale의 LLM 교육 자료
선정GitHub Trending Jupyter Notebook Daily #3 · 2026-10-23
스타 / 포크34,523 / 3,267 · 2026-10-23 GitHub 기준
주 언어Jupyter Notebook · 소스에는 Python 스크립트 49개도 포함
생성일2024-05-28
최신 릴리스v1.0.2 · 2026-01-29
최근 pushGitHub API 날짜 2026-08-08
최근 커밋1e0b3a1 · 2026-08-09 01:17:26 +08:00
메시지: 8장 이미지에 원본 파일 URL 사용
UTC로는 2026-08-08 17:17:26으로, 날짜 표기는 시간대에 따라 달라집니다.
코드 규모Python 11,446줄 / Shell 109줄 / HTML 239줄
Notebook 코드 셀 원문 약 1,068줄 · 빈 줄·주석 포함
문서 규모Markdown 28개·15,258줄 · 전체 파일 214개, 약 31.2MB
라이선스GitHub API: NOASSERTION
LICENSE.txt·README: CC BY-NC-SA 4.0
API의 미분류 표시를 라이선스 부재나 무제한 사용 허용으로 해석하지 마세요.
실행 구성장별 의존성 6개 · 통합 잠금 파일·Dockerfile·CI workflow 없음
활용하기 전에 알아둘 한계
교재의 설명과 예제 코드의 실제 경계를 함께 읽으세요

환경 재현: 장별 버전이 다르고 6장은 여러 패키지 버전을 고정하지 않습니다. torchdata.datapipes import와 설치 버전의 호환성도 확인해야 합니다. 본문의 LoRA/QLoRA 설명이 별도 완성 스크립트와 동일한 범위라고 보장되지는 않습니다.

병렬화·재시작: 5장의 ddp_ 파일은 실제로 torch.nn.DataParallel을 사용합니다. 저장은 모델 state_dict 중심이며 optimizer·scaler·난수 상태까지 보존하는 정확한 재개와 구분해야 합니다. 기본 저장 주기는 학습 배치 1,000회입니다.

SFT의 의미: 5장은 PAD와 EOS 공유에 따른 손실 제외를 점검해야 합니다. 6장 preprocess()도 제외할 위치에 pad_token_id를 사용합니다. 실제 모델이 그 ID를 무시하는지 확인하고, Dataset과 모델 중 누가 정답 이동을 맡는지 명확히 하세요.

데이터·평가: 6장 finetune.py는 파일의 첫 10,000줄을 샘플로 쓰고 최대 길이를 2,048로 전달합니다. 별도의 평가 분할 없이 손실이 줄었다는 사실만으로 일반화나 한국어 성능을 판단할 수 없습니다. 데이터도 중국어 중심 예제가 많습니다.

응용의 규모: 7장 RAG는 전체 벡터 비교·정렬 방식이고, Agent의 도구 실행은 한 번의 왕복 구조입니다. 운영 서비스에 필요한 입력 검증, 재시도, 반복 제한, 사용자별 상태 관리는 추가 설계 대상입니다.

원격 연산·코드 실행: 8장은 TRIO 서비스와 계정·이용량에 의존합니다. ReTool의 로컬 실행기는 파일·네트워크·환경변수를 완전히 격리하지 않습니다. 도구 결과의 형식 정리와 시스템 보안 경계는 서로 다른 문제입니다.

공개·재사용: 라이선스 본문은 비상업 조건이 포함된 CC BY-NC-SA 4.0입니다. 교재·코드·외부 모델·데이터를 재배포하거나 제품에 포함할 때는 각 대상의 조건을 따로 확인해야 합니다.

마지막 학습 확인
한 토큰의 경로를 끝까지 말해 보세요

문장의 토큰이 임베딩과 Attention을 지나 다음 토큰 점수가 되고, 정답·마스크와 만나 손실이 되는 흐름을 설명해 보세요. 이어 RAG가 입력을 어떻게 바꾸고 Agentic RL이 행동의 학습 신호를 어떻게 바꾸는지 연결하면, 이 교재의 큰 줄기를 잡은 것입니다.