리서치 / 교차 서비스 topics
멀티태스크 랭킹 — 참여 확률 여러 개를 하나의 점수로
멀티태스크 랭킹은 랭커가 "이 유저가 이 아이템을 좋아할까"를 스칼라 하나로 예측하지 않는 구조다. 행동 종류별로 확률을 따로 예측한 뒤 하나의 점수로 합쳐서 정렬한다. 행동마다 확률을 하나씩 내는 출력을 헤드라고 부른다.
score = Σ_k w_k · P(action_k | user, item)
다만 이 가중합은 여러 결합 형태 가운데 하나일 뿐이다. Meta는 같은 회사 안에서 지면마다 덧셈형, 곱셈형, 곱셈 페널티를 각각 다른 글에서 수식으로 공개했다(Meta 케이스의 표). "멀티태스크 랭킹 = 가중합"은 관찰이 아니라 가정이다.
여기서 실제로 정해야 하는 설계 결정은 네 가지다.
- 어떤 행동을 헤드로 둘 것인가
- 어떤 형태로 결합할 것인가
- 음수 행동을 어떻게 섞을 것인가
- 가중치 w_k를 어디서 가져올 것인가
⚠ 이 노트에서 Meta system card(Transparency Center가 지면별 예측 헤드 목록을 공개하는 규제 대응 문서)에 기반한 주장은 모두 2026-08-09에 열람한 스냅샷이다. 예측 헤드 개수가 대표적이다. 전체 주의 사항은 ../services/facebook.md의 "system card 인용의 유효기간" 절에 정리해 두었다.
왜 이 구조인가
행동마다 헤드를 나누고 가중치를 따로 두면, 목적 함수를 코드 배포 없이 바꿀 수 있고 희소한 행동의 학습 신호도 지킬 수 있다 추정. 대가는 가중치가 그대로 제품 정책이 된다는 점이다.
- 목적 함수를 코드 배포 없이 바꿀 수 있다. 모델은 확률만 출력하고, 제품 판단(리트윗이 좋아요보다 몇 배 중요한가)은 가중치로 분리된다 추정. X가 가중치를 전부 런타임 주입으로 분리한 구조가 이 해석과 일치한다.
- 희소한 행동(신고, 차단)을 별도 헤드로 두면 학습 신호가 사라지지 않는다. 스칼라 하나로 합치면 흔한 행동에 가려진다 추정.
- 대가도 있다. 가중치가 곧 제품 정책이 되는데, X에서도 그 값은 3세대 공개까지 공개 소스에 남지 않았다. X 사례가 이 문제를 가장 잘 보여 준다.
케이스: Meta — 결합 형태가 지면마다 다르다
Meta는 한 회사 안에서 결합 형태를 4종 공개했다(음수항이 있는 덧셈을 덧셈과 따로 셀 때). Facebook이 공개한 결합 형태는 선형 하나이고, Instagram은 세 가지를 서로 다른 문제에 쓴다고 공개했다.
| 지면 | 형태 | 수식 (별도 표기 없으면 verbatim) | 연도 |
|---|---|---|---|
| Facebook Feed | 덧셈 | Vijt = wijt1·Yijt1 + wijt2·Yijt2 + … + wijtk·Yijtk |
2021 |
| Instagram Suggested Posts | 곱셈 | Value(Post) = (probability_like)^weight_like * (1- probability_not_interested)^weight_see_less |
2020 |
| Instagram Explore | 덧셈(음수항 포함) | Expected Value = W_click * P(click) + W_like * P(like) – W_see_less * P(see less) + etc. |
2023 |
| Instagram Notifications | 곱셈 페널티 | Score(c) = R(c) × D(c), D(c) = ∏_{i=1..m} (1 - w_i · p_i(c)) ⚠ verbatim 아님(표 아래 설명) |
2025 |
Notifications 행의 수식은 verbatim이 아니다. 원문은 LaTeX 이미지이고, 표의 두 식은 그 이미지를 옮긴 것이다 관찰 2026-08-09. 곱셈 구조와 R·D 정의는 본문 산문이 뒷받침한다 확인 — feed-diversity.md.
- Facebook Feed 수식의 출처는 2021년 글이다 확인 — How machine learning powers Facebook's News Feed ranking algorithm.
- Instagram Suggested Posts 수식의 출처는 2020년 글이다 확인 — Designing a Constrained Exploration System.
- Instagram Explore 수식의 출처는 2023년 글이다 확인 — Scaling the Instagram Explore recommendations system.
- Instagram Notifications 수식의 출처는 2025년 글이다 확인 — A New Ranking Framework for Better Notification Quality on Instagram.
지면별 맥락은 facebook.md와 instagram.md에 있다. 여기서는 형태 비교만 한다.
읽는 법
곱셈형과 덧셈형은 음수 신호에 주는 권한이 다르다 추정. 다만 2020년 곱셈형과 2023년 덧셈형은 지면이 서로 다르고, 같은 지면의 전후를 비교한 자료도 없다 추정.
- Facebook 수식의 가중치
w는(i,j,t)로 인덱싱된다. 포스트·유저·시점별로 개인화된 가중치라는 뜻이다 확인 2021.- 원문은 이렇게 쓴다: "for each post i, we estimate Yijt", "the characteristics of the post X_it toward viewer j at time t". 따라서
i는 포스트,j는 뷰어다. - 다만 심볼릭 수식뿐이라 실제 구현이 정말
i(포스트)에 의존하는지는 확인할 수 없다 추정.
- 원문은 이렇게 쓴다: "for each post i, we estimate Yijt", "the characteristics of the post X_it toward viewer j at time t". 따라서
- 곱셈형과 덧셈형은 음수 신호에 주는 권한이 다르다 추정. 이 판단은 수식 형태에서 직접 따라 나온다.
- 곱셈형은
P(not_interested) → 1이면 value가 0으로 붕괴하므로 사실상 hard veto(거부권)다 추정. - 덧셈형은 음수항이 아무리 커도 다른 항이 이를 넘어설 수 있으므로 교환 가능한 비용이다 추정.
- 곱셈형은
- 곱셈형도 로그를 취하면
Σ w·log P라는 선형식이 된다 추정. 즉 Facebook의Σ w·P와 "선형결합"이라는 점은 같고, P를 쓰느냐 log P를 쓰느냐만 다르다. - ⚠ 2020년 곱셈형과 2023년 덧셈형을 "전환"으로 읽으면 안 된다. 지면이 다르다(Suggested Posts와 Explore). 같은 지면의 전후를 비교한 자료가 없다 추정.
- 네 번째인 Notifications 수식은 결합이라기보다 다양성 페널티다. 자세한 내용은 feed-diversity.md에 있다.
X와 정반대인 부분 — 음수를 곱하는가
음수 확률을 점수에 곱하느냐는 같은 문제를 두고 두 회사는 반대 방향으로 갔다.
- Instagram 2020은 음수 확률을 곱해서 거부권을 준다(
(1 - P_not_interested)^w) 확인 2020. - X 2025는 일부러 곱하지 않는다. 임계값을 넘으면 상수 페널티만 더한다 확인(설계 결정 2).
- X 2026은 곱하되, 음수 총점을
0.001스케일로 압축해 양수 후보와 절대 섞이지 않게 한다 확인 —params/config.rs:40(4세대47c1bcd).
X는 음수 헤드가 순위를 크게 바꾸는 것을 막는 데 두 세대를 썼고, Instagram은 곱셈형에서 점수가 0으로 붕괴하는 성질을 오히려 기능으로 활용한다 추정. 두 회사가 왜 이렇게 설계했는지는 코드에도 블로그에도 적혀 있지 않다.
가중치를 어디서 가져오는가 — 회사 간 진짜 분기점
가중치를 정하는 방법은 회사마다 공개된 내용이 다르다. Facebook은 설문 correlation을, Instagram은 offline replay·tuning과 Bayesian optimization을 공개했고, X는 방법을 공개하지 않았다 확인.
| 주체 | 공개된 방법 | 근거 |
|---|---|---|
| 설문과 각 이벤트의 correlation으로 정한다 | 확인 2021 | |
| offline replay/offline tuning과 Bayesian optimization | 확인 2020, 2023 | |
| X | 방법 미공개. 주입 지점(FeatureSwitch 파라미터 / query feature)과 값 범위가 코드에 있고, 4세대부터는 기본값 미러도 있다 | 확인 공개 레포(c54bec0·xai-org/x-algorithm)를 전수 확인했다. 3세대까지 값은 코드에 없었고, 4세대(47c1bcd)의 param.rs가 프로덕션 기본값을 미러한다 |
- Instagram 원문은 이렇게 쓴다 확인.
- 2020년 글: "offline replay over user sessions and b) online Bayesian optimization"
- 2023년 글: "There are two popular approaches to parameters tuning: Bayesian optimization and offline tuning."
- 이 2023년 Instagram 글에는 "replay"라는 단어가 0회 나온다(2026-08-09 전문 확인) 확인.
- Facebook 원문과 "correlation"의 의미, 설문이 왜 희소성 문제를 겪지 않는지는 survey-signals.md에 정리했다. 여기서 반복하지 않는다.
- 같은 회사인 Meta가 같은 문제에 다른 답을 공개했다. 추정 실제로는 둘 다 쓰고 강조점만 다를 가능성이 높지만, 공개된 문장 수준에서는 명확히 갈린다.
- 3세대 공개까지는 세 주체 모두 숫자를 공개하지 않았다. Meta는 방법을 공개하고 값은 감추었으며, X는 주입 지점과 범위를 공개하고 값은 감추었다. 공개하는 항목이 다를 뿐 결론은 같았다.
- X는 4세대 공개(2026-08-13)부터 가중치 기본값을 크론 미러로 레포에 공개한다 확인 — x.md 가중치 절.
- Meta에도 X의
ModelWeights목록에 대응하는 자료가 하나 있다. Transparency Center가 예측을 사용 빈도에 따라 3단계로 나눈 것으로, 가중치 대신 서열만 공개한 형태다 확인 — Our Approach to Facebook Feed Ranking (2025-06-11 갱신).- 세 단계는 Used most frequently / occasionally / less frequently다.
헤드 수 19~22 vs ~10 — 비교하면 안 되는 두 숫자
"X는 헤드가 두 배"라고 쓰면 안 된다. 두 숫자의 성격이 다르다. X 쪽은 소스코드에서 센 숫자이고, Meta 쪽은 system card라는 공개 문서에 실린 숫자다 확인.
| 출처 | 헤드 수 | 출처 종류 |
|---|---|---|
| X Phoenix(트랜스포머 랭킹 모델) 공개 체크포인트, 3세대 | 19 | 소스코드 |
| X 2026 서빙 코드, 3세대 | 22 | 소스코드 |
| X 2026 VMRanker(DPP 슬레이트 최적화) proto 복사본, 3세대 | 21 | 소스코드 |
| X 4세대 README (2026-08-13) | 24 | 문서 |
| X 2025 Navi / Phoenix | 15 / 13 | 소스코드 |
| Facebook Feed 카드 | 10 | system card |
| Facebook Feed Recommendations 카드 | 10 | system card |
| Threads Feed 카드 | 10 | system card |
| Instagram 각 카드 | 총 개수 미확정 | system card |
- X 쪽 숫자는 설계 결정 3에서 인용한 파일이 근거다 확인.
- Meta 쪽 숫자는 각 system card가 근거다 확인 — fb-feed, fb-feed-recommendations, ig-threads-feed (2023~, 규제 대응 문서).
헤드 수를 하나로 정할 수 없는 사정은 양쪽 모두에 있다.
- system card는 모델 덤프가 아니라 공개 커뮤니케이션 산출물이다. Facebook에서는 카드에 없는 헤드가 같은 Transparency Center 본문에 있다 확인 — facebook.md.
- Facebook Feed 카드는 10종을 나열한다. 같은 Transparency Center 본문은 hide/snooze/unsubscribe·report·angry reaction 예측이 분포를 낮추는 방향으로 쓰인다고 따로 서술한다 확인(같은 노트).
- Instagram은 카드에서 추출한 목록과 카드에 적힌 개수가 어긋나는 지면이 4개(Feed·Stories·Reels Chaining·Suggested Accounts)라서 총 개수를 확정하지 못했다 확인 — instagram.md.
- Threads 카드가 전체 헤드를 실은 것인지 "significant predictions"만 실은 것인지 판별할 수 없다 추정 — threads.md.
- X 쪽 숫자도 하나로 정해지지 않는다. 3세대 레포 안에서 19/21/22, 이전 세대에서는 13/15/17이 나온다.
결론은 양쪽 모두 같다. "헤드 수"라는 단일 값은 존재하지 않는다. 어느 파일과 어느 카드를 봤는지 반드시 같이 적어야 한다.
부정 헤드가 있는지 여부는 Meta 안에서도 갈린다.
- Instagram은 수식 안에 항으로 명시해서 공개했다(
– W_see_less·P(see less)) 관찰 2026-08-09. - Facebook은 본문 서술로만 밝혔다 관찰 2026-08-09.
- Threads 카드에는 부정 예측 헤드가 하나도 없다 관찰 2026-08-09 (ig-threads-feed 카드 본문 1건).
X 2026은 부정 헤드 5개(not_interested/block/mute/report/not_dwelled)를 둔다 확인.
케이스: X — 설계 결정 6가지
X의 공개 코드에서 헤드 목록과 음수 헤드를 섞는 방식은 세대마다 다르다 확인(설계 결정 2, 설계 결정 3). 2026 Rust에서는 가중합 뒤에도 작성자 다양성 감쇠와 OON(out-of-network, 팔로우하지 않은 계정의 후보) 배수가 점수를 바꾸고, VMRanker가 그 점수를 덮어쓸 수 있다 확인(설계 결정 6). 이 절의 2026 Rust 서술과 줄 번호는 3세대 공개(2026-05-15) 기준이고, 4세대 README의 헤드 24개와 2026-09-24 커밋(4세대+28)의 랭킹 스코어러 삭제는 x.md 랭킹 절에 있다 확인.
1. 가중치는 코드에 없다
X는 세 세대 모두 가중치를 FeatureSwitch(런타임 설정)로 분리했다 확인. 2025 Scala와 3세대까지 레포에 있는 것은 default 값이고, 프로덕션 값은 레포 밖에 있었다 확인. 4세대 공개(2026-08-13)부터는 프로덕션 기본값을 옮겨 오는 크론 미러가 레포에 있다 확인(x.md 가중치 절).
| 세대 | 파라미터 수 | 범위 | 레포상 default |
|---|---|---|---|
| 2023 Scala | 17 | 제각각이다: 9개는 0..100, 부정 4종은 음수 전용(Report −20000..0 포함), GoodClick류 상한 1,000,000 | 10개 1.0, 7개 0.0 |
| 2025 Scala | 30 | 2023과 같은 구조 (Report −20000..0 동일) | 전부 0.0 |
2026 Rust (3세대 0bfc279) |
22 | 미공개 (params 모듈 자체가 없음) |
미공개 |
2026 Rust (4세대 47c1bcd) |
26 (헤드 25, 새-작성자 부스트 1) | 범위 표기 없음 | 프로덕션 기본값 미러 (예: favorite 0.5, reply 5.0, report −234.0) |
- 2023 행의 근거는
ScoredTweetsParam.scala:151(2023)이다 확인. - 2025 행의 근거는
home-mixer/.../param/HomeGlobalParams.scala:788-978(2025)이다 확인. - 2026 3세대 행의 근거는
home-mixer/scorers/ranking_scorer.rs:43-66이다 확인. - 2026 4세대 행의 근거는
home-mixer/params/param.rs다 확인.
⚠ 이전 판은 2023을 "0.0~100.0, 음수 표현 불가"로 적었는데, 원문과 대조해 보니 오류였다. 음수 전용 범위 4종이 2023 ef4c5eb부터 있었다. 2026-08-09에 수정했다(x.md 가중치 절).
2025에서 default가 전부 0.0인 것은 "좋아요 가중치가 0"이라는 뜻이 아니다. "가중치는 전부 런타임에 주입되고 레포에는 없다"는 뜻이다. 이것을 잘못 읽으면 정반대 결론이 나온다.
범위 자체에 정책이 명시된 경우는 있다. 부정 신호 파라미터 셋은 음수 전용으로 잠겨 있다 확인. 운영자가 실수로도 양수를 넣을 수 없다.
ReportParam은 min-20000.0/ max0.0이다.WeakNegativeFeedbackParam·StrongNegativeFeedbackParam은 min-1000.0/ max0.0이다.- 이 경계는 2023부터 같고, 2025에 신설된 것이 아니다 확인.
HomeGlobalParams.scala:924-946과 202372eda9a를 대조했다.
널리 인용되는 X 가중치 표(fav 0.5, retweet 1.0, reply 13.5, report −369.0 …)의 출처는 2023-04-05 시점 ML 레포 README 한 곳뿐이고, 서빙 레포 31개 커밋 어디에도 나온 적이 없다. 출처와 신뢰도는 x.md의 "가중치 — 무엇이 공개됐고 무엇이 아닌가" 절에 정리했다.
2. 음수 헤드를 곱셈에 넣는가 넣지 않는가
음수 헤드를 곱셈에 넣을지는 가장 크게 갈리는 부분이다. X 2025는 곱하지 않고, 임계값을 넘을 때 상수 페널티만 더한다 확인. 2026 Rust는 방식을 바꿔서 음수 헤드도 그대로 곱한다. 대신 총점이 음수가 되면 그 점수 전체를 압축한다 확인.
X 2025의 집계 코드는 양수 가중치를 점수에 곱해 더하고, 음수 가중치는 임계값을 넘을 때 가중치 값만 더한다 확인 — home-mixer/.../util/RerankerUtil.scala:106-121.
// 양수 가중치
combinedScore + score * weight
// 음수 가중치 — 임계값을 넘으면 상수 페널티만
normScore = if (maxHeadScore == 0.0) 0.0 else score / maxHeadScore
if (normScore > thresholdNormalized || score > thresholdConstant)
combinedScore + weight // score를 곱하지 않는다
else combinedScore
곱하지 않는 이유는 코드에 적혀 있지 않아 추정으로 둔다. 음수 헤드는 확률이 작고 노이즈가 커서, 곱셈으로 넣으면 점수가 크게 흔들린다 추정. 임계값을 넘었는지만 보면 계단 함수가 되어 안정적이다 추정.
정규화 기준이 요청 안의 배치 상대값이라는 점이 중요하다. 같은 포스트라도 함께 채점된 후보 구성이 바뀌면 음수 페널티가 발동하는지 여부가 달라진다 확인 — RerankerUtil.scala:43-65.
- 정규화 식은
score / maxHeadScore이고, 분모는 그 요청에 들어온 후보 전체의 헤드별 최댓값이다.
전환 중인 흔적도 있다. EnableNegSectionRankingParam을 켜면 식이 combinedScore + weight * (1.0 min (score + 0.1))로 바뀐다. 그 바로 위에는 가능한 한 빨리 배포하고 정리하라는 주석("This should be shipped and cleaned as soon as possible")이 달려 있다 확인 — RerankerUtil.scala:115-118.
레포 default 상태에서는 음수 헤드가 아무 감점도 만들지 않는다는 점에 주의해야 한다. 관련 boolean 4개가 전부 false다 확인 — HomeGlobalParams.scala:545-573.
- 네 boolean은
NormalizedNegativeHead,ConstantNegativeHead,UseWeightForNegHeadParam,EnableNegSectionRankingParam이다.
2026 Rust에서 음수 총점을 압축하는 함수는 아래와 같다 확인 — ranking_scorer.rs:175-183 (3세대 0bfc279).
fn offset_score(combined_score: f64, w: &ScoringWeights) -> f64 {
if w.total_sum == 0.0 { combined_score.max(0.0) }
else if combined_score < 0.0 {
(combined_score + w.negative_sum) / w.total_sum * NEGATIVE_SCORES_OFFSET
} else { combined_score + NEGATIVE_SCORES_OFFSET }
}
이 함수는 2025 Scala 함수를 그대로 옮긴 것이다. 세 갈래 분기가 순서까지 한 줄씩 대응한다 확인 — RerankerUtil.scala:130-135.
if (modelWeightsSum == 0) combinedScoreSum.max(0.0)
else if (combinedScoreSum < 0)
(combinedScoreSum + negativeModelWeightsSum) / modelWeightsSum * Epsilon
else combinedScoreSum + Epsilon
변수는 이렇게 대응한다.
modelWeightsSum↔w.total_sumnegativeModelWeightsSum↔w.negative_sumEpsilon↔NEGATIVE_SCORES_OFFSET
두 세대의 상수 값도 같다. 2025의 대응 상수는 val Epsilon = 0.001이고(RerankerUtil.scala:16), 3세대에서 공개되지 않았던 NEGATIVE_SCORES_OFFSET은 4세대(47c1bcd) 공개에서 0.001로 확인된다 확인 — params/config.rs:40. 두 레포는 "코드 중복 0"이라고 알려져 있지만 점수 집계 수식은 포팅됐다 확인.
이 식에서 음수 총점은 0.001 스케일로 눌린다. 양수 후보의 최솟값이 0 + 0.001 = 0.001이므로, 음수 후보는 항상 모든 양수 후보 아래로 간다. 순서는 보존하되 두 무리가 섞이지 않게 하는 설계다.
같은 식이 더 이상 쓰이지 않는 파일 weighted_scorer.rs:87-89에도 남아 있다. 이 파일은 p::NEGATIVE_WEIGHTS_SUM / p::WEIGHTS_SUM을 상수로 읽는다. 즉 가중치 합을 요청별로 계산하는 구조로 바뀌기 전 세대의 형태다 추정.
3. 헤드 목록은 세대마다 다르고, 레포 안에서도 여러 벌이 함께 존재한다
X의 헤드 목록은 세대마다 다르고, 같은 레포 안에서도 파일마다 다르다. 3세대 레포(2026-05-15)에서는 서빙 코드, 공개 체크포인트, VMRanker로 보내는 proto의 헤드 수가 22개, 19개, 21개로 모두 다르다 확인. 2025 레포에서는 두 종류의 랭커가 각자 헤드 세트를 갖는다 확인.
3세대 2026 Rust 랭커가 읽는 헤드는 22개다 확인 — ranking_scorer.rs:12-39,146-170.
| 구분 | 헤드 |
|---|---|
| 참여 | favorite, reply, retweet, quote, share, share_via_dm, share_via_copy_link |
| 클릭·체류 | click, profile_click, photo_expand, quoted_click, dwell |
| 영상 | vqv(video quality view), quoted_vqv |
| 연속값 | cont_dwell_time, cont_click_dwell_time |
| 팔로우 | follow_author |
| 음수 | not_interested, block_author, mute_author, report, not_dwelled |
공개된 Phoenix 체크포인트의 ACTIONS는 19개인데, quoted_vqv, not_dwelled, click_dwell_time이 빠져 있다 확인 — phoenix/runners.py:233-253.
즉 공개 모델(19)과 프로덕션 서빙 코드(22)의 헤드 수가 서로 맞지 않는다. 공개 체크포인트가 구버전이거나 일부 헤드를 제외한 것으로 보인다 추정. vm_ranker.rs가 proto로 복사하는 필드는 quoted_vqv_score가 빠진 21개라서, 세 군데가 전부 다르다 확인 — vm_ranker.rs:69-93.
이 19/22/21 불일치는 3세대까지의 사실이다. 4세대 README는 헤드를 24개로 적는다 확인(x.md Phoenix 절).
2025 Scala에서는 두 종류의 랭커가 각자 헤드 세트를 갖는다.
- Navi(프로덕션 heavy ranker)는 15개를 쓴다 확인 —
home-mixer/.../model/PredictedScoreFeature.scala:294-311.- 헤드: fav, reply, retweet, reply_engaged_by_author, click_engaged, click_dwell, good_profile_click, vqv, vqv_immersive, bookmark, share, dwell, video_quality_watched, video_watch_time_ms, negative_feedback_v2
- Phoenix(트랜스포머 rescorer)는 13개를 쓴다. 가중치는 Navi와 같은
ModelWeights를 공유하고, 별도 세트가 없다 확인 —PhoenixPredictedScoreFeature.scala:174-189.- 헤드는 Navi 목록에서 몇 개가 빠지고
open_link,screenshot이 추가된 구성이다.
- 헤드는 Navi 목록에서 몇 개가 빠지고
Navi의 음수 헤드는 negative_feedback_v2 하나뿐이다. 2026에서 5개(not_interested/block/mute/report/not_dwelled)로 세분화된 것은 세대 간 변화다 확인.
2025 레포에는 PredictedScoreFeature라는 같은 이름의 파일이 두 개 있고, 헤드 세트가 서로 다르다. 실제 스코어러가 import하는 것은 model/ 쪽이고, scorer/ 쪽을 근거로 "X는 report 헤드를 쓴다"고 쓰면 틀린다 확인 — RerankerUtil.scala:4의 import 경로.
model/쪽은 15개Seq이고, 실제로 쓰인다.product/scored_tweets/scorer/쪽은 17개Set이고, Report·StrongNegativeFeedback·VideoPlayback50 등을 포함한다.
4. 가중치 주입 경로가 요청 단위일 수 있다
2025 Navi는 가중치를 param이 아니라 query feature(요청 단위로 붙는 피처)에서 읽는다.
가중치를 읽는 코드는 아래 한 줄이다 확인 — RerankerUtil.scala:28-29.
weight = query.features.get(feature.weightQueryFeature).getOrElse(0.0)
헤드마다 이런 query feature 이름이 3종 있다 확인 — PredictedScoreFeature.scala:43-56.
- 세 이름은
weightQueryFeatureName,biasQueryFeatureName,debiasQueryFeatureName이다.
즉 목적 함수가 요청마다 달라질 수 있다. 유저 세그먼트별·실험별로 다른 가중치를 주입하는 구조로 보이지만, 값을 채우는 upstream 서비스는 레포에 없다 추정.
같은 레포의 Phoenix 쪽은 param에서 직접 읽는다(query.params(feature.modelWeightParam)). 두 랭커의 주입 경로가 서로 다르다 확인 — PhoenixModelRerankingScorer.scala:51.
5. 부적격 헤드는 0이 아니라 bias로 대체된다
X 2025는 쓸 수 없는 헤드 자리에 0이 아니라 bias 값을 넣는다. 영상이 없는 포스트의 vqv 헤드가 그런 경우다.
판정 코드는 아래 한 줄이다 확인 — RerankerUtil.scala:34-37.
score = if (isEligible) predictedScoreOpt.getOrElse(0.0) else bias
0을 넣으면 영상이 없는 포스트가 구조적으로 불리해지기 때문으로 보인다 추정.
자격 조건도 사람이 직접 정한 값이다 확인 — PredictedScoreFeature.scala:154-164,220-232,273-279.
- 영상 헤드에는
duration >= 10초조건이 붙는다. EnableDwellOrVQVParam을 켜면 Dwell과 VQV가 상호 배타가 된다.
임계값 클리핑도 있다. VQV 점수가 ScoreThresholdForVQVParam 미만이면 0.0으로 잘리고, binary scheme을 켜면 상수로 대체된다 확인 — :176-188.
6. 가중합 이후에도 점수가 계속 바뀐다
가중합은 끝이 아니다. 3세대 공개(2026-05-15) 기준 2026 Rust에서는 가중합 뒤에 오프셋, 정규화, 작성자 다양성 감쇠, OON 배수가 차례로 적용되고, 그 뒤 VMRanker가 점수를 전부 덮어쓸 수 있다 확인.
- 헤드별 가중합을 구한 뒤
offset_score를 적용한다(ranking_scorer.rs:146-183). normalize_score(candidate, raw)를 적용한다. 3세대에는 함수 본체가 공개되지 않았다(crate::util모듈이 없었다). 4세대(47c1bcd)의 랭킹 스코어러는 이 함수를 부르지 않고,home-mixer/util/은 공개돼 있다 확인 —ranking_scorer.rs.- 작성자 다양성 감쇠: 같은 작성자의 n번째 포스트에
(1 - floor) · decay^n + floor를 곱한다(:186-217). - OON 배수: in-network가 아닌 후보에만
effective_oon을 곱한다(:220-239, 272-275). - 이후 슬레이트를 DPP(determinantal point process)로 최적화하는
VMRanker가 점수를 전부 덮어쓸 수 있다(vm_ranker.rs:47-52).
이 순서는 3세대 0bfc279의 ranking_scorer.rs:248-284와 phoenix_candidate_pipeline.rs:300에서 확인했다 확인.
2025 Scala에도 같은 성격의 단계가 있지만, 방식은 덧셈이다. MMR(maximal marginal relevance) 재순위가 관련성 점수에 이미 고른 후보와의 최소 거리를 가중해 더하고, 그 값으로 ScoreFeature를 덮어쓴다 확인 — DiversityRescoringFeatureHydrator.scala:70,102.
- 식은
score = relevance + diversityWeight · minDistance이고, 이 계산은DiversityRescoringFeatureHydrator가 맡는다.
다양성 레이어는 feed-diversity.md에서 자세히 다룬다.
케이스: Netflix — 헤드×가중치 프레임 자체를 벗어난다
Netflix는 헤드별 확률에 가중치를 곱해 더하는 X·Meta식 프레임에 맞지 않는다. 공개된 목적함수가 가중합이 아니라 보상 함수다. X·Meta의 프레임을 식으로 쓰면 Σ w·P(action)이다.
- 컨텍스추얼 밴딧 프레임을 쓴다. 추천이 행동이고, 유저 반응이 보상이다 확인 — Recommending for Long-Term Member Satisfaction at Netflix, 2024.
- north star(최종 목표 지표)는 retention인데, 직접 최적화하지는 않는다. "계정당 월 1회" 수준으로 희소하고 노이즈가 큰 신호라서 개별 추천에 귀속되지 않기 때문이다 확인(같은 글).
- 공개된 프록시 보상은
r(user, item) = f(play, complete, thumb)꼴이다. 행동별 확률 예측치의 가중합이 아니라 관측된 행동들의 함수다. 예측 헤드 목록과 가중치 벡터로 나누는 분해 자체가 등장하지 않는다 확인(같은 글). - 지연 피드백을 예측으로 보완한다. Delayed Feedback Prediction Model이
p(최종 피드백 | 관측 피드백)을 예측해 보상에 넣는다 확인(같은 글).- thumbs는 시청 후 며칠 뒤에 올 수 있어서 실시간 학습과 충돌하는데, 이 모델은 그 문제를 정면으로 다룬다 확인(같은 글).
- X와 Meta 문헌에는 대응하는 장치가 없다.
- CTR 과최적화의 문제를 명시한다: "CTR 과최적화는 클릭베이트를 조장한다" 확인(같은 글).
- 가중치 튜닝이 아니라 보상 설계(reward engineering) 루프로 접근한다 확인(같은 글). 가중치를 정하는 "방법"으로 보면 Facebook(설문 correlation)과 Instagram(Bayesian opt)에 이은 세 번째 답이다.
- 루프의 순서는 가설 → 보상 수정 → 밴딧 정책 학습 → A/B다 확인(같은 글).
다만 프레임이 다르다고 해서 헤드가 없는 것은 아니다. FM(Netflix의 개인화 추천 foundation model)은 multi-token과 다중 예측 헤드를 갖는다 확인 — FM 블로그 2025. 가중 목적함수도 쓴다("5분 트레일러와 2시간 완주가 같을 수 없다") 확인(같은 글). 차이는 그 가중이 학습 목적함수 안에 있고, 서빙 시점의 Σ w·P 결합층이 공개 문헌에 없다는 점이다. GenRec(2026)에서는 아예 reward-weighted loss로 학습한 LLM이 직접 랭킹한다 확인 — GenRec 블로그. 결합이 모델 파라미터로 흡수된 형태다.
화면에 보이는 % Match는 랭킹 점수가 아니라 표시용이다. 이 값은 "시청 습관·행동 분석만으로" 산출한다 확인 — 2017 발표. 유저에게 보이는 숫자와 정렬에 쓰는 숫자가 다른 시스템을 명시적으로 보여 주는 사례다(netflix.md).
이 케이스에서 일반화할 수 있는 것
여러 서비스를 나란히 놓고 보면 결합 형태는 지면마다, 헤드 수는 출처마다, 음수 신호를 다루는 방향은 회사마다 다르다.
- "가중합"은 가정이지 관찰이 아니다. Meta 한 회사만 봐도 덧셈, 곱셈, 음수항이 있는 덧셈, 곱셈 페널티 4종이 지면별로 공개돼 있다. 새 서비스를 볼 때
Σ w·P를 기본 가정으로 놓고 시작하면 안 된다. - "모델이 무엇을 예측하는가"는 공개해도 "무엇을 중요하게 보는가"는 공개하지 않는다. 공개하는 항목이 다를 뿐 감추는 것은 같다.
- X는 헤드 목록이 코드에 있고 가중치가 설정에 있다. 3세대까지는 그 값이 레포에 없었고, 4세대 공개부터는 기본값이 레포에 미러된다 확인.
- Meta는 헤드 목록이 카드에 있고, 가중치를 정하는 방법이 블로그에 있으며, 값은 어디에도 없다.
- 음수 신호를 처리하는 방향이 회사마다 반대다. X는 곱셈을 피하는 쪽이고(2025 상수 페널티, 2026 음수 총점 압축), Instagram 2020은 곱해서 거부권을 주는 쪽이다. 같은 문제에 정반대 설계가 둘 다 프로덕션에 있다.
- 헤드 수는 출처 종류에 따라 달라진다. 소스코드는 파일 단위로, system card는 문서 단위로 불일치한다. 숫자를 비교하려면 먼저 두 숫자가 같은 종류인지 물어야 한다.
- X는 파일마다 다르다(19/21/22/15/13/17).
- Meta는 문서마다 다르다(카드의 10종과 본문에만 있는 부정 예측).
- 가중합 점수는 최종 순위가 아니다. 다양성·OON·슬레이트 최적화가 뒤에 이어진다.
- 실제로 X 2026은
weighted_score와score를 별도 필드로 둘 다 저장한다 확인 —ranking_scorer.rs:277-281(3세대0bfc279). - Meta도 포인트와이즈(후보마다 따로 점수를 매기는) pass 1 뒤에 컨텍스트 pass 2를 따로 둔다 확인 2021.
- 실제로 X 2026은
Σ w·P프레임은 보편이 아니라 광고형 지면의 관행일 수 있다. Netflix는 같은 문제(여러 행동 신호의 통합)를 보상 함수와 밴딧으로 푼다. 사업 모델이 구독(retention)이면 목적함수도 세션 참여 극대화가 아니라 장기 만족 프록시 쪽으로 기운다. 프레임 선택이 기술이 아니라 수익 모델의 함수일 가능성이 있다 추정.- north star를 직접 최적화하지 않는 이유는 신호 밀도다. 월 1회 오는 retention이나 희소한 설문 같은 진짜 목표는 프록시 보상과 가중치 결정 레이어로 밀려나고, 랭커는 밀도 높은 행동 신호를 다룬다.
- Facebook의 설문 correlation과 Netflix의 reward engineering이 같은 구조의 두 구현이다.
누가 이걸 쓰는가
X, Facebook, Instagram은 이 구조를 쓴다 확인. Threads는 헤드 10종을 공개했지만 결합 방식과 가중치 정보는 없고, Netflix는 가중합 대신 보상 함수와 밴딧을 쓴다 확인.
- X: 2023·2025·2026 세 세대 모두 이 구조를 쓴다. 헤드 수는 세대와 파일에 따라 다르다(3세대 19~22, 4세대 README 24, 설계 결정 3). 가중치는 FeatureSwitch와 query feature로 주입한다 확인 — x.md.
- Facebook:
Vijt = Σ wijtk·Yijtk라는 덧셈형을 쓴다. pass 1은 포인트와이즈 멀티태스크 신경망이고, 가중치는 설문 correlation으로 정한다. Feed 카드에 헤드 10종이 있고, 본문에만 있는 부정 예측이 더 있다 확인 — facebook.md. - Instagram: 결합 형태가 3종이다(2020 곱셈, 2023 음수항이 있는 덧셈, 2025 곱셈 페널티). 가중치는 offline replay/offline tuning과 Bayesian optimization으로 정한다. 지면×컴포넌트마다 별도 MTML 모델을 둔다(
ig_stories_tray_mtml, 프로덕션 1,000개 이상) 확인 — instagram.md. - Threads: 헤드 10종은 공개했는데, 결합 방식과 가중치 정보가 0이고 부정 헤드도 0이다. 같은 회사 안에서도 지면마다 공개 수준이 다르다는 것을 가장 선명하게 보여 주는 증거다 확인 — threads.md.
- Netflix:
Σ w·P형태가 아니다. 밴딧에 프록시 보상r=f(play, complete, thumb)과 지연 피드백 예측을 결합한다. north star는 retention이다 확인 — netflix.md. - 그 외 서비스: 아직 조사하지 않았다. YouTube의 multi-gate MoE(mixture-of-experts, 2019 RecSys)와 Pinterest 계열이 같은 범주에 있을 가능성이 높지만, 이 저장소에서는 아직 1차 출처로 확인하지 않았다. 열린 질문이다.
출처
공개 소스코드
- xai-org/x-algorithm — 2026, 공개 소스코드 (Apache 2.0).
home-mixer/scorers/,phoenix/. 본문 2026 Rust 줄 번호 인용의 기준 커밋은0bfc279(3세대)이고, 4세대 값은47c1bcd에서 읽었다. - twitter/the-algorithm — 2025-09-03 커밋
c54bec0, 공개 소스코드.home-mixer/. - twitter/the-algorithm-ml — 2023-04-05
projects/home/recap/README.md. fav 0.5/retweet 1.0/reply 13.5/report −369 표의 1차 출처다.
Meta 엔지니어링 블로그 (결합 수식의 1차 출처)
- How machine learning powers Facebook's News Feed ranking algorithm — 2021, 블로그.
Vijt = Σ w·Y와 3-pass 구조를 설명한다. - Designing a Constrained Exploration System — 2020, 블로그. 곱셈형 value model과 Bayesian optimization을 다룬다. ⚠ How Instagram suggests new content와 같은 글이고 제목만 다르다.
- Scaling the Instagram Explore recommendations system — 2023, 블로그. 덧셈형과 음수항, Bayesian optimization과 offline tuning을 다룬다.
- A New Ranking Framework for Better Notification Quality on Instagram — 2025, 블로그.
Score = R×D곱셈 페널티를 다룬다. - Journey to 1000 models: Scaling Instagram's recommendation system — 2025, 블로그.
ig_stories_tray_mtml명명 규칙이 나온다.
Meta Transparency Center (규제 대응 1차 문서)
- Our Approach to Facebook Feed Ranking — 2025-06-11 갱신. 예측의 사용 빈도 3단계가 나온다.
- fb-feed · fb-feed-recommendations · ig-threads-feed — AI system card, 지면별 예측 헤드 목록.
Netflix
- Recommending for Long-Term Member Satisfaction at Netflix — 2024, 블로그. 보상 함수 프레임의 1차 출처다.
- Foundation Model for Personalized Recommendation — 2025, 블로그. 다중 헤드와 가중 목적함수를 다룬다.
- GenRec — 2026, 블로그. reward-weighted loss를 다룬다.
- Goodbye Stars, Hello Thumbs — 2017, 발표. % Match는 표시용이다.