1차 운영 선택 · 2026-08-19

오고 있는 버스 보기

차량 상태 조건부 좌석 분포 · seat_distribution · A18

한 줄 결론 — 지금 오고 있는 그 버스가 목표 정류장에 도착할 때 잔여좌석이 몇 석일지를 분포로 예측하고, 만석 확률은 그 분포의 0 지점으로 읽는다. 다른 후보들이 쓰지 않는 실시간 잔여석을 쓰므로 쓰임이 다르다.

2026-08-19 팀 검토로 1차 운영 모델이 됐다. 그 전(08-17~08-19)에는 02번 「재료 다 넣기」였고, 그 모델은 폐기가 아니라 차량 상태를 쓰지 않는 레인에 남는다. 1차 운영 선택은 운영 적용을 위한 팀 선택이고 성능 입증이 아니다 — 평가는 여전히 잠정이고 일일 채점도 계속된다. 이 모델을 서빙할 v4 계약은 아직 검토 요청 상태다.

이 모델이 나온 배경

앞선 후보 하나는 아침 러시에서 현행 기본보다 6~7% 나빴다. 그것이 채택되지 못한 이유였다. 원인을 찾는 과정에서 세 가지가 드러났다.

A18 은 그 셋을 고친 결과다. 아침 러시에서 현행 기본보다 21~23% 낫고, 앞선 후보가 실패했던 구간이다.

Q. 이 모델은 무엇을 계산하나?

버스가 목표 정류장에 도착할 때 잔여좌석이 0석일 확률, 1석일 확률, 2석일 확률…을 전부 낸다. 하나의 숫자가 아니라 분포다.

만석 경보는 그 분포의 0 지점을 읽은 것이다. 그래서 "만석이다/아니다"만 답하는 모델과 달리 "만석은 아닌데 두 자리밖에 안 남을 것 같다" 같은 것도 말할 수 있다. 다만 지금 서비스가 파는 것은 만석 경보이고, 이 페이지의 점수도 만석 확률만 채점한 것이다.

Q. 왜 분포로 예측하나?

만석은 표본의 1.7%뿐인 드문 사건이다. 드문 사건만 따로 배우려 하면 표본이 모자란다. 반면 좌석 분포 전체를 배우면 나머지 98.3%도 학습에 쓸 수 있고, 만석은 그 분포의 끝에서 나온다.

다만 0석은 다른 숫자와 성질이 다르다. 0석과 1석의 차이는 1석과 2석의 차이와 같지 않다 — 0석은 "탈 수 없음"이라는 질적 경계다. 그래서 만석 확률만은 분포 끝에서 파생하지 않고 라벨에서 직접 배운다. 이 두 단계를 합치는 구조를 허들이라 부른다.

Q. 원리가 무엇인가?

구조는 관측 하나에서 나왔다 — 지평이 멀어지면 정보의 출처가 옮겨간다.

학습에 안 쓴 날의 설명력 R² (8/18 하루) 1.00.50 12346812 지평(정류장 수) 차량 상태 정류장 상태 둘 다 .958 .002 .310 .337
차량 상태 = 지금 그 버스의 잔여석, 정류장 상태 = 그 (노선, 정류장, 시간대)의 과거 평균 점유율. 셋 다 도착 점유율을 맞히는 단순 회귀이며, 학습은 8/14~17 이고 시험은 학습에 안 쓴 8/18 하루다. 지금 좌석이 알려주는 것은 0.958 에서 0.002 로 완전히 무너진다. 정류장이 알려주는 것은 0.336 에서 0.310 으로 거의 그대로다. 그래서 먼 지평일수록 둘을 함께 써야 한다 — 8정류장에서 차량만 0.179 인데 합치면 0.442 이고, 12정류장에서는 0.002 대 0.337 이다. 이 그림은 A18 본체가 아니라 왜 두 재료가 다 필요한가를 보이는 단순 회귀다. A18 의 실제 성적은 아래 「오늘 성적」에 있다. 시험이 하루뿐이라는 것도 함께 보라 — 평일 표본이 쌓이면 다시 재야 한다.

가장 순진한 방법은 도착 좌석을 곧바로 맞히는 것이다. 그런데 그 값의 분산이 크다. 대신 지금 좌석에서 얼마나 변할지를 맞히면, 2분 뒤라면 변화량의 분산이 절대값 분산의 3.1%다. 이미 아는 정보를 버리지 않는 쪽이 유리하다.

그런데 멀어지면 이게 무너진다. 평일 아침 8정류장을 지나는 동안 좌석이 평균 13.8석 움직인다. 지금 좌석과 도착 좌석의 관계를 자유롭게 추정하면 기울기가 0.434까지 떨어진다 — 절반도 설명하지 못한다.

그 변화를 만드는 것은 정류장이다. 어느 정류장에서 몇 명이 타는지가 좌석을 줄인다. 그래서 정류장의 과거 이력을 함께 쓴다. 위 그림에서 8정류장 지평의 설명력이 차량만으로 0.179 인데 정류장을 더하면 0.442 로 오르는 것이 그 몫이다.

Q. 어떻게 계산하나?

네 단계

