0. 시작하기 전에: 무엇을 만들고 무엇이 필요한가¶
이 장에서는 명령을 하나도 실행하지 않습니다. 다 읽고 나면 네 가지 질문에 답할 수 있어야 합니다. 정확히 무엇을 만드는가, 과정은 몇 단계이고 각 단계는 어느 머신에서 하는가, 미리 무엇을 준비해야 하는가, 각 구성 요소가 어떻게 맞물리는가.
0.1 한 문장으로: 범용 모델을 전문 서비스로 "신입 교육"하기¶
인터넷에서 오픈소스 범용 대형 모델 Qwen3-4B(약 40억 개 파라미터, 알리바바 Tongyi Qianwen 팀 공개)를 내려받습니다. 무엇이든 대화할 수 있지만 말하는 방식은 우리가 통제할 수 없습니다. 답이 너무 길거나, 초등학생 수준을 넘는 단어를 쓰거나, 자꾸 되묻기도 합니다.
다음으로 "아이의 질문 → 이상적인 답변" 형태의 시범 대화 600개로 모델을 파인튜닝해, 정해진 한 가지 말투를 가르칩니다. 먼저 답하고, 짧은 문장을 쓰고, 틀린 추측은 먼저 바로잡고, 안전한 작은 관찰 활동을 하나 제안하는 방식입니다. 마지막으로 학습된 모델을 HTTP 서비스로 배포합니다. OpenAI API를 호출할 줄 아는 백엔드 코드라면 주소만 바꾸면 바로 사용할 수 있습니다.
채용에 비유하면 다음과 같습니다.
| 현실의 채용 | 이 튜토리얼에서 대응하는 것 |
|---|---|
| 지식이 폭넓은 신입 사원을 채용한다 | 오픈소스 베이스 모델 Qwen3-4B를 다운로드한다 |
| "모범 응대 예시" 묶음을 보여 주며 신입 교육을 한다 | 시범 대화로 SFT 파인튜닝을 한다 |
| 교육 내용은 작은 수첩에만 적고, 원래 지식은 바꾸지 않는다 | LoRA: 원래 모델은 그대로 두고 약 130 MB의 증분만 학습한다 |
| 교육이 끝나면 전화 응대 업무에 투입한다 | vLLM으로 모델을 API로 배포한다 |
| 응대 품질을 샘플링해 점검하고 하루에 몇 통을 받을 수 있는지 본다 | 호출 테스트, 블라인드 평가, 부하 테스트 |
가장 중요한 경계 하나: 파인튜닝이 가르치는 것은 "어떻게 말하는가"이지 "무엇을 아는가"가 아닙니다. 구체적인 과학 사실은 호출하는 쪽이 매 요청마다 프롬프트에 넣어 주고(이 프로젝트에서는 "팩트 카드"라고 부릅니다), 모델은 그것을 알맞은 방식으로 설명하는 일을 맡습니다. 베이스 모델이 상식을 이미 알고 있는데 왜 팩트 카드가 필요한지, 왜 학습할 때도 넣는지는 2.2절에서 따로 답합니다.
0.2 전체 흐름: 각 단계를 어디서 하고 무엇이 나오는가¶
① 读懂数据 dataset.json → 1150 条训练样本 (第 2 章,只读)
↓
② 准备 GPU 训练环境 .venv-train + 8 GB 基座模型 (第 3 章,GPU 终端)
↓
③ 训练 LoRA Notebook 跑 72 步 → 130 MB 的 adapter (第 4 章,JupyterLab)
↓
④ 合并导出 基座 + adapter → 8 GB 的完整模型 (第 4 章,JupyterLab)
↓
⑤ vLLM 部署 完整模型 → OpenAI 兼容的 HTTP 服务 (第 5 章,GPU 终端)
↓
⑥ 调用与压测 本地电脑经 SSH 隧道调用,测延迟和吞吐 (第 5 章,本地终端)
↓
⑦ 评估值不值 和“原模型 + 好提示词”盲评对照 (第 6 章,GPU 终端)
(그림: ① 데이터 이해, dataset.json → 학습 샘플 1150개(2장, 읽기만) → ② GPU 준비, .venv-train + 8 GB 베이스 모델(3장, GPU 터미널) → ③ LoRA 학습, 노트북에서 72스텝 → 130 MB adapter(4장, JupyterLab) → ④ 병합과 내보내기, 베이스 + adapter → 8 GB 완전한 모델(4장, JupyterLab) → ⑤ vLLM으로 OpenAI 호환 HTTP 서비스 배포(5장, GPU 터미널) → ⑥ 내 컴퓨터에서 SSH 터널을 거쳐 호출하고 부하 테스트(5장, 로컬 터미널) → ⑦ 그만한 가치가 있었는지 평가, "베이스 모델 + 좋은 프롬프트"와 블라인드 비교(6장, GPU 터미널).)
| 단계 | 어디서 | 무엇으로 | 산출물 | 크기 | 장 |
|---|---|---|---|---|---|
| ① 데이터 이해 | 아무 컴퓨터, 파일을 읽기만 함 | 브라우저 또는 에디터 | 600개 대화가 어떻게 학습 샘플 1150개가 되는지 이해 | — | 2장 |
| ② GPU 준비 | GPU 호스트의 터미널 | pip, 다운로드 스크립트 | .venv-train 학습 환경과 models/qwen3-4b/ 베이스 모델 |
환경 약 10 GB, 베이스 모델 8 GB | 3장 |
| ③ 학습 | GPU 호스트의 JupyterLab | 노트북을 셀 단위로 실행 | adapter/(LoRA 증분), loss-curve.png |
약 130 MB | 4장 |
| ④ 병합과 내보내기 | 같은 노트북 | PEFT | merged-model/, 단독으로 실행 가능한 완전한 모델 |
약 8 GB | 4장, 5장 |
| ⑤ 배포 | GPU 호스트의 터미널 | vLLM | 8000번 포트에서 대기하는 OpenAI 호환 서비스 | VRAM 약 20 GB | 5장 |
| ⑥ 호출과 부하 테스트 | 내 컴퓨터 → SSH 터널 → GPU | Python 표준 라이브러리 / OpenAI SDK, vllm bench serve |
모델 답변, 지연 시간과 처리량 수치 | — | 5장 |
| ⑦ 평가 | GPU 호스트 | 명령줄 스크립트 + 사람의 채점 | "그냥 프롬프트를 쓰는 것보다 LoRA가 얼마나 나은가"에 대한 근거 | — | 6장 |
①~⑥ 단계가 메인 경로이며 GPU 가동 시간은 약 2~3시간입니다. ⑦ 단계는 "파인튜닝할 가치가 있는가?"에 답하는 단계로, GPU 시간과 사람의 채점이 더 필요하므로 메인 경로가 끝까지 동작한 뒤에 해도 됩니다.
0.3 머신 두 대, 창 세 개: 어떻게 함께 동작하는가¶
전체 과정에는 머신 두 대만 관여합니다. GPU 호스트는 시간 단위로 빌려 쓰고 끝나면 중지합니다. 내 컴퓨터는 읽기, 패키징, 호출을 담당합니다.
你的电脑(Mac / Linux / Windows 均可)
├── 浏览器:读这本书
├── 浏览器:打开平台提供的 JupyterLab ──HTTPS──→ GPU 主机的 JupyterLab
├── 终端 A:打包项目,用平台上传功能传到 GPU 主机
├── 终端 B:SSH 隧道,本地 18000 端口 ──SSH──→ GPU 主机 8000 端口
└── 终端 C:运行 Python 调用示例,请求发往本地 18000
云上 GPU 主机(Linux + RTX 4090)
├── JupyterLab:运行 Notebook,训练、保存、合并
├── 终端 1:装环境、下载模型、启动 vLLM 服务(监听 127.0.0.1:8000)
└── 终端 2:nvidia-smi 看显存、运行压测
(그림: 내 컴퓨터(Mac / Linux / Windows 모두 가능)에서는 이 책을 읽는 브라우저, 플랫폼의 JupyterLab을 연 브라우저 탭(HTTPS로 GPU 호스트의 JupyterLab에 연결), 프로젝트를 패키징해 플랫폼 업로드 기능으로 올리는 터미널 A, 로컬 18000번 포트를 GPU 호스트의 8000번 포트로 잇는 SSH 터널용 터미널 B, 로컬 18000번 포트로 Python 호출 예제를 실행하는 터미널 C를 씁니다. 클라우드 GPU 호스트(Linux + RTX 4090)에서는 노트북을 실행하는 JupyterLab(학습, 저장, 병합), 환경 설치와 모델 다운로드, vLLM 서비스 시작(127.0.0.1:8000에서 대기)을 하는 터미널 1, nvidia-smi로 VRAM을 확인하고 부하 테스트를 실행하는 터미널 2를 씁니다.)
| 구성 요소 | 무엇인가 | 왜 필요한가 |
|---|---|---|
| GPU 클라우드 호스트 | NVIDIA GPU 한 장이 달린, 시간 단위로 빌리는 Linux 서버 | 학습과 추론은 모두 방대한 행렬 연산이라 CPU로는 너무 느립니다. 학습에 쓰는 4비트 양자화 라이브러리는 NVIDIA만 지원합니다 |
| 플랫폼 이미지 | 인스턴스를 시작할 때 고르는 시스템 템플릿. Ubuntu, Python 3.11, GPU 드라이버가 미리 설치되어 있음 | OS와 드라이버 설치를 생략할 수 있습니다. 드라이버는 플랫폼만 제공할 수 있습니다 |
| JupyterLab | 브라우저 안의 대화형 Python 에디터. 보통 플랫폼에 기본 포함 | 학습 코드를 셀 하나씩 실행하며 각 셀의 출력을 볼 수 있고, 오류가 나면 그 셀만 다시 실행하면 됩니다 |
가상 환경 .venv-* |
프로젝트 전용 Python 의존성 디렉터리 | 학습과 배포에 필요한 라이브러리 버전이 서로 충돌하므로, 따로 설치해야 서로 덮어쓰지 않습니다 |
| 커널(Kernel) | Jupyter 뒤에서 실제로 코드를 실행하는 Python 프로세스 | .venv-train이 등록한 커널을 골라야 노트북이 프로젝트의 의존성을 쓸 수 있습니다. VRAM은 커널을 종료해야만 해제됩니다 |
| SSH 터널 | 로컬 포트를 원격 포트로 전달하는 암호화된 통로 | 모델 서비스는 GPU 호스트의 127.0.0.1에서만 대기하고 인터넷에 노출되지 않습니다. 로컬에서는 터널을 통해 내 컴퓨터에 있는 것처럼 호출합니다 |
0.4 미리 준비할 것¶
| 항목 | 요구 사항 | 설명 |
|---|---|---|
| 기술 | Linux 터미널을 다룰 줄 알고, Python 코드를 읽을 수 있고, HTTP와 JSON을 앎 | 수학, PyTorch, 머신러닝은 필요 없습니다. 필요한 개념은 1장에서 처음부터 설명합니다 |
| GPU 클라우드 계정 | 종량제 과금을 지원하는 GPU 클라우드 플랫폼이면 어디든 가능, 계정에 잔액이 있어야 함 | RTX 4090(VRAM 24 GB) 인스턴스를 시작할 수 있어야 하고, 플랫폼이 JupyterLab과 SSH 로그인을 제공해야 합니다. 저자는 Zhichuan Cloud (gpuhome.cc)를 사용합니다. 가입할 때 초대 코드 zt4f01m0을 입력하면 신규 사용자 쿠폰을 받을 수 있습니다. 3.1절을 참고하세요 |
| 호스트 사양 | RTX 4090 × 1, RAM ≥ 32 GB(병합 시 ≥ 64 GB 권장), 데이터 디스크 ≥ 100 GB | Ubuntu 22 + Python 3.11 + CUDA 12.x 이미지를 고릅니다. 자세한 내용은 3.1절에 있습니다 |
| 예산 | 메인 경로는 가동 시간 약 2~3시간, 평가 실험은 추가로 10~20시간 | Zhichuan Cloud의 RTX 4090D 종량제 요금 ¥1.25/시간 기준으로 메인 경로는 약 ¥3~4, 평가 실험은 약 ¥13~25입니다. 계산 방법은 7.1절에 있습니다 |
| 내 컴퓨터 | 인터넷 접속, 터미널, SSH 클라이언트. 로컬에서 검사나 호출 예제를 실행하려면 Python 3.11 설치 | 로컬 GPU는 필요 없고, CUDA를 직접 설치할 필요도 없습니다 |
| 네트워크 | GPU 호스트가 PyPI(또는 플랫폼의 미러)와 ModelScope에 접근할 수 있어야 함 | 중국 본토의 호스트는 보통 huggingface.co에 접근할 수 없으므로, 이 튜토리얼은 기본적으로 ModelScope에서 다운로드합니다 |
| 이 프로젝트의 파일 | 이 저장소, 또는 전자책 "소스 다운로드" 페이지의 실습 패키지 | GPU에 올리는 것은 tools/build_gpu_zip.py로 만든 경량 패키지이며, 3.3절에서 설명합니다 |
| 시간 | 1~2장 읽기에 1~2시간, GPU 작업은 한 번에 연속 2~3시간 | GPU는 가동 시간만큼 과금되므로 앞의 두 장을 다 읽은 뒤에 인스턴스를 시작하세요 |
준비하지 않아도 되는 것: OpenAI 계정이나 API 키(vLLM 서비스는 OpenAI와 같은 인터페이스 형식을 쓰지만 전부 내가 빌린 머신에서 돌아갑니다), Hugging Face 계정(모델은 공개되어 있고 ModelScope에서 받습니다), 나만의 학습 데이터(프로젝트에 데이터셋이 들어 있습니다).
0.5 읽는 순서¶
이 책의 순서가 곧 작업 순서입니다. 각 장의 첫머리에서 그 장에서 할 일, 필요한 것, 끝나면 얻는 것을 설명합니다.
| 장 | 내용 | 어디서 읽고/하는가 | 필수인가? |
|---|---|---|---|
| 0~1 | 목표, 준비, 필수 개념 | 읽기만 | 필수: 1장은 이후 모든 숫자와 파라미터의 사전입니다 |
| 2 | 학습 데이터가 어떻게 생겼고 어떻게 학습 샘플이 되는가 | 읽기만, 원하면 로컬에서 검사 실행 | 필수: 모델이 실제로 무엇을 배우는지 알아야 합니다 |
| 3 | 머신 임대, 환경 설치, 모델 다운로드 | GPU 터미널 | 필수 |
| 4 | 노트북에서 학습, loss 관찰, 답변 비교, 병합 | GPU JupyterLab | 필수 |
| 5 | vLLM 배포, 로컬 호출, 부하 테스트 | GPU 터미널 + 로컬 | 필수 |
| 6 | "베이스 모델 + 좋은 프롬프트"와의 비교, 블라인드 평가 | GPU 터미널 | 메인 경로가 동작한 뒤에 |
| 7 | 비용, 문제 해결 표, 최종 점검 | — | 문제가 생기면 찾아보고, 마지막에 대조해 점검합니다 |
| 8, 부록 | 자동화 테스트, 코드 지도, 학습 코드 전문 | — | 코드를 고치고 싶을 때 읽습니다 |
개념을 이미 아는 독자는 3장부터 시작하고, 낯선 파라미터를 만나면 1장이나 해당 노트북 셀 아래의 표로 돌아와도 됩니다.
0.6 실습 과제: 초등학생과 함께 과학 탐구하기¶
고객 상담이나 코드 생성 같은 흔한 과제를 쓰지 않는 이유는 무엇일까요? "초등학생에게 과학을 설명하기"는 답변 스타일이 뚜렷해서 파인튜닝 전후의 변화가 한눈에 보이기 때문입니다. 또 과학 사실은 확인할 수 있으므로 모델이 지어내는지 판단하기 쉽습니다.
왜 과학 탐구인가¶
아이가 "왜?"라고 물은 뒤에는 틀린 추측을 하거나, 이해하지 못하거나, 직접 확인해 보고 싶어 하거나, 그냥 당장 답을 알고 싶어 할 수 있습니다. 이런 반응을 통해 표현이 귀여운지만 비교하는 것이 아니라, 모델이 정말로 설명을 조정하는지 관찰할 수 있습니다.
목표는 사실이 정확하고, 아이가 이해할 수 있고, 다음 단계가 탐구할 만한 것입니다. 재미는 상식과 어긋나는 현상, 한계가 분명한 비유, 안전한 작은 관찰에서 나올 수 있으며, 모든 답변에 이야기나 과장된 말투가 필요한 것은 아닙니다. 아이가 그만하겠다고 하면 대화는 끝나며, 대화가 길어지는 것을 성공의 기준으로 삼지 않습니다.
다루는 범위¶
| 분류 | 데이터 값 | 학습 주제 예시 |
|---|---|---|
| 천문과 우주 | space |
낮과 밤, 계절, 달의 위상, 유성 |
| 지구와 날씨 | earth |
구름과 비, 눈송이, 암석, 강 |
| 생명과 자연 | life |
씨앗, 뿌리, 나비, 먹이 그물 |
| 생활 속 물리 | physics |
그림자, 소리, 부력, 용해 |
| 공학과 도구 | engineering |
아치교, 지레, 비행기, 태양 전지 |
분류마다 학습용 대화 100개, 검증용 10개, 테스트용 10개가 있습니다. 이것은 한정된 교육용 샘플이지 초등 교육 과정의 완전한 지식 베이스가 아닙니다. grade_band는 1~2학년, 3~4학년, 5~6학년으로 나뉩니다. 학년대는 설명의 출발점일 뿐 실제 반응이 우선하며, 같은 학년의 아이들이 똑같이 이해한다고 단정할 수 없습니다.
프롬프트와 파인튜닝은 어떻게 역할을 나누는가¶
요청 하나에는 네 가지가 들어 있고, 각각 한 가지 일을 맡습니다.
| 구성 요소 | 무엇을 담당하는가 | 언제 효과가 있는가 |
|---|---|---|
| 시스템 프롬프트(역할 프롬프트) | 역할과 기본 규칙: "너는 초등학생과 함께 과학을 탐구하는 친구야……" | 매 요청마다 보냅니다 |
| 팩트 카드 | 이 질문이 근거로 삼을 수 있는 과학 자료 | 질문마다 골라서 매 요청에 보냅니다 |
| SFT 시범 대화 | 오해, 이해하지 못함, 관찰 결과 불일치, 선호 변화에 어떻게 대응하는지 시범을 보임 | 학습할 때만 쓰며, 배운 방식은 adapter에 저장됩니다 |
| LoRA | 일부 파라미터만 업데이트하도록 제한하며, 베이스 모델은 건드리지 않음 | 학습 시점에 어떤 파라미터를 바꿀지 결정합니다 |
이 프로젝트의 시범 경로 10가지는 오해 바로잡기, 다른 방식으로 설명하기, 구체적인 후속 질문 받아 주기, 선택적 관찰, 추측 검증하기, 쉬운 데서 깊은 데로 나아가기, 비유의 한계 밝히기, 서로 다른 관찰 결과 맞춰 보기, 문제를 내지 않고 이어 묻기, 자연스럽게 끝내기입니다. 같은 주제 안에서는 일부 답변이 재사용되므로, 대화 500개를 서로 독립된 과학 질문 500개로 이해하면 안 됩니다.
"프롬프트는 결코 파인튜닝을 대신할 수 없다"고 약속하지는 않습니다. 6장에서는 베이스 모델에 완전한 프롬프트와 예시 3개를 붙인 B 그룹과, 똑같은 입력을 쓰는 LoRA C 그룹을 비교합니다. 학습에서 보지 못한 과학 주제에서 C가 아이의 반응에 더 일관되게 대응하는지 확인합니다. 개선이 없다면 솔직하게 B를 유지합니다.
0.7 현재 갖춰진 것¶
업데이트: 2026-10-10.
| 항목 | 현재 상태 |
|---|---|
| 단일 데이터 소스 | data/dataset.json: 팩트 카드 60장, 합성 대화 600개(500 train / 50 val / 50 test) |
| 학습 샘플 | train 대화 500개가 답변 목표 1150개로 펼쳐집니다. 노트북은 기본적으로 1 에폭 전체(약 72스텝)를 실행합니다 |
| 노트북 | 토큰화, 마스킹, QLoRA, loss 곡선, 저장, 다시 불러오기, 비교, BF16 병합, 그리고 마지막으로 vLLM 배포와 호출 |
| 배포 | vLLM으로 병합 모델을 서빙하거나 adapter를 직접 마운트. 표준 라이브러리 / OpenAI SDK로 호출. vllm bench serve로 부하 테스트 |
| 로컬 검증 | 데이터 검사와 아주 작은 CPU 학습 메커니즘 테스트. 검증 기록을 참고하세요 |
| 아직 실제 GPU에서 검증되지 않음 | 4B 학습 시간과 VRAM, vLLM 서비스, 부하 테스트 수치. GPU 호스트에서 직접 측정한 값을 기준으로 삼으세요 |
0.8 결론의 한계 (책 전체에서 한 번만 말합니다)¶
- 모든 대화는 합성 초안(
review.status="draft")이며 사람이 하나하나 검토하지 않았습니다. 답변은 짧은 편이고 구조가 반복됩니다. 작업 흐름과 엔지니어링을 배우기에는 충분하지만, "LoRA가 반드시 더 낫다"는 결론을 내리기에는 부족합니다. - 답변을 생성할 수 있다는 것은 파일과 서비스가 동작한다는 뜻일 뿐이고, loss가 떨어졌다는 것은 모델이 참고 답변과 더 비슷해졌다는 뜻일 뿐입니다. 답변 품질은 4.5절의 사람 비교와 6장의 블라인드 평가로 판단해야 합니다.
- 파인튜닝이 바꾸는 것은 "어떻게 말하는가"이며, 모델이 새로운 지식이나 실시간 정보를 저절로 얻게 되지는 않습니다. 사실은 여전히 호출하는 쪽이 프롬프트에 넣습니다.
점검: "데이터 → 학습 → 병합 → 배포 → 호출" 흐름을 그리고 각 단계가 어느 머신에서 실행되며 어떤 파일을 만드는지 말할 수 있습니다. 인스턴스를 시작하기 전에 준비할 것을 나열할 수 있습니다. 대화 하나에서 "지식은 무엇인가"와 "반응에 따라 무엇이 바뀌었는가"를 짚어 낼 수 있습니다.