Gemma4 직접 파인튜닝해보니 — LoRA부터 GGUF 변환, 환각까지 우리가 겪은 것들

왜 Gemma4를 직접 파인튜닝했나

저희는 local.aria-ai라는 로컬 CEO 에이전트 프로젝트를 운영하고 있습니다. 클라우드 API에 의존하지 않고 내 PC에서 돌아가는 경량 모델로 회사 운영을 보조하게 만드는 게 목표였고, 베이스 모델로는 구글의 Gemma4(2.6B, E2B)를 골랐습니다. 일반 Gemma는 “당신은 누구입니까”라고 물으면 그냥 범용 어시스턴트라고 답합니다. 저희가 원한 건 “Aria”라는 정체성을 갖고, Leo(개발)·Lia(리서치)·Mia(문서)·Kai(분석) 네 워커에게 작업을 라우팅하는 CEO 역할을 수행하는 모델이었습니다. 이걸 가능하게 하는 게 파인튜닝입니다.

결과부터 말씀드리면 — 파인튜닝 자체는 성공했지만, 그 뒤로 GGUF 변환, 실전 배포, 환각 문제까지 한 단계씩 막힐 때마다 새로운 함정이 기다리고 있었습니다. 이 글은 그 과정에서 실제로 겪은 시행착오를 그대로 정리한 후기입니다.

1단계: 데이터셋 준비 — 1,149개로 시작하다

파인튜닝의 절반은 데이터셋입니다. 저희는 세 가지 출처를 합쳐 총 1,149개 샘플을 만들었습니다.

  • 기존 대화 트레이스 836개
  • 저희가 운영하는 옵시디언 위키 문서 297개
  • 워커 라우팅 패턴(어떤 요청을 어떤 워커로 보내야 하는지) 16개

지금 돌이켜보면 이 1,149개라는 숫자가 나중에 생긴 거의 모든 문제의 근본 원인이었습니다. 미리 말씀드리면, 적은 샘플로도 “짧고 고정된 답변”은 확실히 학습되지만, “여러 정보를 종합해서 설명해야 하는 질문”에는 한계가 뚜렷합니다.

2단계: LoRA 파인튜닝 — 생각보다 빨랐다

학습은 Google Colab의 무료 T4 GPU로 진행했습니다. 베이스 모델은 google/gemma-2-2b-it가 아니라 게이트(접근 승인)가 걸리지 않은 미러 모델인 unsloth/gemma-2-2b-it를 사용했는데, 이게 나중에 GGUF 변환 단계에서 다시 발목을 잡습니다(뒤에서 설명합니다).

LoRA 학습은 143스텝, 약 10분, 최종 loss 1.67로 마무리됐습니다. 무료 GPU로 10분이면 끝난다는 게 의외로 가벼웠던 부분입니다. 어댑터 파일(safetensors)만 떨어지기 때문에 이 단계는 비교적 순탄했습니다.

3단계: GGUF 변환에서 두 번 연속 막히다

LoRA 어댑터는 그 자체로는 쓸 수 없고, 베이스 모델에 합친 뒤 Ollama가 읽을 수 있는 GGUF 포맷으로 변환해야 합니다. 여기서부터 진짜 고생이 시작됐습니다.

1차 실패 — Colab에서 GGUF 쓰기 작업(f16, 정상 크기 5.23GB)을 실행했는데, 로그에 Writing: 0% 0.00/5.23G라고 찍힌 시점에서 곧바로 다음 셀(다운로드)을 실행해버렸습니다. 그 결과 다운로드된 파일은 GGUF 매직 헤더와 텐서 개수(288개)는 정상이었지만, 실제 크기는 5.77MB밖에 안 되는 빈 껍데기였습니다. 양자화 단계도 당연히 건너뛰어졌습니다.

2차 실패 — 이번엔 충분히 기다려서 f16 변환(5.24GB)은 100% 완료됐는데, 양자화가 또 스킵됐습니다. 원인은 llama.cpp가 클론만 되고 빌드는 안 되어 있어서 양자화 실행 파일(llama-quantize) 자체가 없었던 것이었습니다. GGUF 변환·양자화는 GPU가 필요 없는 작업이라, 결국 Colab을 포기하고 로컬 PC에서 직접 처리하기로 방향을 바꿨습니다.

로컬에서 진행하면서도 사소한 함정이 줄줄이 나왔습니다.

  • 기본 python 명령이 Python 3.14의 free-threaded(3.14t) 빌드를 가리켜서 torch의 C 확장이 로드되지 않음 → 일반 GIL 빌드로 별도 가상환경을 새로 만들어 해결
  • numpy 1.26 버전을 고정 설치하려다 Cython 빌드 실패 → 버전 고정을 풀고 numpy 2.x로 해결
  • 병합 단계에서 google/gemma-2-2b-it 401 Gated repo 에러 → 변환 노트북에 베이스 모델이 잘못 하드코딩돼 있던 것을 발견, 학습 때 썼던 게이트 없는 미러 모델로 통일해 해결
  • f16 변환은 됐지만 tokenizer.model이 병합 과정에서 누락 → LoRA 어댑터 폴더에 있던 파일을 복사해 해결

