본문 바로가기
강화학습

[강화학습 30] 무엇을 이겼다고 말할 수 있나 (완결)

by 카이스토 2026. 9. 15.

CH 30 강화학습 · Part 5 보상을 사람에게서 배우다

무엇을 이겼다고 말할 수 있나 (완결)

강화학습 논문의 학습곡선은 유난히 잘 뒤집힌다. 코드를 한 줄도 바꾸지 않고 시드 하나만 갈아도 두 알고리즘의 순위가 바뀐다. 지도학습에서는 잡음이던 것이 여기서는 정책이 데이터를 만들기 때문에 누적 증폭된다. 이 장은 "이겼다"는 문장이 성립하기 위한 최소 조건을 통계로 적는다. d = 0.5σ 의 차이를 양측 α=0.05·검정력 0.8 로 잡으려면 양쪽에 시드 64개가 필요하고, 논문이 흔히 쓰는 3~5개로는 2σ 이상의 차이만 보인다. 그리고 30장 전체를 한 장의 지도로 닫는다.

  • 01 직관
  • 02 수식 읽는 법
  • 03 손으로 풀기
  • 04 코드
  • 05 시각화

약어 및 기호 정의

시드 seed
난수 생성기의 초기값. 초기 가중치·탐험의 동전던지기·환경의 초기 상태를 전부 결정한다. 시드 하나가 한 번의 독립 실행이다
표준오차 SE
standard error. 표본평균이 실행마다 얼마나 흔들리는지. SE = s/√n — 시드를 4배 늘려야 절반이 된다
효과크기 d
effect size (Cohen's d). 두 알고리즘 성능 차이를 표준편차 단위로 잰 값. d = (μB − μA)/σ
검정력 1−β
power. 차이가 정말 있을 때 그것을 유의하다고 판정할 확률. 관례상 0.8
신뢰구간 CI
confidence interval. 같은 실험을 반복하면 95%의 구간이 참값을 덮는 구간. 오차막대의 정체
IQM
interquartile mean, 사분위 평균. 정렬한 뒤 위아래 25%를 버리고 가운데 50%만 평균 낸 값
순열검정 permutation test
두 집단의 라벨을 무작위로 섞어 관측된 차이가 얼마나 흔한지 직접 세는 검정. 정규성 가정이 없다
최댓값 편향 maximization bias
여러 잡음 추정치의 최댓값을 대푯값으로 삼으면 참값보다 위로 치우치는 현상. 8장 Q러닝의 그것과 같은 계산이다
평가 누수 evaluation leakage
보고할 성능을 재는 바로 그 환경·시드에서 하이퍼파라미터나 체크포인트를 골라 버리는 것
보상 해킹 reward hacking
적어 놓은 보상은 최대로 만들지만 의도한 행동은 아닌 해. 27·29장의 굿하트 법칙
절제 실험 ablation
구성요소를 하나씩 꺼 보며 각각의 기여를 재는 실험. 13장 Rainbow가 그 표준형이다
프로토콜 protocol
무엇을 몇 번, 언제, 어떤 지표로 잴지 실험 전에 정해 문서로 박아 두는 것
STEP 01

직관: 시드 하나가 순위를 바꾼다

여기까지 스물아홉 장을 읽었다면 이런 문장에 익숙해졌을 것이다. "우리 방법이 베이스라인을 앞선다." 그 옆에는 두 개의 곡선이 그려져 있고, 우리 것이 위에 있다. 이 장은 그 문장이 언제 참인지를 묻는다.

먼저 사실 하나를 인정하고 시작하자. 같은 코드, 같은 하이퍼파라미터, 같은 환경에서 시드만 바꿔 돌리면 최종 성능이 크게 벌어진다. 지도학습에서도 시드는 결과를 흔들지만, 흔들림의 폭이 다르다. 이미지 분류에서 시드를 바꿔 정확도가 92.1%에서 91.8%로 움직인다면, 강화학습에서는 같은 설정이 "과제를 푼다"와 "아무것도 배우지 못한다"로 갈리는 일이 드물지 않다.

왜 이렇게 다른가. 1장의 문제 3으로 돌아가면 답이 있다. 지도학습의 데이터셋은 고정되어 있다. 시드를 바꾸면 초기 가중치와 미니배치 순서가 바뀌지만, 보는 데이터는 같다. 강화학습에서는 정책이 데이터를 만든다. 시드가 바뀌어 초반 몇 번의 탐험이 달라지면, 그 결과로 채워지는 재생버퍼가 달라지고, 버퍼가 달라지면 가치추정이 달라지고, 가치추정이 달라지면 다음에 갈 곳이 달라진다. 작은 차이가 되먹임 고리를 타고 누적 증폭된다.

ch30-d1
도해 1. 개념 모식도이지 측정값이 아니다. 요지는 하나다 — 같은 알고리즘 안의 시드 간 흔들림이 두 알고리즘 사이의 차이만큼 크면, 어느 지점에서 곡선을 끊느냐가 승자를 정한다. 이런 그림에서 "A가 B보다 낫다"를 읽어 내려면 다발의 폭부터 재야 한다.

이 되먹임에는 이름이 여러 개 붙어 왔다. 8장에서는 최대화 편향(maximization bias)이었고, 9장에서는 치명적 삼위일체(deadly triad)였으며, 26장에서는 분포 이탈(distribution shift)이었다. 세 개 모두 "추정의 오차가 다음 데이터를 오염시키고, 오염된 데이터가 오차를 키운다"는 같은 고리의 서로 다른 단면이다. 실험 결과의 분산도 같은 고리에서 나온다.

그래서 이 장에서 던지는 질문은 알고리즘에 관한 것이 아니라 주장에 관한 것이다.

  • 시드 몇 개를 돌려야 "이겼다"고 말할 수 있나
  • 오차막대는 무엇의 오차인가 — 시드 간 표준편차인가, 평균의 표준오차인가
  • 학습 중 가장 좋았던 체크포인트를 보고하는 것은 왜 반칙에 가까운가
  • 100판 중 55승은 이긴 것인가
  • 평균과 중간값이 서로 다른 승자를 가리킬 때 무엇을 믿나

이 장이 다루지 않는 것

알고리즘을 하나 더 소개하지 않는다. 대신 앞의 스물아홉 장이 만들어 낸 모든 주장을 어떤 조건에서 믿을 수 있는지를 적는다. 여기 나오는 통계는 「통계학 기본」 시리즈의 10장(편향–분산) · 12장(가설검정) · 14장(비모수 검정)에서 이미 다룬 도구들이다. 새로 배울 것은 그 도구를 강화학습의 실험 설계에 어떻게 얹는가 하나다.

STEP 02

수식 읽는 법: 오차막대가 말하는 것

시드 간 분산과 평균의 표준오차

알고리즘 하나를 시드 n 개로 돌려 최종 점수 x1, …, xn 을 얻었다고 하자. 우리가 보고하고 싶은 것은 이 알고리즘의 참 기대성능 μ 이고, 손에 있는 것은 표본평균뿐이다.

(30.1) x̄ = 1n nΣi=1 xi  ,  s2 = 1n−1 nΣi=1 (xi − x̄)2  ,  SE = s√n s 와 SE 는 다른 것이다. s 는 한 번 더 돌리면 얼마나 다른 값이 나올까, SE 는 이 평균을 얼마나 믿을까를 잰다. 시드를 늘려도 s 는 줄지 않고 SE 만 줄어든다.

논문 그림의 음영 띠가 무엇인지는 반드시 캡션에 적혀 있어야 한다. 같은 데이터로 그려도 ±s 띠와 ±SE 띠는 √n 배 차이가 나고, 시드 25개라면 다섯 배다. 띠가 겹치지 않는다는 그림 하나로 우열을 주장하는 글에서 띠의 정체를 밝히지 않았다면, 그 그림은 아무것도 주장하지 않은 것이다.

95% 신뢰구간은 자유도 n−1 의 t 분포 임계값을 붙여 만든다.

(30.2) CI95 = x̄ ± t0.975, n−1 · s√n n=5 면 t=2.776, n=10 이면 2.262, n=30 이면 2.045. 시드가 적으면 임계값 자체가 커져 구간이 두 번 벌어진다 — √n 이 작아서 한 번, t 가 커서 또 한 번.

두 알고리즘 비교 — t검정과 순열검정

알고리즘 A와 B를 각각 n 개 시드로 돌렸다. 귀무가설은 "둘의 참 기대성능이 같다"이다. 분산이 같다고 보면 합동분산 sp2 를 써서

(30.3) t = x̄B − x̄Asp √(2/n)  ,  sp2 = (n−1)sA2 + (n−1)sB22n−2 「통계학 기본」 12장의 두 표본 t검정 그대로다. 분모가 차이의 표준오차이므로, 분자의 차이가 아무리 커도 시드 간 흔들림이 그만큼 크면 t 는 커지지 않는다.

t검정은 정규성을 가정한다. 강화학습의 최종 점수 분포는 정규와 거리가 먼 경우가 많다 — 과제를 푼 시드와 못 푼 시드로 봉우리가 둘인 분포가 흔하다. 이럴 때는 가정을 지우고 순열검정(permutation test)을 쓴다. 「통계학 기본」 14장의 도구다.

(30.4) p = #{ 섞은 배치 중 |차이| ≥ 관측된 |차이| } + 1섞은 횟수 + 1 A·B 라벨을 떼고 2n 개를 한 통에 넣은 뒤 무작위로 반씩 나누기를 반복한다. 분자·분모에 +1 을 넣는 것은 관측값 자신도 하나의 배치로 세기 위해서다 — 이걸 빼면 p 가 0으로 나올 수 있다.

승률의 표준오차

보드게임이나 대전형 과제에서는 점수 대신 승률을 보고한다. n 판 중 k 승이면 추정치는 p̂ = k/n 이고, 이항분포의 분산 np(1−p) 에서 곧바로

(30.5) SE(p̂) = √( p̂(1 − p̂)n )  ,  귀무가설 p=0.5 아래에서는 SE0 = 12√n p(1−p) 는 p=0.5 에서 최대 0.25 다. 즉 실력이 비슷할수록 판수가 더 필요하다. 100판이면 SE0=0.05 이므로 55%는 1 표준오차밖에 안 된다.

왜 IQM 인가

평균은 이상치 하나에 통째로 끌려간다. 중간값은 이상치에 강하지만 버리는 정보가 너무 많고, 시드가 적을 때 계단처럼 움직여 신뢰구간이 넓다. 사분위 평균(interquartile mean, IQM)은 그 사이다.

(30.6) IQM = 1n − 2⌊n/4⌋ n−⌊n/4⌋Σi=⌊n/4⌋+1 x(i) x(i) 는 정렬한 i 번째 값. 위아래 25%를 버리고 가운데 50%만 평균 낸다. n=10 이면 양끝 2개씩을 버리고 6개를 평균한다.

정리하면 세 지표는 이상치 하나가 결론을 바꿀 수 있는가에서 갈린다. 평균은 바꿀 수 있고, 중간값은 못 바꾸고, IQM은 못 바꾸면서 중간값보다 분산이 작다. 강화학습 평가 지표를 다룬 Agarwal 등의 원논문(Deep RL at the Edge of the Statistical Precipice, 2021)이 보고한 바로는, Atari 100k 벤치마크에서 같은 데이터로도 평균·중간값·IQM이 서로 다른 순위를 내놓는 일이 실제로 일어난다. 그 논문이 IQM을 권한 이유가 여기 있다.

평가 누수 — 결과를 보고 고르면 안 되는 것들

통계적으로 가장 흔한 반칙은 검정을 잘못 고르는 것이 아니라 고를 자유를 남겨 두는 것이다. 강화학습에는 그 자유가 최소한 세 군데 있다.

① 하이퍼파라미터를 평가 환경에서 고른다  →  ② 최고 체크포인트만 보고한다  →  ③ 잘 나온 시드만 싣는다

②는 8장에서 이미 손으로 계산한 그 편향이다. 체크포인트 K 개의 평가값이 참 성능 μ 주위로 독립적인 잡음 N(0, σ2) 을 가진다면

(30.7) 𝔼[ maxk Xk ] − μ  =  σ · 𝔼[ maxk Zk ]  >  0   (K ≥ 2) K=2 일 때 정확히 σ/√π. 8장에서 Q러닝의 과대추정을 계산할 때 나온 바로 그 값이다 — 같은 공식이 알고리즘 안에서 한 번, 논문을 쓰는 손에서 한 번 작동한다.

③은 더 노골적인데, 자기도 모르게 저지르기 쉽다. 시드 10개를 돌려 3개가 발산했고 "설정이 잘못됐을 것"이라며 다시 돌려 넣으면, 그 순간 보고하는 대상이 알고리즘이 아니라 "잘 되는 알고리즘"으로 바뀐다. 발산한 시드도 알고리즘의 일부다.

보상 설계가 만드는 편법

마지막으로, 측정이 정확해도 측정 대상이 잘못일 수 있다. 27장에서 본 보상 정형화(reward shaping)는 잠재함수 Φ 를 써야만 최적 정책을 보존한다.

(30.8) r′(s, a, s′) = r(s, a, s′) + γΦ(s′) − Φ(s) 이 꼴을 벗어난 정형화는 최적 정책을 바꾼다. "골대에 가까워지면 +0.1" 같은 거리 보상을 이 꼴 없이 넣으면, 골을 넣지 않고 골대 앞을 맴도는 정책이 정직하게 최적이 된다.

종료 조건도 같다. 시간 초과로 끝난 에피소드를 종료로 처리하면 부트스트랩이 끊겨 가치가 낮게 추정되고, 에이전트는 "오래 버티기"의 값어치를 배우지 못한다. 29장의 KL 페널티 역시 같은 자리에 있다 — 보상모델의 점수만 올리고 실제 품질은 떨어뜨리는 해를 막기 위해 참조 정책에서 너무 멀어지지 말라는 항을 억지로 붙인 것이다.

(30.9) J(π) = 𝔼[ rφ(x, y) ] − β · KL( π(·|x) ‖ πref(·|x) ) 28·29장의 목적함수. β 를 0으로 두면 rφ 의 허점을 찾아 들어간다 — 보상함수를 배우면 배운 보상함수에도 편법이 생긴다는 것이 굿하트 법칙의 RLHF 판본이다.
STEP 03

손으로 풀기: 시드 몇 개가 필요한가

① 시드 수 계산

두 알고리즘의 참 성능 차이가 표준편차의 절반, 즉 d = 0.5σ 라고 하자. 이것을 양측 α=0.05 로, 검정력 0.8 로 검출하려면 한쪽에 시드 몇 개가 필요한가. 정규 근사에서 표본크기 공식은 다음과 같이 나온다.

1
차이의 표준오차는 σ√(2/n) 이다 (두 독립 표본 각각 n 개)
2
유의하려면 |x̄B − x̄A| > z0.975 · σ√(2/n)
3
참 차이가 dσ 일 때 이 사건의 확률이 0.8 이 되려면 dσ = (z0.975 + z0.80) · σ√(2/n)
4
n = 2 (z0.975 + z0.80)2d2  — σ 가 소거된다. 필요한 것은 절대 차이가 아니라 효과크기다
5
z0.975 = 1.959964, z0.80 = 0.841621 → 2(1.959964 + 0.841621)2 = 15.697759
6
d = 0.5 → n = 15.697759 / 0.25 = 62.79. t 분포로 정확히 풀면 n = 64 (그때 검정력 0.8015)

분자 15.6978 을 외워 두면 암산으로 끝난다. n ≈ 16/d2 — 효과크기가 절반이 되면 시드는 네 배로 늘어야 한다.

효과크기별 필요 시드 수 (양쪽 각각, 양측 α=0.05 · 검정력 0.8). 정규 근사는 15.6978/d2, 정확값은 비중심 t 분포로 구한 최소 정수
효과크기 d 정규 근사 n 정확한 n (t) 그때 검정력 두 알고리즘 합계
0.2σ (작은 차이) 392.44 394 0.8006 788
0.5σ 62.79 64 0.8015 128
1.0σ 15.70 17 0.8070 34
1.5σ 6.98 9 0.8476 18
2.0σ 3.92 6 0.8764 12
3.0σ (거대한 차이) 1.74 4 0.9389 8

정규 근사와 t 분포 정확값의 차이가 d 가 클수록 벌어지는 것에 주목하자. d=3 에서 근사는 1.74를 내놓지만 실제로는 4가 필요하다. 시드가 두세 개일 때는 분산 추정 자체가 불안해서 임계값이 크게 부풀기 때문이다. 시드 2개로 t검정을 하면 자유도가 2이고 t0.975,2 = 4.303 이다.

이제 방향을 뒤집자. 논문이 흔히 쓰는 3~5 시드로는 어느 정도 차이까지 보이는가. 같은 식을 d 에 대해 풀면 된다.

시드 n 개로 검정력 0.8 을 얻는 최소 효과크기 — "이 정도보다 작은 차이는 있어도 보이지 않는다"
시드 n (양쪽 각각) 검출 가능한 d d=0.5σ 에서의 실제 검정력 읽는 법
3 3.07σ 0.0768 13번 중 1번만 잡아낸다
5 2.02σ 0.1077 9번 중 1번
10 1.32σ 0.1851 5번 중 1번
20 0.91σ 0.3379 3번 중 1번
30 0.74σ 0.4779 동전던지기와 비슷
64 0.50σ 0.8015 목표 달성
100 0.40σ 0.9404 —

이 표가 말하는 불편한 사실

시드 3개로 돌린 실험이 "유의하지 않다"고 나왔다면 그것은 차이가 없다는 증거가 아니다. 3σ 미만의 차이는 애초에 보이지 않는 장비로 본 것이기 때문이다. 반대로 시드 3개로 유의하게 나왔다면 그것도 조심해야 한다 — 검정력이 0.08인 장비에서 나온 유의한 결과는 상당 부분이 우연이고, 효과크기는 거의 항상 과대추정된다(승자의 저주).

현실적인 타협은 이렇다. 시드 수를 늘릴 수 없다면 주장을 낮춰라. "A가 B보다 낫다" 대신 "시드 5개에서 A의 IQM이 B보다 높았으나 신뢰구간이 겹친다"라고 쓰면 그 문장은 참이다.

② 최고 체크포인트 편향

두 알고리즘의 참 성능이 정확히 같다고 하자. 그런데 A는 마지막 체크포인트를 보고하고, B는 학습 중 평가한 K 개 체크포인트 가운데 제일 좋은 것을 보고한다. 각 평가에는 독립인 잡음 N(0, σ2) 이 있다. B가 얼마나 유리해지는가.

1
Z1, …, ZK ~ N(0,1) 독립. 최댓값의 분포함수는 F(z)K = Φ(z)K
2
밀도는 미분해서 K Φ(z)K−1 φ(z)
3
𝔼[max] = ∫ z · K Φ(z)K−1 φ(z) dz
4
K=2 는 닫힌꼴로 1/√π = 0.5641896 — 8장의 σ/√π 와 같은 값
5
큰 K 에서는 √(2 ln K) 로 증가한다 (느리지만 멈추지 않는다)
참 성능이 0인데 K 개 체크포인트의 최댓값을 보고할 때 생기는 편향 (단위 σ)
K 𝔼[max] 수치적분 근사 √(2 ln K) 의미
1 0.000000 0.0000 편향 없음 — 정직한 보고
2 0.564190 1.1774 1/√π 닫힌꼴. 8장과 동일
5 1.162964 1.7941 차이가 없는데 1.16σ 앞선 것처럼 보인다
20 1.867475 2.4477 —
100 2.507594 3.0349 참 차이 0 → 외견상 2.5σ

표의 마지막 줄과 앞의 시드 수 표를 겹쳐 읽어 보자. 2.5σ 의 차이는 시드 6개면 검정력 0.97로 "유의하게" 검출된다. 다시 말해 성능이 전혀 개선되지 않은 방법이, 체크포인트를 100개 찍어 최고점만 고르고 시드 6개로 t검정을 돌리면, 통계적으로 유의한 개선으로 보고될 수 있다. 어떤 수치 조작도 하지 않고, 모든 숫자를 정직하게 계산해서 그렇게 된다.

양쪽이 같은 K 로 최고점을 고른다면 편향이 상쇄되지 않느냐고 물을 수 있다. 기댓값은 상쇄되지만 분산은 상쇄되지 않는다. 그리고 새 방법이 더 자주 평가받고 더 오래 학습되는 것이 현실이므로, 대칭이 깨지는 쪽이 보통 새 방법에 유리하다. 편향의 크기는 K 의 비에 달려 있는데, K=100 과 K=5 의 차이만 해도 2.507594 − 1.162964 = 1.3446σ 다.

③ 100판 중 55승은 이긴 것인가

대전형 과제에서 새 에이전트가 기존 에이전트에게 100판 중 55승을 거뒀다. 무승부는 없다고 하자.

1
귀무가설 p = 0.5 아래 표준오차 SE0 = 1/(2√100) = 0.05
2
z = (0.55 − 0.50)/0.05 = 1.0000 — 정확히 1 표준오차
3
양측 p 값(정규 근사) = 2Φ(−1) = 0.3173
4
정확한 이항검정 p = 0.3682 → 유의하지 않다
5
95% 신뢰구간 반폭은 1.96 × √(0.55·0.45/100) = 1.96 × 0.049749 = 0.0975 → [0.452, 0.648]

신뢰구간이 0.5를 넉넉히 포함한다. 100판 중 55승은 이겼다는 증거가 아니다. 60승이면 어떨까. z = 2.0, 정규 근사 p = 0.0455 로 간신히 걸치지만 정확한 이항검정은 p = 0.0569 로 0.05를 넘는다. 이 경계에서 근사와 정확값이 갈린다는 것은 알아 둘 만하다.

그러면 55% 승률을 실제로 입증하려면 몇 판이 필요한가. 이번에는 대립가설 p1=0.55 아래의 분산을 쓴다.

n = ( z0.975√(0.25) + z0.80√(p1(1−p1)) )2(p1 − 0.5)2 = (1.959964 × 0.5 + 0.841621 × 0.497494)20.0025 = 782.53 정확한 이항 계산으로는 n = 786 (그때 검정력 0.8013).
참 승률이 0.55 일 때 판수별 검정력과 95% 신뢰구간 반폭 (귀무 p=0.5, 양측 0.05)
판수 n 검정력 CI 반폭 ±1.96/(2√n) 판정
100 0.1361 ±0.0980 거의 못 잡는다
400 0.4807 ±0.0490 절반의 확률
786 0.8013 ±0.0350 목표 달성
1000 0.8801 ±0.0310 —

참고로 1000 판 중 550 승은 z = 3.1623, 정확한 이항 p = 0.0017 로 분명히 유의하다. 같은 55%인데 100판과 1000판의 결론이 반대다. 승률을 보고할 때 판수를 함께 적지 않으면 그 숫자는 의미가 없다.

④ 평균과 IQM이 순위를 뒤집는 예

직접 만들어 보자. 시드 10개씩 돌린 두 알고리즘의 최종 점수다.

A = { 40, 42, 44, 45, 46, 47, 48, 49, 50, 51 }
B = { 5, 8, 10, 12, 30, 33, 35, 38, 40, 420 }

A는 열 번 모두 비슷하게 잘한다. B는 아홉 번 시원찮다가 한 번 크게 터졌다. 손으로 계산한다.

1
A의 합 = 40+42+44+45+46+47+48+49+50+51 = 462 → 평균 46.2
2
B의 합 = 5+8+10+12+30+33+35+38+40+420 = 631 → 평균 63.1  B 승
3
중간값: A는 (46+47)/2 = 46.5, B는 (30+33)/2 = 31.5  A 승
4
IQM은 양끝 ⌊10/4⌋=2 개씩 버리고 가운데 6개. A: (44+45+46+47+48+49)/6 = 279/6 = 46.5
5
B: (10+12+30+33+35+38)/6 = 158/6 = 26.3333  A 승
6
평균은 B, 중간값과 IQM은 A — 같은 데이터, 반대 결론

어느 쪽이 옳은가. 질문을 바꿔야 한다. "이 알고리즘을 한 번 돌리면 무엇을 얻을 가능성이 높은가"가 궁금하면 A가 낫다. B를 한 번 돌리면 90% 확률로 40점 이하다. "여러 번 돌려서 제일 좋은 것을 쓰겠다"면 B가 낫다. 평균은 후자에 가까운 질문에 답한다. 논문이 보고하는 것은 대개 전자여야 하는데 쓰는 지표는 평균이다.

신뢰구간을 보면 더 분명하다. A의 95% CI는 [43.681, 48.719] 로 좁고, B는 [−27.127, 153.327] 로 음수까지 뻗는다. 점수가 음수일 수 없는 과제인데도 그렇다. 이런 구간을 오차막대로 그리면 "차이 없음"밖에 읽을 것이 없다.

검정도 해 보자. 두 표본 t검정은 t = 0.4236, p = 0.6769 다. 순열검정은 더 극적인데, 10만 번 섞어도 관측된 차이 이상이 거의 매번 나와 p ≈ 1.000 이다. 이유는 간단하다. 420 이라는 값 하나가 어느 쪽에 들어가든 그쪽 평균을 42 만큼 밀어 올리므로, 관측된 차이 16.9 는 섞기만 하면 흔히 나오는 크기다. 순열검정이 "이 차이는 이상치 하나의 위치가 만든 것"이라고 말해 주는 것이다.

⑤ 이 시리즈에서 손으로 확인한 경계값

서른 장을 지나며 손으로 끝까지 푼 값들이 있다. 대부분 알고리즘을 고르는 기준이 아니라 무엇이 언제 깨지는지를 정하는 경계다. 한자리에 모은다.

강화학습 30장에서 손계산으로 확인한 경계값 — 전부 지면에서 검산되는 크기의 예제에서 나왔다
장 경계값 수치 무엇을 가르는가
01 γ* = 2−1/3 0.7937005 참는 정책과 당장 챙기는 정책이 뒤집히는 감가율
05 ‖T V − T V′‖∞ ≤ γ‖V − V′‖∞ γ < 1 벨만 연산자가 축약이 되는 조건. 가치반복 수렴의 전부
08 𝔼[max] − max 𝔼 (m=2) σ/√π = 0.5641896 Q러닝의 최대화 편향. 더블 Q러닝이 지우려는 양
09 κ = 1 + α(2γ − 1) > 1 γ > 1/2 (α=0.1·γ=0.99 → κ=1.098) 베어드 반례에서 삼위일체가 발산으로 넘어가는 선
18 유효 지평 1/(1 − γλ) γ=0.99, λ=0.95 → 16.81 GAE가 실제로 몇 걸음을 내다보는지
22 𝔼[min] = q − σ√((1−ρ)/π) ρ=0 → −0.5641896σ TD3 쌍둥이 크리틱이 만드는 과소추정. 08과 부호만 반대
23 δh = ε(Lh−1)/(L−1) ε=0.01, h=50: L=0.95 → 0.185 / L=1.05 → 2.093 모델 오차가 롤아웃 길이를 따라 눌리는지 터지는지
26 Δ/(1 − γ) Δ=0.5, γ=0.99 → 50 오프라인 RL에서 분포 이탈 오차가 누적되는 상한
30 n = 2(zα/2+zβ)2/d2 15.6978/d2 (d=0.5 → 64) "이겼다"는 문장을 쓸 수 있는 최소 시드 수

표를 관통하는 것

08장의 +σ/√π 와 22장의 −σ/√π 가 같은 크기로 부호만 다른 것은 우연이 아니다. max 는 위로, min 은 아래로 같은 양만큼 치우친다. TD3가 쌍둥이 크리틱의 최솟값을 쓰는 것은 과대추정을 같은 크기의 과소추정으로 맞바꾸는 거래이고, 과소추정이 덜 위험하다는 판단이 그 뒤에 있다.

그리고 30장의 𝔼[max] 는 08장과 완전히 같은 적분이다. 차이는 누가 최댓값을 고르는가뿐이다 — 08장에서는 알고리즘이, 30장에서는 논문을 쓰는 사람이 고른다. 같은 편향이 코드 안과 코드 밖에서 두 번 작동한다.

STEP 04

코드: 검정력·편향·지표를 직접 재기

앞의 세 계산을 numpy로 확인한다. 손으로 구한 값과 맞아야 한다.

rl30.pynumpy + scipy
import numpy as np
from scipy import stats
rng = np.random.default_rng(30)
# (a) 시드 n 개로 d = 0.5σ 의 차이를 검출할 확률
def power_sim(n, d, trials=20000, alpha=0.05):
    A = rng.standard_normal((trials, n))
    B = rng.standard_normal((trials, n)) + d
    _, p = stats.ttest_ind(B, A, axis=1)
    return (p < alpha).mean()
print("[a] d=0.5s 를 양측 0.05 로 검출할 확률")
for n in (3, 5, 10, 20, 64):
    print(f"    seed {n:3d} -> power {power_sim(n, 0.5):.4f}")
# (b) 참 성능이 같은데 한쪽만 '최고 체크포인트'를 보고한다
print("[b] 최고 체크포인트 편향 (참 성능 0, 체크포인트 잡음 N(0,1))")
for K in (1, 5, 20, 100):
    x = rng.standard_normal((200000, K))
    print(f"    K={K:4d}  mean {x.mean():+.4f}   max {x.max(axis=1).mean():+.4f}")
# (c) 같은 데이터, 세 지표가 서로 다른 순위를 준다
A = np.array([40, 42, 44, 45, 46, 47, 48, 49, 50, 51], float)
B = np.array([5, 8, 10, 12, 30, 33, 35, 38, 40, 420], float)
def iqm(x):                       # 사분위 평균: 위아래 25% 를 버린 평균
    x = np.sort(x); k = len(x) // 4
    return x[k:len(x) - k].mean()
print("[c] 시드 10개, 같은 데이터 / 다른 지표")
for nm, x in (("A", A), ("B", B)):
    se = x.std(ddof=1) / np.sqrt(len(x)); tc = stats.t.ppf(0.975, len(x) - 1)
    print(f"    {nm}: mean {x.mean():7.3f}  median {np.median(x):6.3f}  IQM {iqm(x):6.3f}"
          f"  95%CI [{x.mean()-tc*se:8.3f}, {x.mean()+tc*se:7.3f}]")
# (d) 같은 비교를 t검정과 순열검정으로
t, p = stats.ttest_ind(B, A)
pool = np.concatenate([A, B]); obs = abs(B.mean() - A.mean()); hit = 0
for _ in range(100000):
    q = rng.permutation(pool)
    hit += abs(q[:10].mean() - q[10:].mean()) >= obs
print(f"[d] diff {B.mean()-A.mean():+.3f}   t={t:.4f} p={p:.4f}   "
      f"permutation p={(hit+1)/100001:.4f}")
[a] d=0.5s 를 양측 0.05 로 검출할 확률
    seed   3 -> power 0.0769
    seed   5 -> power 0.1114
    seed  10 -> power 0.1868
    seed  20 -> power 0.3379
    seed  64 -> power 0.7987
[b] 최고 체크포인트 편향 (참 성능 0, 체크포인트 잡음 N(0,1))
    K=   1  mean +0.0023   max +0.0023
    K=   5  mean +0.0000   max +1.1629
    K=  20  mean +0.0006   max +1.8679
    K= 100  mean +0.0001   max +2.5070
[c] 시드 10개, 같은 데이터 / 다른 지표
    A: mean  46.200  median 46.500  IQM 46.500  95%CI [  43.681,  48.719]
    B: mean  63.100  median 31.500  IQM 26.333  95%CI [ -27.127, 153.327]
    rank  mean B>A / median A>B / IQM A>B
[d] diff +16.900   t=0.4236 p=0.6769   permutation p=1.0000

세 블록 모두 손계산과 맞는다. [a] 의 시뮬레이션 검정력 0.0769 / 0.1114 / 0.1868 / 0.3379 / 0.7987 은 비중심 t 분포로 구한 이론값 0.0768 / 0.1077 / 0.1851 / 0.3379 / 0.8015 와 몬테카를로 오차 범위에서 일치한다. 시드 64개에서 0.80에 닿는다는 것이 표로도 시뮬레이션으로도 확인됐다.

[b] 의 mean 열은 전부 0 근처인데 max 열만 오른쪽으로 밀린다. K=5 에서 1.1629, K=100 에서 2.5070 — 수치적분값 1.162964, 2.507594 와 소수 셋째 자리까지 같다. 참 성능이 0인 알고리즘을 100번 평가해서 최고점을 보고하면 2.5σ 짜리 개선으로 보인다는 것이 20만 번의 시뮬레이션으로 확인된다.

[d] 의 순열검정 p = 1.0000 은 오류가 아니다. 10만 번의 재배치 중 사실상 전부가 관측된 차이 이상을 만들어 냈다. 이상치 420 이 어디로 가느냐가 차이를 지배하기 때문이다. 여기서 얻는 교훈은 순열검정이 t검정보다 보수적이어서가 아니라, 이 데이터에서 "평균의 차이"라는 통계량 자체가 쓸모없다는 것이다.

코드에서 눈에 띄는 것

rng = np.random.default_rng(30) 한 줄이 이 장 전체의 주제다. 시드를 고정했기 때문에 이 출력은 누가 돌려도 같다. 논문의 실험 코드에 이 줄이 없다면, 그 논문의 표는 재현할 수 없다.

그리고 power_sim 은 학습을 전혀 하지 않는다. 정규분포에서 숫자를 뽑아 t검정을 돌릴 뿐이다. 검정력은 알고리즘의 성질이 아니라 실험 설계의 성질이기 때문이다. GPU 시간을 쓰기 전에 이 20줄을 먼저 돌려 보면, 계획한 시드 수로 무엇을 볼 수 있는지 미리 알 수 있다.

STEP 05

시각화: 필요한 시드, 편향, 그리고 30장의 지도

앞의 표들을 그림으로 겹쳐 보면 결론이 한눈에 들어온다.

ch30-d2
그림 1. 검정력 0.8·양측 α=0.05 에서 시드 수와 검출 가능한 효과크기의 관계. n ≈ 16/d2 이므로 곡선은 1/√n 로 천천히 내려온다. 음영 구간이 대부분의 논문이 쓰는 시드 수이며, 그 구간에서 볼 수 있는 것은 2σ 이상의 차이뿐이다. 0.5σ 짜리 개선을 주장하려면 점선이 곡선과 만나는 n=64 까지 가야 한다.

다음은 최고 체크포인트 편향이다. 막대그래프 대신 왜 그렇게 되는지가 보이는 그림으로 그린다.

ch30-d3
도해 2. 세로 점선이 참 성능이고, 짧은 세로 막대들이 K 번의 평가 결과다. 분포는 전혀 이동하지 않았다. 움직인 것은 표본의 오른쪽 끝뿐이고, "최고 체크포인트"는 그 끝을 보고한다. 𝔼[max] 는 √(2 ln K) 로 커지므로 평가를 자주 할수록 공짜로 좋아 보인다.

세 번째는 평균·중간값·IQM이 갈리는 자리다.

ch30-d4
도해 3. 가로축은 점수이고 오른쪽 끝은 축을 끊어 420 을 표시했다. A는 세 지표가 한 점에 모이고, B는 지표마다 다른 자리를 가리킨다. 평균만 이상치 쪽으로 36.8 만큼 끌려가 순위를 뒤집는다. 오차막대를 그리면 B의 95% 구간은 음수까지 내려가 아무 주장도 하지 못한다.

그리고 서른 장의 지도

1장에서 세 개의 문제를 정의하며 시작했고, 커리큘럼에서는 그것을 관통하는 네 질문으로 늘렸다. 서른 장을 다 지난 지금, 각 질문이 어느 장에서 어떤 도구를 만났는지 한 장에 담을 수 있다.

ch30-d5
도해 4. 강화학습 30장의 전체 지도. 위쪽 띠는 5부 구성, 가운데 줄은 30개 장, 아래 네 줄은 시리즈를 관통하는 네 질문이다. 각 질문이 어느 장에서 새 도구를 얻었는지 점으로 찍었다. 네 줄이 모두 서른 번째 장의 세로선에서 끝난다 — 신용할당도 탐험도 과대추정도 걸음 크기도, 결국 "그래서 그게 개선인지 어떻게 아는가"라는 같은 질문 앞에 선다.

수미상관 — 다시 도해 1로

1장의 도해 1은 두 개의 상자였다. 왼쪽에서 모델이 받는 것은 방향이 있는 벡터였고, 오른쪽에서 정책이 받는 것은 부호 있는 스칼라 하나였다. 그 그림 아래에 이렇게 적었다. "강화학습의 모든 알고리즘은 이 빈약한 신호에서 어떻게든 방향을 만들어 내려는 시도다."

서른 장이 그 시도의 목록이었다. 벨만 방정식은 스칼라 하나를 상태마다의 값으로 퍼뜨리는 방법이었고(4·5장), 몬테카를로와 TD와 GAE는 그 값을 어느 시점에 귀속시킬지 정하는 서로 다른 규칙이었으며(6·7·18장), 정책경사 정리는 스칼라를 곱한 로그확률의 기울기가 진짜 방향이 된다는 것을 증명했다(15장). 기저선·신뢰영역·클리핑은 그 방향을 얼마나 믿고 걸을지의 문제였다(16·19·20장).

그런데 28~29장에서 한 겹이 더 생겼다. 그 스칼라조차 주어지지 않는다. "좋은 답변"의 보상함수를 적을 수 있는 사람은 없다. 그래서 사람에게 두 답을 보여 주고 어느 쪽이 나은지 묻고, 그 선호로부터 브래들리–테리 모형을 세워 보상함수 자체를 학습했다. 1장의 오른쪽 상자에서 r = −1 이라고 적혀 있던 자리가, 이제는 또 하나의 학습된 모형이다.

그러자 편향이 한 층 더 쌓였다. 배운 보상함수에는 허점이 있고, 정책은 그 허점을 찾아낸다 — 29장의 보상 해킹이다. 그래서 KL 페널티로 참조 정책에 묶어 두고, 그래도 새는 것은 사람이 다시 본다. 신호는 1장보다 더 빈약해진 것이 아니라 더 불확실해졌다. 스칼라 하나가 아니라, 스칼라 하나를 내놓는 추정된 함수가 되었기 때문이다.

그 위에서 "이겼다"고 말하려면 무엇이 필요한가. 이 장의 답은 소박하다. 시드를 충분히 돌리고, 무엇을 잴지 미리 정하고, 최고점 대신 마지막 점을 보고하고, 이상치에 끌리지 않는 지표를 쓰고, 신뢰구간을 함께 적는 것. 새로운 알고리즘이 아니라 낡은 통계다. 하지만 정답 대신 점수만 받는 세계에서, 그 점수가 정말 올라갔는지 확인할 방법은 그것뿐이다.

이것만 기억하자 — 서른 장을 닫으며

  1. 강화학습이 받는 것은 스칼라 하나였고, 서른 장은 그 하나에서 방향을 만드는 방법의 목록이었다. 벨만 백업·TD·GAE·정책경사·신뢰영역이 전부 같은 문제의 서로 다른 답이다.
  2. 28~29장에서는 그 스칼라마저 사람의 선호에서 배워야 했다. 보상을 학습하는 순간 학습된 보상에도 허점이 생기고, KL 페널티는 그 허점에서 너무 멀리 가지 말라는 밧줄이다.
  3. 그리고 무엇을 이겼다고 말하려면 n ≈ 16/d2 개의 시드가 필요하다. 0.5σ 를 주장하려면 양쪽에 64개다. 알고리즘보다 실험 설계가 먼저다.

흔한 오해

  1. "오차막대가 겹치지 않으면 유의하다" — 막대가 ±s 인지 ±SE 인지 ±1.96SE 인지에 따라 답이 다르다. 두 개의 95% 신뢰구간이 살짝 겹쳐도 차이는 유의할 수 있고, 반대로 ±SE 막대가 안 겹쳐도 유의하지 않을 수 있다. 검정은 차이에 대해 해야 한다.
  2. "시드를 많이 돌리면 분산이 줄어든다" — 줄어드는 것은 평균의 표준오차뿐이다. 알고리즘 자체의 시드 간 표준편차 s 는 그대로다. "한 번 돌렸을 때 무엇을 얻는가"가 궁금한 실무자에게는 s 가 더 중요한 숫자다.
  3. "학습 중 최고 성능을 보고하는 것이 알고리즘의 잠재력을 보여 준다" — 참 성능이 같아도 K=100 이면 2.5076σ 의 가짜 우위가 생긴다. 보고하려면 체크포인트 선택도 학습의 일부로 간주해, 선택에 쓰지 않은 별도의 시드·환경에서 다시 평가해야 한다.
  4. "평균이 가장 공정한 요약이다" — 이상치 하나가 순위를 뒤집는다. 손계산 예제에서 평균은 B(63.1)를, 중간값(31.5)과 IQM(26.333)은 A(46.2 / 46.5 / 46.5)를 가리켰다. 같은 데이터에서 반대 결론이 나왔다면 먼저 물어야 할 것은 "어느 지표가 옳은가"가 아니라 "내가 답하려는 질문이 무엇인가"이다.

댓글