후보 모델

한계 재보기

Gradient boosting

허용된 재료의 최고 기록만 재는 비운영 비교자다.

확률이 정직한가0.0053얼마나 빗나갔나0.0155확신하고 틀린 벌점0.0524만석을 골라내나0.9910

1. Q. 정류장·시간만 봐도 AUC .9613인데, 왜 더 복잡하게 보나?

노선×정류장×30분 셀은 833개다. 이 중 281개(33.73%)는 학습 표본이 10건 이하다. 719개(86.31%)는 만석을 한 번도 못 봤다. 셀 비율만으로는 얇은 칸을 0% 위험으로 굳히기 쉽다.

Gradient boosting에는 허용된 신호를 한꺼번에 넣었다. 위치와 시간뿐 아니라 앞선 버스의 절대 잔여석, 직전 통과, 헤드웨이 구간, 노선 전체 좌석 분포까지 준다. 목적은 배포가 아니다. 다른 운영 후보가 놓친 신호가 얼마나 남았는지 잰다.

오픈북 모의고사와 같다. 허용된 재료는 다 펼치지만, 그 답안지를 운영안으로 내지는 않는다.

보조 코퍼스의 시간순 검증 10,040건 중 만석은 583건이었다. isotonic 보정 뒤 이 모델은 Brier .0155, log loss .0524, AUC .9910을 냈다. 잠정 운영 후보인 구간 검열 모형은 .0268, .0878, .9751이었다. 차이는 남았다. 직접 수집분 validation 443건·만석 14건에서는 이 모델이 Brier .0144, log loss .0731, AUC .9595, 구간 검열이 .0244, .0987, .9093이었다. 다만 두 모델의 차이는 이 표본으로 가리지 못한다. train과 validation이 같은 날짜라 isotonic 보정 fold가 없었고 양쪽 예측 모두 raw다.

여기서 상한은 수학적 한계가 아니다. 이번 재료, 이번 구현, 이번 검증에서 찍힌 참조 기록이다.

상한 측정 파이프라인
가용 특징 46개
반복 분할
작은 나무 250개 잎≤63·잎 표본≥30
오차 보정값 합산
합산 log-odds
sigmoid
raw p_full
OOF 보정
isotonic 58개 임계점
사용자 확률로 뒤집기
p_board=1-p_full
상한 측정 전용 운영 후보 아님

허용 특징 46개가 250개 나무를 거쳐 raw 만석확률이 되고, 시간순 OOF isotonic이 눈금을 다시 맞춘다.

2. 원리

Q. 나무 한 그루는 무엇을 하나?

한 그루는 짧은 예·아니오 질문을 이어 붙인다.

질문을 통과한 행들은 같은 잎에 모인다. 잎에는 만석 여부를 바로 적지 않는다. 현재 점수를 올리거나 내릴 보정값을 적는다. 숫자 결측은 억지로 0이나 평균으로 채우지 않는다. 나무가 결측을 어느 쪽으로 보낼지도 학습한다.

Q. 왜 한 그루로 끝내지 않나?

첫 나무가 크게 틀린 행을 다음 나무가 다시 본다. 그다음 나무도 남은 오차를 본다. 이 구현은 log loss를 줄이는 작은 나무 250개를 순서대로 더한다.

F_m(x) = F_{m-1}(x) + 0.05 h_m(x)

마지막에는 p_full = 1 / (1 + exp(-F_250))로 만석확률을 만든다. 250개 나무의 다수결이 아니다. 앞선 답을 뒤 나무가 조금씩 고치는 누적 계산이다. 조기 종료는 쓰지 않았다. 250개를 끝까지 더했다.

Q. 얇은 셀은 어떻게 다루나?

정류장×시간 셀을 따로 외우는 규칙은 없다. 대신 한 잎에 최소 30건이 들어가야 한다. 나무 한 그루의 잎은 최대 63개다. 잎 보정값에는 L2 10을 건다. 얇은 셀 하나만으로 잎을 만들지 못하고, 같은 갈림길을 지난 다른 정류장·시각과 묶인다.

이것이 boosting식 풀링이다. 누구와 묶을지 미리 정하지 않고 잔여석·위치·시간의 경계로 정한다. 다만 나무 250개의 잎을 겹치면 최종 구역은 30건보다 훨씬 얇아질 수 있다. 규제가 있어도 운영 모델로 바로 쓰지 않는 이유다.

범계역 한 행의 확률 변화
시작.0584
첫 잎 +.5084 log-odds
1개.0935
49개 보정
50개.8095
100개 보정
150개.9365
100개 보정
250개 raw.9114
확률 눈금 교정
isotonic.8577
1-p_full
탈 확률.1423

2026-08-06 07:28:58 KST, 3330번 롯데백화점.범계역 검증행의 실측 계산이다.

3. Q. 실제 한 건은 어떻게 흘렀나?

