회사 상품 추천 모델 만들기 (7) 작은 시너지, 큰 교훈, 과적합 안정화와 회고

· 유창연 · 21 min read

단독으로 회귀했던 learning rate 변경이 정규화 강화와 결합되면 시너지를 낸 과정. 학습 곡선의 정점이 epoch 1에서 10으로 이동한 이유, 그리고 7부작 전체에서 여전히 모르는 것 위주의 회고.

회사 상품 추천 모델 만들기 (7) 작은 시너지, 큰 교훈, 과적합 안정화와 회고

앞 편 요약: #6에서 네 가지 실패를 가정 종류별(데이터·피처·구현·수학적)로 분류했습니다. 그 중 learning rate 변경(D_lr)은 단독으로 회귀했고, 정규화 강화(D_reg)는 단독으로 유효했습니다.

이 편의 핵심 트레이드오프는 “하이퍼파라미터를 독립적으로 볼 것인가, 상호작용으로 볼 것인가”였습니다. 결론은 단독 변경의 결과만 보면 결합의 시너지를 놓친다는 것이었습니다. 그리고 이 시너지가 시리즈 마지막 단계의 결과를 만들었습니다.

이 편의 절반은 회고입니다. 알게 된 것보다 여전히 모르는 것에 더 큰 지면을 할애했습니다. 시리즈를 마칠 때 가장 정직한 마무리라고 생각해서입니다.


1. 과적합 진단: CB의 학습 곡선

#4에서 채택한 wb_rs 위에 #6의 실험을 거치며 정착한 베이스를 “CB”(content baseline)로 불렀습니다. 5-seed mean=0.0573±0.0021. 안정적이지만 학습 곡선을 보면 이상한 패턴이 있었습니다.

e1=0.0379  e2=0.0539  e3=0.0569  e4=0.0518  e5=0.0506
e6=0.0574  e7=0.0521  e8=0.0509  e9=0.0491
loss:  3.93 ──────────────────────────────→ 3.14

train loss는 매끄럽게 감소하는데, val recall은 epoch 3에서 정점을 찍었다가 진동하면서 하락합니다. e6에서 한 번 더 회복하고 다시 하락. 자료를 찾아보니 전형적인 과적합 + LR 노이즈 패턴이라고 합니다.

2. 두 가지 변경의 단독 효과

이 패턴을 잡기 위해 두 변경을 따로 시도했습니다.

D_lr (learning rate 변경)

학습 후반의 진동을 줄이기 위해 LR(learning rate, 학습률)을 낮추고 cosine annealing(코사인 모양으로 학습률을 천천히 줄이는 스케줄) 스케줄을 도입했습니다.

학습률(learning rate)·epoch·스케줄

#3에서 학습을 “틀린 만큼 한 걸음씩 내려가기”로 설명했습니다. 학습률(learning rate) 은 그 한 걸음의 보폭입니다. 보폭이 너무 크면 골짜기를 건너뛰며 진동하고, 너무 작으면 바닥에 닿기도 전에 시간이 다 갑니다.

epoch(에폭) 는 학습 데이터를 처음부터 끝까지 한 바퀴 도는 것을 말합니다. 10 epoch면 같은 데이터를 열 번 반복해 본다는 뜻이고, 한 epoch는 다시 여러 개의 작은 묶음(step·배치)으로 나뉩니다.

cosine annealing 은 학습이 진행될수록 보폭을 코사인 곡선 모양으로 서서히 줄이는 방법입니다. 처음엔 큰 걸음으로 빠르게 내려오다가 골짜기 근처에선 조심스럽게 줄여 안착시키는 전략입니다. 다만 보폭을 너무 일찍 줄이면 정작 바닥에 닿기 전에 멈춰버리는데, 이 편에서 D_lr이 단독으로 회귀한 게 바로 그 경우였습니다.

항목CBD_lr
learning_rate1e-33e-4
scheduleconstantCosineAnnealingLR(T_max=30)
num_epochs1030 (cosine을 여유롭게)
patience34

기대: 후반 진동 감소, 더 안정적인 정점.

결과(3-seed 스크리닝):

변형meanbest_epoch판정
CB0.05946베이스
D_lr0.05437단독은 회귀

회귀였습니다. LR을 낮춰 진동은 줄었지만, 그 LR이 정점에 도달하기에는 부족했다는 추정입니다. cosine annealing이 후반에 LR을 더 낮추면서 모델이 충분히 학습되지 못한 채로 학습이 끝났습니다.

D_reg (regularization 강화)

과적합을 직접 잡기 위해 정규화를 강화했습니다.

