체크포인트는 결과가 아니다: PPO 정책을 검증하는 seed·scenario·metric·acceptance gate

G1 motion-tracking 학습에서 가장 판단하기 어려웠던 순간은 로봇이 바로 넘어질 때가 아니었습니다. 오히려 64개 환경이 10초 동안 모두 살아남고, 기준 궤적의 96.92%까지 이동했을 때였습니다. 화면만 보면 거의 완성된 보행처럼 보였습니다. 하지만 평균 발 간격은 기준 동작의 0.210m보다 넓은 0.283m였고, 발 crossing은 0.450%까지 남아 있었습니다. 이 정책은 다음 연구의 base로는 쓸 수 있었지만, “잘 걷는 정책”으로 승인할 수는 없었습니다.

반대 사례도 있었습니다. A14의 5초 trace에서는 reset이 한 번도 없었고 좌우 support도 42.95%와 43.20%로 거의 대칭이었습니다. 같은 설정을 15초로 늘리자 slow와 nominal 명령에서 낙상률이 62.5%와 87.5%, crossing은 15.8%와 28.6%가 됐습니다. 짧은 영상이 거짓이었던 것은 아닙니다. 다만 그 영상이 답할 수 있는 질문보다 큰 결론을 붙였을 때 문제가 생깁니다.

PPO가 저장한 model_1591.pt는 결과가 아니라 학습 상태를 직렬화한 파일입니다. 그 파일이 어떤 명령에서 얼마나 오래 버티는지, 기준 동작을 얼마나 따라가는지, 관절과 발이 안전한지, 다른 seed에서도 같은 경향이 나오는지는 별도의 평가 계약이 답해야 합니다. 이 글에서는 PPO의 목적함수와 actor–critic 구조를 다시 설명하지 않습니다. 대신 현재 G1 프로젝트에서 checkpoint를 candidate, accepted subsystem, accepted parent로 구분하기 위해 실제로 사용한 평가 파이프라인과 시행착오를 정리합니다.

G1 PPO 체크포인트가 wiring smoke, 5초 sentinel, 고정 scenario, multi-seed paired evaluation, promotion gate를 거치는 평가 funnel과 A14, M1-M8, M68-M69의 실제 중단 사례를 정리한 다이어그램
앞 단계의 통과는 다음 단계로 갈 수 있다는 뜻일 뿐입니다. 뒤 단계에서 실패한 정책을 앞 단계의 성공 영상으로 되살릴 수는 없습니다.

학습 곡선, 체크포인트, 정책 결과는 서로 다른 증거입니다

학습 중 표시되는 mean reward는 현재 reward contract 아래에서 수집한 rollout의 평균입니다. reward가 올라갔다는 사실은 optimizer가 그 목적함수를 더 잘 맞추고 있다는 증거입니다. 그러나 reward에 발 crossing, 관절 포화, 실제 기준 동작과의 자세 차이가 충분히 반영되지 않았다면 사람이 원하는 보행과 함께 올라간다고 보장할 수 없습니다. reward를 고쳤는데 보행이 더 나빠졌던 기록에서 확인했듯이 scalar reward는 여러 실패 모드를 서로 상쇄할 수 있습니다.

체크포인트는 그 시점의 actor·critic weight, normalizer, optimizer state 등을 다시 불러올 수 있게 저장한 artifact입니다. 저장에 성공했다는 사실은 파일이 존재한다는 뜻이고, load smoke가 성공했다는 사실은 schema가 맞는다는 뜻입니다. 둘 다 보행 성능을 말해 주지는 않습니다. 체크포인트를 결과로 바꾸는 것은 같은 artifact를 학습과 분리된 조건에서 실행하고, 미리 정한 metric과 threshold로 판정하는 과정입니다.

증거 답할 수 있는 질문 답할 수 없는 질문
mean reward·loss 현재 목적함수와 optimizer가 움직였는가 로봇이 안전하고 자연스럽게 걷는가
checkpoint 저장·reload 학습 상태를 직렬화하고 복원할 수 있는가 새 episode·seed·명령에서도 성능이 유지되는가
짧은 trace·영상 특정 초기 조건에서 눈에 띄는 실패가 바로 나오는가 긴 horizon의 drift와 희귀 실패가 없는가
고정 평가 suite 정해진 scenario와 metric gate를 통과하는가 평가하지 않은 외란·실물 조건까지 안전한가