아래 사례는 직접 수집분이 아니라 보조 코퍼스의 검증행이다.

항목실제 값
질의2026-08-06 07:28:58 KST
장소3330번 롯데백화점.범계역, 축 순번 55
최근접 상류 버스축거리 1, stateCd=0, 잔여석 0
상류 버스 좌석 변화분당 -6.6165석
직전 통과만석, 출발 후 잔여석 0
노선 응답차량 22대, 만석 3대, 평균 잔여석 29.1364석
계획 배차5분
실제 정답이 버스도 출발 후 만석

학습분의 만석은 705/12,065건이었다. 시작 log-odds는 ln(705/11,360) = -2.7797, 만석확률은 .0584다.

첫 나무에서 이 행은 상류 잔여석 0 ≤ 1.5, 정류장 순번 55 ≤ 64를 차례로 통과했다. 학습률을 반영한 잎 보정값은 +.5084였다.

F_1 = -2.7797 + .5084 = -2.2712 → p_full = .0935
단계만석확률
시작값.0584
나무 1개 뒤.0935
나무 50개 뒤.8095
나무 150개 뒤.9365
나무 250개 뒤 raw.9114
isotonic 보정 뒤.8577
사용자용 p_board=1-p_full.1423

250개 잎 보정값의 합은 +5.1104였다. 최종 점수는 2.3307, raw 만석확률은 .9114가 됐다. 나무가 늘수록 확률이 한 방향으로만 움직이지는 않는다. 150개 뒤 .9365에서 마지막에는 .9114로 내려왔다.

train 내부 시간순 OOF 10,236건 중 만석 572건으로 맞춘 58개 임계점의 isotonic 보정기가 .9114를 .8577로 바꿨다. 실제 출발은 만석이었다. 한 건을 맞힌 사례일 뿐, 이 한 줄이 성능 증거는 아니다.

4. Q. 무엇을 넣고, 무엇을 뺐나?

최종 입력은 46열이다. 문자열은 노선과 원천 표시만 숫자로 바꿨다. 원천별로 따로 적합하므로 원천 표시는 한 적합 안에서 변별력이 없다.

묶음실제 입력넣은 이유
축·시각노선, 정류장 순번, 로컬 분·시·30분대·요일·날짜 인덱스, 방향·회차, 축거리, 계획 배차, 직전 폴 간격만석 위험의 1차 구조가 위치와 시간에 있었기 때문
상류 버스존재, 축거리, 동률 수, 절대 잔여석, 상태, 좌석 변화율, 축 속도곧 올 관측 버스의 현재 상태를 쓰기 위해
통과 버스존재, 축거리, 동률 수, 절대 잔여석, 상태방금 지나간 공급 상태를 남기기 위해
통과 이력직전 통과의 잔여석·만석 여부·나이, 최근 통과 나이 구간, 관측 헤드웨이 구간, 공간 헤드웨이점시각을 만들지 않고 과거 공급 간격을 쓰기 위해
노선 응답 요약차량 수, 좌석값이 있는 차량 수, 만석 대수, 잔여석 평균·표준편차·최솟값·p10·중앙값·p90한 버스 밖의 노선 전체 혼잡 국면을 잡기 위해

안 넣은 재료도 명확하다.

아직 못 써본 재료이유
정원·점유율라벨은 출발 후 잔여석 0 하나로 정해진다. 코퍼스 가명 차량과 외부 vehId의 교집합은 0/77이다. 관측 최대는 정원의 하한일 뿐이라 대리 정원도 쓰지 않는다.
crowded 혼잡도대상 노선 유형 11은 공식 제공 유형 13·15·23 밖이다. 직접 수집분에서 같은 잔여석에 다른 코드가 붙은 값이 26개라 잔여석만의 함수는 아니다. 다만 같은 (vehId, remainSeatCnt)에서는 충돌이 0셀이고 산식도 미확정이라 아직 넣지 않았다.
queryTime·HTTP Date같은 노선 재호출 38쌍 중 queryTime이 7번 되감겼다. HTTP Date도 같은 시계였다. 시간·속도·헤드웨이는 로컬 응답 수신시각만 쓴다.
도착 ETA공급자의 예측값이며 v1 목표가 아니다. 분·초 필드도 6슬롯 중 2슬롯이 단순 나눗셈과 달랐다.
미래의 출발 후 잔여석입력에 넣으면 정답 누수다. 출발 후 0/양수는 label_full을 만드는 데만 쓴다.
-1의 0 치환-1은 결측이다. 0으로 바꾸면 가짜 만석이 된다. 결측으로 남긴다.
상한과 운영 후보의 간격
모델ECEBrierlog lossAUC
정류장×시간 기준선 raw.0063.0327.1033.9613
구간 검열 isotonic.0053.0268.0878.9751
GB 상한 isotonic.0053.0155.0524.9910

