Runaway If You Can — 지속적 강화학습 난이도 조절
플레이어가 쉬운 경로를 한 번 발견하면 난이도가 무너지는 문제를 지속적 강화학습(Continual RL) 기반 추격 시스템으로 푼 연구 프로젝트. Unity 32×32 그리드에서 DQN으로 학습한 경찰 NPC가 플레이어 경로를 예측해 배치되고, 매 회차 플레이 데이터를 이어 학습해 체감 난이도를 유지한다. 광운대학교 소프트웨어학부 졸업 연구(이정원 · 전귀로, 2024) — 환경 설계, RL 에이전트, 실험 전담. 이 페이지는 실제 Unity 프로젝트 코드와 논문 PDF를 직접 읽은 분석이다.
① 시스템 전체 구조 — 학습과 추론의 분리
이 프로젝트의 가장 중요한 아키텍처 결정은 학습(offline, Python)과 추론(runtime, Unity)의 분리다:
- 외부 Python 환경에서 커스텀 DQN으로 플레이어 경로 예측 모델을 학습 → ONNX로 export
- Unity
Assets/Models/ppo_model_202407061637.onnx에 배치 - 런타임에는 ML-Agents가 아니라 Unity Barracuda의 ModelInference가 ONNX를 직접 추론 (
ModelInference.cs:10-36— inputTensorName="input", outputTensorName="max_indices") - ML-Agents(com.unity.ml-agents 2.0.1)는 패키지 의존성으로만 존재 — 런타임 추론 경로에는 Barracuda가 대체
모델 파일명이 ppo_model_…인데 논문은 DQN으로 기술한다 — 실제 학습 스크립트가 저장소에 없어
(Python 학습 코드·trainer YAML 0건, 학습 씬 PathLearnerScene은 빌드 제외 — EditorBuildSettings.asset:18)
어느 쪽이 정확한지는 코드로 확정 불가 [INFERENCE]. 면접에서는 "논문은 DQN, 모델 파일명은 ppo —
학습 코드는 저장소에 없고 논문 §3.2의 명세가 ground truth"라고 정확히 말하는 것이 안전하다.
② 관측·행동·보상 — 정확한 설계
관측 (11-dim, PoliceManager.RequestDecision:101-138)
- 도둑의 최근 5 step 이동 방향 벡터 — 각 step은 (dx, dy) 2차원, 5개 = 10차원
- 자기 팀 ID 1차원 — 같은 정책으로 3개 팀이 서로 다른 위치를 점유하게 하는 conditioning
- decisionInterval = 1초마다 추론 트리거
행동 ([20, 20] → 배치 좌표, PoliceManager:141-148)
- 네트워크 출력 [20,20]에서 argmax 2개 정수를 취함 (max_indices)
- 각 정수에서 10을 빼서 −10~+9 오프셋 → 도둑 현재 그리드 좌표에 가산 → clamp로 보드 내 배치
- 즉 행동 공간은 "도둑의 다음 위치일 것 같은 곳 ×2(가로/세로)"를 상대 좌표로 직접 예측
- 경찰 3팀(teamSize=2, 총 6명)은 결정된 위치로 즉시 재배치 (Team.Move — 거리 무관 순간이동 후 wander 재개)
보상 (그룹 공유)
- + 도둑이 경찰이 배치된 위치에 도착 (포착 성공)
- − 도둑이 경찰 위치를 피했을 때 매 스텝 도둑-경찰 거리 합 (멀수록 벌)
- 3팀이 하나의 그룹 보상을 공유 — 서로 협업하는 분업 배치가 저절로 학습되도록 유도
③ 지속적 강화학습 루프 — 이 연구의 핵심 기여
플레이
GridManager의 Sensor 트리거가 플레이어(도둑)의 그리드 이동을 playerGridPath에 누적 (GridManager.cs:21, 63-68)
경로 추출
논문 기준: 한 회차에서 경찰에 가장 적게 잡힌 '하위 3+ 경로'를 학습 데이터로 선별
데이터 저장
GridManager.ExportPath가 JSON으로 ./Build/PathLearner/Path/PlayerPath/에 기록 (GridManager.cs:83-97)
이어 학습
기존 가중치 w_t를 이어 받아(w_t → w_(t+1)) replay buffer에 추가 학습 — cold start 회피. 단 현재 빌드에서는 GameManager.EndGame의 ExportPath 호출이 주석 처리되어 비활성 (GameManager.cs:69)
배포
학습된 모델을 ONNX로 export → Assets/Models에 배치 (ppo_model_202407061637.onnx)
핵심 아이디어 두 가지: (1) 가중치 이어받기(w_t → w_(t+1))로 매 회차 cold start를 피하고, (2) 하위 경로만 선별해(경찰에 가장 적게 잡힌 경로 = 플레이어의 '이기는 패턴') replay buffer에 추가함으로써 플레이어가 변해도 정책이 따라가게 만든다. 일반 지속학습의 치명약인 catastrophic forgetting 완화를 experience replay가 담당하는 구조.
④ 배경지식 — 강화학습·ML-Agents 필수 개념
- MDP (Markov Decision Process)
- 강화학습의 수학적 틀 — 상태 S, 행동 A, 전이 P, 보상 R, 할인 γ. 에이전트는 정책 π(a|s)로 누적 할인 보상 Σγᵗrₜ을 최대화한다. 이 게임에서 S=최근 5 step 경로+팀 ID, A=배치 상대좌표 2개, R=포착/거리 페널티.
- Q-learning과 DQN
- Q(s,a) = 기대 누적 보상을 학습해 argmax 행동을 고르는 가치 기반 방법. 표를 쓰면 상태 공간이 커지 못 버티므로, DQN(DeepMind 2015)은 Q를 신경망으로 근사한다. 이 프로젝트의 11→256×5→[20,20] 네트워크가 그 근사기다.
- Experience Replay — 왜 필요한가
- 연속된 게임 프레임은 강하게 상관돼 있어 그대로 학습하면 네트워크가 최근 경험에 휘둘린다. 전환 (s,a,r,s′)을 버퍼(여기선 50,000)에 쌓고 무작위 배치(256)로 샘플링해 상관을 깨고 데이터 재사용율도 높인다.
- Target Network — 왜 필요한가
- 학습 목표(벨만 타깃)를 같은 네트워크로 만들면 목표가 계속 움직여 발산한다. 128 step마다만 갱신되는 고정 복제본으로 타깃을 계산해 안정화.
- ε-greedy 탐험
- 확률 ε로 무작위 행동(탐험), 1−ε로 최선 행동(활용). ε를 1.0→0.01로 감쇠(여기선 100 episode마다 ×0.9)하며 초반엔 넓게 탐험하고 후반엔 학습한 정책을 다듬는다.
- ML-Agents Toolkit의 역할과 한계
- Unity 공식 RL 프레임워크 — Python 학습기(mlagents-learn, 기본 PPO/SAC)와 Unity 환경 간 gRPC 통신, Agent.CollectObservations/OnActionReceived 인터페이스, Heuristic 모드 제공. 이 프로젝트는 학습을 커스텀 DQN으로 직접 짰고(논문 §3.2), 런타임 추론은 ML-Agents의 인퍼런스 경로 대신 Barracuda가 ONNX를 직접 실행한다 — "ML-Agents를 썼다"기보다 "ML-Agents 환경 관례 + 자체 DQN + Barracuda 배포"가 정확한 표현.
- Barracuda / ONNX
- ONNX는 신경망 중간 표현 표준. Barracuda(Unity의 신경망 추론 엔진, 후속 Sentis)는 Unity 런타임 안에서 ONNX 모델을 GPU/CPU로 실행한다. Python 학습 ↔ 게임 배포를 잇는 다리.
- Continual RL과 catastrophic forgetting
- 스트리밍 데이터로 계속 학습하면 새 패턴을 배우며 옛 정책을 잊는다(forgetting). 완화책 세 범주 — replay(과거 재생), 정규화(옛 가중치 보호, EWC 등), 아키텍처(모듈 분리). 이 프로젝트는 replay 기반: 매 회차 하위 경로만 버퍼에 더하고 가중치를 이어받는다.
- 동적 난이도 조절 (Dynamic Difficulty Adjustment)
- 플레이어 실력에 맞춰 게임이 스스로 난이도를 조절하는 설계 영역. 룰 기반(체력 보정)이 일반적이지만, 이 프로젝트는 상대를 학습하는 NPC라는 점에서 모델 기반 DDA에 속한다 — 면접에서 "난이도를 직접 조절한 게 아니라, 플레이어의 공략 패턴을 학습해 무력화하는 NPC를 만들어 난이도가 유지되는 구조"라고 설명하면 차별점이 명확해진다.
⑤ 게임 본체 — 추론을 감싼 시스템
- 그리드: GridManager가 32×32 센서 그리드(cellSize=30)로 월드를 이산화 (GridManager.cs:14, 39)
- 플레이어: FPS 컨트롤 — walk/run, 스태미나, 총기. 사망 시 EndGame(Lose) (PlayerController.cs)
- 승리 조건: EscapePoint에서 1000$ 지불 → EndGame(Win) (EscapePoint.cs)
- 경찰 개별 AI: 상태머신(wander/chase/moveTo/runaway) + NavMeshAgent 추적, DetectionCamera 시야 감지, 팀 전체 chase 전파 (HumanController.cs / PoliceController.cs / HumanChaseState.cs) — ML 배치와 전통 FSM이 이원화된 구조
- 경로 추종: PathController의 A* (PathFinding)
⑥ 면접 한 줄 답변
- "무엇을 학습시켰나요?" — 경찰 NPC의 '배치 위치 정책'입니다. 도둑의 최근 5 step 이동방향(10)+팀 ID(1)=11차원 관측으로, 다음 위치일 법한 그리드 좌표 2개를 [20,20] 출력의 argmax로 직접 예측합니다.
- "왜 DQN인가요?" — 상태·행동 공간이 이산적이고 그리드 크기가 고정이라 가치 기반 접근이 자연스럽습니다. Experience Replay(50k/batch 256), Target Network(128 step), ε-greedy 감쇠로 안정화했고, 3팀이 그룹 보상을 공유해 협업 배치가 유도됩니다.
- "ML-Agents를 어떻게 썼나요?" — 환경 관례(관측/행동 인터페이스)는 ML-Agents 패키지(2.0.1) 기반이지만, 학습은 논문 §3.2의 커스텀 DQN으로 했고 런타임 추론은 ONNX를 Barracuda가 직접 실행합니다. 학습-배포 분리가 핵심 구조입니다.
- "지속적 학습은 어떻게?" — 매 회차 플레이 경로를 JSON으로 저장하고, 그중 경찰에 가장 적게 잡힌 하위 경로만 replay buffer에 추가한 뒤 기존 가중치를 이어받아 재학습(w_t → w_(t+1))합니다. cold start 회피 + replay로 forgetting 완화.
- "난이도 조절과 무슨 상관?" — 직접 난이도를 조절하지 않습니다. 플레이어가 반복해서 이기는 경로를 학습 데이터로 넣어 그 패턴을 예측·차단하는 NPC가 되므로, 체감 난이도가 자연히 유지되는 모델 기반 DDA입니다.
- "한계는?" — 학습 스크립트가 저장소에 없어 논문 명세가 ground truth이고, 빌드에서는 경로 export 호출이 주석 처리돼 지속 학습 수집이 비활성 상태입니다. 관측이 최근 5 step뿐이라 장기 계획 경로 예측은 어렵습니다.