항목CBD_reg
dropout0.20.35
weight_decay1e-51e-4

정규화: weight decay와 dropout, 그리고 patience

정규화(regularization) 는 모델이 학습 데이터를 통째로 외우는(과적합) 걸 막는 장치들을 통칭하는 말입니다.

weight decay(가중치 감쇠) 는 모델 내부의 숫자(가중치)가 너무 커지지 않도록 매 걸음 살짝 0 쪽으로 잡아당기는 것입니다. 특정 입력 하나에 과하게 민감해지는 걸 눌러 일반화를 돕습니다. 값을 1e-5에서 1e-4로 올렸다는 건 그 잡아당기는 힘을 10배로 키웠다는 뜻입니다.

dropout 은 (#3에서 봤듯) 학습할 때마다 뉴런 일부를 임의로 꺼서, 몇몇 뉴런에만 기대지 못하게 하는 장치입니다. 0.2에서 0.35로 올리면 매번 더 많은 뉴런을 끄는 셈입니다.

patience(인내) 는 early stopping(조기 종료)의 설정값입니다. val 성능이 더 나아지지 않는 epoch가 patience만큼(예: 3번) 연속되면 학습을 멈춥니다. 과적합이 본격화되기 전에 끊어주는 안전장치입니다.

결과:

변형meanbest_epoch판정
CB0.05946베이스
D_reg0.06033단독 유효

유의미한 개선이었습니다. 정점이 epoch 6에서 3으로 빨라졌습니다. 모델이 빨리 좋은 지점에 도달했다가 그 이후는 정규화에 막혀 더 못 올라간 모습이었습니다.

여기서 단독 결과만 보면 결정이 명확해 보입니다. D_reg 채택, D_lr 폐기. 그게 greedy 의사결정입니다.

3. 결합 시너지: D_lrreg

#4에서 배운 것이 있었습니다. 단독 1위가 결합 1위가 아닐 수 있다. 단독으로 회귀했던 D_lr을 D_reg와 결합해 보기로 했습니다. 비용은 한 실험 약 15분(5-seed).

변형meanstdbestbest_epochadj_p (vs CB)
CB0.05730.00210.05946(기준)
D_reg0.06090.00430.064810.037 ✓
D_lrreg (lr + cosine + reg 결합)0.06310.00640.0688100.004 ✓✓

결합이 단독보다 좋았습니다. 그리고 정점의 epoch가 1에서 10으로 이동했습니다.

왜 시너지가 났는가 (메커니즘 추정)

곡선을 같이 그려보면 답이 보였습니다.

  • CB: 빠르게 정점 도달 후 진동·하락 (LR 큼 + 정규화 약함)
  • D_reg 단독: 정규화에 막혀 너무 빨리 정점에 도달, 그 이후 학습 신호 못 받음
  • D_lr 단독: LR 낮아 진동은 줄었으나 정점에 도달하기 전에 cosine이 학습을 식힘
  • D_lrreg: 정규화가 과적합을 막아주는 동안 cosine schedule이 LR을 천천히 낮추면서 느리지만 꾸준히 정점으로 climb. 정점이 epoch 10으로 이동

직관적으로 말하면, D_reg는 천장을 만들고 D_lr은 그 천장까지 가는 시간을 늘려주는 조합이었던 것 같습니다. 단독으로는 한쪽만 작동해 부족했지만, 결합되면 서로의 약점을 가렸습니다.

정직 한 줄: 위 메커니즘 설명은 추정입니다. 더 깊은 메커니즘(왜 best_epoch가 정확히 10인지, 다른 LR/dropout 조합에서도 같은 패턴이 재현되는지)은 충분히 검증하지 못했습니다. “왜 시너지가 나는지 정확히는 모릅니다.”

4. ensemble 시도와 최종 채택

D_lrreg를 5-seed로 재학습(D_lrreg_keep_5)한 결과를 보존해두고, ensemble을 두 가지 시도했습니다.

후보recall@20vs 단일 best
단일 best (seed 44, best_epoch=21)0.0725
5-seed mean0.0657 ± 0.0061−0.0068
RRF prediction ensemble0.0669−0.0056
임베딩 평균 ensemble0.0603−0.0122 (#6에서 다룬 rotation invariance 실패)

ensemble들이 단일 best를 못 이깁니다. 서빙 비용이 5배(5개 모델 유지)인데 결과가 더 나쁘다면 채택할 이유가 없었습니다.

최종 채택: D_lrreg 단일 best (seed 44), recall@20 = 0.0725.

정직한 한 줄

0.0725는 행운의 seed입니다. 같은 코드를 새 seed로 다시 돌리면 0.0657 ± 0.0061 분포 안에서 결과가 나옵니다. 즉 다음 번 학습은 0.06대일 가능성이 큽니다. 실제 기대 성능은 0.0657이라고 보는 게 정확하고, 0.0725는 그 분포에서 가장 위에 있는 점일 뿐입니다.

배포 시점에는 단일 best 모델 가중치를 그대로 쓰지만, 다음 라운드 비교의 기준은 5-seed mean으로 잡아야 한다는 게 결론이었습니다.

5. 전체 궤적

7부작 전체에 걸친 recall@20 궤적을 한 표로 정리하면 이렇습니다.

단계recall@20비고
#1 첫 모델 (깨진 상태)0.0048인기도 floor의 30%, 작동 안 함
#3 1차 재설계 (콘텐츠 Two-Tower + logQ)0.053411배 향상, 구조적 결함 해소
#4 wb_rs 추가 (피처 확장)~0.0581wishs_brands + request_signal
#6/#7 D_lrreg 안정화 (5-seed mean)0.0657 ± 0.0061학습 동역학 개선, 기대 성능
#7 D_lrreg 단일 best (seed 44)0.0725정준 채택, 행운 seed
참고 baseline R* (인기도 floor)0.01664.4배 상회

이 표를 보면 각 단계가 다른 종류의 문제를 풀었다는 게 드러납니다.

  • #3: 구조 문제 (ID emb, logQ 부재)
  • #4: 피처 문제 (어떤 신호를 더 줄 것인가)
  • #6/#7: 학습 동역학 문제 (LR, 정규화의 상호작용)

각 단계가 다른 도구를 요구했고, 한 도구로 다른 단계의 문제를 풀려고 했다면 다 실패했을 것 같습니다. 처음에는 “모델만 잘 만들면 된다”는 감각이 있었는데, 실무에서는 문제의 층위가 다 다르고 각 층위가 다른 종류의 작업이라는 걸 알게 됐습니다.

6. 회고: 여전히 모르는 것

이 시리즈를 쓰면서 정리해본 결과, 알게 된 것보다 모르는 것이 훨씬 많았습니다.

모델 본체에 대해 모르는 것

  • 0.0725가 한계인가: Two-Tower in-batch sampled softmax 구조의 천장 근처라고 추정하지만, 검증하지 못했다. cross-batch negative sampling(arXiv 2110.15154)으로 negative 다양성을 높이면 더 올릴 수 있을지 모른다.
  • 이미지 멀티모달의 효과: 99.97%가 CDN 썸네일이라 CLIP으로 인코딩 가능. GPU만 확보되면 시도할 follow-up. 효과는 미지수.
  • 검색의도 attention 결합: #6에서 mean-pool로는 안 됐지만 attention으로 정교하게 결합하면 다를 수 있다.
  • EMA per-step 재시도: #6의 decay 0.999 per-step 또는 per-epoch에 맞춰 decay 재조정. 둘 중 어느 쪽이 잘 될지 모른다.

평가·방법론에 대해 모르는 것

  • 오프라인 ↔ 온라인 일치 여부: recall@20=0.0725가 실제 비즈니스 지표(CTR, ATC, CVR)로 얼마나 옮겨질지 알 수 없다. 다음 단계의 A/B test가 답할 질문.
  • MDE 정밀 산출: #5에서 추정만 했고 정밀한 산출은 안 했다. 실험 설계의 최소 표본 크기를 정하는 데 필요.
  • 5-seed가 적절한가: N=10으로 늘리면 변동 추정이 더 정확해질 것이다. 비용 대비 가치는 미지수.
  • 다른 retrieval 패러다임과의 정직한 비교: LightFM, ALS, neural CF 등을 같은 평가 셋에서 돌려본 적이 없다. “Two-Tower가 이 도메인에서 더 낫다”는 주장은 사실 강하게 검증된 게 아니다.

도메인·서비스에 대해 모르는 것

  • 활성 유저 9천 명의 다양성: 평균적인 활성 유저에게 맞춰진 모델이 신규·저활동 유저에게도 같은 품질을 주는지 모른다.
  • 카테고리별 성능 분포: 전체 recall@20만 봤고, 카테고리별 분포(예: 여성복은 잘 되고 아동복은 안 되는지)는 측정 안 했다.
  • 추천 결과의 다양성·신선도: recall만 봤고, 추천 리스트의 다양성(같은 브랜드만 반복되지 않는지)이나 신선도(최근 상품이 적절히 나오는지)는 평가 인프라가 없다.
  • 유저의 명시적 피드백: “이 추천이 별로다” 같은 피드백을 받아본 적이 없다. 오프라인 지표가 실제 만족도와 일치하는지 모른다.

프로젝트 운영에 대해 모르는 것

  • 재학습 주기: 주 1회·월 1회 중 어느 게 적절한지. 데이터 churn 속도와 모델 안정성의 균형을 측정해야 함.
  • 모니터링 지표: 운영 중 모델이 깨지는 신호를 무엇으로 잡을 것인지. recall@20 모니터링은 무거우니 가벼운 proxy 지표가 필요.
  • 롤백 절차: 새 모델이 더 나빠졌을 때 빠르게 이전 모델로 되돌리는 절차가 정해져 있지 않다.

7. 도구 사용에 대한 솔직한 기록

#1~#6 곳곳에서 의사결정 과정과 코드 검증에 LLM(Claude, ChatGPT 등)을 활용했습니다. 어떤 일에 어떻게 썼는지 정직히 정리합니다.

유용했던 영역:

  • 논문 요약과 핵심 수식 검증 (Yi et al. 2019 logQ, Izmailov et al. 2018 SWA 등)
  • 대안 패러다임 빠른 비교 (CF/CBF/Two-Tower, 단독 ablation/greedy/전수 탐색)
  • 코드 리팩토링, 누수 가드 테스트 작성
  • 틀린 가설을 더 빨리 인식하기 (EMA decay 0.999가 per-step용이라는 지적은 LLM이 빨리 잡아줬다)

유용하지 않았던 영역:

  • 도메인 특화 결정 (churn 46~74%가 의류 도매에서 무엇을 의미하는지, signal weight 5.0이 적절한지)
  • 평가 셋 정의 (GT=all vs strong 같은 의사결정의 무게)
  • 실패 가설의 근본 원인 추정 (계절 데이터 반증의 원인을 churn으로 좁히는 것은 도메인 데이터를 직접 봐야 가능)

가장 큰 함정: LLM은 그럴듯한 설명을 항상 만들어 줍니다. “왜 D_lrreg가 시너지를 내는가”에 LLM이 그럴듯한 메커니즘 설명을 줘도, 그 설명이 맞는지는 별개입니다. 위 본문에서 “왜 시너지가 났는가”를 추정으로 적고 “정확히는 모른다”고 단서를 단 이유가 이것입니다.

의사결정과 트레이드오프 판단은 본인의 책임이었고, LLM은 검증·요약·반례 제시의 도구로 썼다는 게 가장 정확한 표현인 것 같습니다.

8. 시리즈 마지막 정리

7부작에 걸쳐 다룬 결정의 큰 줄기입니다.

#핵심 결정
1Two-Tower 패러다임 선택 (CF·CBF 비교 후 도메인 적합성)
2후보풀 1.1M → 142K, 운영 안전 장치
3Item ID 임베딩 제거, 콘텐츠 표현 + logQ
4단독 ablation의 한계, 전수 부분집합 탐색으로 wb_rs
5best-of-N + R* baseline + paired permutation + Holm
64가지 실패의 분류학 (데이터·피처·구현·수학적 가정)
7D_lrreg 결합 시너지, 5-seed mean=0.0657 (단일 best 0.0725)

이 시리즈에서 얻은 결과 수치보다 결정의 흐름과 그 흐름에서 배운 것이 더 가치 있다고 생각합니다. 다음 추천 프로젝트(혹은 다른 ML 프로젝트)에서, 이 시리즈를 다시 펴서 “그때 어떤 도구로 어떤 결정을 했더라”를 빠르게 재구성하는 게 이 글의 진짜 용도입니다.

마치며

실무에서 처음 추천 모델을 만들면 어디서 막히는지를 7부작에 담아봤습니다. 막히는 지점은 모델 그 자체보다 모델 주변에 더 많았습니다. 데이터 파이프라인의 누수, 평가 셋 정의의 모호함, 측정의 불확실성, 가설의 잘못된 종류들. 그 주변을 한 번 돌고 나서야 모델 자체의 튜닝이 의미를 가졌습니다.

이 시리즈가 비슷한 위치에서 시작하는 다른 누군가에게, 그리고 무엇보다도 미래의 저에게 길잡이가 되기를 바랍니다.


이 시리즈의 기획·작성에는 Claude/ChatGPT 등의 LLM 도구를 활용했습니다. 의사결정과 트레이드오프 판단은 본인이 했고, 도구는 글 구조 정리·논문 요약 검증·코드 인용 점검에 사용되었습니다. 7편 회고(§7)에 도구 사용의 솔직한 평가를 적어두었습니다.

댓글

Back to Blog

관련 게시글

View All Posts »