알고리즘 편향과 피드백 루프¶
이 절의 다른 편향들은 모두 정적이다. 표본이 한 번 치우치면 그만큼 치우친 채로 남는다. 알고리즘이 개입하면 상황이 달라진다. 모형의 예측이 사람의 행동을 바꾸고, 바뀐 행동이 내일의 자료를 만들고, 그 자료로 모형이 다시 학습한다. 편향이 고정된 값이 아니라 시간에 따라 자라는 양이 된다.
이 페이지는 인공지능 학습 패러다임과 편향이 만나는 지점이다. 1.3에서 본 세 갈래는 모두 "이미 있는 자료에서 배운다"는 전제 위에 있었다. 그 자료가 어떻게 만들어졌는지 묻지 않으면 무슨 일이 벌어지는지가 여기서 드러난다.
정의 1. 알고리즘 편향¶
알고리즘 편향(algorithmic bias) 은 알고리즘의 출력이 특정 집단에 대해 체계적으로 부정확하거나 불리하게 작동하는 현상이다. 알고리즘 자체가 편향을 만들어 내는 경우보다, 훈련자료에 이미 들어 있던 편향을 학습하고 재생산하는 경우가 압도적으로 흔하다.
피드백 루프(feedback loop) 는 모형의 출력이 그 모형의 다음 훈련자료 생성에 영향을 주는 구조다. 배포된 모형이 현실을 바꾸고, 바뀐 현실이 자료가 되어 모형에 되돌아온다. 이때 편향은 상쇄되지 않고 증폭된다.
자료는 세계를 기록하지 않는다. 측정 과정을 기록한다¶
핵심 오류는 늘 같은 곳에서 시작된다. 우리가 가진 변수를 우리가 알고 싶은 변수로 착각하는 것이다.
Lum과 Isaac(2016)은 예측 치안 알고리즘 PredPol을 오클랜드의 마약 범죄 자료에 적용해 보았다. 두 자료를 나란히 놓으면 문제가 즉시 드러난다.
- 2011년 전국약물사용조사에 근거한 추정으로, 오클랜드의 마약 사용은 도시 전역에 대체로 고르게 분포한다.
- 2010년 오클랜드 경찰의 마약 검거 기록은 저소득·비백인 밀집 지역에 극심하게 집중되어 있다.
알고리즘은 마약 범죄가 어디서 일어나는지를 배운 것이 아니다. 경찰이 어디를 순찰했는지를 배웠다. 그리고 이미 순찰이 집중된 지역을 다시 지목했다.
목표변수와 대리변수의 간극
우리가 예측하고 싶은 것은 범죄이지만, 자료로 존재하는 것은 검거다. 둘의 관계는
인데, 두 번째 항이 지역마다 다르다면 검거 자료는 범죄의 편향된 대리변수다. 순찰이 두 배인 지역은 범죄가 같아도 검거가 두 배다.
같은 구조가 도처에 있다. 채용 모형의 목표는 "좋은 직원"이지만 자료는 "과거에 채용되어 좋은 평가를 받은 사람"이고, 의료비 예측 모형의 목표는 "아픈 정도"이지만 자료는 "지출된 의료비"다. 의료 접근성이 낮은 집단은 같은 질병 부담에도 지출이 적으므로, 지출을 건강의 대리변수로 쓰면 그 집단이 체계적으로 덜 아픈 것으로 추정된다.
모형을 검토할 때 첫 질문은 알고리즘이 아니라 목표변수여야 한다. 우리가 실제로 최적화하고 있는 것이 무엇인가?
편향은 넣지 않아도 자란다¶
정적인 편향이라면 여기서 끝난다. 문제는 모형이 배포되면 자료 생성 과정 자체가 바뀐다는 데 있다.
모형이 A구역을 고위험으로 지목한다 → 순찰이 A구역에 집중된다 → A구역의 검거가 늘어난다 → 그 기록이 내일의 훈련자료가 된다 → 모형은 A구역이 위험하다고 더 확신한다 → 순찰이 더 집중된다 → …
이 순환에서 주목할 점이 둘이다.
첫째, 아무도 편향을 넣지 않았다. 인종 변수를 모형에서 제거해도 아무 소용이 없다. 우편번호, 통근 시간, 이전 검거 기록 같은 변수들이 인종의 대리변수로 작동하기 때문이다. 변수 하나를 지우는 것으로는 그 변수와 상관된 정보 전체를 지울 수 없다.
둘째, 모형이 스스로 자기 예측을 참으로 만든다. A구역의 검거가 실제로 늘었으므로, 사후 평가에서 모형은 "정확했다"는 판정을 받는다. 자기실현적 예측은 성능 지표로 잡히지 않는다. 오히려 성능이 좋아 보인다.