그래서 글에서도 “학습됐다”라는 한 단어를 피하고 implemented, executed, observed, candidate, accepted를 구분합니다. 예를 들어 M8/model 1591은 route-preserving base로 관찰됐지만 accepted walking은 아닙니다. M69는 세 seed의 deterministic arm gate를 통과했지만 승인 범위가 arm subsystem에 한정돼 있습니다. 이 상태 언어가 없으면 파일 하나에 너무 큰 주장을 붙이게 됩니다.

평가 계약은 결과를 보기 전에 고정해야 합니다

평가 계약은 “잘 나오면 볼 지표 목록”이 아닙니다. 어떤 checkpoint와 baseline을 비교할지, 어떤 seed와 명령을 쓸지, episode를 몇 초 실행할지, 어느 metric을 계산할지, threshold를 넘으면 어떤 범위까지 승인할지를 결과를 열기 전에 고정한 문서입니다. 결과를 본 뒤 불리한 metric을 빼거나 horizon을 줄이면 평가는 학습의 일부가 되어 버립니다.

evaluation_contract = {
  "candidate": checkpoint_sha,
  "baseline": baseline_checkpoint_sha,
  "policy_seed": [42, 43, 44],
  "scenario": ["stand_still", "walk_forward_slow", "walk_forward_nominal"],
  "horizon_s": [10, 15, 15],
  "episodes_per_scenario": 32,
  "metrics": ["fall", "route", "gait", "morphology", "action"],
  "thresholds": frozen_gate_version,
  "promotion_scope": "subsystem | parent | router"
}

현재 workspace의 training/eval/eval_rl.py는 checkpoint, seed, scenario YAML, episode 수를 받아 scenario별 JSON과 CSV를 저장합니다. training/eval/scenarios/loco_scenarios.yaml에는 stand still, slow 0.5m/s, nominal 1.0m/s뿐 아니라 lateral, diagonal, turn, backward 명령도 정의돼 있습니다. metrics.py는 episode별 metric을 계산한 뒤 threshold를 어긴 항목을 failure reason으로 남기고, failure가 하나도 없을 때만 passed를 만듭니다.

이 구현도 아직 완전한 자동 승격 시스템은 아닙니다. 시각적 3D morphology, randomized physics, 실제 로봇 센서 계약은 별도 gate에 남아 있습니다. 중요한 점은 코드가 모든 안전을 보장한다는 것이 아니라, 같은 질문을 같은 방식으로 다시 물을 수 있다는 것입니다. 평가 계약의 첫 목적은 재현 가능한 반박 가능성을 만드는 데 있습니다.

2초 smoke와 5초 sentinel은 성능 평가가 아니라 비용 절감 장치입니다

긴 평가를 바로 실행하면 observation shape 오류, 잘못된 normalizer, NaN action 같은 배선 실패에도 수십 분의 GPU 시간을 쓸 수 있습니다. 그래서 첫 단계는 1–2초 wiring smoke입니다. checkpoint가 로드되는지, actor input과 action 차원이 맞는지, 모든 값이 finite인지, expected hash와 schema가 일치하는지만 봅니다. 여기서 통과한 정책을 “걷는다”고 부르지 않습니다.

다음은 5초 sentinel입니다. early fall, 발이 즉시 벌어지는 현상, action 폭주, 좌우 support 붕괴처럼 빨리 드러나는 실패를 저렴하게 잡습니다. sentinel의 역할은 full evaluation을 대신하는 것이 아니라, 명백히 실패한 checkpoint를 긴 suite에 보내지 않는 것입니다. 따라서 sentinel pass의 판정은 continue evaluation이지 accept policy가 아닙니다.

