학습이 끝났다. 이제 어댑터를 어떻게 배포하나? 갈림길은 두 개다: 과제가 하나면 병합해서 배포, 과제가 여럿이면 어댑터를 그대로 서빙. 그리고 그 전에, QLoRA 사용자를 기다리는 함정이 하나 있다.
함정 먼저 — QLoRA 병합의 정밀도 문제
4비트 base에 어댑터를 직접 병합하지 마라. 어댑터는 학습 중 base의 역양자화된 16비트 뷰를 보고 학습됐다 — 4비트 원본에 그대로 합치면 품질이 조용히 무너진다(표준 레퍼런스). 올바른 순서: base를 bf16으로 로드(또는 NF4를 역양자화) → merge_and_unload() → 그 다음에 한 번만 양자화. Unsloth 사용자는 save_pretrained_gguf() 한 줄이 이 과정을 올바르게 처리한다.
경로 A — 병합 후 GGUF로 (과제 1개, 로컬 배포)
- 파이프라인: merge_and_unload → convert_hf_to_gguf.py → llama-quantize Q4_K_M. 결과: 8B 파인튜닝이 4.6GB 파일이 되어 8GB RAM 노트북에서 돈다 — QLoRA로 학습한 바로 그 기계 포함.
- 배포 도구: Ollama(Modelfile 3줄: FROM + ADAPTER, CPU/GPU 자동 분할), LM Studio(GUI, 비개발자 동료에게 파일만 건네면 됨), llama-server(OpenAI 호환 API).
- 속도 기대치: 7B Q4 기준 Apple Silicon 20-83 t/s(M2→M4 Max), 3090 164 t/s — 공식 벤치표 참고.
경로 B — 어댑터 그대로 서빙 (과제 여럿, GPU 서버)
- 어댑터는 10-200MB — base 하나에 어댑터 수십 개를 얹는 것이 16GB 사본 수십 개보다 낫다는 것이 S-LoRA(MLSys 2024)의 요지: 한 GPU에서 수천 개의 어댑터 동시 서빙.
- 구현: vLLM --enable-lora — OpenAI API의 model 필드로 어댑터 선택, 런타임 load/unload API로 재시작 없이 새 버전 배포(공식 문서). A100 한 장이 100 동시 사용자에 ~2,400 t/s.
- 가벼운 대안: llama-server --lora-scaled adapter.gguf:0.7 — 요청별로 어댑터 강도를 0(원본)~1(파인튜닝)로 조절하며 A/B 테스트를 라이브로 할 수 있다.
llama.cpp vs vLLM — 정직한 비교
Argonne 벤치마크의 결론: 데이터센터 GPU에서 llama.cpp는 가장 느린 프레임워크다(GQA·배칭 활용 미흡, 배치가 클수록 격차 확대). 규칙: 배치 1 개인용·소비자 하드웨어 = llama.cpp/Ollama, 실제 GPU + 동시 사용자 = vLLM. 마지막 팁: "GGUF가 깨졌어요"의 대부분은 변환 버그가 아니라 chat template/EOS 토큰 실수다.
정리
한 줄 교훈: 16비트에서 병합하고 양자화는 한 번만, 과제가 여럿이면 병합하지 말고 어댑터로 서빙하라 — 그리고 학습한 그 기계가 서빙도 할 수 있다. 다음 글: 만든 모델이 진짜 좋아졌는지, 소비자 GPU에서 재는 법.
— LoRA 실전 시리즈 ⑦ / 다음 글: 소비자 GPU에서 평가하기
댓글 없음:
댓글 쓰기
국정원의 댓글 공작을 지탄합니다.