두 그룹 인원이 안 맞습니다
SRM, 배정이 틀어졌다는 증거
50:50으로 설계한 실험에서 A안 50,600명, B안 49,400명이 나왔다면 1.2%p 차이가 아니라 우연으로는 1만 번에 한두 번 나올 결과입니다. 전환율 비교는 뒤로 미루고 배정부터 확인해야 합니다.
들어가며
실험 대시보드의 첫 줄은 보통 그룹별 인원입니다. 대부분 눈길을 주지 않고 바로 아래 전환율로 내려갑니다. A안 50,600명, B안 49,400명. 그리고 B안 전환율이 5% 높고 유의합니다. 채택해도 될까요.
첫 줄을 건너뛰면 안 되는 이유가 이번 글의 내용입니다. 인원 차이가 우연으로 얼마나 벌어질 수 있는지 먼저 세어 봅니다. 이어서 한 줄로 검정하는 법, 배정이 틀어지는 원인, 틀어진 배정이 전환율 비교를 어떻게 기울이는지를 봅니다.
인원 차이는 우연으로 얼마나 벌어지나
유저를 동전 던지듯 50:50으로 나누면 두 그룹 인원이 정확히 같을 수는 없습니다. 그러나 벌어질 수 있는 폭은 정해져 있습니다. 총 인원이 n명이면 한 그룹 인원의 표준편차는 √(n × 0.25)입니다.
| 총 인원 | 표준편차 | 우연으로 나올 수 있는 범위 (±2SD) | 비율로 보면 |
|---|---|---|---|
| 2,000명 | 22명 | 955 ~ 1,045명 | 47.76% ~ 52.24% |
| 20,000명 | 71명 | 9,859 ~ 10,141명 | 49.29% ~ 50.71% |
| 100,000명 | 158명 | 49,684 ~ 50,316명 | 49.68% ~ 50.32% |
| 200,000명 | 224명 | 99,553 ~ 100,447명 | 49.78% ~ 50.22% |
| 2,000,000명 | 707명 | 998,586 ~ 1,001,414명 | 49.93% ~ 50.07% |
그림 1. 50:50 배정에서 한 그룹의 인원이 우연으로 흔들리는 폭. 범위 밖은 100번에 5번 미만으로 나옵니다.
총 100,000명이면 한 그룹은 49,684명에서 50,316명 사이에 들어와야 정상입니다. 50,600명은 상한보다 284명 많고, 표준편차로 재면 3.8배 떨어져 있습니다. 비율로는 50.6%라 사소해 보이지만, 인원이 많을수록 허용되는 폭이 좁아지기 때문에 1.2%p는 이미 큰 차이입니다.
인원 차이의 크기는 퍼센트가 아니라 표준편차 몇 배인지로 읽어야 합니다.
한 줄로 검정합니다
관측 인원이 설계 비율과 맞는지는 카이제곱 적합도 검정으로 봅니다. 이 검정에서 p값이 작으면 배정이 설계와 다르다는 뜻이고, 이 현상을 SRM(sample ratio mismatch, 표본 비율 불일치)이라고 부릅니다.
from scipy.stats import chisquare
observed = [50_600, 49_400] # A안, B안에 배정된 유저 수
stat, p = chisquare(observed) # 기대값은 50:50
print(f"카이제곱 {stat:.2f}, p = {p:.5f}")
# 카이제곱 14.40, p = 0.00015
| A안 / B안 | 차이 | 표준편차 배수 | p값 | 판정 |
|---|---|---|---|---|
| 50,120 / 49,880 | 240명 | 0.8 | 0.45 | 정상 |
| 50,300 / 49,700 | 600명 | 1.9 | 0.058 | 정상이지만 다음 날 다시 본다 |
| 50,600 / 49,400 | 1,200명 | 3.8 | 0.00015 | SRM. 결과를 쓰지 않는다 |
| 52,000 / 48,000 | 4,000명 | 12.6 | 10−36 | SRM. 배정 로직 자체를 의심한다 |
그림 2. 총 100,000명, 설계 50:50일 때 사례별 검정 결과. 설명을 위해 만든 가상 예시입니다.
기준은 보통의 0.05가 아니라 0.001을 씁니다[2]. 인원은 실험 기간 내내 매일 확인하고, 한 조직이 돌리는 실험은 수십 개라서 0.05를 쓰면 가짜 경보가 잦아집니다. 0.001이면 정상 실험 1,000개 중 1개꼴로만 잘못 울립니다. 반대로 두 번째 줄처럼 0.058이 나왔다면 당장 멈출 일은 아니지만, 다음 날 인원이 더 쌓였을 때 한 번 더 봅니다.
SRM 검정은 코드 두 줄이고, 기준은 0.05가 아니라 0.001입니다.
배정이 틀어지는 원인
SRM은 통계 현상이 아니라 시스템 어딘가가 고장 났다는 결과입니다. 원인은 배정 단계와 기록 단계 둘 중 하나에 있습니다[1].
| 단계 | 원인 | 어떻게 드러나나 |
|---|---|---|
| 배정 | 해시 함수가 특정 ID 패턴에 치우친다 | 실험 첫날부터 비율이 어긋난다 |
| 배정 | 봇이나 크롤러가 한쪽으로만 들어간다 | 특정 국가나 기기에서만 비율이 어긋난다 |
| 배정 | 리다이렉트가 한쪽만 느려 유저가 이탈한다 | 느린 쪽 인원이 적고, 남은 유저의 성과는 좋아 보인다 |
| 기록 | 한쪽에서만 앱이 더 자주 죽어 로그가 안 남는다 | 크래시율이 높은 기기에서 비율이 어긋난다 |
| 기록 | 데이터 파이프라인 필터가 한쪽 이벤트를 더 걸러 낸다 | 원천 로그와 마트 인원이 다르다 |
| 기록 | 실험 도중 배정 규칙을 바꿔 일부 유저가 양쪽에 다 찍힌다 | 규칙을 바꾼 날부터 비율이 변한다 |
그림 3. 자주 나오는 SRM 원인. 어느 세그먼트에서 비율이 어긋나는지가 원인을 좁히는 단서입니다.
원인을 찾는 순서는 세그먼트별 인원 비율입니다. 전체에서는 50.6:49.4인데 iOS에서만 55:45라면 iOS 쪽 배정이나 로그를 봅니다. 날짜별로 잘라서 어느 날부터 어긋났는지 보면 배포 이력과 맞춰 볼 수 있습니다.
SRM은 고칠 통계가 아니라 찾아야 할 고장이고, 세그먼트별 인원 비율이 고장의 위치를 알려 줍니다.
인원이 빠지면 결과가 기웁니다
SRM이 있을 때 전환율 비교를 왜 쓸 수 없는지 숫자로 보겠습니다. 유저의 30%는 저활동 유저(전환율 1%), 70%는 일반 유저(전환율 6.71%)이고, 전체 전환율은 5.00%입니다. A안과 B안의 실제 효과는 같습니다. 다만 B안에서 앱이 더 자주 죽어서 저활동 유저의 로그 20%가 기록되지 않는다고 해 보겠습니다.
B안에 남은 유저는 94,000명이고 전환율은 5.25%입니다. A안 5.00%보다 0.26%p, 상대로 5.1% 높습니다. 효과가 없는데 효과가 보입니다. 전환율이 낮은 유저가 B안에서만 빠졌기 때문입니다. 이 상황을 그룹당 50,000명으로 1만 번 시뮬레이션하면 SRM 검정은 100%에서 p < 0.001로 잡아냅니다. 전환율 차이는 43.8%의 실험에서 B안이 유의하게 높게 나옵니다.
| 저활동 유저 로그 유실 | 배정 비율 (A : B) | B안 전환율 | 가짜 상대 효과 |
|---|---|---|---|
| 5% | 100 : 98.5 | 5.06% | +1.2% |
| 10% | 100 : 97 | 5.12% | +2.5% |
| 20% | 100 : 94 | 5.25% | +5.1% |
| 30% | 100 : 91 | 5.39% | +7.9% |
그림 5. 유실 비율에 따른 가짜 효과. 유실이 어느 집단에 몰리느냐에 따라 방향은 반대가 될 수도 있습니다.
유실이 저활동 유저에 몰리면 B안이 좋아 보이고, 고활동 유저에 몰리면 나빠 보입니다. 어느 쪽인지 모르는 채로는 보정도 못 합니다. 그래서 SRM이 있는 실험의 전환율 비교는 방향도 크기도 믿을 수 없고, 보고서에 싣지 않습니다.
빠진 유저가 무작위가 아니면 남은 유저의 비교는 실험이 아니라 선택된 표본의 비교입니다.
발견했을 때
| 순서 | 할 일 | 하지 않을 일 |
|---|---|---|
| 1 | 실험을 멈추고 결과 공유를 보류한다 | 전환율이 유리하니 일단 공유하기 |
| 2 | 세그먼트별(OS, 국가, 신규와 기존, 날짜) 인원 비율을 본다 | 전체 비율만 보고 원인을 추측하기 |
| 3 | 원천 로그와 분석 마트의 인원을 대조한다 | 마트 숫자만으로 판단하기 |
| 4 | 배포 이력과 어긋나기 시작한 날짜를 맞춰 본다 | 통계적 보정으로 넘어가기 |
| 5 | 원인을 고친 뒤 새로 배정해 다시 돌린다 | 어긋난 데이터에서 일부만 잘라 쓰기 |
그림 6. SRM을 발견했을 때의 절차.
다섯 번째가 자주 생략됩니다. 원인을 찾았으니 어긋난 기간만 빼고 분석하자는 제안이 나옵니다. 그러나 유실이 언제부터 어느 집단에서 있었는지 완전히 알 수 없다면, 잘라 낸 데이터도 같은 편향을 품고 있습니다. 배정을 새로 하는 것이 가장 싼 방법입니다.
SRM이 확인된 실험의 결론은 보류가 아니라 폐기이고, 다음 실험이 그 결론을 대신합니다.
나가며
- 50:50 배정에서 총 100,000명이면 한 그룹 인원은 49,684명에서 50,316명 사이에 들어와야 합니다. 50,600명은 표준편차의 3.8배 밖이고, 카이제곱 검정 p = 0.00015입니다.
- SRM 검정은 코드 두 줄이고 기준은 0.001입니다. 매일 확인하고, 어긋나면 세그먼트별 비율로 고장 위치를 찾습니다.
- B안에서 저활동 유저 로그 20%만 빠져도 효과가 없는 실험에서 상대 +5.1%가 나오고, 시뮬레이션의 43.8%에서 유의하게 나왔습니다. SRM이 있는 실험의 전환율 비교는 보고서에 싣지 않습니다.
참고
- Fabijan, A., Gupchup, J., Gupta, S., Omhover, J., Qin, W., Vermeer, L., & Dmitriev, P. (2019). Diagnosing sample ratio mismatch in online controlled experiments: A taxonomy and rules of thumb for practitioners. KDD ’19, 2156-2164. https://doi.org/10.1145/3292500.3330722
- Kohavi, R., Tang, D., & Xu, Y. (2020). Trustworthy Online Controlled Experiments: A Practical Guide to A/B Testing. Cambridge University Press.
본문의 모든 수치는 설명을 위해 만든 가상 예시를 직접 계산한 값입니다. 인원의 표준편차는 이항분포 √(n × 0.5 × 0.5)로, SRM 검정은 카이제곱 적합도 검정(자유도 1)으로 구했습니다. 로그 유실 시뮬레이션은 그룹당 50,000명, 1만 회, 시드 2026 기준이며 전환율 비교는 두 비율 z검정(양측, α = 0.05)입니다. 코드 실행 환경은 Python 3.12, NumPy 2.5.3, SciPy 1.18.1입니다.