보조 코퍼스 validation 10,040건·만석 583건·정류소×날짜 644군집의 점추정이다.

5. 그림 명세

6. Q. 언제 무너지고, 프로토타입과 무엇이 다른가?

언제 실패하나?

프로토타입과 뭐가 다른가?

관점프로토타입Gradient boosting 상한
맞히는 것화면 탑승률은 셀의 1-zeroCount/samples; 좌석 예보는 별도 계산다음 관측 버스의 출발 후 label_full 하나
계산현재 잔여석에서 정류장별 정규 수요를 빼며 0~80석 분포 전파46개 입력을 250개 나무로 나눠 만석 log-odds 합산
얇은 셀history 셀 비만석률을 그대로 표시잎당 최소 30건·L2 10으로 암묵 풀링
고정 규칙2분/순번, 10석 여유폭, 90분 streak 등데이터에서 분할 경계를 학습. 계획 배차와 로컬 시각은 입력으로만 사용
확률 보정별도 보정층 없음train 내부 시간순 OOF 10,236건으로 isotonic 적합
쓰임사용자 화면 기능비교표 전용. 배포·채택 대상 아님

프로토타입의 좌석 예보를 더 복잡하게 만든 모델이 아니다. 질문부터 바뀌었다. 이 모델은 몇 석 남는지, 몇 분 기다리는지, 몇 대 보내는지 답하지 않는다. 운영 후보가 상한과 얼마나 벌어졌는지만 잰다.

재료 한눈에

노선·원천 표시, 정류장 순번, 로컬 분·시·30분대·요일·날짜 인덱스계획 배차간격, 직전 폴 간격, 방향·회차·내부 축거리상류·통과 버스의 존재·축거리·동률·절대 잔여석·상태와 상류 버스의 좌석 변화·축 속도직전 통과의 잔여석·만석 여부·나이, 최근 통과·헤드웨이 구간, 공간 헤드웨이노선 응답의 차량 수·알려진 좌석 수·만석 대수·잔여석 평균·표준편차·최솟값·p10·중앙값·p90정원·점유율crowded 혼잡도queryTime·HTTP Date도착 ETA미래의 출발 후 잔여석-1의 0 치환

아직 못 써본 것과 그 이유

주의 — 이건 "검증해서 버린 재료"가 아니다

아래 성적은 남의 20일 코퍼스로 낸 것이다. 그 코퍼스는 차량 ID가 가명이라 정원·차종·저상 여부를 붙일 수 없었다(조인 0/77). 이 재료들은 테스트 자체를 못 했다.

우리 직접 수집분에는 원본 차량 ID가 남아 정원이 실제로 붙는다 — 고유차량 55/62대(88.7%). 다만 1회차 재대결에서 정원·차종을 넣어도 뚜렷한 개선은 확인되지 않았다. 표본이 얇아 판정이 안 될 뿐, 쓸모없다는 뜻은 아니다. 관측 최대 잔여석으로 정원을 추정하는 것은 계속 금지다 — 원본 ID를 써도 그 값은 관측할수록 올라가서 정원이 아니다.

직접 수집분으로 다시 잰 성적 1회차

우리 수집기가 직접 쌓은 데이터만 쓰고 코퍼스는 뺐다. 검증분 443행·만석 14건, 정류소×날짜 125군집 부트스트랩 5,000회.

지표직접 수집분 (95% 구간)코퍼스
확률이 정직한가0.0165[0.0073, 0.0297]0.0053
얼마나 빗나갔나0.0144[0.0052, 0.0249]0.0155
확신하고 틀린 벌점0.0731[0.0255, 0.1282]0.0524
만석을 골라내나0.9595[0.9239, 0.9901]0.9910
이 숫자로 순위를 매기지 마라

표본이 얇아 21쌍 중 16쌍이 구분되지 않았다. 검증 날짜가 하루뿐이라 날짜 간 변동도 못 본다. 21쌍 중 16쌍을 못 가리고 validation 날짜가 1개라 전체 순위를 발표하지 않는다

코퍼스 순위가 뒤집힌 쌍은 0개다. 방향은 틀리지 않았고 미세한 차이만 미지로 남았다. ΔAUC 0.05를 가리려면 8 수집일, 날짜 변동까지 보려면 15일이 더 든다.

주의 — 이번 실행에서는 보정층이 작동하지 않았다. 검증 날짜가 하루뿐이라 적격 보정 fold가 없었다. 이름에 isotonic이 붙어 있어도 위 숫자는 보정 전 예측이다. 보정 효과는 반증된 것이 아니라 아직 재지 못한 것이다.

쓰는 재료 47개 — 출처 필드까지

