심슨의 역설
고친 사람 github-actions[bot]
심슨의 역설은 데이터를 무리별로 나눠 볼 때와 합쳐 볼 때 결론이 뒤집히는 고장입니다. 요청 종류마다 오류가 적은 새 버전이 전체 오류율로는 더 나쁘게 나오기도 합니다. 합친 숫자가 무리마다 섞인 몫에 끌려가서 생깁니다. 어느 숫자를 믿을지는 데이터가 생긴 사정이 정합니다.
쉽고 빠른 이해
심슨의 역설은 나눠 보면 이기는 쪽이 합쳐 보면 지는 일입니다. 새 버전이 읽기 요청에서도 쓰기 요청에서도 오류가 적습니다. 그래도 전체 오류율은 더 높게 나올 수 있습니다.
따로 이름이 붙은 까닭은 계산이 하나도 안 틀렸기 때문입니다. 숫자는 모두 맞습니다. 그런데도 결론이 뒤집힙니다. 합계 하나만 보면 더 나은 버전을 되돌리게 됩니다.
- 무리에 따라 결과가 크게 다릅니다. 쓰기는 읽기보다 훨씬 자주 실패합니다
- 견주는 두 쪽이 무리를 다른 몫으로 받습니다. 새 버전이 쓰기를 주로 받는 식입니다
- 합친 숫자는 몫이 큰 무리 쪽으로 끌려갑니다. 실패하기 쉬운 요청을 많이 받은 쪽이 나빠 보입니다
나눠 보는 것이 늘 답도 아닙니다. 나눈 기준이 비교하려는 버전 때문에 바뀐 값이면 합친 숫자가 맞습니다. 새 버전이 응답을 키웠다면 응답 크기로 나눠 견주면 안 되는 식입니다. 어느 쪽을 믿을지는 숫자만 봐서는 못 정합니다.
상세
이 절은 배포 하나의 오류율을 따라가며 결론이 어떻게 뒤집히는지 봅니다. 숫자가 뒤집히는 계산을 먼저 풉니다. 그다음 그 계산을 움직이는 셋째 변수로 넘어갑니다.
이어서 나눈 숫자와 합친 숫자 가운데 어느 쪽을 믿을지 가르는 기준을 봅니다. 끝으로 백엔드 지표에서 이 고장이 숨는 곳과 막는 법을 짚습니다.
나눠 보면 앞서고 합치면 뒤지는 배포
기존 버전 A 와 새 버전 B 를 함께 띄워 두고 요청을 나눠 받게 했다고 해 봅시다. 하루 동안 두 버전이 1만 건씩 받았습니다. 받은 요청 가운데 실패한 비율, 곧 오류율로 두 버전을 견줍니다. 이 절에서는 오류율을 100건당 몇 건이 실패했는지로 적습니다.
전체 오류율만 보면 승부는 싱겁습니다. A 는 100건당 1.9건, B 는 7.25건이 실패했습니다. B 를 되돌려야 할 것처럼 보입니다.
요청을 읽기와 쓰기로 나눠 보면 그림이 바뀝니다. 아래 표는 버전마다 요청 종류별로 건수와 오류를 센 것입니다.
| 버전 | 요청 종류 | 건수 | 오류 | 100건당 오류 |
|---|---|---|---|---|
| A | 읽기 | 9,000 | 90 | 1.0 |
| A | 쓰기 | 1,000 | 100 | 10.0 |
| A | 합계 | 10,000 | 190 | 1.9 |
| B | 읽기 | 1,000 | 5 | 0.5 |
| B | 쓰기 | 9,000 | 720 | 8.0 |
| B | 합계 | 10,000 | 725 | 7.25 |
읽기만 견주면 B 가 0.5건으로 A 의 1.0건보다 적습니다. 쓰기만 견줘도 B 가 8.0건으로 A 의 10.0건보다 적습니다. 두 종류 모두에서 B 가 낫습니다. 그런데 합치자 B 가 네 배 가까이 나빠졌습니다.
이렇게 무리마다 나눠 본 결론과 합쳐 본 결론이 거꾸로 서는 일을 심슨의 역설이라고 부릅니다. 계산이 틀린 곳은 없습니다.
합친 숫자가 끌려가는 쪽
합계 오류율은 두 오류율을 반씩 섞은 값이 아닙니다. 각 무리의 오류율에 그 무리가 차지하는 몫을 곱해 더한 값입니다. 이렇게 몫을 곱해 더하는 평균을 가중 평균이라고 부릅니다. 곱하는 몫은 가중치라고 부릅니다.
A 는 받은 요청 열 건 가운데 아홉 건이 읽기입니다. 그래서 A 의 합계는 읽기 오류율 1.0 쪽으로 끌려갑니다. 1.0 × 0.9 에 10.0 × 0.1 을 더하면 1.9 가 나옵니다.
B 는 거꾸로 열 건 가운데 아홉 건이 쓰기입니다. B 의 합계는 쓰기 오류율 8.0 쪽으로 끌려갑니다. 0.5 × 0.1 에 8.0 × 0.9 를 더하면 7.25 입니다.
합계를 가른 것은 버전의 실력이 아니라 요청 구성이었습니다. 쓰기는 어느 버전에서든 읽기보다 훨씬 자주 실패합니다. 실패하기 쉬운 쓰기를 B 가 아홉 배 많이 떠안았습니다. 그 차이가 버전 사이의 작은 우열을 덮었습니다.
역설은 두 조건이 함께 설 때만 생길 수 있습니다. 첫째, 무리에 따라 결과가 달라집니다. 둘째, 견주는 두 쪽이 그 무리들을 서로 다른 몫으로 담고 있습니다.
둘 중 하나라도 빠지면 합계는 무리별 결론을 따라갑니다. 두 버전이 읽기와 쓰기를 같은 몫으로 받았다면 두 종류에서 모두 나은 B 가 합계에서도 낫습니다. 읽기와 쓰기가 똑같이 실패한다면 구성이 달라도 합계가 끌려갈 쪽이 없습니다.
몫을 흔드는 셋째 변수
B 가 쓰기를 아홉 배 많이 받은 까닭은 라우팅에 있습니다. 쓰기 API(Application Programming Interface, 애플리케이션 프로그래밍 인터페이스)부터 새 버전으로 옮기는 중이었다고 해 봅시다. 그러면 요청 종류가 어느 버전으로 갈지를 정합니다.
비교하려던 것은 버전과 실패, 두 변수입니다. 그런데 요청 종류라는 셋째 변수가 둘 모두에 손을 댔습니다. 요청 종류는 어느 버전이 받을지를 정했습니다. 동시에 그 요청이 실패할지에도 영향을 줬습니다.
이렇게 견주는 원인과 결과 양쪽에 함께 영향을 주는 변수를 교란 변수라고 부릅니다. 앞의 배포에서는 B 가 나빠 보인 까닭 전부가 이 교란 변수에서 왔습니다.
flowchart TD
K["요청 종류 · 읽기 또는 쓰기"] --> V["어느 버전이 받나"]
K --> E["실패하나"]
V --> E
그림에서 요청 종류가 내보내는 두 화살표가 교란 변수가 하는 일입니다. 알고 싶었던 것은 버전에서 실패로 가는 화살표 하나뿐입니다. 합친 숫자는 세 화살표를 한데 섞어 보여 줍니다.
두 값이 함께 움직이는 모습을 상관관계라고 합니다. B 와 높은 오류율은 함께 나타났습니다. 그러니 둘 사이에 상관관계는 있습니다.
한쪽이 다른 쪽을 일으키는 것은 인과 관계입니다. B 가 오류를 일으킨 것은 아니었으니 인과 관계는 없었습니다. 교란 변수가 끼면 이렇게 상관관계가 인과 관계처럼 보입니다.
나눈 숫자와 합친 숫자 가운데 어느 쪽
앞의 배포에서는 나눈 숫자가 답이었습니다. 그렇다고 늘 나눠 보는 쪽이 옳은 것은 아닙니다. 같은 꼴의 표라도 셋째 변수가 어떻게 생겼는지에 따라 답이 달라집니다.
가르는 물음은 셋째 변수가 언제 정해졌느냐입니다. 요청 종류는 버전이 정해지기 전에 이미 정해져 있었습니다. 읽기 요청은 어느 버전에 가든 읽기입니다. 이렇게 원인보다 먼저 정해진 변수는 나눠서 견줘야 공정합니다.
반대 경우를 하나 봅니다. 새 버전 C 가 응답 압축을 껐다고 합시다. 압축을 안 하니 응답이 커져서 네트워크로 나가는 바이트가 늘어납니다. 큰 응답은 보내는 데 더 오래 걸립니다.
응답 시간을 응답 크기 구간별로 나눠 보면 구간마다 C 가 더 빠릅니다. 압축하는 데 드는 시간을 아꼈기 때문입니다. 그런데 C 의 응답은 큰 구간으로 몰려서 합계로는 C 가 더 느립니다. 이번에도 나눈 결론과 합친 결론이 거꾸로 섰습니다.
이번에는 합친 숫자가 답입니다. 응답 크기는 버전이 정해진 뒤에 버전 때문에 바뀐 값입니다.
응답이 커져서 늘어난 전송 시간은 크기 구간을 나누면 어느 구간 안에도 안 잡힙니다. 같은 구간 안에서는 두 버전의 응답 크기가 비슷하기 때문입니다. 나눠서 보면 알고 싶던 영향 일부를 스스로 지우는 셈입니다.
셋째 변수가 결과에 아무 영향도 주지 않으면 고민할 것이 없습니다. 그때는 나눈 숫자와 합친 숫자가 같은 쪽을 가리키니 합친 숫자로 견주면 됩니다. 아래 그림은 이 판정을 차례로 묶은 것입니다.
flowchart TD
A["셋째 변수가 결과에 영향을 주나"] -->|아니다| B["합친 숫자로 견준다"]
A -->|그렇다| C["견주는 원인보다 먼저 정해졌나"]
C -->|그렇다| D["나눠서 견준다"]
C -->|"아니다 · 원인이 바꾼 값이다"| E["합친 숫자로 견준다"]
이 물음은 숫자만 들여다봐서는 답할 수 없습니다. 두 배포의 표는 꼴이 같습니다. 셋째 변수가 언제 어떻게 정해지는지는 그 시스템이 어떻게 도는지를 아는 사람이 알려 줘야 합니다.
백엔드에서 숨는 곳
백엔드 지표는 대개 여러 무리를 합친 숫자로 보입니다. 아래 표는 흔한 경우를 섞이는 무리와 뒤집히는 결론으로 나눠 봅니다.
표에는 배포와 실험이 하나씩 나옵니다. 카나리 배포는 새 버전을 요청 일부에만 먼저 내보내 보고 넓혀 가는 배포 방식입니다. 문제가 생겨도 일부 요청만 다치게 하려고 씁니다.
A-B 테스트는 두 안을 사용자에게 나눠 보여 주고 결과를 견주는 실험입니다. 어느 안이 나은지를 실제 사용자 반응으로 가리려고 씁니다.
A-B 테스트에서 견주는 값은 흔히 전환율입니다. 구매나 가입까지 간 사용자의 비율을 말합니다.
| 지표 | 섞이는 무리 | 뒤집히는 결론 |
|---|---|---|
| 카나리 배포의 오류율 | 새 버전으로 먼저 보낸 요청 종류 | 종류마다 나은 새 버전이 전체로는 나빠 보인다 |
| 버전별 평균 응답 시간 | 처리가 오래 걸리는 요청과 금방 끝나는 요청의 비중 | 오래 걸리는 요청을 많이 받은 버전이 느려 보인다 |
| 서버 두 대의 캐시 적중률 | 자주 읽히는 키와 드물게 읽히는 키의 비중 | 드문 키를 많이 받은 서버가 캐시를 못 쓰는 것처럼 보인다 |
| A-B 테스트의 전환율 | 두 안을 본 사용자의 모바일과 데스크톱 비중. [[타임존 | 시간대]]마다 안을 바꿔 보여 줘서 갈렸다 |
네 줄 모두 견주는 두 쪽이 무리를 다른 몫으로 받았습니다. 몫이 갈린 까닭은 라우팅 규칙, 배포 순서, 사용자가 몰리는 시간대처럼 비교와 상관없이 정해진 치우침입니다.
대시보드가 합계 그래프 하나만 그리면 이 뒤집힘은 화면에 안 나타납니다. 합계와 무리별 그래프를 나란히 둬야 보입니다.
알아채는 법과 막는 법
첫째 방법은 결론을 내기 전에 두 쪽의 구성부터 견주는 것입니다. 버전별로 요청 종류 비중을 나란히 놓아 봅니다. 몫이 크게 다르면 합친 숫자를 의심합니다.
둘째 방법은 결과에 영향을 줄 만한 무리로 나눠 다시 세는 것입니다. 나눈 결론과 합친 결론이 같은 쪽을 가리키면 걱정할 것이 없습니다. 거꾸로 서면 「셋째 변수가 견주는 원인보다 먼저 정해졌나」를 물어 어느 쪽을 믿을지 정합니다.
막는 가장 확실한 길은 어느 쪽이 무엇을 받을지를 운에 맡기는 것입니다. 이 방법을 무작위 배정이라고 부릅니다. 요청마다 동전을 던지듯 버전을 고르면 건수가 충분할 때 읽기와 쓰기가 두 버전에 거의 같은 몫으로 갑니다.
무작위 배정은 미처 생각 못 한 셋째 변수까지 고르게 섞어 줍니다. 그래서 두 쪽이 무리를 다른 몫으로 받는 일이 처음부터 생기지 않습니다. 잘 설계된 A-B 테스트가 사용자를 무작위로 나누는 까닭이 이것입니다. 표의 A-B 테스트처럼 시간대마다 안을 바꿔 보여 주면 이 보호를 잃습니다.
무작위로 못 나눌 때는 무리를 먼저 정해 두고 무리 안에서 견줍니다. 이를 층화라고 부릅니다. 읽기는 읽기끼리, 쓰기는 쓰기끼리 견준 뒤 결과를 모읍니다.
모을 때는 두 버전에 같은 가중치를 씁니다. 앞의 배포에 읽기와 쓰기를 반씩 쳐서 모으면 A 는 100건당 5.5건, B 는 4.25건이 됩니다. 같은 몫으로 모으니 무리별 결론대로 B 가 낫게 나옵니다.
표본 편향과 다른 점
표본 편향은 손에 든 데이터가 전체를 닮지 않아서 답이 기우는 일입니다. 데이터 일부가 빠지거나 한쪽으로 치우쳐 뽑혀서 생깁니다. 심슨의 역설은 데이터가 하나도 빠지지 않아도 생깁니다. 앞의 배포 표는 두 버전이 받은 요청을 빠짐없이 센 것입니다.
어긋나는 곳도 다릅니다. 표본 편향은 데이터 자체가 기울었습니다. 심슨의 역설은 데이터는 온전합니다. 합치는 계산이 무리 구성의 차이를 결론에 섞어 넣어서 답이 뒤집힙니다.
둘 다 데이터를 더 모아서는 풀리지 않습니다. 표본 편향은 뽑는 방식을 고쳐야 풀립니다. 심슨의 역설은 견주는 방식을 고쳐야 풀립니다.
관련 항목
심슨의 역설과 나란히 분석을 뒤집는 편향과 역설
생존 편향 · 표본 편향 · 선택 편향 · 베르크손의 역설 · 생태학적 오류 · 기저율 오류
심슨의 역설을 풀어내는 인과의 개념
교란 변수 · 매개 변인 · 상관관계 · 인과 관계 · 인과 추론 · 인과 그래프
심슨의 역설이 뒤집는 계산의 재료
평균 · 가중 평균 · 비율 · 집계 · 모집단 · 표본
심슨의 역설을 막거나 바로잡는 분석 방법
무작위 배정 · A-B 테스트 · 무작위 대조 시험 · 층화 · 층화 추출 · 표준화 (통계) · 회귀 분석
심슨의 역설이 숨어드는 백엔드 지표
다른 이름: Simpson's paradox · 심프슨의 역설 · 심슨 역설