① 기준점
지금 좌석에서 출발
지평에 따라 신뢰도 조절
얼마나
변할까
② 구간 수요
통과할 정류장들의
평소 수요를 합산
물리
제약
③ 경계
좌석은 0~정원을
벗어날 수 없다
0은
따로
④ 만석 판정
라벨에서 직접 배운
만석 확률을 합침

②의 "통과할 정류장들"이 핵심이다. 8개 정류장을 지난다면 그 8개 각각의 평소 수요를 더한다. 도착 정류장 하나만 보면 나머지 일곱을 무시하는 셈이다.

이 모델이 도는 두 노선

지평·커버율·회차 이야기는 전부 이 제원 위에서 이뤄진다. 값은 상류 기준정보 API (getBusRouteInfoItemv2 · getBusRouteStationListv2)를 직접 조회해 확정했다.

항목16503330
routeId234000050204000057
정류장 수8985
turnSeq (회차 순번)4443
회차 정류장안양역 · stationId 208000069 — 두 노선 공통
기점 (순번 1)구리수택차고지 221000046도촌동9단지앞 205000227
종점 (마지막 순번)구리수택차고지 221000047도촌동9단지앞 205000226
첫차 / 막차 (상행)04:10 / 22:3504:50 / 23:30
첫차 / 막차 (하행)05:25 / 23:5505:00 / 23:30
배차 (첨두 / 비첨두)6분 / 15분5분 / 15분
운수사경기여객대원버스
노선 종류직행좌석형시내버스 — 입석이 없다
순번 축 · 회차 순번 이하가 상행(UP), 초과가 하행(DOWN) 1650 89 정류장 회차 44 UP · 순번 1~44 DOWN · 순번 45~89 구리수택차고지 안양역 구리수택차고지 3330 85 정류장 회차 43 UP · 순번 1~43 DOWN · 순번 44~85 도촌동9단지앞 안양역 도촌동9단지앞
가로축은 순번을 그대로 비례로 편 것이다. 회차 지점이 두 노선 모두 거의 정확히 중간에 있다 (1650 은 88구간 중 43번째 = 48.9%, 3330 은 84구간 중 42번째 = 50.0%). 기점과 종점은 이름이 같지만 stationId 가 다르고, 두 노선 모두 stationId 중복이 0건이라 정류장은 ID 하나로 유일하게 지목된다.

Q. 정류장의 무엇을 기억하나?

(노선, 정류장, 시간대) 조합마다 두 값을 기억한다.

기억하지 않는 것도 있다. 과거 만석률은 쓰지 않는다. 셀의 87%가 만석을 한 번도 겪지 않아서, 그 값이 사실상 "그 셀을 몇 번 관측했나"의 함수가 되기 때문이다 (만석 0건 셀에서 상관계수 1.000). 그리고 관측 횟수는 수요가 아니라 수집 밀도를 반영한다 — 우리 폴링은 닷새 사이 8.5배 늘었다.

점유율과 순수요는 그 오염이 없다. 휴일에서 평일로 넘어가는 전이도 훨씬 낫다 — 순위 상관이 순수요 0.920, 점유율 0.805인데 만석률은 0.302다.

Q. 노선을 합쳐서 배우나?

아니다. 노선별로 완전히 나눠 배운다. 계수를 공유하지 않는다.

만석 판정은 잔여석 0이라는 정확한 경계 문제이고, 어느 정류장에서 어떤 속도로 차는지가 노선마다 다르다. 합치면 그 차이만큼 편향이 들어간다. 표본이 늘면 분산은 줄지만 편향은 그대로이므로 결국 노선별이 이긴다.

실측으로도 확인했다. 3330 에서 합침 대비 격차가 학습 2일 +0.190 에서 4일 +0.029 로 6.6배 줄었다. 그리고 노선별로 배운 A18 은 현행 기본 모델보다 36칸 전부에서 CRPS 가 낮다. 36칸 모두 부트스트랩 신뢰구간이 0 을 넘지 않는다 — 전환 손해가 없다.

재료는 어떻게 들어오나

출처는 실시간 차량 위치 API 하나다. 이 API 가 한 번에 주는 것은 vehId · stationSeq · remainSeatCnt · lowPlate · crowded · stateCd 뿐이고, 모델의 재료는 전부 여기서 파생된다.

수집 주기는 고정이 아니라 적응형이다

"15초마다 읽는다"는 정확한 말이 아니다. 수집기는 시간대에 따라 주기를 바꾼다 (adaptive-kst-v1.0.1). 만석이 몰리는 시간에 호출을 몰아주고 심야에는 성기게 두되 끊지는 않는다.

구획시각 (KST)관측 간격이유
첨두07–09 · 17–2015초 (1분 안에 4연발)정류장 통과를 놓치지 않아야 라벨이 성립한다
주간09–17 · 20–2360초만석이 드물어 호출을 아낀다
심야23–07600초막차 이후. 차량 0대 응답 자체가 검증에 필요하다

하루 예상 호출 3,816회, 하드 상한 10,000회다. 그래서 코퍼스의 관측 밀도가 균일하지 않다 — 조밀 구간 6,144건, 한산 구간 2,058건으로, 같은 "8정류장 전"이라도 첨두에는 촘촘히 찍히고 주간에는 성기게 찍힌다. 이 페이지의 점수는 그 혼합 위에서 나온 값이다. 60초 구간만 떼어 다시 채점해 본 적은 없다.

