못 본 것도 세기보정 적용
구간 검열 반영
못 본 통과를 지우지 않고 계산에 남긴다.
일곱 갈래로 만들어 겨루는 중이다. 아직 고르지 않았다. 아래는 무엇을 계산하는지와 어디까지 검증됐는지만 적었다. 원리와 재료는 각 모델 페이지에 있다.
모든 모델이 공통으로 답하는 것 — 기능 정의
지금 이 정류장으로 오고 있는 그 버스가, 이 정류장을 출발할 때 자리가 하나도 안 남아 있을 확률.
p_full = P(출발 후 잔여석 = 0) · 화면에 나가는 값 p_board = 1 − p_full
일곱 모델은 전부 이 하나를 서로 다른 방법으로 계산한다. 아래 정의가 어긋나면 모델이 아니라 정의를 고친다.
조회 한 번이 만드는 판정은 정확히 하나다.
| 판정 단위 | (노선, 정류장, 그 정류장에 다음으로 올 차량) 하나 |
|---|---|
| "다음 버스"의 정의 | 목표 정류장을 아직 지나지 않은 차량 중 축거리가 가장 가까운 하나.
last_passed < station_seq인 차량들 중 station_seq − last_passed 최소 |
| 동률이면 | 덜 진행한 쪽을 고른다 — 출발 < 교차로 < 도착. 그래도 같으면 나중 여정, 그래도 같으면 차량 ID 사전순 |
| 다음 버스가 없으면 | 판정하지 않는다. upstream_bus_exists = 0으로 남긴다 |
| 대상 노선 | 경기 광역버스 1650 · 3330 두 개. 상·하행이 한 축이고 회차 순번으로 방면을 가른다 |
| 거리 세는 법 | 내부 계산은 축 순번, 화면 표시는 정차 정류장만 센다. 둘을 섞지 않는다 |
라벨은 예측이 맞았는지 채점하는 정답이다. 이 정의가 전부의 기준이 된다.
| 만석 (1) | 그 차량이 목표 정류장을 출발한 직후 관측한 잔여석이 0 |
|---|---|
| 여유 (0) | 같은 자리에서 잔여석이 양수 |
| 제외 | 잔여석 -1은 좌석 정보 미제공이다. 0으로 바꾸지 않는다 — 바꾸면 가짜 만석이 생긴다 |
| 인정하는 관측 세 가지 | 정류소 출발(stateCd=2, 순번 = S) ·
교차로 통과(stateCd=0, 순번 = S) · 다음 정류소 도착(stateCd=1, 순번 = S+1).
이 셋이 아니면 라벨로 쓰지 않는다 |
| 왜 도착이 아니라 출발인가 | 도착 시점 0은 만차가 아니다. 하차하면 자리가 생긴다. 실측으로도 도착 시점 만석 3.02%(23/762) 대 출발 이후 11.95%(399/3,339)로 네 배 차이가 난다 직접 수집 176호출. 도착 시점으로 재면 "거의 항상 탈 수 있다"고 잘못 알려주게 된다 |
| 입석은 | 법으로 금지다. 그래서 잔여석 0 = 못 탐이 성립한다. 타는 것과 앉는 것이 같은 말이다 |
여기가 가장 헷갈리는 자리다. 확률에는 사람이 하나 전제돼 있다.
| "나"의 정의 | 그 정류장에 새로 도착해 줄 맨 뒤에 서는 한 사람 |
|---|---|
| 왜 맨 뒤인가 | 자리 C, 원래 대기 D, 출발 후 잔여석 R = max(C−D, 0)이다. R ≥ 1이면 나는 어디 서든 탄다. R = 0이면 맨 뒤에 선 나는 확실히 못 탄다. 이 둘이 합쳐져 P(못 탐) = P(R = 0)이 정확히 성립한다 |
| 이미 줄에 서 있던 사람은 | 우리 값이 실제보다 비관적이다. R = 0이어도 앞쪽에 섰다면 탔을 수 있다. 예보로서는 안전한 쪽으로 틀린다 |
| 대기 인원은 어디에 | 이미 R에 흡수돼 있다. 그날 사람이 많았으면 R이 작게 나온다. 따로 대기자 수를 몰라도 되는 이유다 |
| 깨지는 경우 | 예보를 본 여러 명이 동시에 같은 정류장으로 몰릴 때. 그때는 각자가 "맨 뒤 한 명"이 아니게 된다 |
라벨을 재는 시점과 예측하는 시점은 다르다.
| 예측 시점 | 사용자가 조회하는 그 순간. 버스는 아직 정류장 상류에 있다 |
|---|---|
| 라벨 재는 시점 | 그 버스가 정류장을 출발한 뒤. 학습할 때 정답을 확인하는 자리일 뿐이다 |
| 그때 아는 것 | 그 버스의 현재 잔여석·위치·상태, 앞차가 그 정류장을 어떻게 지났는지, 그 정류장 × 시간대의 과거 위험, 계획 배차간격 |
| 모르는 것 | 지금 그 정류장에 몇 명이 서 있는지, 몇 명이 내릴지 |
| 시각 축 | 우리 응답 수신 시각 하나뿐이다. 서버가 준 queryTime은
우리 수신 시각보다 항상 미래이고 되감김·점프가 있어 계산에 쓰지 않는다 |
| 모델마다 다른 점 | 일곱 중 셋(못 본 것도 세기 · 재료 다 넣기 · 한계 재보기)은 그 버스의 현재 잔여석을 실제로 본다. 나머지 넷은 정류장 × 시간대 평균만 쓴다 |
헷갈리기 쉬운 것들을 명시적으로 빼둔다.
crowded는 별도 필드이고 지금은 재료 후보일 뿐 정답이 아니다정직하게 적어둔다. 여기가 다음 기능이 들어올 자리다.
지금 어디까지 왔나
직접 수집분만으로 한 번 돌렸고, 순위는 못 가렸다.
라벨 1,524건·만석 54건, 검증분 443건·만석 14건으로 일곱을 겨뤘더니
AUC 21쌍 중 16쌍이 구분되지 않았다. 승자를 선언하지 않았다.
다만 코퍼스로 낸 순위가 뒤집힌 쌍도 0개다. 방향은 틀리지 않았고 미세한 차이만 미지다.
가리려면 표본이 더 필요하다 — ΔAUC 0.05 문턱이 8 수집일, 날짜 변동까지 보려면 15일이 더 든다.
출퇴근 수집을 1분으로 좁혀 라벨 생산 속도를 올렸다.
잠정이다. 아래 수치는 남의 20일 코퍼스로 낸 것이고, 기준선(정류장·시간만) 대비다.
솔직하게 1순위 지표(확률의 정직함)에서는 기준선을 확실히 이기지 못했다.
그런데도 앞에 둔 건 나머지 세 지표가 모두 유의하게 좋아졌기 때문이다.
2등인 재료 다 넣기는 빗나감·벌점·안전성이 오히려 더 좋다. 그런데 뒤로 뒀다 —
확률 70~80% 구간에서 평균 .727이라 말한 상황이 실제로는 .870이었다(n=177).
가장 조심해야 할 구간에서 사용자를 안심시키는 셈이다.
직접 수집분으로 다시 돌리니 이 순위를 재현도 반박도 못 했다. 21쌍 중 16쌍이 구분되지 않았다.
뒤집힌 쌍은 0개다.
기준선 셋을 먼저 세웠다. 검증 10,040건 · 만석 583건 · 부트스트랩은 정류장×날짜 644군집. 보정도(ECE)가 1순위 지표다 — 이 확률을 사용자에게 숫자로 그대로 보여주기 때문이다.
| 방법 | 확률이 정직한가 ECE · 낮을수록 좋음 | 얼마나 빗나갔나 Brier · 낮을수록 | 확신하고 틀리면 벌점 log loss · 낮을수록 | 만석을 골라내나 AUC · 높을수록 |
|---|---|---|---|---|
| 아무것도 안 보고 평균만 말하기 | 0.0004 | 0.0547 | 0.2216 | 0.5 |
| 앞차 한 대만 보고 말하기 | 0.0021 | 0.0452 | 0.1793 | 0.7187 |
| 어느 정류장·몇 시인지만 보고 말하기 | 0.0063 | 0.0327 | 0.1033 | 0.9613 |
정류장 × 시간대가 앞차 상태만 보는 것보다 훨씬 강했다(AUC .72 → .96). 어느 정류장에서 몇 시에 기다리느냐가 앞차가 만석이었는지보다 중요하다는 뜻이다. 후보들은 전부 이 선을 넘어야 한다.
통계 용어 그대로는 대화가 안 된다. 각 모델이 실제로 하는 일로 부르기로 했다. 괄호는 원래 이름.
숫자 네 개가 각각 다른 걸 잰다. 하나만 좋아도 소용없다.
"70%"라고 말한 상황들에서 실제로 70%쯤 만석이었나. 어긋나면 값이 커진다. 이게 1순위다 — 이 확률을 사용자에게 숫자로 그대로 보여주기 때문이다. 90%라고 해놓고 절반만 맞으면 앱을 못 믿는다.
예측한 확률과 실제(만석 1 / 아님 0)의 차이를 제곱해 평균낸 값. 전반적으로 얼마나 틀렸는지.
"99% 안전"이라 했는데 만석이면 크게 깎는다. 어정쩡하게 틀린 것보다 자신 있게 틀린 걸 더 나쁘게 본다.
만석인 경우와 아닌 경우를 아무거나 하나씩 뽑았을 때, 만석 쪽에 더 높은 확률을 준 비율. 0.5는 동전 던지기, 1.0은 완벽. 순서를 잘 매기는지만 보고 숫자 자체의 정직함은 안 본다.
한 가지 짚고 갈 것 지금 성적은 전부 레스의 20일 코퍼스로 낸 것이다. 그 데이터는 차량 ID가 가명이라 정원·차종을 붙일 수 없었고, 그래서 정원을 쓰는 방식은 아직 테스트해 본 적이 없다. "쓸모없어서 뺀 것"이 아니라 "붙일 수가 없었던 것"이다. 우리가 직접 모으는 데이터는 원본 차량 ID를 남기므로 정원이 붙는다(40/41, 97.6%). 표본이 쌓이면 다시 겨룬다.
초록 칸은 정류장×시간대 기준선보다 좋은 점수다. "보정"은 isotonic 후처리를 얹은 값.
구간 검열 반영
못 본 통과를 지우지 않고 계산에 남긴다.
로지스틱 + 상호작용
관측 특징의 조합을 직접 점수화한다.
GAM / 스플라인
가까운 정류장과 시간을 매끈하게 잇는다.
경험적 베이즈 축소추정
얇은 셀을 노선 평균 쪽으로 당긴다.
계층 로지스틱 (부분 풀링)
셀 효과를 나눠 쓰며 부분 풀링한다.
모델 위에 확률 보정층을 얹는다.
Gradient boosting
복잡한 모델이 낼 수 있는 상한만 잰다.