파인튜닝 기초와 LoRA (데이터 준비, 언제 필요한지)
파인튜닝은 이미 학습된 모델에 예시를 더 학습시켜 특정 작업의 응답을 개선하는 과정입니다. LoRA는 기본 가중치를 고정하고 선택한 부분에 작은 행렬 쌍을 추가해 학습하는 방법이며, 형식과 정확성을 보장하지는 않습니다.
같은 말:파인튜닝 뜻LoRA 뜻QLoRAmlx_lm lora로컬 모델 파인튜닝
🤔 프롬프트를 아무리 고쳐도 같은 형식으로 답하지 않을 때
로컬 모델에게 회사 보고서 양식대로 요약해 달라고 하면 매번 형식이 조금씩 달라지고, 우리 팀이 쓰는 말투를 예시로 보여 줘도 몇 줄 뒤에는 원래 말투로 돌아갑니다. 프롬프트를 길게 쓸수록 컨텍스트는 차고 결과는 여전히 들쭉날쭉합니다. 이때 떠오르는 방법이 모델 자체를 내 예시로 더 가르치는 파인튜닝입니다.
지금부터 파인튜닝이 언제 필요하고 언제 다른 방법으로 충분한지, 로컬에서 가장 많이 쓰는 LoRA가 어떻게 메모리를 줄이는지, 학습 데이터를 어떤 형식으로 준비하는지, 맥과 엔비디아에서 각각 어떤 도구를 쓰고 결과를 올라마로 어떻게 가져오는지 정리합니다. 도구 이름과 버전은 2026년 10월 3일 공식 문서 기준입니다.
🔑 파인튜닝과 LoRA의 정의
파인튜닝은 예시를 추가로 학습시켜 특정 작업의 응답을 개선하는 과정입니다. LoRA는 원래 가중치를 고정하고 선택한 부분에 작은 행렬 쌍을 추가해 학습하는 방법입니다. 형식이나 정확성이 자동으로 고정되는 것은 아닙니다.
모델 전체를 다시 학습시키는 전체 파인튜닝은 가중치뿐 아니라 학습에 필요한 기울기와 옵티마이저 상태까지 메모리에 올려야 해서 무겁습니다. 준이아빠블로그의 Qwen3-TTS 목소리 학습 실측에서도 0.6B 음성 모델을 전체 파인튜닝하자 24GB 맥미니에서 메모리 사용이 5GB로 시작해 23GB까지 늘었습니다. LoRA는 이 부담을 줄이려고 고안되었습니다.
🧭 파인튜닝이 필요한 때와 아닌 때
파인튜닝은 마지막에 고르는 방법입니다. 먼저 프롬프트를 다듬고, 모델이 모르는 자료가 문제라면 RAG를 붙이고, 그래도 해결되지 않는 것이 형식과 어투라면 파인튜닝을 봅니다.
| 문제 | 먼저 볼 방법 |
|---|---|
| 모델이 내 회사 자료를 모름, 자료가 자주 바뀜 | 로컬 RAG |
| 답에 출처를 보여 줘야 함 | RAG |
| 같은 양식과 어투가 반복해서 틀림 | 예시와 출력 제약을 먼저 시험한 뒤 필요하면 파인튜닝 |
| 지시문이 길어 컨텍스트가 부족함 | 불필요한 지시와 자료를 줄이고 파인튜닝의 효과를 별도 비교 |
파인튜닝으로 지식에 관한 응답도 바뀔 수 있지만, 자료를 넣었다고 모든 사실을 정확하게 꺼내거나 출처를 제시하는 것은 아닙니다. 자주 바뀌는 정보는 검색할 원문을 갱신하는 RAG부터 검토합니다. 선택 기준은 RAG와 파인튜닝 판단 가이드에서도 다룹니다.
필요한 예시 수는 작업과 모델, 도구에 따라 다릅니다. 다른 서비스의 최소 업로드 건수를 로컬 학습의 성공 기준으로 가져오지 않습니다. 먼저 원하는 정답을 사람이 설명할 수 있는지, 틀린 답과 중복을 가려 낼 수 있는지 확인합니다. 같은 예시를 복사해 행 수만 늘리는 것은 새 학습 자료를 확보한 것이 아닙니다.
🧩 LoRA가 메모리를 줄이는 원리
허깅페이스 PEFT 문서는 LoRA를 이렇게 설명합니다. 학습으로 바뀌는 가중치 변화량을 작은 행렬 두 개의 곱으로 표현하고, 선택한 각 가중치에 작은 행렬 쌍을 두고 기본 가중치는 고정합니다. 모델 전체에 행렬 두 개만 생긴다는 뜻은 아닙니다. 결과를 낼 때는 원래 가중치와 작은 행렬의 결과를 더합니다.
- 학습하는 양: PEFT 예시에서 Llama-3.2-1B의 어텐션 일부에 랭크 8로 걸면 학습 파라미터는 524,288개, 전체의 0.0424%입니다
- 결과 파일: 저장하면 주요 어댑터 파일은
adapter_config.json과adapter_model.safetensors이며 도구와 설정에 따라 추가 파일도 저장됩니다. 350M 모델 예시에서 어댑터는 6.3MB였습니다(원본 약 700MB) - 여러 개 두기: 원본은 그대로 두고 작업마다 가벼운 어댑터를 따로 만들 수 있습니다
QLoRA는 기본 모델을 4비트로 양자화해 올린 뒤 그 위에 LoRA를 학습하는 방법입니다. 기본 가중치를 직접 갱신하는 대신 추가한 어댑터를 학습합니다. 메모리 부담은 줄지만 긴 입력과 배치 크기 등에 필요한 공간까지 없어지는 것은 아닙니다.
📝 학습 데이터 준비
로컬 학습 도구는 대부분 JSONL 파일을 받습니다. JSONL은 한 줄에 JSON 하나씩 적는 형식이고, MLX-LM 문서는 예시 하나를 반드시 한 줄에 쓰라고 강조합니다. 대화형(chat) 형식은 이렇습니다.
{"messages": [{"role": "system", "content": "주간 보고 양식으로 요약한다."}, {"role": "user", "content": "이번 주 회의 내용: ..."}, {"role": "assistant", "content": "1. 결정 사항\n- ...\n2. 다음 할 일\n- ..."}]}- 파일 나누기: 학습용
train.jsonl은 필수이고, 검증용valid.jsonl, 시험용test.jsonl을 따로 둡니다 - 질문과 답 형식:
{"prompt": "...", "completion": "..."}처럼 간단히 쓸 수도 있습니다 - 답만 학습: MLX-LM의
--mask-prompt를 주면 입력은 문맥으로 사용하면서 답 토큰에 대해서만 손실을 계산합니다. chat과 completion 형식에 지원되며 chat에서는 마지막 메시지가 대상입니다 - 공개 데이터: 한국어 대화 데이터가 필요하면 AI허브에 사후학습용 데이터가 올라와 있지만 데이터마다 이용 조건이 따로 붙습니다
학습 결과를 공개하거나 업무에 쓸 계획이라면 기본 모델의 라이선스도 확인합니다. 예를 들어 Solar-Open-100B의 라이선스는 파생 AI 모델을 배포하거나 제공할 때 이름을 Solar로 시작하고 Built with Solar 표시와 라이선스 동봉 등의 조건을 요구합니다. Solar라는 이름의 모든 모델에 같은 조건이 적용되는 것은 아닙니다. 라이선스 읽는 법은 오픈 웨이트와 오픈 소스 차이에 정리했습니다.
본문의 데이터 파일 구성을 표현했습니다. 한 줄에는 하나의 완전한 JSON 예시가 들어가며 역할별 데이터를 구분합니다.
🍎 맥에서는 MLX-LM
애플 실리콘 맥에서는 MLX-LM을 사용할 수 있습니다. 설치와 학습 전에 모델 호환성, 학습 데이터와 여유 메모리를 확인합니다. 아래는 형식을 보여 주는 예시로, 경로를 실제 값으로 바꾸기 전에는 실행하지 않습니다. 설치는 별도 가상환경에서 pip install "mlx-lm[train]"로 진행합니다.
mlx_lm.lora --model "/실제/모델/경로" --train --data "/실제/데이터/폴더" --adapter-path "/새로운/결과/폴더" --iters 600--model에 양자화된 모델을 주면 자동으로 QLoRA가 됩니다. 기본 결과 폴더는 adapters/이지만 위 예시는 별도 출력 경로를 지정합니다. 기존 결과를 덮어쓰지 않도록 매 실험의 경로와 설정을 기록합니다. 시험할 때도 학습한 기본 모델과 실제 어댑터 경로를 지정해야 합니다. mlx_lm.lora의 --test는 시험 자료의 손실을 확인하는 옵션이며, 정답률 자체가 아닙니다. 실제 답은 mlx_lm.generate에 같은 --model과 --adapter-path를 지정해 확인합니다. 예시의 600회 반복은 모든 작업에 맞는 권장값이 아닙니다. 공식 문서의 M1 Max 32GB와 WikiSQL 데이터 예시는 배치 1, 학습 층 4개 조건에서 초당 약 250토큰을 보고합니다. 자신의 모델과 입력 길이에 그대로 적용할 속도는 아닙니다. 메모리가 부족하면 --batch-size를 줄이고, --num-layers로 학습할 층 수를 줄이고, --grad-checkpoint를 켭니다.
🟩 엔비디아에서는 Unsloth
엔비디아 GPU에서는 Unsloth를 사용할 수 있습니다. CUDA Capability는 GPU의 연산 기능 수준이며 공식 요구 조건의 기본 기준은 7.0 이상입니다. 구형 기기의 별도 지원 여부와 설치 조건은 모델을 받기 전에 확인합니다. 공식 요구 사항 표의 최소 VRAM 예시는 다음과 같습니다. 실제 필요량은 모델 구조, 입력 길이와 배치 등에 따라 커지므로 이 숫자만으로 장비 구매나 실행 가능 여부를 결정하지 않습니다.
| 모델 크기 | QLoRA (4비트) | LoRA (16비트) |
|---|---|---|
| 8B | 6GB | 22GB |
| 14B | 8.5GB | 33GB |
| 27B | 22GB | 64GB |
Unsloth는 학습률(한 번 갱신할 때 바꾸는 크기)을 2e-4에서 시작하고, 에포크(데이터 전체를 한 번 도는 횟수)는 1~3번, 랭크(작은 행렬의 크기를 정하는 값)는 8이나 16에서 시작하라고 권합니다. 에포크를 3번 넘게 돌리면 예시를 외워 버리는 과적합 위험이 커진다고 적습니다.
🔁 결과를 올라마에서 쓰기
현재 가져오기 문서는 완성된 Safetensors와 GGUF 모델을 안내합니다. v0.34.1의 서버 코드는 어댑터가 들어간 생성 요청을 LoRA adapters are no longer supported 오류로 거부합니다. 예전 글의 ADAPTER 방법은 지금 버전에서 동작하지 않습니다.
지금 쓰는 경로는 병합 후 가져오기입니다.
- 어댑터 병합: 학습에 사용한 정확한 기본 모델과 어댑터를 선택합니다. MLX-LM은
mlx_lm.fuse의--model,--adapter-path와--save-path를 지정하고 PEFT는 지원되는 경우 merge_and_unload()로 병합합니다 - 형식과 모델 지원 확인: MLX-LM의 GGUF 내보내기는 모든 모델에 지원되지 않습니다. 병합된 파일이 가져올 도구에서 지원하는 구조와 형식인지 먼저 확인합니다
- 올라마로 가져오기: Modelfile에
FROM /병합한/폴더또는FROM /파일.gguf를 적고ollama create 내모델을 실행합니다 - GGUF 양자화가 필요하면 먼저: 올라마는 가져올 때 GGUF를 양자화하지 않으므로 llama.cpp의
llama-quantize로 미리 줄입니다
병합과 변환 뒤에는 원래 평가 질문으로 다시 비교합니다. 파일 생성 성공이 답변 품질 유지의 증거는 아닙니다. 기본 모델과 원래 어댑터는 별도로 보존합니다.
어댑터를 병합하지 않고 따로 붙여 쓰고 싶다면 llama.cpp의 llama-server --lora 파일을 씁니다. 가져온 모델을 서버로 여는 방법은 로컬 모델 서빙에서 다뤘습니다.
⚠️ 자주 하는 실수
- 학습 손실이 내려가면 성공이라고 봅니다: Qwen3-TTS 실측에서는 공식 스크립트 버그로 손실이 12.2에서 3.6까지 내려갔는데 출력은 잡음이었습니다. 학습 전에 원본 모델로 손실을 한 번 확인하고, 결과는 실제 답으로 봅니다
- 에포크를 늘릴수록 좋다고 봅니다: 같은 실측에서 4에포크 판이 6에포크 판보다 자연스러웠습니다
- 검증 데이터를 두지 않습니다: 학습 자료에만 맞춘 것인지 알아보기 어려워집니다
- 다른 기본 모델에 어댑터를 붙입니다: 어댑터는 학습한 그 모델에서만 제대로 동작합니다
❓ 자주 묻는 질문
16GB 맥에서도 파인튜닝이 되나요?
호환되는 작은 모델과 짧은 입력에서는 가능할 수 있지만 16GB라는 숫자만으로 보장할 수는 없습니다. 공식 문서도 메모리가 부족하면 양자화 모델, 작은 배치, 적은 학습 층 수로 줄이라고 안내합니다. 다만 모델 크기와 예시 길이에 따라 필요한 메모리가 크게 달라서, 작은 데이터로 한 번 돌려 메모리 사용량을 보는 편이 정확합니다.
파인튜닝한 모델이 원래 능력을 잃지는 않나요?
특정 형식으로 너무 많이 학습하면 다른 요청에서 답이 어색해질 수 있습니다. 그래서 에포크를 1~3번으로 두고, 학습에 쓰지 않은 질문으로 원래 모델과 나란히 비교해 봅니다.
학습 데이터는 모델이 만든 답으로 채워도 되나요?
큰 모델이 만든 답으로 작은 모델을 학습시키는 방식은 증류라고 부르며 실제로 쓰입니다. 다만 모델마다 이용 약관에 다른 모델 학습에 쓰는 것을 제한하는 조건이 있을 수 있으므로 먼저 확인합니다. 증류의 원리는 AI 모델 증류에 정리했습니다.
학습 전에 가상 정답 두 개부터 고치기
첫 연습은 모델을 학습시키지 않고 메모장으로 진행합니다. 목표는 “회의 메모를 날짜, 장소, 시간 세 줄로 요약하되 없는 값은 미정으로 유지한다”입니다.
| 구분 | 원문 | 준비한 정답 |
|---|---|---|
| A | 11월 12일, 2번 회의실, 시간 미정 | 11월 12일 / 2번 회의실 / 오후 2시 |
| B | 11월 13일, 3번 회의실, 오전 10시 | 11월 13일 / 3번 회의실 / 오전 10시 |
A의 정답에서 시간을 미정으로 고칩니다. 잘못 만든 정답을 학습하면 그 오류를 줄이려던 학습 목표와 충돌합니다. 두 예시는 실제 학습에 충분한 데이터라는 뜻이 아닙니다.
고친 A를 JSONL 형식으로 적으면 다음과 같습니다. 파일에서는 코드 블록의 표시 없이 한 줄로 저장합니다.
{"messages":[{"role":"user","content":"날짜, 장소, 시간을 세 줄로 요약하고 없는 값은 미정으로 유지하세요. 회의 메모: 11월 12일, 2번 회의실, 시간 미정"},{"role":"assistant","content":"날짜: 11월 12일\n장소: 2번 회의실\n시간: 미정"}]}줄바꿈은 문자열 안에서 \n으로 적고, 따옴표와 쉼표가 빠지지 않았는지 확인합니다. 그다음 A를 train.jsonl에, B를 test.jsonl에 두는 배정안을 메모합니다. A를 표현만 바꿔 test에도 넣었다면 제거하고 다른 회의의 새 예시로 바꿉니다. 같은 원문이나 거의 같은 대화를 나누어 넣으면 외운 답을 새 자료 처리 능력으로 오해하기 쉽습니다.
실제 학습을 준비할 때는 학습용, 조정에 사용하는 검증용, 마지막 비교용 시험 자료를 문서나 대화 단위로 구분합니다. 원래 모델과 학습한 모델에 같은 미사용 질문을 주고 형식 일치, 원문 값 보존, 미정 처리와 관계없는 요청에서의 오류를 기록합니다. 시험 결과를 보고 계속 고쳤다면 그 자료는 더 이상 최종 미사용 시험이 아니므로 마지막 비교용 자료를 따로 확보합니다.
📋 3줄 요약
-
파인튜닝은 응답을 개선하는 방법이며 LoRA는 선택한 부분의 작은 행렬 쌍을 학습해 부담을 줄입니다.
-
학습용 정답을 원문과 대조하고 같은 문서에서 나온 예시가 학습과 시험에 겹치지 않도록 나눕니다.
-
손실과 파일 생성만으로 성공을 판단하지 않고 미사용 질문에서 원래 모델 및 병합 후 결과를 비교합니다.
📚 참고 자료

제대로 이해했는지 한 문제로 확인해 볼까요?
답을 고르면 바로 풀이가 나와요.
학습에 넣은 회의 메모를 그대로 test.jsonl에도 복사했습니다. 모델이 시험 답을 잘 맞혔다면 어떻게 판단해야 할까요?
예제나 확인 질문을 직접 시도한 뒤 완료 표시해요. 더 연습할 내용이 남으면 표시하지 않고 다음 글을 읽어도 됩니다. 완료 버튼은 이해도를 채점하지 않으며 잘못 표시하면 취소할 수 있어요.

