Gemma/Qwen 지식 증류: Multi-label 태그 분류 실험
JMTguri는 서울/경기/인천/제주 지역의 음식점 리뷰를 수집·분석하여 맛집을 추천하는 서비스다. 약 9.5만 개 음식점에 대해 자체 구축한 리뷰 데이터셋을 보유하고 있으며, Gemini 2.5 Flash Lite를 활용한 시멘틱 분석으로 39개 태그(가성비, 혼밥, 데이트 등)를 자동 부여하고 있다.
유형: Multi-label classification (39개 태그 중 0~5개 선택)
입력: 음식점 카테고리/이름 + 리뷰 텍스트 (최대 20개)
출력: JSON 배열 (예: ["가성비", "혼밥", "웨이팅있음"])
평가 지표: Micro F1, Exact Match (전체 태그 셋 일치율)
일반 벤치마크에서 Qwen 3.5는 MMLU-Pro, GPQA, 코딩 등 대부분의 지표에서 Gemma 3을 압도한다. 특히 다국어(CJK) 성능에서 큰 격차를 보인다(일본어: 87.8 vs 76.2). 그러나 파인튜닝 적합성은 별개의 문제이므로, 두 모델 계열을 모두 실험했다.
모든 실험에서 LoRA(Low-Rank Adaptation)를 사용했다. 전체 파라미터의 0.15~0.19%만 학습하므로 96GB 통합 메모리에서 충분히 동작한다.
공통 설정: optimizer=adamw, lr=1e-5, mask_prompt=true, seed=42
첫 실험에서는 원본 데이터(리뷰 20개, max_seq=4096)로 Gemma를, 축약 데이터(리뷰 5개, max_seq=512)로 Qwen을 각각 2000 iteration 학습했다. Qwen의 max_seq가 짧은 이유는 bf16 모델의 Metal GPU 메모리 제약 때문이다.
Gemma는 base 40.4%에서 75.6%로 35.2pp 상승하며 파인튜닝 효과를 강하게 보였다. 반면 Qwen은 thinking 모드가 활성화된 상태에서 F1 15-18%에 머물렀다. thinking을 비활성화하자 59-62%로 올라갔지만 Gemma에 크게 미치지 못했다.
Qwen의 낮은 성능 원인은 두 가지였다. 첫째, Qwen 3.5의 chat template이 추론 시 <think>...</think> 블록을 자동 생성하여 max_tokens의 대부분을 소모했다. 둘째, 학습 데이터(리뷰 5개)와 평가 데이터(리뷰 20개)의 분포 불일치가 있었다.
v1에서 확인된 학습/평가 분포 불일치를 해소하기 위해, 원본 데이터에서 Qwen tokenizer 기준 1024 토큰 이하인 예시만 필터링하여 재학습했다. 73,028개 중 27,875개(38.2%)가 조건을 충족했다.
Qwen 9B v2는 75.8%로 Gemma v1(75.6%)을 0.2pp 역전했다. 데이터 품질(원본 리뷰 유지)이 양(73K vs 27K)보다 중요하다는 것을 보여주는 결과다.
v2에서 62%의 데이터를 버린 점을 개선하기 위해, 모든 예시의 리뷰를 동적으로 잘라 1024 토큰 이내로 맞추는 truncation 방식을 적용했다. 73,026개(99.99%)를 보존할 수 있었다. 동시에 이터레이션을 2000에서 4000으로 늘렸다.
Gemma와 Qwen 4B는 소폭 개선되었지만, Qwen 9B는 오히려 2.1pp 하락했다. truncation으로 리뷰가 줄어들면서 정보 손실이 발생한 것이 원인으로 추정된다. 데이터 양(73K)보다 개별 예시의 품질(원본 리뷰 유지)이 더 중요하다는 것을 다시 한번 확인했다.
Gemma 4B v3가 전체 1위(F1=76.5%)이며, Qwen 9B v2가 근소한 차이로 2위(75.8%)를 기록했다. 두 모델 모두 zero-shot 대비 35pp 이상의 개선을 달성했다.
best 모델(Gemma v3)의 태그별 F1을 보면, 명확한 패턴 기반 태그는 높은 성능을, 주관적/희소 태그는 낮은 성능을 보인다.
전체적으로 Precision(78%)이 Recall(75%)보다 높아 모델이 보수적으로 태그를 부여하는 경향이 있다.
Qwen 3.5는 추론 시 <think>...</think> 블록을 자동 생성한다. 파인튜닝 학습 중에는 chat template이 빈 <think></think> 를 삽입하므로 문제가 없지만, 추론 시에는 모델이 자유롭게 수백 토큰의 사고 과정을 생성하여 실제 답변(태그 JSON)을 위한 토큰이 부족해진다.
해결: tokenizer.apply_chat_template(messages, enable_thinking=False)
로 추론 시에도 빈 think 블록을 강제 삽입. 이를 통해 Qwen 4B F1이 18% → 59%(+41pp), 9B가 15% → 62%(+47pp)로 개선되었다.
Qwen bf16 모델에서 max_seq_length=512로 설정하면 일부 예시의 assistant 응답이 잘려 학습 토큰이 0개가 되고, mask_prompt=true 상태에서 NaN loss가 발생했다. 이 NaN이 gradient를 오염시켜 이후 모든 학습이 실패한다.
해결: Qwen tokenizer로 실제 토큰 길이 분포를 측정한 결과, 21%의 예시가 512 토큰을 초과했다. max_seq_length=1024 로 설정하여 99%를 커버하면서도 GPU 메모리 범위(26-30GB) 내에서 학습이 가능했다.
장시간 학습 중 kIOGPUCommandBufferCallbackErrorInnocentVictim 에러로 프로세스가 종료되는 현상이 반복되었다. OOM이 아닌 Metal 프레임워크의 GPU error/recovery 메커니즘에 의한 것으로, MLX LoRA의 --save-every 옵션으로 주기적 checkpoint를 저장하고 --resume-adapter-file 로 이어서 학습하는 방식으로 대응했다.
v2(27K, 원본 리뷰 유지)와 v3(73K, 리뷰 truncation) 비교에서 Qwen 9B는 데이터가 2.6배 많은 v3에서 오히려 F1이 2.1pp 하락했다. 개별 예시의 정보 완결성이 더 중요하다.
일반 벤치마크에서 Qwen 3.5는 Gemma 3을 대부분의 지표에서 압도하지만(MMLU-Pro: 86.1 vs 67.5), 파인튜닝 태스크에서는 Gemma 4B(76.5%)가 Qwen 4B(71.8%)를 5pp 앞선다. Gemma의 bf16 안정성과 thinking 모드 부재가 파인튜닝에 유리하게 작용했다.
Qwen 4B(71.8%) → 9B(75.8%)로 4pp 개선이 있었지만, 파라미터가 2배 이상 증가한 것 대비 제한적이다. 이 태스크에서는 4B 모델로도 충분한 성능을 달성할 수 있으며, 추론 속도(Gemma 4B: 0.7/sec, Qwen 9B: 0.8/sec)도 비슷하여 실용적 관점에서 Gemma 4B가 최적의 선택이다.
Qwen의 thinking 모드 사례에서 보듯, 학습 시 chat template이 적용하는 포맷과 추론 시 포맷이 일치하지 않으면 성능이 급락한다(18% vs 59%). 파인튜닝 시 반드시 추론 환경과 동일한 template 설정을 확인해야 한다.
적합한 용도: 신규 가게의 초기 태그 자동 부여 → 사용자 피드백으로 보정. 프론트엔드 필터링/검색용 태그로 활용. 잘못된 태그 1-2개는 사용자 경험에 큰 영향이 없으며, 주요 태그(가성비, 주차가능 등)의 정확도가 80-92%로 높다.
부적합한 용도: 태그 기반의 정밀한 추천 시스템. 희소 태그(접대/비즈니스, 비건옵션 등)의 정확도가 0-50%로 낮아 이를 기반으로 한 필터링은 신뢰도 부족.
Recall 보정: 추론 시 temperature를 0.0 → 0.3으로 올리거나, 3회 추론 후 다수결 투표(voting)로 recall 개선. 현재 P=78% R=75% → P≈75% R≈78%로 균형을 맞출 수 있음.
Teacher 모델 업그레이드: 현재 ground truth는 Gemini Flash Lite(가장 약한 모델). Gemini 2.5 Flash로 재생성하면 태그 정확도 자체가 올라가서 F1 ceiling이 상승. 예상 비용 $5-10.
Gemma 9B/27B 실험: Gemma 4B bf16이 안정적이었으므로 더 큰 모델은 추가 성능 향상 가능. 96GB RAM에서 27B 4-bit 학습이 가능할 것으로 예상.
전체 시멘틱 추출 파인튜닝: 태그뿐 아니라 signature_menu, vibe, good/bad, tips까지 포함한 full extraction. 출력이 길어지므로 assistant 토큰 비율 문제가 자연스럽게 해소됨.
프로덕션 통합: ollama 또는 mlx_lm 서버로 로컬 추론 서비스 구축. 신규 가게 수집 파이프라인에 자동 태그 부여 단계 추가.