단계 주요 질문 통과가 허용하는 다음 행동 통과가 의미하지 않는 것
wiring smoke shape·hash·finite·load가 맞는가 짧은 실행 시작 동역학 안정성
5초 sentinel 즉시 드러나는 실패가 없는가 10–15초 suite 실행 late drift·희귀 낙상 없음
고정 scenario 명령별 metric vector가 gate 안인가 paired·multi-seed 비교 다른 초기화·외란 강건성
multi-seed 효과 방향과 안전 gate가 반복되는가 시각·randomized 승격 심사 실물 배포 가능성

episode reset 계약도 이 단계에서 검증해야 합니다. done 환경의 recurrent hidden state나 observation history가 초기화되지 않으면, 짧은 smoke는 우연히 통과해도 여러 episode를 모으는 full evaluation에서 이전 상태가 새 episode로 새어 들어갑니다. evaluation harness의 버그와 policy의 실패를 구분하려면 reset·seed·time alignment를 먼저 고정해야 합니다.

scenario matrix는 한 속도의 성공을 보행 능력으로 일반화하지 못하게 합니다

보행 정책은 하나의 숫자로 명령받지 않습니다. 정지, 느린 전진, nominal 전진, 빠른 전진, 횡이동, 회전, 후진은 서로 다른 failure mode를 드러냅니다. 정지에서는 CoM 높이와 불필요한 관절 진동이 중요하고, 느린 전진에서는 최소 displacement와 발 끌림이 중요합니다. 빠른 전진에서는 tracking error와 낙상 허용치가 더 크게 설정될 수 있지만, crossing이나 관절 포화 같은 구조적 안전 기준을 느슨하게 만들 이유는 없습니다.

현재 scenario YAML에서 stand still은 10초 동안 fall rate 5% 이하, CoM height standard deviation 0.03m 이하, velocity error 0.1 이하를 요구합니다. slow는 15초 동안 최소 5m, fall rate 10% 이하, velocity error 0.2 이하를 요구합니다. nominal은 15초 동안 최소 10m, fall rate 15% 이하, velocity error 0.3 이하를 요구합니다. 숫자는 현재 개발 단계의 regression gate이며 곧바로 실물 안전 인증을 뜻하지 않습니다.

평가할 때는 scenario를 행, metric family를 열로 둔 matrix를 만듭니다. 한 칸의 성공이 다른 칸을 덮지 못하게 하기 위해서입니다. 예를 들어 nominal displacement가 좋아져도 stand-still action rate가 폭증했다면 parent promotion은 멈춥니다. slow에서 잘 걷더라도 turn에서 발이 교차하면 “전진 전용 subsystem” 이상의 주장을 할 수 없습니다. 승격 범위는 통과한 행과 열의 교집합이어야 합니다.

생존율 하나 대신 route·gait·morphology·action을 함께 봅니다

휴머노이드 평가에서 fall rate는 필요하지만 충분하지 않습니다. 로봇이 명령을 무시한 채 제자리에서 버티면 생존율은 높습니다. 반대로 reference travel을 따라가면서도 다리를 과도하게 벌리거나 발을 교차하면 displacement는 좋습니다. action을 크게 흔들어 두 지표를 동시에 맞춘 정책은 simulator에서는 살아도 actuator와 sim-to-real 경계를 망가뜨릴 수 있습니다.

metrics.py가 기록하는 항목은 이 상쇄를 드러내기 위한 metric vector입니다. survival에는 fall과 episode duration, route에는 commanded displacement와 velocity tracking, gait에는 air time·step frequency·support fraction·좌우 asymmetry가 들어갑니다. morphology에는 foot crossing, lateral separation, waist·hip·arm의 reference-relative position과 velocity가 들어가고, action에는 action rate·torque·raw action maximum이 들어갑니다.

pass(policy) =
  survival_gate
  AND route_gate
  AND gait_gate
  AND morphology_gate
  AND action_gate

# 평균 점수가 높아도 한 safety gate를 보상해 통과할 수 없다.