관측 시각은 상류 시계를 쓰지 않는다

상류 응답의 queryTime 은 1,294건 전부에서 우리 수신 시각보다 2.2~20.5초 뒤였다. 시계가 어긋나 있어 그대로 쓸 수 없다. 그래서 observedAt우리 수집기가 상류 응답을 받은 시각으로 정의한다.

설계행렬 31열이 어디서 오나

차량에서 오는 것 · 정류장에서 오는 것 · 위치와 시간, 셋으로 나뉜다.

차량에서 오는 것 — 예측 시점의 상태

현재 잔여석 ÷ 정원지금 얼마나 비어 있나. remainSeatCnt / lowPlate
현재 만석 여부이미 0석인가
낮은 좌석 구간20석 이하를 더 촘촘히 본다
혼잡도 4단계crowded — 다만 잔여석의 거친 재부호화라 추가 정보는 거의 없다
정원그 차량이 보여 준 최대 잔여석 그대로. remainSeatCnt 의 차량별 최대 종전에는 lowPlate 로 45·40·68 세 갈래를 붙였는데, 계열 판정이 어긋나는 차량이 있었다 — lowPlate=0 인데 6,334회 관측에서 40 을 한 번도 넘지 않는 차량이 셋, 49 에서 멈추는 단층이 하나. 관측 최대를 그대로 쓰면 계열 판정 자체가 필요 없어지므로 오판도 없다. 말뭉치가 일곱 갈래로 갈리고(44석 388,825행 · 45석 62,404 · 40석 55,263 · 68석 49,303 · 49석 13,435 · 36석 138 · 41석 114) 최빈값은 45가 아니라 44다. 정원 초과로 버려지는 행이 0건이 된다(종전 규칙에서는 222행을 버렸다). 차량의 형식승인 좌석수는 이것과 다른 양이라 합치지 않는다 — 저상 신차는 41 이 유력하다. 종전 47 은 어느 근거에도 없던 값이었다.
상류 좌석 기울기앞선 정류장들에서 좌석이 얼마나 빠르게 줄고 있었나

정류장에서 오는 것 — 그 지점의 이력

직전 도착 차량그 차의 잔여석과 만석 여부
직전 연속 만석최근 몇 대가 연속으로 만석이었나
셀 평균 점유율노선·시간대 구획 안에서 표준화. 학습일만 사용
셀 순수요 구간합통과할 정류장 전체의 합. 대기열 정보가 들어올 자리
셀 결측 지시자이 정류장 이력이 없을 때 켜진다

위치와 시간

정류장 상대위치노선 안 0~1 로 정규화한 뒤 8매듭 곡선. 라벨을 전혀 쓰지 않는다
시간대아침 07–09 · 저녁 17–20 · 그 외
지평지평마다 전용 모델을 따로 적합한다. 1~12 전부와 16 을 적합해 재 봤고, 서빙은 12까지다. 지평 자체는 특징이 아니라 모델을 나누는 축이다

지금은 쓰지 않는 것

과거 만석률실제 경과 시간배차 간격 도착 예정 API날씨요일대기열

실제 경과 시간은 도착한 뒤에만 알 수 있으므로 특징으로 쓰면 미래를 훔쳐보는 것이 된다. 배차 간격은 넣어 봤으나 해로웠다 — 직전 도착 차량 정보와 중복이고, 3330 의 8정류장 아침에서 0.41석 나빠졌다. 도착 예정 API 는 수집조차 하지 않는다. 완벽한 값을 안다고 가정한 오라클 상한이 0.045석에 그쳐 수집 대상에 넣지 않았다. 그래서 이 모델도 API 도 "몇 분 후"를 낼 수 없다 — v3 도 주지 않았고 v4 도 주지 않는다.

Q. 무엇을 못 보나?

센서가 잔여좌석 하나뿐이라는 사실에서 오는 한계가 있다. 두 노선은 직행좌석형이라 입석이 없다 — 이것은 확정 사실이다. 그래서 잔여석 0은 곧 승차 불가이고, 좌석 감소량이 곧 순 승차 인원이라는 등식은 정확하다. 문제는 다른 데 있다.

못 보는 것
도착 예정 시각도착정보 API 를 수집하지 않는다. 지평은 정류장 수로만 준다
정류장 대기 인원어떤 채널로도 들어오지 않는다. 만석이 새로 생기는 순간을 결정하는 값이다
만석일 때의 초과 수요좌석이 0이면 관측값도 계속 0이다. 센서가 포화된다
승차와 하차의 분리순변화만 보인다. 앞문 승차와 뒷문 하차가 동시에 일어난다
실제 정원의 근거차량마다 그 차가 실제로 보여 준 최대 잔여석이다. 연구 자료와 운영 평가기가 같은 규칙을 쓴다(2026-08-19 양쪽 교정). 관측이 적어 상한에 도달하지 못한 차량은 정원이 과소평가되고 그때 점유율이 위쪽으로 눌린다 — 이 말뭉치에서 가장 낮은 값이 36석(관측 138행)이다
상태 비중 수요 관측 비만석 좌석 있음 → 좌석 있음 98.21% 순변화 관측 이미 만석 만석 → 만석 1.40% 전혀 안 보임 여기서 만석됨 좌석 있음 → 만석 0.23% 하한만 관측 만석 해제 만석 → 좌석 있음 0.16% 하차만 관측
한 정류장 구간 52,199건의 분해. 만석 도착 848건 중 86%가 이미 만석이고 14%만 그 구간에서 만석이 됐다. 8정류장 예보가 맞혀야 하는 것은 후자이고, 그 표본이 119건뿐이다.
만석이 되는 순간 센서가 포화된다

