본문 바로가기
개인 프로젝트

LLM + RAG 프로젝트 [7] 타율 0 해결을 위한 튜닝

by 포잉띠 2025. 9. 4.

프리필터 고집 금지 (가장 큼)

  • 정규식 키워드 매칭 실패하면 바로 0건 된다.
  • 파이프라인을 2경로로 바꿔라:
    A. Dense 전 컬렉션 탐색(topK 20) + B. 키워드/별칭으로 좁힌 후보(topK 50)유니온 → 리랭크.
  • 프리필터는 “가산점”이지 “필수”가 아니어야 한다.

게이팅(“컨텍스트 없음” 판정) 완화

  • topk_gap 같은 임계값 때문에 정답을 버리고 있다. 분포를 본 뒤 상대 임계값을 낮추거나, 최상위가 섹션/제목 정합이면 일단 답을 생성하게 해라.
  • 최소 안전장치: 상위 1~3건이 동일 타이틀로 수렴하면 “있다”로 간주.

리랭커 추가 (정확도 급상승 구간)

  • 상위 50~100개를 cross-encoder 리랭크로 재정렬.
    예: bge-reranker-v2-m3(다국어) 같은 소형 모델이면 로컬도 충분.
  • 리랭크 점수로 최종 임계값을 잡아 “모름” 억지 출력 줄여라.

하이브리드 검색

  • Dense + BM25(형태소/불용어 포함) 를 합산(MMR/가중합).
  • Lucene/Elasticsearch/Nori 쓰면 한글 띄어쓰기/변형에 강해진다. Dense 단독보다 “Bang Dream / BanG Dream! / 방도리” 같은 케이스가 잘 잡힌다.

별칭 사전(aliases) 주입

  • 문서마다 aliases 필드를 만들어 인덱스에 함께 벡터화·BM25 색인:
    • 원제/공식 영어/로마자/한국어 표기, 띄어쓰기·구두점 제거판, 시리즈·시즌 약칭(“귀칼”, “5신부”, “방도리”…).
    • 나무위키 리다이렉트/넘겨주기, 괄호부제, 시리즈편명(“환락의 거리편”)을 전부 수집.
  • 정규화 함수(NFKC, 소문자, 공백/특수문자 제거, 한글 자모 정규화)로 쿼리와 별칭을 동일 규칙으로 비교.
  • 빠른 후보 뽑기는 rapidfuzz로 partial_ratio(norm(q), norm(alias)).

섹션 타게팅

  • 쿼리에 ‘요약/줄거리/등장인물/설정/평가’가 보이면 해당 섹션만 우선 조회.
  • 인덱싱 시 모든 청크의 앞머리에 [제목] [섹션]를 프롬프트처럼 붙여라. (제목 미포함 청크는 매칭이 약해진다.)

청크·오버랩 재조정

  • 요약/개요 위주 쿼리는 350~600자 청크 + 60~100자 오버랩이 안정적.
  • 타이틀 문서의 첫 1~2개 청크에는 작품/시즌/별칭 리스트를 함께 넣어 초기 매칭을 강하게.

질의 리라이팅/확장

  • LLM으로 쿼리→동의어/별칭/언어변형 3~5개 생성 → 하이브리드 검색에 병렬 투입.
  • 예) “5등분의 신부” → [오등분의 신부, The Quintessential Quintuplets, 5신부].

도메인 미니-파인튜닝(선택)

  • 수집한 질의-정답/정답문서 쌍으로 hard negative 떠서 bi-encoder를 1~2 epoch만 적응.
  • 안 하더라도, 리랭커만 박아도 체감 향상 크다.

평가 벤치 셋업

  • 100문항 정도로 Recall@k / MRR / Answerable@k를 자동 측정.
  • 카테고리: 작품 단일질의(“Bang Dream 알아?”), “작품+요약”, “시즌편 요약”, “등장인물/관계”, “별칭/오탈자”.

 

우선 리서치 결과를 GPT에 집어넣고 요약 돌렸다.

현재 chroma top-k 타율부터 해서 모든 튜닝이 개판이기 때문에 하나씩 진행해 나갈 예정이다.

LLM 파인튜닝은 후순위로 미룰 예정이다.

일단은 생성에는 문제가 없고 전형적인 GIGO 상황이라 데이터셋도 다시한번 검토해보고

chroma에 잘 들어갔나도 확인해보고 위 절차를 하나씩 밟아야겠다.

ㄱㄱ씽

 


 

1차 확인 결과

chroma에 들어간게 개판이었다. ㅋㅋ....

요약 + 다른 컨텍스트 까지 집어넣었어야 했는데 요약만 잔뜩 들어갔고 title로 안들어가고 doc_id로 들어가서 

정확히 반반무, 사실상 a/b가 안되고 뭔짓을 해도 동일한 데이터를 바라보게 되더라.

일단 chroma에 재인덱싱부터 진행하고 다시 기존 버전과 a/b 테스트 진행해야겠다.

 

 


bge-reranker-v2-m3 사용 X, 표본 500개, top-6, 요약 섹션만 

 

 

--- Report (B) ---
recall@5  0.130
p95       501 ms
dup_rate  0.000

=== Self A/B on Chroma (N=500, k=6, section='요약', by=title) ===
Hit@k      | A=0.1000  B=0.1320  Δ%=32.00%  Δ=0.0320  CI95=(0.0140,0.0520)  win=51.60%
MRR        | A=0.0832  B=0.1290  Δ%=55.05%  Δ=0.0458  CI95=(0.0268,0.0668)  win=52.60%
nDCG       | A=0.0873  B=0.1297  Δ%=48.64%  Δ=0.0424  CI95=(0.0240,0.0627)  win=52.60%
Latency    | A=448.9800  B=360.9744  Δ%=-19.60%  Δ=-88.0056  CI95=(-93.2400,-82.8946)  win=93.00%

전체적으로 지표 자체가 변하긴 했다.

레이턴시가 기존 개판 인덱싱일때보다 90ms정도 빨라졌다. 

다만 recall 수치가... 13%? 라서 조금 씁....................


bge-reranker-v2-m3 사용 O, 표본 41개, top-6, 모든 섹션

 

--- Report (B) ---
recall@5  0.122
p95       505 ms
dup_rate  0.000

=== Self A/B on Chroma (N=41, k=6, section='', by=title) ===
Hit@k      | A=0.0976  B=0.1220  Δ%=25.00%  Δ=0.0244  CI95=(0.0000,0.0732)  win=51.22%
MRR        | A=0.0976  B=0.1220  Δ%=25.00%  Δ=0.0244  CI95=(0.0000,0.0732)  win=51.22%
nDCG       | A=0.0976  B=0.1220  Δ%=25.00%  Δ=0.0244  CI95=(0.0000,0.0732)  win=51.22%
Latency    | A=412.9744  B=340.0477  Δ%=-17.66%  Δ=-72.9267  CI95=(-98.1084,-49.1230)  win=87.80%

 

모든섹션, 리랭크 썼을때 속도는 훨씬 빨라졌지만 정확도 자체는 흠... 더 떨어졌다......

다행힌건 이래도 중복이 안잡히고 있어서 다행이다.