파인튜닝(Fine-Tuning)과 RAG: 12번의 실패한 실험이 내게 가르쳐준 것
요약
금융 데이터 예측 시스템 구축 과정에서 수행한 12번의 파인튜닝 및 RAG 실험 실패 사례를 분석합니다. 엄격한 통계적 검증을 통해 파인튜닝과 RAG가 데이터 자체의 예측 가능성을 높여주지는 못한다는 결론을 도출합니다.
핵심 포인트
- 파인튜닝과 RAG는 입력 측면의 개선일 뿐, 데이터의 신호(Signal) 자체를 생성하지 못함
- 생성 손실(Generative loss) 지표가 실제 다운스트림 품질을 보장하지 않음
- 단순 Train/Test 분할 대신 워크 포워드, 순열 검정 등 엄격한 통계적 평가가 필수적임
- 모델 규모가 커져도 손실값이 낮아진다고 해서 반드시 예측 성능이 향상되는 것은 아님
인터넷에는 파인튜닝(Fine-tuning) 대 RAG(검색 증강 생성)에 대해 매우 강한 의견들이 존재하지만, 그중 대부분은 실제로 실험을 수행해 본 적이 없는 사람들의 의견입니다.
저는 노이즈가 심한 금융 결과에 대해 미래 예측을 수행하는 실제 시스템에서 약 12번의 실험 — LLM(대규모 언어 모델) 파인튜닝, 임베더(Embedder) 파인튜닝, 그리고 6가지 방식의 RAG — 을 진행했습니다. 이 과정에서 여러분이 가장 좋아하는 결과조차 무너뜨릴 정도의 통계적 엄밀함, 즉 워크 포워드 분할(Walk-forward splits), 순열 검정(Permutation tests), 다중 비교 교정(Multiple-comparison correction), 사전 등록된 킬 게이트(Pre-registered kill-gates)를 적용했습니다. 대부분의 실험은 실패했습니다. 하지만 그 사후 분석(Autopsies)은 그 어떤 자극적인 의견보다 훨씬 유용했습니다. 왜냐하면 그 모든 실패가 동일한 하나의 규칙을 가리키고 있었기 때문입니다.
글의 나머지 내용을 이해하기 위해, 그 규칙을 먼저 말씀드리겠습니다.
파인튜닝(Fine-tuning)과 RAG는 모두 입력(Input) 측면, 즉 모델이 알고 있는 어휘와 모델이 볼 수 있는 컨텍스트(Context) 측면에서 작동합니다. 두 방식 모두 당신이 예측하려는 대상이 데이터로부터 실제로 예측 가능한지 여부를 바꾸지는 못합니다. 과업이 "단어를 알기 / 사실을 찾기 / 지식을 회상하기"인 경우에는 둘 다 효과적입니다. 하지만 과업이 "노이즈가 심한 결과를 예측하기"인 경우, 두 방식 모두 존재하지 않는 신호(Signal)를 만들어내지 못합니다. 그저 당신이 스스로를 속일 수 있는 더 설득력 있는 방법들을 제공할 뿐입니다.
이제 실패한 사례들을 보여드리겠습니다.
내 결과가 블로그들의 통념과 다른 이유에 대하여
아래의 거의 모든 실험은 단순한 훈련/테스트 분할(Train/test split) 방식에서는 좋아 보였습니다. 실패는 오직 엄격한 규율 아래에서만 드러났습니다. 엠바고를 포함한 워크 포워드(Walk-forward), 유의성을 확인하기 위한 순열 검정(Permutation tests), 많은 셀을 테스트했기에 적용한 Holm/BH-FDR 교정, 단순히 시장 베타(Market beta)를 포착하는 것이 아님을 확인하기 위한 SPY 반사실적(Counterfactual) 검증, 그리고 손익(P&L)이 아무리 좋아 보여도 반드시 통과해야 하는 구속력 있는 캘리브레이션 게이트(Calibration gate) 등이 그것입니다.
그 격차가 바로 이야기의 핵심입니다. 만약 당신이 단일 무작위 분할에서 파인튜닝이나 RAG를 평가하고 헤드라인 지표만 읽는다면, 정직하게 테스트하는 순간 증발해 버릴 우위(Edges)를 "발견"하게 될 것입니다. 기술이 어려운 것이 아닙니다. 평가는 어렵습니다.
파트 1 — 파인튜닝(Fine-tuning): 4번의 사후 분석
더 큰 모델, 더 낮은 손실(Loss), 더 나쁜 결정
저는 동일한 작업에 대해 약 777개의 레이블링된 예시(labeled examples)를 사용하여 7B 모델과 14B 오픈 모델을 각각 파인튜닝(Fine-tuning)했습니다. 14B 모델은 더 나은 평가 손실(evaluation loss)을 기록하며 종료되었습니다 (0.97 대 1.01). 하지만 홀드아웃 데이터(held-out data)에 대해서는 더 나쁜 결정을 내렸습니다: 7B의 승률이 68.4%인 반면 14B는 46.2%에 그쳤으며, 이는 22%포인트의 격차(p=0.019)를 보였습니다.
두 가지 교훈을 얻었으며, 두 가지 모두 직접 겪으며 배우기에는 비용이 많이 들었습니다:
- 생성 손실(Generative loss)은 다운스트림 품질(downstream quality)을 예측하지 못합니다. 14B 모델은 손실 지표가 측정하는 것(토큰 재현)에는 더 뛰어났지만, 제가 중요하게 생각하는 것(정답을 맞히는 것)에는 더 서툴렀습니다. 만약
eval_loss를 기준으로 모델을 선택한다면, 여러분은 확신을 가지고 더 나쁜 모델을 출시하게 될 것입니다. - 적은 데이터는 큰 모델에 불리합니다. 수백 개에서 수천 개의 예시만 있는 경우, 추가적인 용량(capacity)은 더 깊은 구조를 찾아내는 것이 아니라, 노이즈와 실수를 포함한 레이블 생성기(label generator) 자체를 암기해 버립니다. 작은 모델의 제한된 용량은 암묵적 규제(implicit regularization) 역할을 합니다. 즉, 단순하고 일반적인 패턴을 학습하도록 강제됩니다. 예시가 약 3,000개 미만이라면, 더 작은 모델을 선택하십시오.
분류기(Classifier)를 파인튜닝하면 안전한 답을 학습한다
방향성을 결정하도록 모델을 파인튜닝하려는 세 번의 별도 시도에서 모두 동일한 결과가 나타났습니다: 모델이 안전하고 다수결에 따르는 답변으로 수렴(converge)한 것입니다. 모델은 신호(signal)를 학습한 것이 아니라, *레이블의 분포(distribution of the labels)*를 학습했습니다. 노이즈가 있는 타겟의 경우, 이는 "흔한 클래스를 예측하고 위험을 감수하지 마라"는 의미가 됩니다.
찾을 신호가 없을 때 파인튜닝이 하는 일이 바로 이것입니다. 레이블 분포에는 완벽하게 맞추지만(fit), 여러분에게 아무런 정보도 주지 않습니다. 확신에 차 있고 손실이 낮은 모델이 나타났다고 해서, 그것이 유의미한 엣지(edge)가 존재한다는 증거는 아닙니다.
초소형 트랜스포머(Tiny-transformer)의 함정
저는 주문 흐름(order-flow)을 시퀀스로 토큰화(tokenize)하고, 방향성 타겟(direction target)을 대상으로 약 13만 개의 파라미터를 가진 트랜스포머(transformer)를 학습시켰습니다. 모델은 결코 수렴하지 않았습니다. 학습 손실(training loss)은 정확히 동전 던지기 확률인 -log(0.5) 바닥에 머물렀습니다. 반면, 12개의 수동 제작된 피처(features)를 사용해 동일한 타겟으로 학습시킨 평범한 그래디언트 부스팅(gradient-boosted) 모델은 AUC 0.71에 도달했습니다.
그 신호는 실재했고 학습 가능했습니다. 단지 아키텍처가 그 규모(scale)를 감당하지 못했을 뿐입니다. "트랜스포머(Transformer)를 사용하라"는 말은 신호(signal)나 규모(scale)를 대체할 수 없습니다. 13만 개의 파라미터를 가진 모델은 12개의 좋은 피처(feature)를 통해 GBM이 읽어내는 정보를 원시 토큰(raw tokens)으로부터 발견할 수 있는 용량(capacity)이 전혀 없습니다. 당신이 복제하고 있는 논문에 나온 아키텍처가 아니라, 당신이 실제로 가지고 있는 데이터에 맞춰 아키텍처를 선택하십시오.
임베더(Embedder) 파인튜닝: 전적으로 당신의 어휘(vocabulary)에 달려 있습니다
이 실험은 해피엔딩으로 끝났으며, 파인튜닝이 언제 효과가 있는지를 알려주기 때문에 모든 결과 중 가장 유용합니다. 저는 두 개의 코퍼스(corpora)를 사용하여 작은 임베더(all-MiniLM-L6-v2)를 도메인 적응(domain-adapted)시켰고, 홀드아웃 쿼리(held-out queries)에 대한 검색 품질(MRR, nDCG@10)을 측정했습니다:
- 기본 모델이 이미 이해하고 있는 "풍부한(rich)" 코퍼스: 기본 MRR 1.00 → 파인튜닝 후 MRR 1.00. Δ+0.00. 성능 향상이 전혀 없었습니다. 이는 개선할 여지(headroom)가 없었기 때문에 나타난 정확한 결과입니다.
- 기본 모델이 본 적 없는 코드네임 뒤에 개념이 숨겨진 "불투명한 전문 용어(opaque-jargon)" 코퍼스: 기본 MRR 0.306 → 파인튜닝 후 0.547. MRR +0.24, 상대적 +79%.
결론은 자명합니다: 임베더의 파인튜닝은 당신의 코퍼스 어휘가 기본 모델이 이미 알고 있는 것과 달라지는 정도만큼만 수행하십시오. 이미 잘 다뤄진 도메인인가요? 얻을 것이 없습니다. 내부 전문 용어, 사내 코드네임, 특수 어휘인가요? 바로 그 지점이 파인튜닝이 제값을 하는 곳입니다. (전체 통제 연구는 오픈 소스로 RE-call에서 확인 가능합니다.)
파인튜닝의 핵심 관통선(through-line): 파인튜닝은 어휘와 행동(vocabulary and behavior) 도구입니다. 모델이 갖지 못했던 단어, 형식, 스타일을 가르칩니다. 노이즈가 심한 타겟(target)으로부터 예측 신호(predictive signal)를 만들어내지는 못합니다. 오히려 다운스트림 메트릭(downstream metric) 대신 손실(loss)을 기준으로 측정한다면, 파인튜닝은 당신을 적극적으로 오도할 것입니다.
파트 2 — RAG: 여덟 번이나 부정된 아이디어
"작동은 하지만, 도움이 되지 않는다"
파트 2 — RAG: 여덟 번이나 부정된 아이디어
"작동은 하지만, 도움이 되지 않는다"
유혹적인 아이디어는 다음과 같습니다: 관련 과거 이력(history)을 검색(retrieve)하여 분류기(classifier)의 프롬프트(prompt)에 주입함으로써, 모델이 더 많은 문맥(context)을 바탕으로 결정하게 만드는 것입니다. 저는 이를 여덟 번에서 아홉 번에 걸쳐 테스트했습니다. 두 개의 모델, 세 개의 임베딩 스택(embedding stacks), 네 가지 검색 대상(자신의 이력, 섹터 동료, 결과 감독 REPLUG-LSR 어댑터, 니치 조건부 슬라이싱), 그리고 세 가지 패러다임(유사도, 결과 감독 증류(outcome-supervised distillation), 구조화된 특징 추출(structured-feature extraction))을 대상으로 진행했습니다.
매번 타당성 검사(sanity check)는 통과했습니다. 즉, RAG가 *작동(fired)*하여 모델 결정의 25~33%를 변화시켰습니다. 하지만 매번 그 변화된 결정들은 실제 미래 결과(forward outcome)와 비교했을 때 통계적으로 노이즈와 구별할 수 없는 수준이었습니다. 여러 차례의 반복과 다중 비교 교정(multiple-comparison correction)을 거치는 동안, ΔWR(승률 변화량)은 신뢰 구간이 0을 가로지르며 0 근처에서 머물렀습니다.
실패의 메커니즘을 정확히 명시할 가치가 있습니다: RAG는 검색된 과거가 언제 관련이 있는지는 모른 채, 점수의 크기(magnitude)만을 증폭시켰습니다. 긍정적인 과거 문맥은 현재 상황이 반전되었을 때조차 모델을 더 낙관적(bullish)으로 몰아붙였습니다. 그것은 정확성(correctness)이 아니라 확신(confidence)을 더했을 뿐입니다. "작동한다(It fires)"와 "도움이 된다(it helps)"는 완전히 다른 주장이며, 오직 전자의 경우만이 사실이었습니다.
겉보기 알파(apparent-alpha)의 함정
한 가지 RAG 변형 모델은 승리자처럼 보였습니다. 구조화된 특징(structured-feature) 설정은 연간 +11%의 겉보기 알파(apparent alpha)를 기록했는데, 이는 전략을 실제로 배포하게 만들 법한 수치였습니다. 하지만 해당 랭커의 AUC는 0.486이었습니다. 동전 던지기보다 못한 결과였습니다.
이 +11%는 실력이 아니라 국면 착취(regime exploitation)였습니다. 모델이 다수 클래스(majority class)를 더 자주 예측했고, 평가 기간이 우연히 그 클래스에 보상을 주는 시기였던 것입니다. AUC가 0.5 미만인 랭커는 정의상 승자와 패자를 구별할 능력이 없습니다. 따라서 해당 모델이 만들어낸 어떠한 손익(P&L)도 모델의 능력이 아니라 해당 기간의 특성일 뿐입니다.
이것은 이 포스트 전체에서 가장 위험한 실패입니다. 왜냐하면 당신을 치켜세우는 지표(P&L)와 진실을 말해주는 지표(AUC/calibration)가 서로 반대 방향을 가리키기 때문입니다. Calibration(교정)을 각주가 아닌, 반드시 통과해야 하는 구속력 있는 게이트(binding gate)로 만드세요. 확률적 기댓값(chance)보다 낮은 성능을 가진 모델로 얻은 수익성 있는 백테스트(backtest)는 신기루에 불과하며, 실제 돈이 투입되는 라이브(live) 환경에 도달할 때까지 아무 문제 없이 살아남을 것입니다.
RAG가 실제로 제 자리를 찾은 곳
여기서 "RAG는 끝났다"라고 말하는 사람들이 오해하는 부분이 있습니다. RAG는 모든 분야에서 실패한 것이 아닙니다. RAG는 예측(prediction) 분야에서 실패했을 뿐입니다. RAG는 검색(retrieval) 형태의 모든 작업, 즉 검색 가능한 지식 베이스(knowledge base), 인간이 읽을 수 있는 관련 컨텍스트(context) 추출, 모델이 학습하지 않은 사실에 대한 시스템 접근 권한 부여 등에서는 영구적으로 살아남았습니다.
그것이 바로 제 나머지 글들이 다루는 경계선입니다. 의미론적 검색을 통해 코드를 찾는 RAG(semantic code search)는 훌륭하게 작동합니다. 에이전트에게 작동하는 _메모리(memory)_를 제공하는 RAG(RE-call)도 훌륭하게 작동합니다. 하지만 예측하기 어려운 미래를 _판단(call)_하게 만드는 RAG는 아홉 번이나 실패했습니다. 동일한 기술이지만 결과는 정반대입니다. 왜냐하면 이들은 같은 약어를 쓰고 있지만 서로 다른 문제이기 때문입니다.
RAG의 핵심 관통선: RAG는 모델이 _보는 것(sees)_을 바꾸는 것이지, 타겟이 예측 가능한지(predictable) 여부를 바꾸는 것이 아닙니다. 만약 데이터 안에 신호(signal)가 있다면, 검색(retrieval)은 그 신호를 표면으로 끌어올리는 데 도움을 줍니다. 만약 신호가 없다면, 검색은 모델이 더 자신감 있어 보이는 방식으로 틀리게 만들 뿐입니다.
파트 3 — 이 모든 것을 설명하는 규칙
실패 사례들을 나열해 보면 모두 동일한 형태를 띠고 있습니다:
- Fine-tuning(파인튜닝)은 모델의 **어휘(vocabulary)와 행동(behavior)**에 작용합니다.
- RAG는 모델이 사용할 수 있는 **가용 컨텍스트(available context)**에 작용합니다.
- 두 방법 모두 예측 작업의 성패를 결정하는 유일한 질문, 즉 **"타겟이 실제로 입력값의 함수인가?"**에 대해서는 작용하지 않습니다.
따라서 결정해야 할 것은 "Fine-tuning인가 RAG인가"가 아닙니다. _"당신이 가진 문제는 어떤 종류인가?"_입니다.
- "모델이 나의 단어 / 형식 / 도메인을 모릅니다." → Fine-tune (파인튜닝). 만약 검색(Retrieval) 문제라면 임베더(Embedder)를 (단, 어휘가 진정으로 일치하지 않는 경우에만 — RE-call 결과 참조); 만약 행동이나 스타일의 문제라면 LLM을 파인튜닝하세요. 측정은 반드시 다운스트림 메트릭(Downstream metric)으로 해야 하며, 절대 손실(Loss)로 해서는 안 됩니다.
- "모델이 학습되지 않은 사실 / 코드 / 메모리에 접근할 수 없습니다." → RAG. 이것은 검색(Retrieval)의 본진이며, RAG는 이 분야에서 매우 탁월합니다.
- "모델이 나의 노이즈 섞인 결과(Noisy outcome)를 더 잘 예측해야 합니다." → 둘 다 해결책이 될 수 없습니다. 그 어떤 양의 파인튜닝이나 검색도 데이터에 없는 신호(Signal)를 만들어낼 수는 없습니다. 그리고 두 방법 모두 당신이 그렇지 않다고 믿게 만드는 설득력 있는 방식(낮은 손실, 더 두터운 백테스트, 혹은 "작동하는" 것처럼 보이는 RAG)을 제공할 것입니다. 진짜 신호를 찾으십시오. 만약 신호가 없다면 그것을 인정하고, 그것을 재발견하기 위해 비용을 지불하는 일을 멈추십시오.
그리고 위의 모든 결과를 신뢰할 수 있게 만든 메타 레슨(Meta-lesson)은 이것입니다. 거의 모든 결과가 정직한 평가를 받기 전까지는 좋아 보였다는 점입니다. 더 큰 모델은 손실(Loss) 측면에서 승리했습니다. 죽은 RAG 변형 모델은 P&L(손익) 측면에서 승리했습니다. 단순한 분할(Naive splits) 방식은 헤드라인 수치에서 승리했습니다. 워크포워드(Walk-forward), 순열(Permutation), 다중 비교 교정(Multiple-comparison correction), 그리고 구속력 있는 캘리브레이션 게이트(Binding calibration gate)가 흥미롭지만 가짜였던 승리 더미를 수십 개의 신뢰할 수 있는 "아니오"와 두 개의 진짜 "예"로 바꾸어 놓았습니다.
기술은 쉽습니다. 그것이 제대로 작동했는지 아는 것이 업무의 전부입니다.
이 모든 것의 검색 측면 — 실제로 작동하는 RAG를 구축하는 방법과 임베더를 파인튜닝하는 것이 언제 이득이 되는지에 대한 완전한 통제 연구 — 은 오픈 소스로 공개되어 있습니다: RE-call. 응용 측면 — 코드 검색을 위한 RAG와 일상적인 Claude Code에서의 에이전트 메모리 — 은 저의 시리즈인 Claude Code, Beyond the Prompt에 있습니다. 파인튜닝을 하거나 RAG를 결합하려 한다면, 먼저 정직한 평가를 수행하십시오. 그것이 신기루(Mirage)에 빠지는 것보다 훨씬 저렴합니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기