좌석이 0이면 몇 명이 더 타려 했는지 관측값은 계속 0이다. 연속 만석 구간이 중앙값 6개, 최대 12개 정류장이고, 그동안 못 탄 사람은 전부 정류장에 남는다. 그 수를 세는 채널이 없다.

혼잡도 필드로 회복되지도 않는다 — 만석 구간 169개 중 값이 변한 것은 1개(0.6%)뿐이다.

승차와 하차를 따로 보는 것도 불가능하다. 15초 폴링이 도는 첨두 구간에서 정류장 체류의 63.1%를 세 번 이상 관측하는데도, 하차 뒤 승차를 뜻하는 비단조 궤적은 0.55%뿐이다. 앞문 승차와 뒷문 하차가 동시에 일어나기 때문이다. 한 구간 변화의 57.1%가 0인데, 그것이 "아무 일 없었다"인지 "3명 타고 3명 내렸다"인지 구분할 수 없다.

이 수치는 첨두의 조밀한 관측에서 나온 것이다. 60초로 벌어지는 주간 구획에서는 같은 보증이 없다 — 체류를 한 번밖에 못 보는 통과가 늘고, 그만큼 라벨의 검열 폭이 커진다.

회차를 어떻게 다루나

두 노선은 왕복이다. 순번 1~85(3330)·1~89(1650)가 하나의 연속 경로이고 중간의 안양역이 회차 지점이다 — 상류 노선 기준정보의 turnSeq1650은 44, 3330은 43이다. 상류를 직접 조회해 확정했다. v3 초판 예시의 turnSequence: 31 과 3330 의 routeId: 234000016 은 실제와 다른 값이라 2026-08-19 에 43·204000057 로 교정했다.

0.55 0 만석 관측 0건 (순번 28~51) 회차 43 정점 0.49 2 84 52
3330 노선의 순번별 평균 점유율. 승객은 회차 직전 구간에서 서서히 다 내린다 — 회차에서 좌석 상태가 끊기지 않는다. 음영 구간에서는 만석이 한 번도 관측되지 않았다(67,286행 중 0건). 1650 도 같은 모양이다 — 순번 36~51 에서 33,476행 중 0건이고, 52~55 부터 1.890% 로 재개된다 (3330 은 52~55 에서 0.748%). 공백 구간은 회차점보다 넓고, 회차점에 맞춰 대칭이지도 않다.

회차 너머의 버스도 예보한다

재구성한 운행의 순번 폭이 중앙 86/89(1650)·81/85(3330)이고, 회차를 이어서 지난 운행이 94.0%·72.4%다. 회차 전후 관측 간격은 중앙 0.3분 — 차고지에 서지 않고 그냥 지나간다. 회차는 운행의 끝이 아니다. 그러므로 회차 너머 정류장에 대해서도 그 차량은 도착 예정이 맞고, "도착 예정 버스 없음"이라고 쓰면 거짓이 된다. 실제로 API 가 내보내는 도착 항목의 10.0%(1650)·12.8%(3330)가 회차를 넘는 건이다.

다만 근거가 바뀐다

회차를 넘는 순간 차량의 현재 잔여석이 설명력을 잃는다. 도착 잔여석에 대한 R² 다.

지평회차 안 넘음 (R²)회차 넘음 (R²)
2정류장0.8730.370
4정류장0.6450.034
8정류장0.1780.011 (상관이 음수 −0.105 로 뒤집힌다)

그런데도 예보는 맞는다. 회차를 넘는 표본의 평균 점유율이 0.105이고 점유율 0.7 이상은 0건이기 때문이다 — 승객은 이미 다 내린 뒤다. 정류장 통계만으로도 "여기는 만석이 아니다"가 맞는 답이 된다.

지평구분표본실제 만석률예측 평균최대 예측브라이어
8회차 안 넘음37,9191.872%1.534%0.9930.01202
8회차 넘음4,2940.000%0.052%0.0200.00000
12회차 안 넘음33,5161.930%1.878%0.9810.01371
12회차 넘음6,3240.617%0.238%0.3380.00563

회차 너머 예보를 내는 것은 차량 상태가 아니라 정류장 통계다. 답은 맞지만 근거가 다르다.

서빙 상한을 12로 올리면 이 구간이 더는 안전하지 않다. 8정류장에서는 회차 너머 표본이 전체의 10.2%이고 실제 만석이 한 건도 없었지만, 12정류장에서는 15.9%로 늘고 실제 만석률이 0.617%가 된다. 모델은 이 구간에 평균 0.238% 만 얹으므로 과소예측이다. 절대량이 작아 종합 지표에는 거의 안 보이지만 (브라이어 0.00563), 회차 너머만 따로 코호트로 채점해 본 적이 아직 없다 — 아래 「다음 확인 사항」에 남겨 두었다.