최종적으로 Q4_K_M 양자화를 거쳐 5.23GB였던 f16 파일이 1.71GB로 줄었고, Ollama에 gemma4-aria:latest로 등록했습니다. 첫 스모크 테스트(“Aria야 안녕, 네 역할이 뭐야?”)에 CEO 역할과 4개 워커 구성을 정확히 설명하는 응답이 돌아왔습니다. CPU 추론으로 37초가 걸렸지만, 일단 LoRA가 반영된 모델이 살아 움직인다는 걸 확인한 순간이었습니다.

4단계: 실전에 배포하니 또 다른 버그가 기다리고 있었다

모델이 도니까 끝난 줄 알았는데, 실제 텔레그램 봇으로 테스트하자마자 두 가지 문제가 터졌습니다.

타임아웃과 깨진 응답 — Ollama 호출 코드가 시스템 프롬프트와 사용자 메시지를 하나의 raw 텍스트로 합쳐서 /api/generate로 보내고 있었습니다. 그런데 파인튜닝은 Gemma의 채팅 템플릿(<start_of_turn>user/model) 포맷으로 진행했기 때문에, raw prompt로 호출하면 모델이 형식을 못 맞추고 이상한 메타 발화를 만들어냈습니다. /api/chat으로 바꿔 messages 구조를 쓰게 하니 바로 정상화됐습니다. 타임아웃도 60초에서 180초로 늘리고, 매 요청마다 모델이 재로딩되지 않도록 keep_alive를 30분으로 설정했습니다.

정체성 환각 — “너는 누구야?”라고 물으니 “저는 네이버에 등록된 AI 챗봇인데요” 같은 황당한 답이 나왔습니다. 1,149개라는 비교적 적은 샘플로는 베이스 모델이 사전학습 단계에서 흡수한 “OO 챗봇입니다” 같은 한국어 패턴을 완전히 덮어쓰지 못한 겁니다. 시스템 프롬프트에 “어떤 회사에도 속하지 않는다”는 명시적 제약을 추가해서야 정상화됐습니다.

5단계: RAG로 정확한 문서를 줘도 사실을 지어낸다

여기서부터가 가장 인상적이었던 발견입니다. 위키 검색 기능을 키워드 매칭에서 임베딩 기반 의미 검색으로 바꾸면서 정확도는 확실히 좋아졌습니다. 예전에는 “local.aria-ai 프로젝트 진행 단계”를 물으면 전혀 무관한 “Aria AI 블로그 코퍼스” 문서를 가져오는 식이었는데, 검색 로직을 고치고 나서는 정확히 맞는 문서를 찾아왔습니다.

문제는 그 다음이었습니다. 정확한 문서를 컨텍스트로 줘도, 모델이 거기 없는 내용을 자신 있게 지어냈습니다. 실제 GitHub 저장소가 없는데 “https://github.com/local-aria-ai가 생성됨”이라고 답하거나, 실행도 안 한 평가를 “98.2% 성공률로 완료”라고 답하는 식이었습니다. 심지어 이 프로젝트는 Gemma·Ollama로만 구성돼 있는데 전혀 관련 없는 GPT-4, GPT-3.5, Jenkins CI/CD까지 표로 만들어 끌어다 쓰기도 했습니다. 프롬프트에 “참고한 문서명을 표기하라”는 지침을 넣었더니, 모델은 그 지침 문구 자체를 그대로 출력하고 실제 문서명은 채우지 않았습니다.

정리하면 이렇습니다.

  • 짧고 고정된 질문(정체성 등)에는 파인튜닝 + 프롬프트만으로 충분히 정확합니다.
  • 여러 문단을 종합해야 하는 복잡한 질문은, 검색이 완벽해도 모델이 사실을 지어내는 걸 프롬프트만으로 막을 수 없습니다.
  • 검색 인프라(임베딩 기반 의미 검색) 자체는 잘못이 없고 계속 쓸 수 있는 자산입니다. 문제는 1,149개 샘플로 학습된 2.6B 모델의 생성 능력 한계였습니다.

마무리 — 다음에 할 일

저희는 이 시점에서 “지식 학습 정확도 올리기”는 일단 보류하고, 검증된 부분(워커 라우팅, 작업 결과 자동 위키 저장)부터 안정화하는 쪽으로 방향을 틀었습니다. 환각을 근본적으로 줄이려면 두 가지 선택지가 남아있습니다 — 학습 데이터를 더 늘리거나, 더 큰 모델로 교체하는 것입니다. 둘 다 비용이 드는 결정이라 천천히 검증하면서 진행할 계획입니다.

작은 모델을 직접 파인튜닝해서 운영해보면, 데모 단계에서는 멋지게 작동해도 실전 배포·RAG 연동까지 가면 생각보다 많은 함정이 숨어 있다는 걸 체감하게 됩니다. 이 글이 비슷한 시도를 하시는 분들께 어떤 단계에서 무엇이 막힐 수 있는지 미리 가늠하는 데 도움이 되길 바랍니다.

댓글 달기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

위로 스크롤