여기서 AND는 단순한 코딩 취향이 아닙니다. 승인 기준에서 안전 관련 항목을 reward처럼 가중합하면 route의 큰 개선이 작은 crossing을 상쇄할 수 있습니다. 연구 후보를 정렬할 때 Pareto score를 쓸 수는 있지만, 최종 promotion에서는 사전에 정한 hard gate를 별도로 유지합니다. M68 seed44의 saturation 0.003125%를 평균 개선으로 덮지 않은 이유도 여기에 있습니다.

candidate와 baseline은 같은 tape에서 짝지어 비교합니다

새 policy만 실행해 “이동 거리 3m”라고 적으면 좋아진 것인지 나빠진 것인지 알 수 없습니다. candidate와 parent baseline을 같은 initial state, command sequence, reference phase, episode horizon에서 실행해야 변화량을 읽을 수 있습니다. 가능한 경우 환경별 결과를 짝지으면 쉬운 초기화가 candidate에 우연히 몰리는 효과도 줄일 수 있습니다.

v600과 v601의 사례가 명확합니다. v600은 한 번의 PPO smoke 뒤 slow displacement가 baseline 2.8568m에서 2.6089m로 0.2479m 감소했고, morphology/raw-action gate도 실패했습니다. v601의 15초 slow 평가는 최종 displacement delta가 -0.5444m였고 early·mid·late 구간이 각각 -0.1027, -0.1566, -0.2835m로 계속 벌어졌습니다. candidate의 절대 이동 거리만 보면 걷는 것처럼 보이지만, paired delta는 시간이 갈수록 parent route에서 이탈하는 정책임을 보여 줬습니다.

paired evaluation에는 checkpoint hash와 normalizer hash도 함께 묶어야 합니다. PPO resume state 계약에서 model weight만 불러와 한 iteration을 더 학습했을 때 정책이 급격히 무너졌듯이, 같은 파일명이라도 optimizer·normalizer·runner state가 다르면 비교 조건이 달라집니다. baseline과 candidate를 고르는 순간 artifact identity를 봉인해야 합니다.

한 seed의 개선은 후보를 만들지만 재현성을 만들지는 않습니다

PPO는 초기 weight, rollout 순서, 병렬 환경의 reset과 샘플링에 따라 결과가 달라질 수 있습니다. 따라서 seed 42에서 좋아진 checkpoint는 가설을 지지하는 candidate일 뿐입니다. 같은 변경을 fresh seed에서 다시 시작해 효과 방향, effect size, safety tail이 유지되는지 봐야 합니다. 여기서 seed는 결과가 마음에 들 때까지 바꾸는 손잡이가 아니라 평가 계약에 미리 적는 독립 반복 단위입니다.

Henderson 등의 Deep Reinforcement Learning that Matters는 환경과 알고리즘의 비결정성 때문에 단일 성능 값만으로 개선을 판단하기 어렵고, baseline과 실험 보고를 더 엄격히 표준화해야 한다고 지적했습니다. Agarwal 등의 Deep Reinforcement Learning at the Edge of the Statistical Precipice는 적은 수의 run에서도 point estimate만 제시하지 말고 불확실성 구간과 성능 분포를 함께 보라고 제안합니다. 공개 구현인 rliable는 stratified bootstrap confidence interval, performance profile, interquartile mean과 improvement probability를 제공합니다.

현재 G1의 세 seed subsystem gate는 대규모 benchmark 통계라고 부르기에는 작습니다. 그래서 “통계적으로 보편적인 개선”이라고 쓰지 않습니다. 세 fresh seed에서 사전 지정된 방향이 반복됐는지, 한 seed라도 hard safety gate를 어겼는지를 확인하는 최소 재현 gate로 사용합니다. run 수가 적을수록 raw per-seed metric, paired delta와 실패 tail을 숨기지 않는 것이 중요합니다.

A14는 5초 성공이 15초 안정성을 증명하지 못한다는 사례였습니다

A14는 기존 정책의 waist·arm authority를 0.25, hip roll을 0.75로 줄이고 hip yaw와 lower-body action은 보존한 조합이었습니다. 5초 trace에서는 reset이 0이었고 slow support fraction은 좌우 42.95%와 43.20%, 발 간격은 0.099m였습니다. 짧은 window만 보면 좌우 지지가 대칭이고 넘어지지 않는 후보였습니다.