모델은 회차점을 입력으로 받지 않는다

turnSeq모델의 특징이 아니다. 설계행렬 어디에도 "회차를 넘는가"라는 열이 없다. 그런데도 위 표처럼 맞는 이유는, 셀 층이 (노선, 정류장, 시간대)별 통계를 학습하므로 앞 그림의 공백 구간을 이미 알고 있기 때문이다. 회차라는 개념을 따로 주입하지 않아도 그 정류장들에는 낮은 만석 확률을 낸다.

turnSeq 가 쓰이는 곳은 두 군데뿐이다 — 순번을 UP/DOWN 으로 가르는 것, 그리고 화면에 "회차 후 도착"을 표시할지 고르는 것. 뒤엣것은 API 필드가 아니다 — 클라이언트가 sequence − horizonStopsturnSequence 와 견줘 직접 계산한다. 그 값이 틀려도 확률은 틀리지 않는다. 틀리는 것은 방향 표시와 회차 표시다.

Q. 노선을 따라가면 무엇이 보이나?

45230 44526070 평균 잔여석 (1650 · 아침) 승차 구간 만석 정체 구간 하차 거점 만석률 0%만석률 24~37%순수요 음수 만석 발생 지점
배경 띠의 주석이 각 구간의 만석률이다. 만석은 노선 전역에서 나지 않고 연속 몇 개 구간에만 발생한다. 8정류장 예보의 성패는 seq 53~55 같은 발생 지점을 맞히는 데 달려 있고, 그 지점의 대기 인원이 우리가 못 보는 값이다.

Q. 지표를 왜 넷이나 보나?

만석이 표본의 1.7%뿐이라 분포 정확도 지표 하나로는 만석 경보의 품질을 못 잰다. 나머지 98.3%가 지표를 지배하기 때문이다.

이번 설계에서 분포 지표와 만석 지표가 정반대를 가리킨 층이 세 번 나왔다. 분포 정확도로는 무의미한데 만석 확률의 정확도로는 가장 크게 기여한 층이 있었고, 그 반대도 있었다. 그래서 네 지표를 함께 보고, 어느 하나만으로 판정하지 않는다는 것을 규칙으로 두었다.

지표재는 것좋은 방향
브라이어확률 예측이 얼마나 정확한가낮게
로그 손실틀린 것을 얼마나 확신했나낮게
보정"30% 확률"이라 한 것 중 실제로 30%가 일어났나0에 가깝게
만석 구분만석인 것을 위쪽에 잘 세우나높게

이 점수는 어디서 나왔나

이 페이지의 숫자는 출처가 다르다

다른 후보들의 점수는 매일 도는 일일 평가가 원장이다. A18 은 아직 일일 평가에 들어 있지 않다. 여기 실린 값은 2026-08-19 설계 세션에서 같은 평가 규약·같은 지표 구현·같은 점수 정책을 그대로 불러 오프라인으로 계산한 것이다. 학습은 앞선 한국 표준시 날짜만, 채점은 뒤 날짜만 쓰는 확장창 롤링 오리진도 동일하다.

같은 규칙으로 쟀지만 원장이 다르므로 다른 후보와 같은 칸에 놓고 순위를 매기지 않는다. 일일 평가에 편입되면 그때부터 다른 후보들과 같은 방식으로 갱신된다.

먼저 알아야 할 것 — 이 모델은 다른 후보들과 재료가 다르다

기존 일곱 후보는 정류장과 시간대로 예측한다. "이 정류장은 화요일 아침에 만석일 확률" 같은 형태다. 버스를 특정하지 않으므로 언제든 답할 수 있다.

A18 은 지금 몇 정류장 앞에 있는 그 버스의 잔여석을 함께 쓴다. 그래서 답이 더 정확하지만, 버스가 특정되고 추적되고 있어야 한다. 더 나은 모델이라기보다 다른 질문에 답하는 모델이고, 점수를 나란히 볼 때 이 조건 차이를 함께 읽어야 한다.

이 페이지가 쓰는 말

채점을 나누는 단위. 노선 2 × 지평 6 × 시간대 3 = 36칸이 기본이고, 분포 정확도는 노선 2 × 지평 6 × 지표 2 = 24칸, 아침 구간만 보면 노선 2 × 지평 6 = 12칸이다. "96 판정"은 이 조합에 검정 종류를 곱한 다중비교 시험군의 크기다
모델에 얹는 재료 묶음 하나. A18 은 여섯 층이다 — 셀 통계 · 정류장 위치 곡선 · 구간합 수요 · 사전확률 이동 · 지평 적응 기준점 · 상대 구간. 위 「어떻게 계산하나」의 네 단계는 계산 순서이고 층과는 다른 축이다
밴드시간대 구획. 아침 07–09 · 저녁 17–20 · 그 외
CRPS분포 예측의 오차. 만석 여부만 보는 브라이어와 달리 좌석 수 분포 전체를 채점하며 단위가 좌석(석)이다. 낮을수록 좋다
판별력만석과 비만석을 가르는 능력. 값이 0.5 부근이면 무작위다. 이 페이지의 「만석 구분」(PR-AUC)과는 다른 지표(ROC-AUC)이므로 값의 크기를 견주지 마라
현행 기본지금 운영 후보로 올라 있는 상호작용 로지스틱 회귀. 차량 상태를 쓰지 않는다. 이 페이지에서 "현행 기본보다 N% 낫다"는 CRPS 기준이다
j12지평 12정류장판 A18. 평가기에 seat_distribution_j12 로 등록돼 있다

