다중 비교
고친 사람 github-actions[bot]
다중 비교는 데이터로 차이를 가리는 검정을 여러 번 할수록 우연만으로 「차이가 있다」는 판정이 나오기 쉬워지는 문제입니다. 검정 하나가 틀릴 확률은 작습니다. 하지만 검정을 스무 개 모으면 그중 하나쯤 틀리는 일이 흔해집니다. 그래서 여러 지표를 한꺼번에 비교하는 실험은 판정 문턱을 검정 수에 맞춰 고칩니다.
쉽고 빠른 이해
다중 비교는 검사를 여러 번 할수록 헛것을 진짜로 볼 확률이 커지는 문제입니다. 지표 스무 개를 하나씩 검사하면 아무것도 안 바뀌었어도 하나 이상이 「달라졌다」고 나올 확률이 약 0.64입니다.
이걸 모르면 우연히 튄 지표 하나를 보고 새 기능이 효과가 있다고 결론 냅니다. 그 결론으로 배포를 정하면 아무 효과 없는 변경이 남습니다.
- 검사 하나는 차이가 없어도 가끔 「달라졌다」고 잘못 말합니다. 흔히 쓰는 기준이면 스무 번에 한 번꼴입니다
- 검사가 늘면 그 잘못이 한 번이라도 나올 확률이 쌓입니다
- 그래서 검사 수에 맞춰 기준을 엄하게 조이거나, 볼 지표를 미리 하나로 정합니다
대가도 있습니다. 기준을 조이면 진짜 있는 차이도 놓치기 쉬워집니다.
상세
한 사람이 동전을 열 번 던져 앞면이 아홉 번 나오면 다들 동전을 의심합니다. 그런데 교실의 백 명이 저마다 열 번씩 던졌다면 이야기가 다릅니다. 그중 누군가 아홉 번이 나와도 이상하지 않습니다. 드문 일도 기회가 많으면 어딘가에서 일어납니다.
다중 비교는 이 일이 통계 판정에서 벌어지는 것입니다. 통계에서는 데이터로 차이가 있는지 가리는 절차를 검정이라고 합니다. 검정 하나만 놓고 보면 드문 결과가, 검정을 여럿 모으면 어딘가에서 거의 반드시 나옵니다. 그 결과를 「발견」으로 읽으면 잘못된 결론이 나옵니다.
이 절은 지표 스무 개를 검정하는 실험 하나로 검정 한 번의 오류가 검정 수에 따라 쌓이는 과정을 계산하고 코드로 재현합니다. 그다음 이 문제가 생기는 조건과 문턱을 고치는 방법을 봅니다.
검정 한 번이 틀릴 확률
검정이 하는 일은 「차이가 있다」와 「차이가 없다」 가운데 하나를 고르는 것입니다. 측정값은 늘 조금씩 흔들립니다. 그래서 눈에 보인 차이만으로는 진짜 변화인지 흔들림인지 알 수 없습니다. 새 버전의 평균 응답 시간이 조금 줄었을 때 검정으로 가립니다.
출발점은 「차이가 없다」는 가정입니다. 이 가정을 귀무가설이라고 부릅니다. 데이터가 이 가정과 잘 안 맞으면 가정을 버립니다.
가정을 버리면 「차이가 있다」고 판정합니다. 이 판정을 「유의하다」고 말합니다. 응답 시간이 유의하게 줄었다면 흔들림이 아니라 진짜 변화로 본다는 뜻입니다.
얼마나 안 맞아야 버릴지는 미리 정한 문턱이 가릅니다. 이 문턱이 유의 수준입니다. 흔히 0.05를 씁니다. 차이가 정말 없을 때도 스무 번에 한 번꼴로는 「차이가 있다」고 판정하겠다는 약속입니다.
차이가 없는데 「차이가 있다」고 판정하는 잘못을 1종 오류라고 합니다. 유의 수준은 곧 검정 하나가 1종 오류를 낼 확률입니다. 검정 하나만 하면 이 확률은 0.05에 머뭅니다.
검정이 늘면 쌓이는 확률
A/B 테스트는 사용자를 두 무리로 나눠 서로 다른 버전을 보여 주는 실험입니다. 두 무리의 결과를 견줘 새 버전이 무엇을 바꿨는지 봅니다.
이때 견주는 값이 지표입니다. 지표는 응답 시간·체류 시간·오류율처럼 서비스 상태를 숫자로 잰 값입니다. 이 실험에서 지표를 스무 개 본다고 해 봅시다.
새 버전이 아무것도 안 바꿨다고 가정합니다. 그러면 지표 스무 개 모두 귀무가설이 맞습니다. 이때 「유의하다」는 판정이 하나라도 나오면 그것은 전부 1종 오류입니다.
「하나 이상 틀린다」를 바로 세기는 번거롭습니다. 반대 경우인 「하나도 안 틀린다」를 구해 1에서 빼는 편이 쉽습니다. 지표 하나가 맞게 「차이 없음」으로 판정될 확률은 1에서 0.05를 뺀 0.95입니다.
지표끼리 서로 영향을 주지 않는다고 칩니다. 그러면 스무 개가 모두 맞을 확률은 0.95를 스무 번 곱한 약 0.36입니다. 하나 이상 틀릴 확률은 1에서 0.36을 뺀 약 0.64입니다.
검정 수를 m, 유의 수준을 α(알파)라고 쓰면 이 확률은 1 − (1 − α)ᵐ 입니다. 아래 그림은 α 가 0.05일 때 m 이 늘어남에 따라 이 값이 어떻게 커지는지 보입니다.
xychart-beta
title "검정 수에 따라 1종 오류가 하나 이상 날 확률"
x-axis "검정 수" [1, 2, 5, 10, 20, 50, 100]
y-axis "확률" 0 --> 1
bar [0.05, 0.10, 0.23, 0.40, 0.64, 0.92, 0.99]
검정이 열네 개쯤이면 이 확률이 절반을 넘습니다. 백 개쯤 되면 거의 틀림없이 하나는 우연만으로 유의하게 나옵니다.
지표끼리 얽혀 있을 때
실제 지표는 서로 얽혀 있는 일이 많습니다. 구매까지 간 사용자의 비율인 전환율이 오르면 매출도 같이 오르기 쉽습니다. 이렇게 얽혀 있으면 앞의 곱셈 식이 맞지 않습니다.
그래도 넘지 않는 선이 하나 있습니다. 1종 오류가 하나 이상 날 확률은 검정 수 곱하기 유의 수준보다 커지지 않습니다. 이 선은 얽혀 있든 아니든 성립합니다.
지표 스무 개에 이 선을 대 보면 20 곱하기 0.05는 1입니다. 확률은 원래 1을 못 넘으니 이 선은 아무것도 알려 주지 않습니다. 이 선이 쓸모 있어지는 것은 문턱을 낮출 때입니다. 문턱을 0.0025로 낮추면 20 곱하기 0.0025는 0.05가 됩니다.
가족 오류율
함께 판정하는 검정 묶음을 통계에서는 「가족」이라고 부릅니다. 그 가족 안에서 1종 오류가 하나라도 날 확률을 가족 오류율(family-wise error rate)이라고 합니다. 앞에서 계산한 0.64가 지표 스무 개 가족의 가족 오류율입니다.
다중 비교 문제를 한 문장으로 줄이면 이렇습니다. 검정마다 유의 수준을 0.05로 지켜도 가족 오류율은 0.05보다 커집니다.
검정 수에 맞춰 문턱을 고치는 일을 보정이라고 합니다. 보정 방법들은 이 가족 오류율이나 뒤에 나오는 거짓 발견률을 통제하려는 것입니다.
코드로 재현하기
코드로 재현하려면 「차이가 없는 검정」 하나를 흉내 내야 합니다. 귀무가설이 맞으면 검정 하나가 「유의」로 나올 확률은 유의 수준과 같습니다. 유의 수준을 그렇게 정했기 때문입니다. 그래서 0과 1 사이 난수가 α 보다 작으면 그 검정이 우연히 유의하게 나온 것으로 칩니다.
아래 rate(m, alpha) 는 차이가 없는 검정 m 개를 한 묶음으로 돌립니다. 이 묶음을 trials 번
되풀이합니다. 그리고 하나라도 유의가 나온 묶음의 비율을 돌려줍니다.
random.random() 은 0과 1 사이 난수를 줍니다. any(...) 는 안의 값이 하나라도 참이면 참입니다.
trials 의 기본값 100_000 은 십만을 읽기 쉽게 끊어 쓴 표기입니다.
import random
def rate(m, alpha, trials=100_000):
hits = 0
for _ in range(trials):
if any(random.random() < alpha
for _ in range(m)):
hits += 1
return hits / trials
print(rate(1, 0.05)) # 약 0.05
print(rate(20, 0.05)) # 약 0.64
print(rate(20, 0.0025)) # 약 0.049
첫 줄은 검정 하나라서 유의 수준 0.05가 그대로 나옵니다. 둘째 줄은 검정 스무 개 묶음입니다. 앞에서 계산한 0.64와 맞습니다. 난수를 쓰므로 돌릴 때마다 끝자리는 조금씩 달라집니다.
셋째 줄은 유의 수준을 검정 수로 나눠 문턱으로 쓴 경우입니다. 0.05를 20으로 나눈 0.0025를 문턱으로 쓰면 가족 오류율이 0.05 아래로 내려옵니다. 이 방법은 아래 「보정 방법」에서 다시 봅니다.
문제가 생기는 조건
다중 비교 문제는 아래 세 조건이 함께 있을 때 생깁니다.
- 진짜로는 차이가 없는 검정이 둘 이상 섞여 있습니다
- 검정마다 같은 유의 수준으로 따로 판정합니다
- 그중 하나라도 유의하면 「발견」으로 칩니다
조건 하나가 빠지면 문제가 안 생깁니다. 미리 정한 지표 하나만 보고 나머지는 판정에 안 쓰면 셋째 조건이 빠집니다. 문턱을 검정 수에 맞춰 낮추면 둘째 조건이 빠집니다. 뒤에 나오는 대응들은 이 두 조건 중 하나를 깨는 방법입니다.
「비교」라는 이름
이름의 「비교」는 집단 여럿을 두 개씩 짝지어 견주는 데서 왔습니다. A·B·C·D 네 버전을 견주면 짝은 A–B, A–C, A–D, B–C, B–D, C–D 여섯 개입니다. 집단이 k 개면 짝은 k × (k − 1) ÷ 2 개입니다. 집단이 다섯 개면 짝은 열 개입니다.
짝마다 검정을 한 번씩 하므로 검정 수가 집단 수보다 빨리 늘어납니다. 지금은 짝 비교가 아니어도 검정을 여러 번 하는 경우를 모두 이 이름으로 부릅니다. 다중 검정이라고도 합니다.
백엔드에서 만나는 모습
백엔드 개발자가 이 문제를 만나는 곳은 대개 실험과 성능 측정입니다. 아래 표는 검정 수가 늘어나는 흔한 경우를 모았습니다.
| 상황 | 검정이 늘어나는 방식 |
|---|---|
| 지표 여럿 | 전환율·체류 시간·오류율을 하나씩 검정합니다 |
| 사용자 쪼개 보기 | 국가별·기기별·신규와 기존 사용자별로 다시 검정합니다 |
| 버전 여럿 | 네 버전을 짝마다 비교해 검정이 여섯 번이 됩니다 |
| 중간에 자주 들여다보기 | 같은 실험을 날마다 다시 검정합니다 |
| 벤치마크 항목 여럿 | 응답 시간·처리량·메모리 사용량을 항목마다 성능 회귀인지 검정합니다 |
사용자 쪼개 보기는 특히 알아채기 어렵습니다. 전체로는 차이가 없는데 「모바일 신규 사용자에게만 효과가 있다」 같은 결론이 이렇게 나옵니다. 쪼갠 무리마다 검정을 한 번씩 더 한 셈입니다. 그래서 하나쯤은 우연히 유의하게 나옵니다.
중간에 들여다보는 경우도 같은 뿌리입니다. 같은 데이터를 날마다 검정하면 검정 수가 날짜 수만큼 늘어납니다. 여러 번 들여다볼 것을 처음부터 셈에 넣고 문턱을 짜는 방법이 순차 검정입니다.
보정 방법
보정 방법은 무엇을 통제하느냐로 크게 둘로 갈립니다. 하나는 가족 오류율을 통제합니다. 다른 하나는 「유의」로 나온 판정 가운데 틀린 것의 비율을 통제합니다.
가장 단순한 쪽이 본페로니 보정입니다. 유의 수준을 검정 수로 나눠 각 검정의 문턱으로 씁니다. 검정이 스무 개면 0.05를 20으로 나눈 0.0025입니다. 앞에서 본 「검정 수 곱하기 유의 수준」 선 덕분에, 지표가 얽혀 있어도 가족 오류율이 0.05를 넘지 않습니다.
검정이 수백, 수천 개가 되면 본페로니 보정의 문턱은 너무 낮아집니다. 그러면 거의 아무것도 유의하게 안 나옵니다. 이때는 거짓 발견률을 통제하는 쪽을 씁니다.
「유의」로 나온 판정 가운데 1종 오류가 차지하는 비율은 실험마다 조금씩 다릅니다. 같은 실험을 여러 번 되풀이해 이 비율을 평균 낸 값이 거짓 발견률입니다. 틀린 판정이 몇 개 섞이는 것은 받아들이고 그 평균만 낮게 지킵니다.
보정의 대가
차이가 있는데 「차이가 없다」고 판정하는 잘못을 2종 오류라고 합니다. 1종 오류의 반대편입니다. 문턱을 낮추면 1종 오류는 줄지만 2종 오류는 늘어납니다.
있는 차이를 잡아낼 확률을 검정력이라고 합니다. 보정으로 문턱을 낮추면 검정력이 떨어집니다. 같은 검정력을 지키려면 측정해 모으는 데이터, 곧 표본을 더 늘려야 합니다. 그만큼 실험이 길어집니다.
보정보다 먼저 줄이는 법
검정 수를 애초에 줄이면 보정의 대가도 줄어듭니다. 실험 전에 판정에 쓸 주 지표 하나를 정해 둡니다. 나머지 지표에서 나온 유의 결과는 결론이 아니라 다음 실험으로 다시 확인할 후보로 읽습니다.
무엇을 어떻게 검정할지 실험 전에 적어 두는 일을 사전 등록이라고 합니다. 적어 둔 것과 다르게 분석했다면 그 사실이 드러납니다. 그래서 검정을 몇 번 했는지 나중에 숨길 수 없습니다.
p-해킹과 가르는 선
원하는 결과가 나올 때까지 분석을 바꿔 가며 되풀이하는 일을 p-해킹이라고 합니다. 지표를 바꿔 보고 기간을 바꿔 봅니다. 데이터도 걸러 봅니다. 그러다 유의하게 나온 결과 하나만 보고합니다.
p-해킹은 다중 비교가 숨은 꼴입니다. 검정은 여러 번 했는데 보고에는 하나만 남습니다. 그래서 몇 번으로 나눠 보정해야 하는지 읽는 사람이 알 수 없습니다. 다중 비교는 검정 수가 드러나 있으면 보정할 수 있습니다. p-해킹은 그 수부터 감춥니다.
관련 항목
다중 비교가 일어나는 가설 검정의 구성 요소
가설 검정 · 귀무가설 · 대립가설 · 유의 수준 · p값 · 통계적 유의성 · 검정 통계량
다중 비교가 부풀리거나 맞바꾸는 오류율
1종 오류 · 2종 오류 · 가족 오류율 · 거짓 발견률 · 검정력
다중 비교에 맞춰 유의 수준을 고쳐 잡는 보정 방법
본페로니 보정 · 홀름 보정 · 시다크 보정 · 벤야미니-호흐베르크 절차 · 투키 검정
다중 비교가 끼어드는 실험과 분석 방법
A/B 테스트 · 다변량 테스트 · 분산 분석 · 하위 집단 분석 · 순차 검정 · 성능 회귀 · 벤치마크
다중 비교를 감추거나 막는 분석 관행
다른 이름: multiple comparisons · multiple testing · 다중 비교 문제 · 다중 검정