하지만 15초 corrected evaluation에서는 slow/nominal fall rate가 62.5%/87.5%, crossing이 15.8%/28.6%였습니다. 상체와 hip roll authority를 상수로 줄이는 방식이 초기 2–3초의 safe distribution은 만들었지만, 시간이 지나며 생기는 heading·lateral error를 회복할 control authority가 부족했습니다. 이 실패는 “5초 평가는 쓸모없다”가 아니라 “5초 평가는 early screen으로만 유효하다”는 경계를 보여 줬습니다.

이후 A15에서 상태에 따라 authority를 부드럽게 복원하고 2초 warm-up까지 넣었지만, heading과 displacement는 일부 좋아지는 대신 raw waist/arm action이 다시 커졌습니다. 세 구조적 cycle이 15초 acceptance를 개선하지 못해 GPU 반복을 중단했습니다. 짧은 성공 장면을 더 매끄럽게 만드는 대신, full-horizon failure mode를 바꾸지 못했다는 판정이었습니다.

M1–M8은 생존과 경로가 좋아져도 morphology 때문에 멈출 수 있음을 보여 줬습니다

motion-tracking M1/model 298은 corrected deterministic 64-env, 10초 평가에서 95.31% survival을 기록했지만 robot/reference travel은 1.670/3.720m, progress는 43.35%였습니다. M2는 survival 98.44%, progress 46.95%로 좋아졌고, M3/model 496은 64/64 timeout과 progress 50.16%를 달성했습니다. 이 단계에서 “넘어지지 않는다”는 문제는 거의 닫혔지만 reference를 따라가는 문제는 절반만 풀렸습니다.

M4와 M5의 contiguous PPO block은 sampler state를 보존하면서 progress를 61.94%, 73.62%로 높였습니다. M6/model 1093은 82.43%에 도달했지만 평균 stance가 reference보다 46% 넓고 waist peak가 55% 컸으며 crossing도 0.166%였습니다. route progress가 80%를 넘자 숫자 평가만 계속하지 않고 renderer-independent morphology와 직접 Isaac Sim review를 gate에 넣었습니다.

M7은 progress 90.02%까지 올라갔지만 ankle error와 crossing 0.341%가 남았습니다. M8/model 1591은 64/64 survival과 progress 96.92%를 달성했지만 stance 0.283m 대 reference 0.210m, crossing 0.450%, ankle error 8.48/7.43cm로 morphology가 정체됐습니다. unchanged PPO를 더 돌리는 대신 model 1591을 route-preserving base로만 남겼습니다. 이 기록이 말하는 것은 PPO가 실패했다는 것이 아니라, route objective의 수렴을 walking acceptance로 확대할 수 없다는 것입니다.

단계 대표 checkpoint 통과한 증거 막힌 증거 판정
M1 298 survival 95.31% progress 43.35% bounded continuation
M3 496 64/64 10초 생존 progress 50.16% route 학습 계속
M6 1093 progress 82.43% stance +46%, waist peak +55%, crossing 0.166% visual review gate
M8 1591 progress 96.92% stance 0.283/0.210m, crossing 0.450% base only, walking 미승인

M68–M69는 재현성과 승인 범위가 별개라는 사례였습니다

M68의 state-conditioned arm actor는 seed 42에서 vertical CAM z RMSE 3.77%, arm contribution 6.43% 개선을 보였습니다. 여기서 결과를 닫지 않고 seed 43·44를 M62/2100에서 fresh 125 update로 실행했습니다. 세 seed 모두 z-CAM 3.67–4.09%, arm contribution 5.88–6.43% 개선을 재현했습니다. 그러나 seed44에서 right-shoulder-pitch residual saturation이 0.003125% 발생해 zero-saturation gate가 실패했습니다.