재료종류어느 API의 어느 필드
목표 축 순번 (station_seq)stateCd=1이면 현재 stationSeq-1, stateCd=0·2이면 현재 stationSeq를 마지막 통과 순번으로 놓는다. 인접 적격 응답에서 이 값이 전진한 끝점을 통과 사건 순번으로 삼는다.주의 — 모델 축은 routestation 전체 순번이다. 학습 행은 서비스 정차점만 남겨도 축 간격에는 미정차·경유 지점이 들어간다.파생msgBody.busLocationList[].vehId, msgBody.busLocationList[].stationSeq, msgBody.busLocationList[].stateCd; routestation.staOrder; S1 station_seqGBIS 버스위치정보 getBusLocationListv2 + GBIS routestation 마스터 + S1 통과 사건 빌더
KST 하루 중 분 (query_minute_of_day)KST 시각에서 hour*60+minute을 계산한다.주의 — msgHeader.queryTime은 쓰지 않는다. 초는 버린다.파생timing.response_received_at.kst, timing.response_received_at.utc; S1 E4 response_received_kst, response_received_utc; 코퍼스 collectedAt; S1 query_minute_of_day우리 수집기 응답 수신시각; 제한 코퍼스는 수집기가 붙인 collectedAt
KST 시 (query_hour)KST 응답 수신시각의 hour를 뽑는다.주의 — msgHeader.queryTime의 시각이 아니다.파생timing.response_received_at.kst, timing.response_received_at.utc; S1 E4 response_received_kst, response_received_utc; 코퍼스 collectedAt; S1 query_hour우리 수집기 응답 수신시각; 제한 코퍼스는 수집기가 붙인 collectedAt
KST 30분대 (query_half_hour)응답 수신시각을 KST로 바꾸고 hour*2 + 1[minute>=30]으로 0~47을 만든다.주의 — msgHeader.queryTime과 HTTP Date는 시간 계산에 쓰지 않는다. 코퍼스 collectedAt은 본문 파싱 뒤 시각이다.파생timing.response_received_at.kst, timing.response_received_at.utc; S1 E4 response_received_kst, response_received_utc; 코퍼스 collectedAt; S1 query_half_hour우리 수집기 응답 수신시각; 제한 코퍼스는 수집기가 붙인 collectedAt
요일 (query_day_of_week)KST 날짜에 Python weekday()를 적용한다. 월요일 0, 일요일 6이다.주의 — 공휴일 표시는 없다. 요일만 구분한다.파생timing.response_received_at.kst, timing.response_received_at.utc; S1 E4 response_received_kst, response_received_utc; 코퍼스 collectedAt; S1 query_day_of_week우리 수집기 응답 수신시각; 제한 코퍼스는 수집기가 붙인 collectedAt
날짜 순번 (query_day_index)KST 날짜에서 2026-07-19를 뺀 일수를 쓴다.주의 — 이 데이터셋의 고정 기준일에 묶인 값이다. 달력 날짜의 보편적 ID가 아니다.파생timing.response_received_at.kst, timing.response_received_at.utc; S1 E4 response_received_kst, response_received_utc; 코퍼스 collectedAt; S1 query_day_index우리 수집기 응답 수신시각; 제한 코퍼스는 수집기가 붙인 collectedAt
계획 배차간격 (master_dispatch_interval_min)현재 구현은 평일 06:30~09:29·17:00~20:29에 peekAlloc, 그 밖 평일에 npeekAlloc을 고른다. 토요일은 satNpeekAlloc, 일요일은 sunNpeekAlloc을 고른다.주의 — 계획값이지 실제 헤드웨이가 아니다. 현재 코드의 피크 조건에 평일 검사가 들어가 토·일 *PeekAlloc 분기는 닿지 않는다. we*도 고르지 않는다.파생route.peekAlloc, route.npeekAlloc, route.satPeekAlloc, route.satNpeekAlloc, route.sunPeekAlloc, route.sunNpeekAlloc, route.wePeekAlloc, route.weNpeekAlloc; timing.response_received_at.kst, timing.response_received_at.utc; S1 master_dispatch_interval_minGBIS route 마스터 + 우리 수집기 응답 수신시각 + S1 특징 빌더
직전 적격 응답 간격 (previous_poll_gap_sec)같은 원천·수집 묶음·노선·KST 날짜·고정 수집창 안의 직전 적격 응답과 초 차이를 낸다. 0<gap<=90인 연결만 쓴다.주의 — 실패·정상 0대 호출은 학습 스냅샷이 아니다. 90초 초과, 날짜 경계, 수집창 경계에서는 연결을 끊는다.파생timing.response_received_at.kst, timing.response_received_at.utc; S1 E4 response_received_kst, response_received_utc; 코퍼스 collectedAt; S1 previous_poll_gap_sec우리 수집기 응답 수신시각 + S1 호출 연결
회차 순번 (turn_seq)대상 노선의 route.turnSeq를 그대로 붙인다.주의 — routestation에서 읽는 값이 아니다.응답 원본route.turnSeq; S1 turn_seqGBIS route 마스터
회차 전 여부 (is_outbound)station_seq<=turn_seq면 1, 아니면 0이다.주의 — 공식 upDown을 그대로 쓴 값이 아니다. 내부 순번축을 회차점에서 둘로 나눈 표시다.파생route.turnSeq, S1 station_seq; S1 is_outboundGBIS route 마스터 + S1 통과 사건
기점부터 축 순번거리 (axis_distance_from_origin_seq)station_seq-1을 계산한다.주의 — 표시용 서비스 정류장 수가 아니라 routestation 전체 순번 차이다.파생routestation.staOrder; S1 station_seq, axis_distance_from_origin_seqGBIS routestation 마스터 + S1 축 빌더
회차점까지 부호 있는 축 순번거리 (signed_axis_distance_to_turn_seq)turn_seq-station_seq를 계산한다.주의 — 회차점을 지나면 음수다.파생route.turnSeq, routestation.staOrder; S1 signed_axis_distance_to_turn_seqGBIS route 마스터 + GBIS routestation 마스터 + S1 축 빌더
기점부터 축 거리 km (axis_distance_from_origin_km)staOrder 순으로 인접한 x,y 좌표의 Haversine 거리를 누적한다.주의 — 도로 주행거리가 아니라 정류장 좌표 사이 대권거리 합이다. 현재 빌더는 station.x, station.y를 읽지 않는다.파생routestation.routeName, routestation.staOrder, routestation.x, routestation.y; S1 axis_distance_from_origin_kmGBIS routestation 마스터 + S1 축 빌더
회차점까지 축 거리 km (axis_distance_to_turn_km)목표 순번의 누적 Haversine 거리와 회차 순번의 누적 거리 차의 절댓값을 쓴다.주의 — 도로망 최단거리나 실시간 이동거리가 아니다.파생route.turnSeq; routestation.staOrder, routestation.x, routestation.y; S1 axis_distance_to_turn_kmGBIS route 마스터 + GBIS routestation 마스터 + S1 축 빌더
상류 버스 존재 (upstream_bus_exists)정규화 마지막 통과 순번이 목표 station_seq보다 작은 차량이 하나라도 있으면 1이다.주의 — API가 노선의 모든 차량을 포착한다는 뜻은 아니다.파생msgBody.busLocationList[].vehId, msgBody.busLocationList[].stationSeq, msgBody.busLocationList[].stateCd; S1 정규화 last_passed; S1 upstream_bus_existsGBIS 버스위치정보 getBusLocationListv2 + S1 스냅샷 특징 빌더
상류 버스 축거리 (upstream_axis_gap_seq)상류 후보 중 목표에 가장 가까운 차량을 고르고 target station_seq-last_passed를 계산한다.주의 — 동률은 출발 stateCd=2, 교차 0, 도착 1 순, 더 최신 운행, 차량키 사전순으로 푼다. 좌석값은 선택 기준이 아니다.파생msgBody.busLocationList[].vehId, msgBody.busLocationList[].stationSeq, msgBody.busLocationList[].stateCd; S1 정규화 last_passed; S1 upstream_axis_gap_seqGBIS 버스위치정보 getBusLocationListv2 + S1 스냅샷 특징 빌더
상류 최근접 동률 수 (upstream_tie_count)최소 upstream_axis_gap_seq에 놓인 후보 차량 수를 센다.주의 — 응답 전체 차량 수가 아니다.파생msgBody.busLocationList[].vehId, msgBody.busLocationList[].stationSeq, msgBody.busLocationList[].stateCd; S1 정규화 last_passed; S1 upstream_tie_countGBIS 버스위치정보 getBusLocationListv2 + S1 스냅샷 특징 빌더
상류 버스 잔여석 (upstream_remain_seats)축거리와 동률 규칙으로 고른 상류 차량의 remainSeatCnt를 붙인다.주의 — -1은 0으로 바꾸지 않고 결측으로 둔다. 이 값은 목표 차량의 미래 라벨이 아니다.파생msgBody.busLocationList[].remainSeatCnt, msgBody.busLocationList[].stationSeq, msgBody.busLocationList[].stateCd, msgBody.busLocationList[].vehId; S1 upstream_remain_seatsGBIS 버스위치정보 getBusLocationListv2 + S1 최근접 상류차 선택
상류 버스 상태 (upstream_state_cd)선택된 상류 차량의 원 stateCd를 붙인다.주의 — 로지스틱은 0·1·2를 원핫 표시로 바꾼다. 상태 의미와 축 위치 정규화는 별개다.파생msgBody.busLocationList[].stateCd, msgBody.busLocationList[].stationSeq, msgBody.busLocationList[].vehId; S1 upstream_state_cdGBIS 버스위치정보 getBusLocationListv2 + S1 최근접 상류차 선택
상류 버스 좌석 변화율 (upstream_seat_change_per_min)선택된 차량을 직전 적격 응답의 같은 차량과 잇고 (현재 좌석-직전 좌석)/(경과초/60)을 계산한다.주의 — 두 좌석값이 비음수이고 0<경과<=90초, 정규화 순번 변화가 0 또는 1일 때만 만든다.파생msgBody.busLocationList[].vehId, msgBody.busLocationList[].stationSeq, msgBody.busLocationList[].stateCd, msgBody.busLocationList[].remainSeatCnt; timing.response_received_at.kst, timing.response_received_at.utc; S1 upstream_seat_change_per_minGBIS 버스위치정보 getBusLocationListv2 + 우리 수집기 응답 수신시각 + S1 차량 연결
상류 버스 축 속도 (upstream_speed_seq_per_min)같은 차량의 정규화 순번 변화량을 (경과초/60)으로 나눈다.주의 — 0<경과<=90초이고 순번 변화가 0 또는 1일 때만 만든다. +2 이상 도약은 속도로 쓰지 않는다.파생msgBody.busLocationList[].vehId, msgBody.busLocationList[].stationSeq, msgBody.busLocationList[].stateCd; timing.response_received_at.kst, timing.response_received_at.utc; S1 upstream_speed_seq_per_minGBIS 버스위치정보 getBusLocationListv2 + 우리 수집기 응답 수신시각 + S1 차량 연결
이미 지난 버스 존재 (passed_bus_exists)정규화 마지막 통과 순번이 목표 station_seq 이상인 차량이 하나라도 있으면 1이다.주의 — 그 차량이 사용자가 탈 다음 차량이라는 뜻은 아니다.파생msgBody.busLocationList[].vehId, msgBody.busLocationList[].stationSeq, msgBody.busLocationList[].stateCd; S1 정규화 last_passed; S1 passed_bus_existsGBIS 버스위치정보 getBusLocationListv2 + S1 스냅샷 특징 빌더
지난 버스 축거리 (passed_axis_gap_seq)이미 지난 후보 중 목표에 가장 가까운 차량을 고르고 last_passed-target station_seq를 계산한다.주의 — 동률 규칙은 상류 선택과 같다. 좌석값은 선택 기준이 아니다.파생msgBody.busLocationList[].vehId, msgBody.busLocationList[].stationSeq, msgBody.busLocationList[].stateCd; S1 정규화 last_passed; S1 passed_axis_gap_seqGBIS 버스위치정보 getBusLocationListv2 + S1 스냅샷 특징 빌더
지난 버스 최근접 동률 수 (passed_tie_count)최소 passed_axis_gap_seq에 놓인 후보 차량 수를 센다.주의 — 응답 전체 차량 수가 아니다.파생msgBody.busLocationList[].vehId, msgBody.busLocationList[].stationSeq, msgBody.busLocationList[].stateCd; S1 정규화 last_passed; S1 passed_tie_countGBIS 버스위치정보 getBusLocationListv2 + S1 스냅샷 특징 빌더
지난 버스 잔여석 (passed_remain_seats)축거리와 동률 규칙으로 고른 지난 차량의 remainSeatCnt를 붙인다.주의 — -1은 결측이다. 목표 정류장을 지난 뒤의 관측이라도 목표 차량의 정답 라벨과는 다른 스냅샷 차량이다.파생msgBody.busLocationList[].remainSeatCnt, msgBody.busLocationList[].stationSeq, msgBody.busLocationList[].stateCd, msgBody.busLocationList[].vehId; S1 passed_remain_seatsGBIS 버스위치정보 getBusLocationListv2 + S1 최근접 지난 차 선택
지난 버스 상태 (passed_state_cd)선택된 지난 차량의 원 stateCd를 붙인다.주의 — 로지스틱은 0·1·2 원핫 표시로 바꾼다.파생msgBody.busLocationList[].stateCd, msgBody.busLocationList[].stationSeq, msgBody.busLocationList[].vehId; S1 passed_state_cdGBIS 버스위치정보 getBusLocationListv2 + S1 최근접 지난 차 선택
직전 통과 라벨 존재 (previous_passage_exists)같은 원천·노선·정류장에서 질의시각 전에 구간이 끝나고 라벨까지 관측된 가장 최근 통과가 있으면 1이다.주의 — 현재 행의 미래 라벨은 후보가 될 수 없다. 원천을 넘겨 연결하지 않는다.파생S1 passage_events.source, passage_events.route, passage_events.station_seq, passage_events.passage_interval_end, passage_events.label_observed_at, passage_events.label_status; 특징 previous_passage_existsS1 passage_events
직전 통과 출발 후 잔여석 (previous_passage_remain_seats)인과 조건을 만족하는 같은 정류장의 가장 최근 통과에서 label_remain_seats를 가져온다.주의 — 현재 목표 차량의 출발 후 좌석이 아니다.파생S1 passage_events.label_remain_seats, passage_events.label_observed_at, passage_events.passage_interval_end; 특징 previous_passage_remain_seatsS1 passage_events
직전 통과 만석 여부 (previous_passage_is_full)인과 조건을 만족하는 가장 최근 같은 정류장 통과의 label_full을 가져온다.주의 — 직전 API 응답 한 대와 같은 뜻이 아니다. 직전으로 확정된 통과 사건이다.파생S1 passage_events.label_full, passage_events.label_observed_at, passage_events.passage_interval_end; 특징 previous_passage_is_fullS1 passage_events
직전 통과 라벨 나이 (previous_passage_label_age_sec)현재 질의 응답 수신시각에서 직전 통과 라벨 관측시각을 뺀다.주의 — msgHeader.queryTime으로 빼지 않는다.파생timing.response_received_at.kst, timing.response_received_at.utc; S1 E4 response_received_kst, response_received_utc; 코퍼스 collectedAt; S1 passage_events.label_observed_at, previous_passage_label_age_sec우리 수집기 응답 수신시각 + S1 passage_events
최근 통과 나이 하한 (latest_passage_age_lower_sec)현재 질의시각에서 최근 통과 구간의 끝을 뺀다.주의 — 통과시각이 폴링 사이 구간이라 한 점의 정확한 나이가 아니다.파생timing.response_received_at.kst, timing.response_received_at.utc; S1 E4 response_received_kst, response_received_utc; 코퍼스 collectedAt; S1 passage_events.passage_interval_end, latest_passage_age_lower_sec우리 수집기 응답 수신시각 + S1 passage_events
최근 통과 나이 상한 (latest_passage_age_upper_sec)현재 질의시각에서 최근 통과 구간의 시작을 뺀다.주의 — 하한과 함께 봐야 한다.파생timing.response_received_at.kst, timing.response_received_at.utc; S1 E4 response_received_kst, response_received_utc; 코퍼스 collectedAt; S1 passage_events.passage_interval_start, latest_passage_age_upper_sec우리 수집기 응답 수신시각 + S1 passage_events
관측 헤드웨이 하한 (observed_headway_lower_sec)같은 원천·노선·정류장의 최근 통과 시작에서 그 전 통과 끝을 빼고 0 아래를 자른다.주의 — 정확 통과시각 차가 아니라 두 검열 구간이 허용하는 하한이다.파생S1 passage_events.passage_interval_start, passage_events.passage_interval_end; 특징 observed_headway_lower_secS1 passage_events의 연속 통과 구간
관측 헤드웨이 상한 (observed_headway_upper_sec)최근 통과 끝에서 그 전 통과 시작을 빼고 0 아래를 자른다.주의 — 계획 배차간격과 다른 관측 구간 상한이다.파생S1 passage_events.passage_interval_start, passage_events.passage_interval_end; 특징 observed_headway_upper_secS1 passage_events의 연속 통과 구간
응답 내 노선 차량 수 (route_vehicle_count)make_observation 정규화에 성공한 차량 행 수를 센다.주의 — 실제 운행 대수 보장이 아니다. 차량키·순번·상태가 잘못된 원행은 먼저 빠진다. 정상 0대와 실패도 학습 스냅샷 행이 아니다.파생msgBody.busLocationList[]; S1 E4 derived/observations.jsonl 행; 코퍼스 vehicles[]; S1 route_vehicle_countGBIS 버스위치정보 getBusLocationListv2
좌석값이 있는 차량 수 (route_known_seat_count)remainSeatCnt>=0인 차량 수를 센다.주의 — -1은 포함하지 않는다.파생msgBody.busLocationList[].remainSeatCnt; S1 route_known_seat_countGBIS 버스위치정보 getBusLocationListv2
응답 내 0석 차량 수 (route_full_bus_count)remainSeatCnt==0인 차량 수를 센다.주의 — 응답에 잡힌 차량만 센다.파생msgBody.busLocationList[].remainSeatCnt; S1 route_full_bus_countGBIS 버스위치정보 getBusLocationListv2
노선 응답 잔여석 평균 (route_seat_mean)응답 안의 비음수 remainSeatCnt에 산술평균을 적용한다.주의 — remainSeatCnt=-1은 제외한다. 유효 좌석값이 없으면 결측이다.파생msgBody.busLocationList[].remainSeatCnt; S1 route_seat_meanGBIS 버스위치정보 getBusLocationListv2 + S1 노선 응답 요약
노선 응답 잔여석 표준편차 (route_seat_std)응답 안의 비음수 remainSeatCnt에 모표준편차(ddof=0)을 적용한다.주의 — remainSeatCnt=-1은 제외한다. 유효 좌석값이 없으면 결측이다.파생msgBody.busLocationList[].remainSeatCnt; S1 route_seat_stdGBIS 버스위치정보 getBusLocationListv2 + S1 노선 응답 요약
노선 응답 잔여석 최솟값 (route_seat_min)응답 안의 비음수 remainSeatCnt에 최솟값을 적용한다.주의 — remainSeatCnt=-1은 제외한다. 유효 좌석값이 없으면 결측이다.파생msgBody.busLocationList[].remainSeatCnt; S1 route_seat_minGBIS 버스위치정보 getBusLocationListv2 + S1 노선 응답 요약
노선 응답 잔여석 10백분위 (route_seat_p10)응답 안의 비음수 remainSeatCntnumpy.quantile 기본 선형 보간의 0.10 분위수를 적용한다.주의 — remainSeatCnt=-1은 제외한다. 유효 좌석값이 없으면 결측이다.파생msgBody.busLocationList[].remainSeatCnt; S1 route_seat_p10GBIS 버스위치정보 getBusLocationListv2 + S1 노선 응답 요약
노선 응답 잔여석 중앙값 (route_seat_median)응답 안의 비음수 remainSeatCntnumpy.quantile 기본 선형 보간의 0.50 분위수를 적용한다.주의 — remainSeatCnt=-1은 제외한다. 유효 좌석값이 없으면 결측이다.파생msgBody.busLocationList[].remainSeatCnt; S1 route_seat_medianGBIS 버스위치정보 getBusLocationListv2 + S1 노선 응답 요약
노선 응답 잔여석 90백분위 (route_seat_p90)응답 안의 비음수 remainSeatCntnumpy.quantile 기본 선형 보간의 0.90 분위수를 적용한다.주의 — remainSeatCnt=-1은 제외한다. 유효 좌석값이 없으면 결측이다.파생msgBody.busLocationList[].remainSeatCnt; S1 route_seat_p90GBIS 버스위치정보 getBusLocationListv2 + S1 노선 응답 요약
스냅샷 공간 헤드웨이 (spatial_headway_axis_seq)선택된 지난 차량의 last_passed에서 선택된 상류 차량의 last_passed를 뺀다.주의 — 양쪽 차량이 모두 있어야 한다. 시간 헤드웨이가 아니라 전체 순번축의 간격이다.파생msgBody.busLocationList[].stationSeq, msgBody.busLocationList[].stateCd, msgBody.busLocationList[].vehId; S1 spatial_headway_axis_seqGBIS 버스위치정보 getBusLocationListv2 + S1 상류·지난 차 선택
노선 (route)직접 수집은 calls.jsonl.route_name, 코퍼스는 route.name을 모델 범주로 읽고 같은 route.routeName 마스터 행을 찾는다. 수집기 설정의 route.route_id와 원응답 routeId는 원문에 남는다.주의 — 현재 S1 구현은 실제로 routeName으로 마스터를 찾는다. msgBody.busLocationList[].routeId를 모델의 route로 복사하지 않는다. 대상 두 노선 밖으로 넓힐 때는 ID 검증이 필요하다.응답 원본msgBody.busLocationList[].routeId; 운영 수집기 route.name, route.route_id; S1 E4 calls.jsonl.route_name; 코퍼스 route.name; route.routeName, route.routeId; S1 routeGBIS 버스위치정보 getBusLocationListv2 + 우리 수집기 노선 메타데이터 + 제한 코퍼스 + GBIS route 마스터
원천 구분 (source_kind)직접 수집 행은 direct_e4, 제한 코퍼스 행은 corpus로 붙인다.주의 — GBIS 응답 필드가 아니다. GBM에는 범주형 예측 재료로 들어가고, 구간 검열에서는 같은 원천끼리 연결하는 데 쓴다.파생S1 source_kindS1 데이터셋 빌더
출발 후 만석 라벨 (label_full)정류장 s 통과 뒤 같은 차량에서 (stateCd,stationSeq)=(2,s),(0,s),(1,s+1) 중 처음 잡힌 유효 좌석값을 찾는다. 0이면 1, 양수면 0이다.주의 — remainSeatCnt=-1은 결측이다. (stateCd,stationSeq)=(1,s)는 도착 상태라 라벨이 아니다. 예측 시점 뒤에 생기므로 학습 정답으로만 쓴다.파생msgBody.busLocationList[].vehId, msgBody.busLocationList[].stationSeq, msgBody.busLocationList[].stateCd, msgBody.busLocationList[].remainSeatCnt; timing.response_received_at.kst, timing.response_received_at.utc; S1 passage_events.label_full, label_remain_seatsGBIS 버스위치정보 getBusLocationListv2 + 우리 수집기 응답 수신시각 + S1 통과 사건 빌더

지금은 안 쓰는 재료 6개

언제 실패하나

기존 프로토타입과 다른 점