왼쪽이 이 고리의 구조다. 네 상자 중 어디에도 특정 구역을 불리하게 만드는 장치가 없다는 점을 먼저 확인하라. 모형은 과거 기록만 보고, 순찰은 그 판단을 따르고, 검거는 순찰이 간 곳에서만 기록되고, 그 기록이 다시 모형에게 돌아간다. 각 단계는 저마다 합리적이다. 문제는 단계가 아니라 그것들이 닫힌 고리를 이룬다는 데 있다. 편향이 한 번 생기고 마는 것이 아니라 한 바퀴 돌 때마다 자기 자신을 입력으로 받는다.
오른쪽은 그 고리를 실제로 30번 돌린 결과다. 두 구역의 참 범죄율은 10%로 완전히 같고, 출발점의 기록 차이는 102 대 100뿐이다. 그런데 기록이 많은 쪽에 순찰을 몰아주면 A구역이 차지하는 검거 기록 비중이 50.5%에서 시작해 30회차에 96.9% 까지 올라간다. 순찰의 20%를 무작위로 배정해도 87.8% 다. 반면 기록에 비례해서 나누면 같은 30회차에 53.0% 에 머문다(배분 규칙에 따른 차이는 아래 주석에서 다시 다룬다).
세 곡선이 갈라지는 방식에도 읽을 것이 있다. 몰아주기 두 곡선(빨강·주황)은 한 번도 꺾여 내려오지 않는다. 고리에는 자기가 만든 오차를 되돌리는 기제가 없기 때문이다. 반면 비례 배분 곡선(파랑)은 \(50\%\) 언저리에서 위아래로 흔들릴 뿐 한쪽으로 달아나지 않는데, 이 규칙 아래에서는 B구역도 계속 순찰을 받아 자기 기록을 갱신할 수 있어서다.
몰아주기 곡선의 기울기가 초기 10회차에 가장 가파른 것은 격차가 가속되기 때문이 아니다. 탐색이 없는 빨강 곡선을 보자. 1회차부터 이미 순찰 100명이 전부 A로 가므로 B의 기록은 100에 얼어붙고 A의 기록만 회차마다 약 100씩 늘어난다. \(t\)회차에서 A의 비중은
가 되어 \(t=10\)에서 \(91.7\%\), \(t=30\)에서 \(96.9\%\)로 100%라는 천장에 눌려 완만해진다. 탐색 20%인 주황 곡선은 B도 매 회차 순찰 10명을 받아 기록이 조금씩 늘므로 천장이 더 낮게 잡힐 뿐 모양은 같다. 요컨대 폭주는 초반에 이미 끝나 있고, 곡선이 평평해진 것은 상황이 나아져서가 아니라 더 나빠질 여지가 없어서다.
그리고 이 그림에서 참값 50%를 아는 것은 우리뿐이다. 시스템 안에서 관측되는 것은 곡선들이지 초록 선이 아니다. 모형이 보는 자료에는 "A구역 기록 비중이 96.9%"라는 사실만 있고, 그 96.9%가 범죄의 분포인지 순찰의 분포인지 구별할 정보는 어디에도 없다. 이것이 앞 절들의 편향과 결정적으로 다른 점이다. 무응답 편향에는 적어도 인구총조사라는 기준선이 있었지만, 여기서는 기준선을 만들 자료 자체를 고리가 생산하지 않는다.
1.3의 강화학습과 같은 구조, 다른 의도
자기 훈련자료를 스스로 만들어 내는 구조는 인공지능 학습 패러다임 절의 강화학습에서 이미 보았다. 에이전트가 행동하고, 그 행동이 다음 상태와 관측을 결정한다.
차이는 의도에 있다. 강화학습은 그것을 설계로 삼는다. 그래서 탐색(exploration)을 명시적으로 집어넣는다. 최적으로 보이지 않는 행동도 일부러 시도해서 자기 믿음이 틀렸는지 확인하는 장치다.
배포된 예측 모형에는 대개 그 장치가 없다. 활용만 하고 탐색은 하지 않는다. B구역이 안전하다고 판단하면 B구역에 가지 않고, 가지 않으므로 B구역의 자료가 갱신되지 않으며, 틀렸더라도 영원히 알 수 없다. 강화학습의 언어로 말하면 이는 탐색 부족으로 인한 최적이 아닌 정책의 고착이다.
COMPAS: 공정성의 정의는 하나가 아니다¶
미국 법원에서 재범 위험을 점수화하는 데 쓰인 COMPAS를 두고 2016년에 벌어진 논쟁은, 알고리즘 편향이 순수한 기술 문제가 아님을 보여 준다.
ProPublica의 지적. 실제로 재범하지 않은 피고인 중 고위험으로 잘못 분류된 비율이 흑인 피고인은 45%, 백인 피고인은 23% 였다. 거짓양성률이 두 배 가까이 차이 난다.
개발사(Northpointe)의 반박. 우리 모형은 두 집단에서 동일하게 보정되어 있다. 고위험 판정을 받은 사람이 실제로 재범할 확률(양성예측도)이 흑인과 백인에서 같다. 거짓양성률 차이는 두 집단의 기저 재범률이 다르기 때문에 생기는 산술적 귀결이다.
두 주장 모두 사실이다. 그리고 이것이 핵심이다.
기저 재범률이 집단마다 다르고 분류기가 완벽하지 않다면, 다음 두 가지를 동시에 만족시키는 것은 수학적으로 불가능하다.
- 보정(calibration): 같은 위험 점수를 받은 사람은 집단과 무관하게 같은 재범 확률을 갖는다.
- 오류율 균등: 거짓양성률과 거짓음성률이 집단 간에 같다.
이는 자료를 더 모으거나 알고리즘을 개선해서 풀 수 있는 문제가 아니다. 어느 공정성을 지킬지 골라야 한다. 그리고 그 선택은 통계적 판단이 아니라 규범적 판단이다. "무고한 사람을 고위험으로 낙인찍지 않는 것"과 "위험 점수의 의미가 집단에 무관하게 일정한 것" 중 무엇이 이 맥락에서 더 중요한가는 자료가 답해 주지 않는다.
이 논쟁에서 배울 것
"알고리즘이 공정한가?"라는 질문에는 답이 없다. "어떤 의미에서 공정한가?" 를 먼저 정해야 답이 생긴다.
실무적 귀결: 공정성 기준은 모형을 만들기 전에, 관련된 이해당사자와 함께, 그 결정이 낳는 대가와 함께 명시해야 한다. 모형을 만든 뒤에 어떤 기준을 통과하는지 찾아보는 것은 통제실험 절에서 본 "갈래길의 정원"의 공정성 판이다.
이 절의 다른 편향과 무엇이 다른가¶
| 무응답·생존자·출판 편향 | 알고리즘 피드백 루프 | |
|---|---|---|
| 시간에 따른 변화 | 고정 | 증폭 |
| 자료 생성 과정 | 분석과 무관하게 진행 | 분석 결과가 바꾼다 |
| 성능 지표에 드러나는가 | 드러나지 않음 | 오히려 좋아 보임 |
| 진단 방법 | 기준선과 대조(등록부, 전수자료) | 기준선 자체가 오염됨 |
마지막 줄이 가장 어려운 지점이다. 출판 편향은 FDA 등록부라는 편향되지 않은 기준선이 있어서 크기를 잴 수 있었다. 피드백 루프에서는 모형이 오래 돌수록 기준선도 함께 오염된다. 과거 자료가 이미 과거 모형의 산물이기 때문이다.
대응책¶
- 목표변수를 감사한다. 우리가 예측하는 것이 우리가 원하는 것인가? 검거인가 범죄인가, 의료비인가 질병 부담인가. 대부분의 심각한 사례가 여기서 시작된다.
- 탐색을 설계에 넣는다. 자원의 일부를 모형의 추천과 무관하게 무작위로 배정한다. 비용이 들지만, 그렇게 얻은 자료만이 편향되지 않은 기준선 역할을 한다. 통제실험 절의 무작위화가 여기서 다시 쓰인다.
- 배포 전에 반사실을 평가한다. "이 모형을 썼다면 어떻게 되었을까"를 과거 자료로 추정하되, 과거 자료가 과거 정책의 산물임을 감안한다.
- 분포 이동을 감시한다. 예측 분포와 실제 결과 분포를 집단별로 시간에 따라 추적한다. 전체 정확도만 보면 루프가 보이지 않는다.
- 공정성 기준을 사전 명시한다. 여러 기준을 동시에 만족시킬 수 없으므로, 무엇을 택하고 무엇을 포기하는지 기록으로 남긴다.
보기 1. 알고리즘 편향과 피드백 루프. 두 지역의 참 범죄율이 똑같이 \(10\%\)이고 인구가 각 \(1{,}000\)명이다. 매 회차에 순찰 \(100\)을 배분하는데, 검거 기록이 많은 쪽을 우범지역으로 지목해 몰아준다. 출발 기록은 \(102\) 대 \(100\)으로 \(2\%\)만 다르다. 순찰의 비율 \(e\)만큼은 두 지역에 반씩 무작위로 배정한다(탐색).
(1) 어느 쪽에 얼마를 보내든 순찰 하루당 검거 건수의 기댓값이 두 지역 모두 똑같이 얼마인지 구하시오.
(2) 탐색률 \(e = 0\)과 \(e = 0.2\)에서 \(30\)회차 뒤의 순찰일수와 모형이 보는 검거 건수 비중을 예측하고, 모의실험과 견주시오.
풀이
(1) 해석적으로. 한 회차에서 한 지역의 범죄 건수는 \(\text{Bin}(1000,\ 0.10)\)이므로 \(E[\text{범죄}] = 100\)이다. 순찰을 \(a\)만큼 보내면 각 범죄가 적발될 확률이 \(a/100\)이고 검거 건수는 \(\text{Bin}(\text{범죄},\ a/100)\)이므로
다. 따라서 \(a > 0\)인 한
로 두 지역 모두 정확히 1이고 순찰 배분과 무관하다. 바로 이것이 순찰일당 검거율을 편향되지 않은 신호라 부르는 이유다. 반면 검거 건수는 \(a\)에 정비례하므로 순찰을 몰아준 쪽이 자동으로 커진다. 같은 자료에서 분자만 보면 편향되고 분모로 나누면 편향이 사라진다.
\(a = 0\)이면 분모가 0이라 비율이 정의되지 않는다. 이 nan 이 요점이다. 순찰을 한 번도 보내지 않은 지역에 대해서는 자료 자체가 존재하지 않는다.
(2) 해석적으로. 탐색률 \(e\)에서 우범지역으로 지목된 쪽은
를 받는다. 출발 기록이 \(102 > 100\)이라 A가 처음부터 우범지역이 되고, A의 기록은 매 회차 더 빨리 늘어나므로 지목이 뒤집히지 않는다. 따라서 \(30\)회차 뒤의 순찰일수는
| \(e\) | A | B |
|---|---|---|
| \(0\) | \(30\times100 = 3000\) | \(0\) |
| \(0.2\) | \(30\times90 = 2700\) | \(30\times10 = 300\) |
이다. 검거 기록은 (1)에서 \(E[\text{검거}] = a\)이므로 순찰일수만큼 쌓인다. 초기 기록을 더하면
가 모형이 보는 A의 비중이다.
(1)(2) 수치적으로. 두 지역의 실제 범죄율이 완전히 동일한데, 초기 검거 기록의 2% 차이만으로 순찰 배분이 어떻게 고착되는지 보여 준다. 모형에는 지역을 차별할 어떤 변수도 들어 있지 않다.
"""되먹임 고리: 범죄율은 같은데 순찰만 한쪽으로 쏠릴 때 무슨 일이 생기는가.
순찰이 많은 곳에서 더 많이 적발되고, 그 기록이 다시 순찰을 늘린다.
자료가 현실을 비추는 것이 아니라 현실을 만들어 내는 경우다.
"""
import numpy as np
rng = np.random.default_rng(7)
TRUE_RATE = np.array([0.10, 0.10]) # 두 지역의 참 범죄율. 완전히 같다
PATROL_TOTAL = 100 # 매 회차에 배분할 순찰 인력
POPULATION = 1000 # 지역당 인구
ROUNDS = 30
def simulate(explore):
"""검거 기록이 많은 쪽에 순찰을 보낸다. explore는 무작위로 배정하는 비율."""
# 출발점: 기록이 딱 2% 차이 난다. 우연히 생긴 차이일 뿐 실체가 없다.
records = np.array([102.0, 100.0])
arrests = np.zeros(2)
patrol_days = np.zeros(2)
for _ in range(ROUNDS):
# (1) 모형의 예측: 기록이 많은 쪽을 "우범지역"으로 지목한다.
# 지역을 식별하는 변수는 어디에도 쓰이지 않는다. 오직 과거 기록뿐이다.
hotspot = np.where(records == records.max(), 1.0, 0.0)
# (2) 배분: (1-explore)만큼은 우범지역에, explore만큼은 두 지역에 반씩.
# explore=0 이면 순찰이 전부 한쪽으로 쏠린다.
share = (1 - explore) * hotspot / hotspot.sum() + explore * 0.5
patrol = PATROL_TOTAL * share
# (3) 현실: 두 지역에서 같은 비율(10%)로 범죄가 일어난다.
crimes = rng.binomial(POPULATION, TRUE_RATE)
# (4) 관측: 그중 순찰을 보낸 만큼만 검거된다.
# 순찰이 0인 지역에서는 범죄가 일어나도 단 한 건도 기록되지 않는다.
caught = rng.binomial(crimes, patrol / PATROL_TOTAL)
# (5) 되먹임: 오늘의 검거가 내일의 학습자료가 된다.
# 여기서 고리가 닫힌다. 순찰 -> 검거 -> 기록 -> 순찰.
records = records + caught
arrests += caught
patrol_days += patrol
return records, arrests, patrol_days
for explore in (0.0, 0.2):
records, arrests, patrol_days = simulate(explore)
# 모형이 보는 신호: 검거 "건수"의 지역별 비중.
# 순찰량에 비례해 부풀려지므로 편향되어 있다.
count_share = 100 * records / records.sum()
# 편향 없는 신호: 순찰 하루당 검거 건수.
# 순찰량으로 나누었으므로 순찰 배분의 영향이 지워진다.
# 다만 순찰이 0이면 0으로 나눌 수 없어 nan이 된다 — 이 nan이 요점이다.
rate = np.divide(arrests, patrol_days, out=np.full(2, np.nan),
where=patrol_days > 0)
print(f"exploration = {explore:.0%}")
print(f" patrol days A/B : {patrol_days[0]:7.0f} / {patrol_days[1]:.0f}")
print(f" arrest counts A/B : {count_share[0]:6.1f}% / {count_share[1]:.1f}%"
" <- what the model sees")
rate_txt = [f"{r:.3f}" if np.isfinite(r) else " ? " for r in rate]
print(f" arrests per patrol : {rate_txt[0]} / {rate_txt[1]}"
" <- the unbiased signal")
print()
출력:
exploration = 0%
patrol days A/B : 3000 / 0
arrest counts A/B : 96.9% / 3.1% <- what the model sees
arrests per patrol : 1.003 / ? <- the unbiased signal
exploration = 20%
patrol days A/B : 2700 / 300
arrest counts A/B : 87.8% / 12.2% <- what the model sees
arrests per patrol : 0.991 / 0.950 <- the unbiased signal
예측이 모두 맞는다. 순찰일수는 \(3000/0\)과 \(2700/300\)으로 유도한 값과 정확히 같고(배분이 결정론적이라 오차가 없다), 검거 건수 비중은 \(96.9\%\)와 \(87.8\%\)로 예측한 \(96.9\%\), \(87.5\%\)에 맞는다. 둘째 값의 \(0.3\)%p 차이는 이항 추출의 몬테카를로 오차다.
순찰일당 검거율은 \(1.003\), \(0.991\), \(0.950\)으로 셋 다 이론값 \(1\) 근처다. B구역의 \(0.950\)이 조금 낮은데, 순찰일수가 \(300\)뿐이라 표준오차가 \(\sqrt{1/300} = 0.058\)이다. \(1\)에서 \(0.86\) 표준오차 떨어진 값이니 어긋남이 아니다.
출력이 세 가지를 한꺼번에 보여 준다.
탐색이 없을 때(0%). B구역의 순찰일수가 0이 된다. 검거 건수는 96.9% 대 3.1%로 갈라지고, 모형은 A구역이 범죄 지역이라고 확신한다. 그런데 B구역의 순찰일당 검거율은 계산조차 되지 않는다(?). 순찰을 한 번도 보내지 않았으므로 B구역이 실제로 어떤지 알아낼 자료가 시스템 안에 존재하지 않는다. 반사실이 소멸한 것이다.
탐색이 20%일 때. 검거 건수는 여전히 87.8% 대 12.2%로 크게 치우쳐 있다. 순찰이 여전히 A에 몰리기 때문이다. 그러나 순찰일당 검거율은 0.991 대 0.950으로 사실상 같다. 두 지역의 범죄율이 동일하다는 참값이 드러난다.
여기서 대응책이 두 겹임이 분명해진다. 무작위 탐색이 편향되지 않은 관측을 만들어 내고, 건수 대신 노출량당 비율로 보는 것이 그 관측을 올바르게 읽어 낸다. 둘 중 하나만으로는 부족하다. 탐색 없이 비율만 보면 분모가 0이고, 탐색을 해도 건수만 보면 여전히 87.8% 대 12.2%다.
배분 규칙이 루프의 세기를 정한다
위 코드는 순찰을 기록이 많은 쪽에 몰아주는 핫스팟 방식을 쓴다. 실제 예측 치안 도구가 지도 위에 상위 구역을 표시하는 방식이 이것이다.
배분을 기록에 비례하도록 바꾸면(share = records / records.sum()) 결과가 크게 달라진다. 400개의 난수 시드로 300회차까지 돌려 보면 순찰 배분의 중앙값은 50.6%, 5~95분위가 45.9%–55.4%에 머문다. 초기 차이가 고착되기는 하지만 폭주하지는 않는다.
같은 자료, 같은 편향, 같은 피드백 구조인데 배분 규칙 하나가 결과를 가른다. "상위 \(k\)개에 집중하라"는 운영 규칙 자체가 편향 증폭 장치라는 뜻이며, 모형의 정확도와는 별개의 문제다.
연습문제¶
연습문제 1. 어떤 회사가 채용 모형에서 성별 변수를 제거하고 "이제 성중립적"이라고 발표했다. 이 주장의 결함을 설명하고, 성별 정보가 여전히 모형에 남아 있을 수 있는 경로를 세 가지 제시하라.
풀이
결함. 변수 하나를 지우는 것은 그 변수를 지우는 것이지 그 변수가 담고 있던 정보를 지우는 것이 아니다. 다른 변수들이 성별과 상관되어 있다면 모형은 그 상관을 통해 성별을 재구성한다. 이를 대리변수(proxy) 라 한다. 남는 정보의 양은 나머지 변수들로 성별을 얼마나 잘 예측할 수 있는가로 결정되며, 변수가 많고 유연한 모형일수록 이 예측이 정확해진다.
경로 예시.
- 경력 단절. 출산·육아 휴직으로 인한 공백은 성별과 강하게 상관된다. 모형이 "연속 근무 기간"을 쓰면 성별 정보가 들어온다.
- 어휘와 활동. 이력서의 특정 단어(동아리명, 여자대학 이름, 스포츠 종목), 자기소개서의 문체.
- 직무 이력의 분포. 특정 직군·부서의 성비가 치우쳐 있으면 이전 직무 자체가 대리변수다.
더 깊은 문제. 목표변수도 함께 봐야 한다. 훈련 레이블이 "과거에 채용되어 높은 고과를 받은 사람"이라면, 과거 채용과 고과 과정에 편향이 있었을 경우 모형은 그 편향을 학습 목표로 삼는다. 입력 변수를 아무리 정제해도 레이블이 오염되어 있으면 해결되지 않는다.
실무적 대응. 변수 제거가 아니라 결과 감사로 접근한다. 배포 전에 집단별 합격률·거짓양성률·양성예측도를 측정하고, 어떤 공정성 기준을 쓸지 사전에 정한다.
연습문제 2. 위의 보기 코드에서 두 지역의 참 범죄율은 동일하다. 그런데도 순찰 배분이 한쪽으로 고착된다.
(a) 이 루프를 끊으려면 무엇이 필요한가? 보기의 출력을 근거로 답하라. (b) 그 개입의 비용은 무엇인가? (c) 이 개입이 통제실험 절의 어떤 개념과 같은 것인지 밝혀라.
풀이
(a) 두 가지가 함께 필요하다.
첫째, 순찰의 일부를 모형과 무관하게 무작위로 배정한다(탐색). 그래야 각 지역에 대해 적발 확률이 모형의 판단과 독립인 자료가 매 회차 생성된다. 탐색이 0이면 보기에서 B구역의 순찰일수가 0이 되고, B구역의 검거율은 계산조차 되지 않는다(?). 추정할 자료 자체가 없는 것이다.
둘째, 검거 건수가 아니라 노출량당 비율로 읽는다. 탐색을 20% 넣어도 검거 건수는 여전히 87.8% 대 12.2%로 치우쳐 있다. 순찰이 여전히 A에 몰리기 때문이다. 그러나 순찰일당 검거율로 보면 0.991 대 0.950으로 사실상 같고, 두 지역의 범죄율이 동일하다는 사실이 드러난다.
둘 중 하나만으로는 부족하다. 탐색 없이 비율만 보면 분모가 0이고, 탐색을 해도 건수만 보면 여전히 편향된 그림을 본다.
(b) 세 가지다.
- 단기 효율 손실. 모형이 정말로 옳은 경우(A구역의 범죄율이 실제로 높은 경우), 무작위 배정분만큼 검거가 줄어든다.
- 형평성 문제의 이전. 무작위 배정을 받는 지역 주민 입장에서는 근거 없는 순찰을 받는 셈이다.
- 운영 복잡성과 정치적 부담. "왜 저기에 경찰을 보내느냐"를 설명해야 한다.
(c) 무작위화다. 통제실험 절에서 무작위화가 하는 일이 정확히 이것이었다 — 처리 배정을 대상의 특성과 독립으로 만들어 교란을 끊는 것. 여기서는 "처리"가 순찰이고 "대상"이 지역이다.
강화학습의 언어로는 탐색(exploration) 이다. 최적으로 보이지 않는 행동을 일부러 시도해 자기 믿음을 검증하는 장치이며, 탐색–활용 절충의 비용이 바로 (b)에서 계산한 단기 손실이다. 배포된 예측 모형이 위험한 이유는 대개 활용만 하고 탐색을 하지 않기 때문이다.
연습문제 3. 어떤 보험사가 의료비 지출액을 예측하는 모형으로 "집중 관리가 필요한 고위험 환자"를 선별한다. 이 설계에 어떤 구조적 문제가 있는지, 목표변수의 관점에서 설명하라.
풀이
문제는 목표변수와 실제 관심사의 불일치다. 회사가 알고 싶은 것은 누가 아픈가인데, 모형이 예측하는 것은 누구에게 돈이 쓰였는가다. 두 양의 관계는
으로 볼 수 있다. 접근성이 집단마다 다르면 지출은 질병 부담의 편향된 대리변수가 된다.
의료 접근성이 낮은 집단 — 보험이 얇거나, 진료비 부담이 크거나, 병원이 멀거나, 의료체계에 대한 불신이 있는 집단 — 은 같은 질병 부담에도 지출이 적다. 따라서 모형은 이들의 위험을 체계적으로 낮게 추정하고, 정작 도움이 필요한 사람을 프로그램에서 배제한다.
피드백 루프. 배제된 집단은 집중 관리를 받지 못하므로 다음 해에도 지출이 낮게 유지되고, 그 기록이 다시 훈련자료가 되어 모형의 판단을 강화한다. 반대로 선별된 집단은 관리를 받아 지출이 기록되고 위험이 계속 확인된다.
성능 지표는 이 문제를 잡아내지 못한다. 모형은 지출 예측이라는 자기 과제를 정확히 수행하고 있다. 문제는 정확도가 아니라 무엇을 예측하도록 정의했는가에 있다.
대응. 목표변수를 지출이 아니라 임상 지표(만성질환 개수, 검사 수치, 미충족 의료 필요)로 바꾸거나, 최소한 접근성 대리변수를 보정해 계층별로 평가한다. 근본적으로 이것은 알고리즘의 문제가 아니라 문제 정의의 문제다.
연습문제 4. COMPAS 논쟁에서 ProPublica와 개발사는 서로 다른 공정성 기준을 사용했다.
(a) 두 기준을 각각 정의하라. (b) 기저 재범률이 집단 간에 다를 때 두 기준을 동시에 만족시킬 수 없는 이유를 설명하라. (c) 이 사실이 "더 좋은 알고리즘을 만들면 된다"는 대응을 왜 무력화하는가?
풀이
(a) 오류율 균등(ProPublica). 실제로 재범하지 않은 사람 중 고위험으로 분류되는 비율(거짓양성률)이 집단 간에 같아야 한다. 실제 관측치는 흑인 45%, 백인 23%였다.
보정 또는 예측 동등성(개발사). 같은 위험 점수를 받은 사람의 실제 재범 확률이 집단과 무관하게 같아야 한다. 즉 "고위험" 판정의 의미가 집단에 따라 달라지지 않아야 한다.
(b) 두 집단의 기저 재범률을 \(p_A \ne p_B\)라 하자. 분류기가 보정되어 있다면, 각 집단에서 예측된 위험의 평균은 그 집단의 실제 재범률과 같아야 한다. 기저율이 높은 집단은 그만큼 더 많은 사람이 고위험 쪽으로 배치되어야 한다는 뜻이다.
그런데 분류기가 완벽하지 않으면 고위험으로 배치되는 사람 중에는 실제로 재범하지 않을 사람도 섞인다. 기저율이 높아 고위험 판정을 더 많이 내리는 집단에서는, 재범하지 않는 사람 중 고위험으로 분류되는 비율도 함께 올라간다. 즉 거짓양성률이 커진다.
따라서 보정을 유지하면 거짓양성률이 벌어지고, 거짓양성률을 맞추면 보정이 깨진다. 완벽한 분류기(\(p\)를 오차 없이 맞히는 경우)나 기저율이 같은 경우가 아니면 양립이 불가능하다. 이는 자료의 문제가 아니라 산술적 귀결이다.
(c) 알고리즘 개선이 할 수 있는 일은 정확도를 높이는 것이다. 그러나 위 불가능성은 정확도가 100%가 아닌 한 성립하며, 재범 예측처럼 본질적으로 불확실한 과제에서 정확도 100%는 도달할 수 없다. 자료를 더 모으거나 모형을 바꾸어도 두 기준의 충돌은 사라지지 않고 다만 작아질 뿐이다.
따라서 이것은 기술적 최적화 문제가 아니라 선택의 문제다. 무고한 사람에게 낙인을 찍지 않는 것과 점수의 의미를 일정하게 유지하는 것 중 이 맥락에서 무엇이 더 중요한지는 자료가 답하지 않는다. 그 결정은 알고리즘을 만들기 전에, 그 결정에 영향받는 사람들과 함께, 명시적으로 이루어져야 한다.
연습문제 5. 어떤 추천 알고리즘이 이용자가 클릭할 확률이 높은 콘텐츠를 상단에 배치한다. 배포 6개월 뒤 클릭률이 크게 올랐다. 이 성과 지표만으로 알고리즘이 좋다고 결론지을 수 없는 이유를 피드백 루프의 관점에서 설명하라.
풀이
클릭률 상승은 알고리즘이 이용자의 선호를 잘 맞혔다는 뜻일 수도 있고, 알고리즘이 이용자에게 보여 준 것이 그것뿐이라는 뜻일 수도 있다. 관측된 자료만으로는 둘을 구분할 수 없다.
루프의 구조. 알고리즘이 어떤 콘텐츠를 상단에 올린다 → 노출되었으므로 클릭이 발생한다 → 클릭 기록이 "이 이용자는 이런 콘텐츠를 좋아한다"는 증거로 축적된다 → 알고리즘이 더 확신하고 더 많이 노출한다. 선호가 측정되는 것이 아니라 형성된다.
결정적 결함: 반사실이 없다. 상단에 올라가지 않은 콘텐츠는 클릭 기회조차 얻지 못한다. "그것을 보여 주었다면 더 많이 클릭했을까"라는 질문에 답할 자료가 시스템 안에 존재하지 않는다. 이는 관찰연구에서 처리를 받지 않은 사람의 반사실을 알 수 없는 것과 같은 문제이며, 모형이 스스로 만들어 낸 자료로 자기를 평가하고 있다.
클릭률이라는 목표변수 자체의 문제. 클릭은 만족의 대리변수일 뿐이다. 자극적인 제목, 분노를 유발하는 콘텐츠, 짧고 즉각적인 자극이 클릭률을 높이지만 이용자의 장기적 만족이나 체류를 높이지는 않을 수 있다. 굿하트의 법칙대로, 지표가 최적화 대상이 되는 순간 그 지표는 원래 재려던 것을 재지 못한다.
어떻게 판단해야 하는가. 이용자의 일부를 무작위로 떼어 알고리즘 없이(또는 다른 알고리즘으로) 서비스하는 홀드아웃 집단을 상시 유지하고, 클릭률이 아니라 장기 지표(재방문, 구독 유지, 만족도 설문)를 비교한다. 이는 다시 무작위화이며, 피드백 루프를 끊는 유일한 신뢰할 만한 방법이다.
연습문제 6. 이 절의 다른 편향들(무응답, 생존자, 출판, 길이 편향)과 알고리즘 피드백 루프의 가장 근본적인 차이를 밝히고, 그 차이가 진단을 왜 어렵게 만드는지 논하라.
풀이
근본적 차이: 자료 생성 과정이 분석과 독립이 아니다.
다른 네 편향에서는 자료가 만들어지는 과정이 우리의 분석과 무관하게 진행된다. 응답자가 응답을 거부하는 것, 펀드가 폐쇄되는 것, 논문이 서랍에 남는 것, 긴 구간이 더 자주 걸리는 것 — 모두 우리가 무엇을 추정하든 그대로 일어난다. 그래서 이들은 정적이다. 한 번 치우친 만큼 치우친 채로 남는다.
피드백 루프에서는 분석의 산물이 자료 생성 과정에 되먹임된다. 모형의 출력이 순찰 배치를, 채용 결정을, 콘텐츠 노출을 바꾸고, 그 결정이 내일 관측될 자료를 만든다. 편향이 상수가 아니라 시간의 함수가 되며, 부호가 같은 방향으로 누적되면 지수적으로 증폭된다.
왜 진단이 어려운가. 세 가지 이유가 겹친다.
- 성능 지표가 경보를 울리지 않는다. 오히려 좋아진다. 모형이 예측한 대로 현실이 바뀌었으므로 사후 정확도가 올라간다. 자기실현적 예측은 예측 오차로 잡히지 않는다.
- 기준선이 함께 오염된다. 출판 편향은 FDA 등록부라는 편향되지 않은 기준선이 있어서 크기를 잴 수 있었다. 피드백 루프에서는 과거 자료가 이미 과거 모형의 산물이므로, 모형이 오래 돌수록 비교 대상 자체가 오염된다.
- 반사실이 소멸한다. 모형이 배제한 선택지에 대한 자료가 아예 생성되지 않는다. B구역에 순찰을 보내지 않으면 B구역의 범죄율은 영원히 갱신되지 않는다.
그래서 대응책도 다르다. 다른 편향들은 사후 보정이나 가중치로 부분적으로 다룰 수 있지만, 피드백 루프는 자료 생성 과정에 개입해야만 끊어진다. 자원의 일부를 무작위로 배분해 오염되지 않은 관측을 계속 만들어 내는 것 — 즉 무작위화를 상시 운영 절차 안에 심는 것 — 이 실질적으로 유일한 해법이다. 이 장이 통제실험에서 시작해 여기로 돌아오는 셈이다.
연습문제 7. 본문의 공정성 불가능성을 수치로 확인하라. 완벽하게 보정된 점수가 어떻게 집단 간에 전혀 다른 오류율을 낳는지 보이고, 오류율을 억지로 맞추면 무엇이 깨지는지도 보여라.
풀이
import numpy as np
rng = np.random.default_rng(0)
n = 200_000
group_a = rng.random(n) < 0.5
x = rng.normal(np.where(group_a, 0.0, -0.8), 1, n)
score = 1 / (1 + np.exp(-x)) # 이 점수는 정의상 완벽히 보정되어 있다
y = rng.random(n) < score # 실제 재범
print("점수 구간별 실제 재범률 — 두 집단이 같아야 보정된 것이다")
bins = np.linspace(0, 1, 6)
for i in range(5):
s = (score >= bins[i]) & (score < bins[i + 1])
print(f" 점수 [{bins[i]:.1f}, {bins[i + 1]:.1f}): "
f"집단A {y[s & group_a].mean():.4f} 집단B {y[s & ~group_a].mean():.4f}")
pred = score > 0.5
print(f"\n같은 임계값 0.5 를 쓰면")
print(f"{'':>8}{'기저율':>9}{'FPR':>9}{'FNR':>9}")
for label, m in [("집단A", group_a), ("집단B", ~group_a)]:
print(f"{label:>8}{y[m].mean():>9.4f}"
f"{pred[m & ~y].mean():>9.4f}{(~pred)[m & y].mean():>9.4f}")
출력:
점수 구간별 실제 재범률 — 두 집단이 같아야 보정된 것이다
점수 [0.0, 0.2): 집단A 0.1463 집단B 0.1259
점수 [0.2, 0.4): 집단A 0.3045 집단B 0.2953
점수 [0.4, 0.6): 집단A 0.5031 집단B 0.4883
점수 [0.6, 0.8): 집단A 0.6965 집단B 0.6787
점수 [0.8, 1.0): 집단A 0.8574 집단B 0.8477
같은 임계값 0.5 를 쓰면
기저율 FPR FNR
집단A 0.5016 0.3211 0.3236
집단B 0.3394 0.1195 0.6082
점수는 구간마다 두 집단에서 거의 같은 재범률을 보인다. 정의상 완벽하게 보정되어 있다. 실제 재범이 \(y \sim \text{Bernoulli}(\text{score})\)로 생성되었으므로 점수 하나를 고정하면 두 집단의 재범 확률이 정확히 같다(표의 첫 구간에서 \(0.1463\) 대 \(0.1259\)로 약간 벌어지는 것은 보정이 깨져서가 아니라, 폭이 \(0.2\)나 되는 구간 안에서 집단B의 점수가 더 아래쪽에 몰려 있기 때문이다). 그런데도
| 기저율 | FPR | FNR | |
|---|---|---|---|
| 집단A | \(0.50\) | \(0.32\) | \(0.32\) |
| 집단B | \(0.34\) | \(0.12\) | \(0.61\) |
거짓양성률이 \(2.7\)배, 거짓음성률은 반대로 \(1.9\)배 차이 난다. ProPublica가 지적한 것과 Northpointe가 반박한 것이 동시에 성립하는 상황이 바로 이것이다.
왜 필연인가. 임계값 위에 있는 사람들의 재범률이 두 집단에서 같으려면(보정), 기저율이 낮은 집단에서는 임계값을 넘는 사람이 상대적으로 더 선별된 사람들이어야 한다. 그 결과 그 집단에서는 실제 재범자를 더 많이 놓치고(FNR↑) 무고한 사람을 덜 잡는다(FPR↓).
오류율을 맞추면.
import numpy as np
rng = np.random.default_rng(0)
n = 200_000
group_a = rng.random(n) < 0.5
x = rng.normal(np.where(group_a, 0.0, -0.8), 1, n)
score = 1 / (1 + np.exp(-x))
y = rng.random(n) < score
t_a = 0.5
fpr_a = (score[group_a & ~y] > t_a).mean()
t_b = np.quantile(score[~group_a & ~y], 1 - fpr_a) # FPR 을 맞추는 집단B 임계값
pred_a, pred_b = score > t_a, score > t_b
print(f"집단A 임계값 {t_a:.3f} → FPR {fpr_a:.4f}")
print(f"집단B 임계값 {t_b:.3f} → FPR {(score[~group_a & ~y] > t_b).mean():.4f}")
print(f"\n고위험 판정자의 실제 재범률(PPV): "
f"A {y[group_a & pred_a].mean():.4f} B {y[~group_a & pred_b].mean():.4f}")
출력:
집단A 임계값 0.500 → FPR 0.3211
집단B 임계값 0.343 → FPR 0.3211
고위험 판정자의 실제 재범률(PPV): A 0.6795 B 0.5190
FPR을 맞추려면 집단B의 임계값을 \(0.343\)으로 낮춰야 한다. 그러면 점수 \(0.45\)인 두 사람이 집단에 따라 서로 다른 판정을 받는다. 그리고 고위험 판정자의 실제 재범률이 \(0.68\) 대 \(0.52\)로 갈라진다. 즉 "고위험"이라는 딱지의 의미가 집단마다 달라진다.
결론. 두 기준은 서로 다른 것을 지킨다. 점수의 의미를 지킬 것인가, 오류의 부담을 나눌 것인가. 자료가 답해 주지 않으며, 어느 쪽을 택하든 다른 쪽을 포기한다는 사실을 문서로 남기는 것이 최선이다. \(\square\)
연습문제 8. 연습문제 1의 "성별 변수를 제거하면 성중립"이라는 주장을 수치로 반박하라. 보호 속성을 뺀 뒤에도 남은 특징들로 그것을 얼마나 복원할 수 있는지, 그리고 결과 격차가 남는지 측정하라.
풀이
import numpy as np
from sklearn.linear_model import LogisticRegression
rng = np.random.default_rng(1)
n = 100_000
A = rng.random(n) < 0.4 # 보호 속성
f1 = rng.normal(np.where(A, 1.0, 0.0), 1, n) # A 와 상관된 특징
f2 = rng.normal(np.where(A, -0.5, 0.3), 1, n) # A 와 상관된 특징
f3 = rng.normal(0, 1, n) # 무관한 특징
X = np.column_stack([f1, f2, f3]) # 보호 속성은 넣지 않았다
recon = LogisticRegression().fit(X, A)
print(f"보호 속성 복원 정확도 {recon.score(X, A):.4f}"
f" (아무것도 모를 때 {max(A.mean(), 1 - A.mean()):.4f})")
y = rng.random(n) < 1 / (1 + np.exp(-(0.8 * f1 - 0.6 * f2 + 0.5 * f3)))
proba = LogisticRegression().fit(X, y).predict_proba(X)[:, 1]
print(f"\n고위험(>0.5) 판정률: A 집단 {(proba[A] > 0.5).mean():.4f}"
f" 비A 집단 {(proba[~A] > 0.5).mean():.4f}")
출력:
보호 속성 복원 정확도 0.7491 (아무것도 모를 때 0.5986)
고위험(>0.5) 판정률: A 집단 0.8373 비A 집단 0.4360
보호 속성을 넣지 않았는데도 나머지 특징으로 \(74.9\%\) 정확도로 복원된다(아무것도 모를 때가 \(59.9\%\)). 그리고 고위험 판정률이 \(83.7\%\) 대 \(43.6\%\)로 갈린다.
이것을 대리 차별(proxy discrimination)이라 한다. 모형은 성별을 "보지" 않지만, 성별과 상관된 특징을 통해 사실상 성별에 따라 다른 결정을 내린다. 우편번호는 인종의 대리이고, 경력 단절 기간은 성별의 대리이며, 이름·모교·취미도 모두 대리가 될 수 있다.
특징이 많을수록 심해진다. 현대 모형은 수백 개의 특징을 쓰는데, 그중 어느 하나도 보호 속성이 아니면서 전체로는 거의 완벽하게 그것을 재구성할 수 있다.
무지를 통한 공정은 없다
보호 속성을 지우는 것("fairness through unawareness")은 가장 흔하면서 가장 무력한 대응이다. 게다가 역효과가 있다. 보호 속성을 알 수 없으면 격차를 측정할 수도 없다. 유럽 여러 나라에서 인종 자료 수집이 금지된 탓에 인종 격차 감사가 불가능한 것이 실제 문제로 지적된다.
그러면 어떻게 하는가. 보호 속성을 수집하되 결정에 쓰지 않고 감사에 쓰는 것이 표준적인 접근이다. 나아가 공정성 제약을 명시적으로 최적화에 넣는 방법(제약 조건, 적대적 학습, 사후 임계값 조정)도 있지만, 연습문제 7이 보여 주듯 어떤 공정성을 택할지 먼저 정해야 한다. \(\square\)
연습문제 9. 연습문제 3의 의료비 선별 모형을 모의실험하라. 두 집단의 건강 필요가 완전히 같은데도 접근성 차이만으로 선별 결과가 얼마나 갈라지는지 측정하라.
풀이
import numpy as np
from sklearn.linear_model import LinearRegression
rng = np.random.default_rng(3)
n = 100_000
B = rng.random(n) < 0.30 # 의료 접근성이 낮은 집단
need = rng.normal(0, 1, n) # 참 건강 필요 — 두 집단 동일
print(f"참 필요 평균: 비B {need[~B].mean():+.4f} B {need[B].mean():+.4f}")
access = np.where(B, 0.90, 1.0) # 접근성 격차는 10% 뿐이다
visits = np.maximum(0, (4 + 2.5 * need) * access + rng.normal(0, 1, n))
proc = np.maximum(0, (2 + 1.5 * need) * access + rng.normal(0, 0.8, n))
spend = np.maximum(0, (3000 + 2000 * need) * access + rng.normal(0, 400, n))
X = np.column_stack([visits, proc])
pred = LinearRegression().fit(X, spend).predict(X) # 의료비를 예측한다
k = int(0.03 * n)
referred = np.zeros(n, bool); referred[np.argsort(-pred)[:k]] = True
deserved = np.zeros(n, bool); deserved[np.argsort(-need)[:k]] = True
print(f"\n의료비 예측으로 뽑은 상위 3%: B 비율 {B[referred].mean():.4f}")
print(f"참 필요로 뽑았어야 할 상위 3%: B 비율 {B[deserved].mean():.4f}")
print(f"→ B 가 {B[deserved].mean() / B[referred].mean():.2f}배 과소 선별된다")
print(f"\n같은 예측 점수 구간에서 실제 필요를 비교하면")
qs = np.quantile(pred, [0.5, 0.7, 0.9, 1.0])
for i, lo in enumerate(qs[:-1]):
s = (pred >= lo) & (pred <= qs[i + 1])
print(f" 예측 점수 상위 {[50, 30, 10][i]:>2}%: "
f"비B 평균 필요 {need[s & ~B].mean():+.4f} B {need[s & B].mean():+.4f}")
출력:
참 필요 평균: 비B -0.0001 B +0.0022
의료비 예측으로 뽑은 상위 3%: B 비율 0.1643
참 필요로 뽑았어야 할 상위 3%: B 비율 0.3080
→ B 가 1.87배 과소 선별된다
같은 예측 점수 구간에서 실제 필요를 비교하면
예측 점수 상위 50%: 비B 평균 필요 +0.1944 B +0.3603
예측 점수 상위 30%: 비B 평균 필요 +0.7620 B +0.9609
예측 점수 상위 10%: 비B 평균 필요 +1.6141 B +1.8173
접근성 격차가 \(10\%\)뿐인데 선별된 사람 중 B 집단의 비율이 \(16.4\%\)로, 마땅한 \(30.8\%\)의 절반에 그친다. 그리고 마지막 표가 결정적이다. 같은 예측 점수를 받은 사람 중 B 집단이 언제나 훨씬 더 아프다.
모형은 아무 잘못이 없다. 의료비를 예측하라는 임무를 정확히 수행했다. 문제는 의료비를 건강 필요의 대리로 삼은 설계에 있다. 의료비는 필요와 접근성의 곱이고, 접근성은 집단마다 다르다.
이것이 Obermeyer 외(2019)가 실제 미국 의료 시스템의 알고리즘에서 발견한 구조다. 그 알고리즘은 수천만 명에게 쓰이고 있었고, 목표변수를 의료비에서 건강 지표로 바꾸자 흑인 환자의 선별 비율이 \(17.7\%\)에서 \(46.5\%\)로 올랐다.
일반 원리. 본문 대응책의 첫 항목이 이것이다. "우리가 예측하는 것이 우리가 원하는 것인가?"
| 원하는 것 | 흔히 쓰는 대리 | 무엇이 섞이는가 |
|---|---|---|
| 범죄 | 검거 | 순찰 강도 |
| 건강 필요 | 의료비 | 의료 접근성 |
| 업무 능력 | 과거 인사 평가 | 평가자의 편향 |
| 채무 상환 능력 | 과거 대출 이력 | 과거 대출 승인 편향 |
대리변수를 쓰지 않을 수는 없다. 원하는 것은 대개 측정 불가능하기 때문이다. 그러나 대리와 목표의 차이가 집단에 따라 다른지는 반드시 물어야 한다. \(\square\)
연습문제 10. 전체 정확도가 높은 모형이 특정 하위집단에서는 쓸모없을 수 있다. 이를 모의실험하고, 왜 전체 지표만 보면 문제가 보이지 않는지 설명하라.
풀이
import numpy as np
from sklearn.linear_model import LogisticRegression
rng = np.random.default_rng(4)
N = 60_000
minority = rng.random(N) < 0.05 # 전체의 5%
x = rng.normal(0, 1, N)
# 소수집단에서는 x 와 y 의 관계가 반대 방향이다
z = np.where(minority, -2.0 * x, 2.0 * x)
y = rng.random(N) < 1 / (1 + np.exp(-z))
pred = LogisticRegression().fit(x.reshape(-1, 1), y).predict(x.reshape(-1, 1))
print(f"전체 정확도 {(pred == y).mean():.4f}")
print(f"다수집단 정확도 {(pred == y)[~minority].mean():.4f}")
print(f"소수집단 정확도 {(pred == y)[minority].mean():.4f} ← 동전 던지기보다 나쁘다")
Xg = np.column_stack([x, minority.astype(float), x * minority])
pred2 = LogisticRegression().fit(Xg, y).predict(Xg)
print(f"\n집단 변수와 상호작용을 넣으면")
print(f" 전체 {(pred2 == y).mean():.4f} 소수집단 {(pred2 == y)[minority].mean():.4f}")
출력:
전체 정확도 0.7531
다수집단 정확도 0.7807
소수집단 정확도 0.2195 ← 동전 던지기보다 나쁘다
집단 변수와 상호작용을 넣으면
전체 0.7807 소수집단 0.7795
전체 정확도 \(0.753\)은 그럭저럭 쓸 만해 보인다. 그러나 소수집단에서는 \(0.220\)으로, 동전을 던지는 것보다 훨씬 나쁘다. 체계적으로 반대로 예측하고 있기 때문이다.
왜 전체 지표에 안 보이는가. 소수집단이 전체의 \(5\%\)뿐이기 때문이다. 이들을 \(0.220\)에서 \(0.780\)까지 완전히 고쳐 놓아도 전체 정확도가 얻는 것은 \(0.05 \times (0.780 - 0.220) \approx 2.8\)%포인트에 불과하다. 반대로 말하면, 소수집단을 통째로 포기하고 다수집단 쪽 기울기에 맞추는 것이 손실함수 입장에서는 남는 장사다. 이는 알고리즘의 결함이 아니라 정확히 시킨 대로 한 결과다.
상호작용항을 넣자 전체는 \(0.753 \to 0.781\)로 조금 오르는 데 그치지만 소수집단은 \(0.220 \to 0.780\)으로 회복된다. 전체 지표의 개선폭이 작다는 것 자체가 문제의 성격을 말해 준다.
어떻게 잡아내는가.
- 집단별로 성능을 쪼개어 보고한다. 전체 정확도 하나만 보고하는 모형 카드는 이 문제를 감춘다. 구글의 Model Cards, 마이크로소프트의 Datasheets 같은 문서화 관행이 이를 요구한다.
- 최악 집단 성능을 목적함수에 넣는다. 평균이 아니라 최소값을 최적화하는 분포적 로버스트 최적화(DRO)가 이 착상이다.
- 소수집단을 과대표집하거나 가중한다. 학습 시 손실 가중치를 조정한다.
- 하위집단을 미리 정의해 둔다. 무엇을 쪼개어 볼지 정하는 것 자체가 판단이며, 정의되지 않은 집단의 문제는 영원히 보이지 않는다.
상용 얼굴 인식 시스템에서 실제로 보고된 구조가 이것이다. 전체 정확도가 \(90\%\)를 넘는다고 발표된 시스템들이 피부색이 어두운 여성에서는 오류율이 \(30\%\)를 넘었다(Buolamwini & Gebru, 2018). 학습 자료에서 그 집단이 소수였고, 평가 지표가 전체 정확도였기 때문이다.
평균은 소수를 지우는 연산이다. 전체 지표 하나로 모형을 평가하는 한, 소수집단의 실패는 원리적으로 보이지 않는다. \(\square\)
정리하며¶
이 절의 다른 편향과 달리 알고리즘 편향은 정적이지 않다. 모형의 예측이 행동을 바꾸고, 바뀐 행동이 내일의 자료가 되며, 그 자료로 모형이 다시 배운다.
- 자료는 세계가 아니라 측정 과정을 기록한다. 오클랜드의 마약 사용은 도시 전역에 고르게 분포했지만 검거 기록은 특정 지역에 집중되어 있었다. 알고리즘이 배운 것은 범죄의 위치가 아니라 순찰의 위치였다.
- 대리변수를 목표변수로 착각하는 것이 거의 모든 사례의 출발점이다. 검거를 범죄로, 진료비를 건강 필요로, 재범 기록을 재범 위험으로 읽는 순간 측정의 치우침이 사실로 둔갑한다.
- 피드백 루프가 편향을 증폭한다. 정적인 편향이라면 한 번 치우친 채로 남지만, 배포된 모형은 자료 생성 과정 자체를 바꾸므로 편향이 자란다.
- 보호 속성을 모형에서 빼는 것으로는 막지 못한다. 상관된 대리변수가 그 자리를 대신하기 때문이다.
이 절이 1.4절 전체의 결론이기도 하다. 표집·무응답·생존자·출판·길이 편향이 모두 "무엇이 자료에 남았는가"를 물었다면, 알고리즘 편향은 여기에 "그리고 그 자료가 다음 자료를 어떻게 만드는가"를 더한다.
다음 절 두 패러다임의 강점과 한계에서 1장을 정리한다. 설계해서 만든 자료와 주워서 쓰는 자료가 각각 무엇을 할 수 있고 무엇을 할 수 없는지를 나란히 놓고 본다.