오늘 성적

이 페이지의 모든 숫자는 노선 2개 · 닷새(8/14~8/18) · 그중 평일 하루에서 나왔다. 그리고 정원 규칙이 연구 경로와 운영 평가기에서 다르다 — 고치면 아래 점수가 전부 움직인다. 자세한 것은 맨 아래 「다음 확인 사항」에 있다. 확정치가 아니라 현시점 산출로 읽어라.

지평은 목표 정류장 몇 개 전에서 예측하는가다. 멀리서 물을수록 어렵다.

서빙 상한은 12정류장으로 확정했다. 화면에는 12정류장 앞까지의 버스를 담는다. 다만 이 페이지의 상세 수치와 코호트 분해는 8정류장을 보고 기준으로 삼는다 — 앞선 설계 판정이 전부 8에서 이뤄졌고 다른 후보와 비교 가능한 축이 8이기 때문이다. 두 숫자는 역할이 다르므로 섞어 읽으면 안 된다.

2정류장 전 · 중앙 2.3분

83.2

브라이어 0.00551
만석 구분 0.840

4정류장 전 · 중앙 6.5분

77.5

브라이어 0.00775
만석 구분 0.725

8정류장 전 · 중앙 15.3분

69.3

브라이어 0.01079
만석 구분 0.536
보고 기준

12정류장 전 · 중앙 25.0분

71.6

브라이어 0.01242
만석 구분 0.419
서빙 상한 · 채택

종합 점수 0~100. 기준 50점은 기후값이다 — 평가일보다 이른 학습일의 만석률만 쓰는 예측. 100점은 완벽, 기준보다 손실이 두 배면 0점이다. 채점 표본은 이 네 지평에서 3.98~4.54만 건, 만석 686~741건(지평 2에서 45,406/741, 지평 12에서 39,840/686). 열두 지평 전체는 아래 표에 있다.

Q. 12정류장 점수가 8보다 높은데, 더 잘하는 것 아닌가?

아니다. 종합 점수 하나만 보면 안 되는 사례가 바로 이것이다. 종합 점수는 다섯 항목의 가중 평균인데, 8에서 12로 가면 네 항목이 떨어지고 한 항목만 오른다. 오른 한 항목의 가중치가 커서 합이 뒤집힌 것이다.

구성 항목가중8정류장12정류장변화
확률 맞춤2060.689.1+28.5
확률 오차3567.663.5−4.1
만석 구분1576.570.5−6.0
놓침 비용1569.666.0−3.6
과신 벌점1577.173.7−3.4
종합10069.371.6+2.3
종합 점수에 대한 가중 기여 변화 (8정류장 → 12정류장, 점) −10 +1+5 확률 맞춤가중 20 +5.70 확률 오차가중 35 −1.44 만석 구분가중 15 −0.90 놓침 비용가중 15 −0.54 과신 벌점가중 15 −0.51
확률 맞춤 하나가 +5.70점을 올리고 나머지 넷이 합쳐 −3.39점을 깎아 순증이 +2.31점이다. 가중 기여는 (항목 점수 변화 × 가중치 ÷ 100)이며, 합이 69.3 → 71.6 의 차이와 정확히 일치한다.

확률 맞춤이 뛴 이유는 모델이 나아져서가 아니다. 먼 지평에서는 차량 상태가 힘을 잃고 정류장 통계에 기대게 되는데, 그쪽은 정의상 평균이 잘 맞는다. 예측을 평평하게 깔면 보정 지표는 좋아진다. 정작 만석 버스를 골라내는 능력인 만석 구분(PR-AUC)은 0.536 → 0.419로, 점수 환산으로도 76.5 → 70.5 로 계속 떨어진다. 그래서 12를 채택한 근거는 이 종합 점수가 아니라 커버율이다(아래).

코호트로 쪼개면

합친 점수는 서로 반대인 구간을 상쇄한다. 그래서 시간대와 요일로 쪼개 본다.

기준 50 100 전체 69.3 아침 통근 81.2 저녁 통근 74.0 그 외 시간 64.1 평일 75.6 주말·공휴일 72.0
8정류장 지평(보고 기준)의 코호트별 종합 점수. 기준선 50점은 기후값 (평가일보다 이른 학습일의 만석률만 쓰는 예측)이다. 아침 통근이 가장 높고 한산한 시간대가 가장 낮다 — 만석이 드문 구간에서는 기후값도 잘 맞으니 격차가 줄어든다.
코호트8정류장(보고 기준)12정류장(서빙 상한)표본(8정류장)
전체69.371.642,233
아침 통근81.277.05,569
저녁 통근74.070.68,825
그 외 시간64.166.827,839
평일75.675.017,674
주말·공휴일72.067.024,559

