Skip to content

feat: 입소 예측 정확도를 백테스트로 측정하고 공개 - #125

Merged
RosieOh merged 2 commits into
mainfrom
feat/forecast-accuracy
Sep 24, 2026
Merged

RosieOh merged 2 commits into
mainfrom
feat/forecast-accuracy

Conversation

@RosieOh

@RosieOh RosieOh commented Sep 24, 2026

Copy link
Copy Markdown
Contributor

Closes #124

왜

확률을 보여주면서 그 확률이 맞는지 확인한 적이 없었다. 사용자는 이 숫자를 보고 다른 시설을 포기할 수도 있다.

정원 관측 시계열이 이미 쌓이므로 과거 시점을 재현해 검증할 수 있다. 예측을 기록하고 몇 달 기다릴 필요가 없다.

기준일(과거 어느 날)
  → 그날 이전 관측만으로 예측 재계산     ← 이후 관측을 섞으면 미래를 보고 예측한 셈
  → 다음 N개월 관측으로 실제 결과 확인
  → (확률, 실제) 쌍을 모아 집계

무엇

내용
AdmissionForecastCalculator 예측 계산을 순수 클래스로 분리. 라이브와 백테스트가 같은 코드를 쓴다
ForecastBacktestService 기준일 30일 간격 표본 수집. 시설 이력은 시설당 한 번만 읽어 모든 기간에 재사용
TBL_FORECAST_ACCURACY (V20) 기간별(1·3·6개월) 측정 결과
지표 확률대별 실제 적중률, Brier 점수, 기준선(항상 평균 발생률로 답했을 때)
노출 예측 응답의 accuracy, GET /facilities/forecast-accuracy(공개)
실행 매주 일 04:30, POST /api/admin/sync/forecast-accuracy/measure(수동)

기준선을 함께 기록하는 이유: 예측이 "평균으로 답하기" 보다 못하면 확률을 보여줄 근거가 없다. 응답의 betterThanBaseline 로 그대로 드러난다.

표본이 적으면 숨긴다: 전체 30건 미만이면 정확도를 아예 내보내지 않고, 구간 표본 10건 미만이면 그 구간만 비운다. "3건 중 3건 적중 = 100%" 는 근거 없는 확률보다 더 나쁘게 오해를 만든다.

측정 기간에 1·3개월을 둔 이유: 6개월만 재면 관측이 6개월 넘게 쌓인 시설이 있어야 표본이 생겨 수집 초기에는 아무것도 검증할 수 없다.

테스트

  • ForecastBacktestServiceTest(7) — look-ahead 방지(기준일 이후 관측이 예측에 섞이지 않음), 구간표 합계 = 전체 표본, 기준선 기록, 기간별 분리, 시설 수 상한
  • ForecastAccuracyServiceTest(6) — 표본 부족 시 숨김, 구간 매칭, 깨진 JSON 격리
  • ForecastAccuracyContractTest — 측정 전 빈 목록 → 관리자 측정(일반 회원 403) → 공개 노출 → 예측 응답의 accuracy 까지 한 흐름
  • 전체 568 tests, 실패 0, skip 0 (V20 포함 스키마 검증 통과)

두 번째 커밋 (test)

이 PR 로 컨텍스트가 14개가 되면서 테스트 워커가 OutOfMemory 로 죽었다(실행 시간도 3분 34초 → 9분 55초). 힙 2g + spring.test.context.cache.maxSize=6 으로 고정했다. 지금은 3분 34초.

한계

관측이 쌓인 만큼만 검증된다. 수집 초기에는 1개월 기간만 표본이 모이고, 그마저 30건 미만이면 화면에 아무것도 나가지 않는다 — 의도한 동작이다.

확률을 보여주면서 그 확률이 맞는지 확인한 적이 없었다. 정원 관측 시계열이 이미 쌓이므로
예측을 기록해 몇 달 기다릴 필요 없이 과거 시점을 재현해 검증할 수 있다.

- AdmissionForecastCalculator: 예측 계산을 순수 클래스로 분리. 라이브와 백테스트가 같은 코드를 쓴다
  (검증용을 따로 구현하면 무엇을 검증했는지 알 수 없다).
- ForecastBacktestService: 기준일 이전 관측만으로 예측을 재계산하고 이후 구간 관측으로 실제 결과를 확인.
  기준일을 30일씩 옮기며 표본을 모은다. 확률대별 적중률, Brier 점수, 기준선(항상 평균으로 답했을 때)을 기록.
- TBL_FORECAST_ACCURACY (V20): 기간별(1·3·6개월) 측정 결과.
- 표본이 30건 미만이면 정확도를 내보내지 않고, 구간 표본 10건 미만이면 그 구간만 비운다.
- 예측 응답에 accuracy(같은 확률대의 실제 적중률) 추가, GET /facilities/forecast-accuracy 공개.
- 주 1회 자동 측정(일 04:30) + POST /api/admin/sync/forecast-accuracy/measure 수동 실행.
통합 테스트 클래스를 추가하면서 컨텍스트가 14개까지 떠, 기본 힙으로는 워커가
OutOfMemoryError 로 죽었다(GC 스래싱으로 실행 시간도 3분 34초 → 9분 55초로 늘었다).
컨텍스트마다 JPA·커넥션풀·캐시를 들고 있어 개수가 곧 메모리다.

- maxHeapSize 2g
- spring.test.context.cache.maxSize=6: 기본값(32)이면 한 번 뜬 컨텍스트가 끝까지 남아
  클래스를 추가할 때마다 메모리가 계단식으로 늘어난다. 넘치면 오래된 컨텍스트를 닫는다.
@RosieOh
RosieOh merged commit 5bd1f34 into main Sep 24, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[ 시설 ] 입소 확률을 보여주면서 그 확률이 맞는지 확인하지 않는다

1 participant