M69는 극단적인 CAM error가 bilinear 입력을 크게 만드는 한 경로만 tanh(error)로 제한했습니다. seed 42·43·44를 각각 fresh 125 update 학습한 endpoint는 z-CAM 3.54–4.37%, arm contribution 5.48–6.66% 개선을 유지했고, termination·crossing·saturation tail이 모두 0이었습니다. 여기서 승인한 것은 bounded arm subsystem입니다. asymmetric actor–critic 글에서 설명한 대로 frozen whole-body actor 위의 격리된 팔 경로가 의도한 효과를 반복했다는 뜻입니다.

그렇다고 M69 parent whole-body policy나 실제 로봇 경로가 승인된 것은 아닙니다. deterministic 30초 뒤에 수행한 paired randomized cohort에서는 observation noise, push, combined perturbation 아래 route·action-rate·crossing 문제가 다시 드러났고 총 192 episode 중 termination도 2회 있었습니다. morphology reference-relative gate와 실제 센서·actuator 검증도 남아 있습니다. subsystem acceptance와 parent promotion, sim-to-real validation을 한 칸씩 분리해야 하는 이유입니다.

현재 평가 파이프라인에서 다음으로 고정할 것

현재 구현은 scenario YAML, episode metric 집계, per-gate failure reason, JSON/CSV artifact까지 갖추고 있습니다. 반면 checkpoint 후보가 생길 때마다 모든 gate가 자동으로 한 manifest에 봉인되고, paired baseline과 multi-seed 결과가 자동으로 연결되며, 시각·randomized 결과까지 promotion 상태를 갱신하는 단계는 남아 있습니다. 다음 작업은 평가 코드를 더 많이 만드는 것이 아니라 서로 다른 증거를 하나의 immutable contract로 묶는 것입니다.

항목 현재 상태 다음 산출물
scenario·threshold YAML로 구현됨 gate version과 SHA 봉인
episode·aggregate metric JSON/CSV 저장 baseline-candidate paired row ID
seed 재현 실험별 수동 manifest seed matrix와 bootstrap CI 자동 생성
visual morphology renderer-independent report와 수동 review 병행 checkpoint별 overlay·영상 링크 고정
randomized robustness 별도 paired cohort로 실행 deterministic 결과와 같은 promotion report에 병합
승인 상태 노트에서 subsystem·parent를 구분 evaluation_manifest.json의 machine-readable scope
evaluation_manifest.json
  artifact: checkpoint + actor/critic/normalizer hashes
  parent: frozen baseline identity
  contract: scenario + seed + horizon + metric + threshold SHA
  evidence: per-episode rows + aggregate + visual + randomized
  decision: rejected | candidate | subsystem_accepted | parent_accepted
  exclusions: claims this evaluation does not support

특히 마지막 exclusions가 중요합니다. deterministic simulator에서 통과한 정책은 randomized physics나 실물 센서 지연을 통과했다고 쓰지 않습니다. arm subsystem을 승인한 결과는 whole-body walking을 승인했다고 쓰지 않습니다. 64개 환경 평균이 좋아도 한 safety tail을 발견했다면 그 tail을 평균 속에 숨기지 않습니다. 평가의 역할은 정책을 돋보이게 만드는 것이 아니라, 현재 증거가 허용하는 주장 범위를 정확히 잘라내는 것입니다.

결국 “로봇이 잘 걷는다”는 한 문장은 여러 AND gate의 축약형입니다. 넘어지지 않고, 명령과 기준 경로를 따라가며, 보행 주기와 자세가 허용 범위에 있고, action이 안전하며, 이 결과가 여러 초기화와 seed에서 반복돼야 합니다. 체크포인트는 이 질문을 시작하는 artifact입니다. 결과는 그 artifact가 고정된 평가 계약을 통과한 범위만큼만 존재합니다.

기술 기준 시점: 2026-07-22. workspace origin/main@ee4d96etraining/eval/eval_rl.py, metrics.py, scenarios/loco_scenarios.yaml과 G1 A14·M1–M8·M68–M69 실험 기록을 기준으로 작성했습니다. 본문의 acceptance는 프로젝트 내부 승격 기준이며 실물 로봇 안전 인증을 뜻하지 않습니다.

댓글 달기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

위로 스크롤