두 열을 섞어 읽지 마라. 전체는 12정류장이 높은데 아침·저녁·평일·주말은 8정류장이 높다 — 종합 점수의 보정 항목이 코호트마다 다르게 움직이기 때문이다. 어느 코호트에서도 만석 구분은 12정류장이 낮다.

종합 점수는 어떤 재료로 만들어지나

다섯 항목은 아래 원지표에서 그대로 온다. 각 항목을 기준(기후값) 50점 · 완벽 100점 · 기준보다 손실 두 배면 0점으로 환산한 뒤 가중합한다. 항목 이름은 화면용 우리말이고, 괄호 안이 실제로 계산되는 값이다.

항목원지표좋은 방향가중치8정류장 원값→ 환산 점수
확률 오차brier — 확률 예측의 평균 제곱오차낮을수록350.01079467.6
확률 맞춤calibration_biasecce_mad — 0에 가까울수록절댓값이 작을수록20−0.002983 · 0.00325060.6
만석 구분pr_auc — 만석을 위쪽에 세우는 능력높을수록150.53637076.5
과신 벌점log_loss — 틀린 확신에 큰 벌점낮을수록150.04079277.1
놓침 비용cost_4 — 만석을 놓치는 쪽에 4배 비중낮을수록150.04088869.6

가중합하면 67.6×0.35 + 60.6×0.20 + 76.5×0.15 + 77.1×0.15 + 69.6×0.15 = 69.26 이고 게시값 69.3 과 맞는다. 같은 이름이 두 눈금으로 나오는 자리가 있다 — 성적 카드의 "만석 구분 0.536"은 원값(PR-AUC)이고, 위 표의 76.5는 그것을 환산한 점수다. 같은 항목이니 헷갈리지 마라.

규약 출처는 score_policy.json(composite-score-v1)이며 기준 예측은 각 평가일보다 이른 학습일의 만석률만 쓰는 값이다. 종합 점수는 규약 자신이 "설명과 비교를 돕는 보조값이며 자료·재현성 관문을 대신하지 않는다"고 적어 둔 값이다.

8정류장 지평(중앙 15.3분) · 원지표 — 보고 기준

지표A18기준(기후값)
브라이어0.0107940.016633확률 예측의 평균 제곱오차. 낮을수록 좋다
로그 손실0.0407920.089082과신에 큰 벌점. 낮을수록 좋다
보정 편향−0.002983−0.002452예측 확률 평균과 실제 발생률의 차. 0에 가까울수록 좋다
누적 보정 오차0.0032500.009023확률 구간별로 얼마나 어긋나는지
만석 구분(PR-AUC)0.5363700.015509만석 경보의 순위 품질. 높을수록 좋다
ROC-AUC0.9735170.359644만석과 비만석을 가르는 능력
놓침 비용0.0408880.067278만석을 놓치는 쪽에 4배 비중을 둔 비용

기준의 ROC-AUC 가 0.5 아래인 것은 기후값이 날짜별 상수라 날짜 사이 순서가 뒤집혔기 때문이다. 같은 날 안에서는 모든 예측이 같은 값이므로 순위를 매길 수 없다.

지평을 어디서 끊나

몇 정류장 전까지 예보할지는 얼마나 많은 정류장을 덮느냐만석 버스를 골라내는 능력이 얼마나 남느냐의 교환이다. 덮는 범위는 실제 운행 기록에서, 골라내는 능력은 채점에서 나온 값이다.

지평중앙 소요정류장 커버율만석 구분무작위 대비판정
8정류장15.3분79.2%0.53631배보고 기준
12정류장25.0분86.3%0.41924배서빙 상한 · 채택
16정류장34.0분87.4%0.32517배기각

8에서 12로 늘리면 커버율이 7.1%p 오른다. 12에서 16으로 더 늘리면 1.1%p 뿐인데 골라내는 능력은 24배에서 17배로 떨어진다. 12에서 끊는다. 무작위 대비는 만석 구분(PR-AUC)을 기저 만석률로 나눈 값이고, 기저 만석률은 지평별 1.68~1.86%다.

이 판정에 종합 점수는 쓰지 않았다. 여기서 교환하는 것은 덮는 범위와 골라내는 능력이다.

커버율은 실제 운행 기록으로 쟀다 — 어느 순간을 잡아 노선의 모든 정류장을 보고, 그 뒤 지평 안에 같은 운행의 버스가 있는 정류장의 비율이다. 표는 하루 전체(심야 포함) 기준이고, 운행 시간대(06~23시)로 좁히면 지평 12에서 90.4%(1650)·91.1%(3330)다.

커버율의 상한은 순번 구조가 정한다

지평 12 로는 순번 12 이하 정류장에서 뒤로 볼 수 있는 칸이 순번−1 개뿐이다. 그런 정류장이 두 노선 각각 12개, 전체 174개 중 13.8% 다. 실제로 그 구간의 커버율은 37~44% 로 떨어지고, 나머지 구간은 98.7~98.9% 다. 커버율을 더 올리는 길은 지평을 늘리는 것이 아니라 이 구간을 따로 푸는 것이다.

구간정류장 비중지평 12 커버율(운행 시간대)
순번 13 이상86.2%98.7% · 98.9%
순번 12 이하13.8%37.4% · 43.6%

한 바퀴 돌아 오는 차량은 담지 않는다

순번 12 이하에서 빈칸을 메우려면 이미 종점 쪽에 있는 차량이 한 바퀴 돌아 오는 것을 세면 된다. 커버율이 +5.6%p 오른다. 그런데 재보니 그 차량이 실제로 그 정류장에 다음으로 도착하는 차량인 경우가 5.1%(1650)·15.6%(3330) 뿐이었다. 그 앞에 중앙 4~7대가 먼저 지나간다.

진짜 이유는 이렇다 — 그 정류장에 실제로 다음 오는 버스는 중앙 8.3~12.0분 뒤에 도착하는데, 그 차는 96.2~96.8%의 경우 차고지에서 대기 중이라 차량위치 API 에 아직 없다. 버스는 마지막 정류장을 한 번도 보고하지 않고 그 직전에서 피드에서 사라지며, 다시 나타나기까지 중앙 65분(1650)·92분(3330)이 걸린다.

그래서 랩어라운드를 담으면 커버율은 오르지만 가리키는 대상이 틀린다. 중앙 86~97분 뒤에 오는 엉뚱한 차량을 "다음 버스"로 보여주게 되고, 지평 12의 시간 규모(24~28분)를 3.5~4배 넘는다. 담지 않는다. 그 구간은 차고지 발차 시각이 있어야 풀리는 별개 문제로 남긴다.

지평은 시간이 아니라 정류장 수다

둘은 서로 환산되지 않는다. 표에 적은 "중앙 소요"는 사후에 잰 중앙값일 뿐 변환식이 아니다. 같은 8정류장이 10%분위 9.1분에서 90%분위 23.0분까지 2.5배로 흩어지고 (25%분위 12.0분 · 75%분위 19.0분), 거꾸로 5~10분 구간에는 2·3·4·6·8정류장이 모두 섞여 있다 (4정류장 38%, 3정류장 26%, 6정류장 23%). 정체 여부에 따라 달라지기 때문이다.

그런데도 정류장 수로 정의하는 이유는, 걸린 시간은 도착한 뒤에만 알 수 있는 값이라 예측에 쓰면 미래를 훔쳐보게 되기 때문이다. 모델은 이 시간 차이를 입력으로 받지 않는다. 화면에 "몇 분 후"를 띄우려면 도착정보 API를 따로 불러야 하는데 그것은 지금 수집 대상이 아니다. 그래서 API 도 horizonStops(몇 정류장 남았나)만 주고 도착 예정 시각은 주지 않는다.

지평 1~12 전부의 성적

클라이언트 계약이 1정류장부터 12정류장까지 예보를 약속하므로, 그 열두 지평을 모두 적합하고 같은 규약으로 채점했다. 처음에는 1·2·3·4·6·8 만 있었고 5·7·9·10·11 은 표본조차 없었다 — 계약이 근거 없는 약속을 하고 있던 셈이라, 자료를 다시 만들어 메웠다.

95 65 1.00 0.35 124681012 지평(정류장 수) 바닥 69.3 종합 점수(왼쪽 축) 만석 구분(오른쪽 축)
두 선이 다르게 움직인다. 만석 구분은 열두 지평 내내 한 번도 반등하지 않고 0.929에서 0.420까지 떨어진다. 종합 점수만 8정류장에서 바닥을 치고 오르는데, 그것은 모델이 좋아져서가 아니라 보정 편향이 −0.0030에서 −0.0007로 줄어든 몫이 가중합에서 앞서기 때문이다. 종합 점수 하나만 보면 거꾸로 읽힌다.
지평중앙 소요종합 점수브라이어만석 구분보정 편향채점 표본
10.3분90.30.002850.9295-0.001447,397
22.3분83.20.005500.8402-0.002345,420
34.3분79.90.006920.7761-0.002545,067
46.5분77.50.007740.7258-0.002544,532
58.7분74.80.008540.6804-0.003043,909
611.0분72.80.009250.6400-0.003243,354
713.0분71.10.010020.5951-0.003142,763
815.3분69.30.010770.5377-0.003042,233
918.0분69.50.011290.4966-0.002641,672
1020.0분70.00.011770.4596-0.001941,092
1122.6분72.10.012150.4318-0.000840,524
1225.0분71.60.012410.4194-0.001139,864

브라이어·로그 손실·만석 구분은 열두 지평 모두에서 단조다 — 멀수록 나빠진다. 꺾이는 것은 종합 점수뿐이고 그 원인은 보정 항목이다. 표본이 지평마다 줄어드는 것은 목표 정류장 뒤로 그만큼의 관측 이력이 필요하기 때문이다. 서빙 상한 12 의 근거는 위 「지평을 어디서 끊나」에 있다.

Q. 언제 실패하나?

Q. 어떻게 검증했나?

채택 규칙을 결과를 보기 전에 문서로 고정하고 그대로 적용했다.

규칙기준실측
유의하게 나빠진 칸0건 / 96 판정0건
분포 정확도 개선24칸 중 20칸 이상24/24, 전부 유의
아침 밴드 개선12칸 전부12/12
만석 확률 정확도나빠진 칸 없음0칸
결정성같은 입력이면 같은 출력두 노선 통과

여기에 네 가지 반증 시험을 더 걸었다.

이번에 확정한 것

다음 확인 사항