예방은 사후 대응보다 언제나 비용이 낮습니다. 과거 데이터로 위험 구역을 미리 식별하면 순찰·예방점검 자원을 선제적으로 배치할 수 있습니다. 다만 목표를 정확히 잡아야 합니다. 우리가 만들 것은 "화재를 맞히는" 점쟁이가 아니라, 한정된 자원을 어디에 먼저 둘지 정하는 우선순위 도구입니다.
이 일은 일기예보와 닮았습니다. 예보관은 내일 비가 정확히 어디에 내릴지 못 박지 않습니다. 대신 '강수 확률이 높은 지역'을 알려 주어 우산을 챙기고 배수로를 점검하게 합니다. 위험도 예측도 같습니다. '이 건물에 불이 난다'가 아니라 '이 구역을 먼저 살펴라'를 내놓습니다. 맞고 틀리고를 다투기보다, 한정된 발품을 어디에 쓸지 정하는 데 쓰는 도구입니다.
- 예측의 공간·시간 단위와 타깃을 업무 목적에 맞게 정의할 수 있습니다.
- 데이터를 시간과 공간 키로 결합하고 누수 가능성을 점검할 수 있습니다.
- 불균형 데이터에서 베이스라인과 예측 모델을 정직하게 비교할 수 있습니다.
- 예측 점수의 편향과 운영 한계를 기록하고 사람 검토로 연결할 수 있습니다.
예측이 왜 가능한가 — 위험은 무작위가 아니다
예측이라는 말에 거부감이 들 수 있습니다. 불이 언제 날지는 사람도 모르는데 기계가 어떻게 아느냐는 것입니다. 맞는 말입니다. 개별 화재 한 건이 언제 날지는 아무도 못 맞힙니다. 하지만 우리가 맞히려는 것은 그게 아닙니다.
동전 하나가 앞이 나올지는 못 맞혀도, 만 번 던지면 앞면이 절반쯤 나온다는 것은 압니다. 화재도 비슷합니다. 특정 건물에 오늘 밤 불이 날지는 몰라도, 오래된 건물이 밀집하고 겨울이 깊은 구역이 새 아파트 단지보다 화재가 잦다는 경향은 데이터에 반복해서 나타납니다. 위험이 특정 조건에 쏠려 있다는 사실, 이것이 예측의 재료입니다.
모델이 하는 일은 마술이 아니라 이 쏠림을 숫자로 정리하는 것입니다. 과거에 '건물 나이·밀집도·계절·최근 출동'이 어떤 조합일 때 화재가 더 잦았는지 패턴을 찾아, 지금 같은 조합을 보이는 구역에 높은 점수를 매깁니다. 그래서 예측이 통하려면 두 가지가 전제돼야 합니다 — 위험이 조건에 따라 실제로 쏠려 있어야 하고, 그 조건이 데이터로 남아 있어야 합니다. 둘 중 하나라도 없으면 아무리 정교한 모델도 헛돕니다.
가상 상황: 점검 인원은 셋, 구역은 수백
가상의 상황을 하나 들어 보겠습니다. 어느 예방과에 점검 인력이 세 명 있고, 관할에는 살펴야 할 구역이 수백 곳입니다. 다음 달에 전부 도는 것은 불가능합니다. 그러면 어디부터 가야 할까요?
지금까지의 방법은 대개 '지난번에 문제 있던 곳'과 '민원이 들어온 곳'입니다. 틀린 방법은 아니지만, 이건 이미 눈에 띈 곳만 보는 방식입니다. 조용하지만 위험이 쌓이는 구역은 순번이 자꾸 뒤로 밀립니다.
위험도 예측은 이 줄 세우기를 감이 아니라 데이터로 하자는 제안입니다. 구급 현장의 중증도 분류(트리아지)를 떠올리면 쉽습니다. 환자가 몰리면 도착 순서가 아니라 위중한 순서로 봅니다. 건물과 구역도 마찬가지로, 한정된 점검 발품을 위험이 높아 보이는 순서로 배분하는 것입니다. 다만 트리아지가 의사의 최종 판단을 대신하지 않듯, 이 점수도 담당자의 결정을 대신하지 않습니다 — 뒤에서 다시 강조합니다.
문제를 먼저 정의한다
모델보다 정의가 먼저입니다. 과녁을 정하지 않고 쏜 화살은 아무리 멀리 날아가도 점수가 되지 않습니다. 세 가지를 정합니다.
- 공간 단위: 행정동 또는 격자(예: 500m × 500m). 자원 배치 단위와 맞춥니다.
- 시간 단위: 주 또는 월. 너무 짧으면 데이터가 희박하고, 너무 길면 둔감해집니다.
- 타깃: 해당 단위·기간의 화재 발생 여부(이진 분류) 또는 건수(회귀). 운영에는 보통 발생 여부가 직관적입니다.
어떤 데이터를 결합하나
서로 다른 출처를 공간(좌표·행정코드) + 시간 키로 한 테이블에 모읍니다.
서로 다른 장부를 같은 자리에 포개려면 공통의 못이 필요합니다. 화재는 반드시 '어디서·언제' 일어나므로, 그 두 축이 자연스러운 결합 키가 됩니다.
| 데이터 | 출처(예) | 주로 쓰는 변수 |
|---|---|---|
| 과거 출동·화재 | 국가화재정보시스템 | 발생 시각·장소·유형·원인 |
| 기상 | 기상청 ASOS·방재기상관측 | 기온·습도·풍속·강수 |
| 건축물 | 건축물대장 | 준공연도·주용도·구조·연면적 |
| 인구·시설 | 행정통계·안전지도 | 주거 밀집도·노유자시설 수 |
격자 단위로 만들 때는 각 출동·건물 좌표를 격자에 공간 조인(spatial join)한 뒤, 기간별로 집계합니다.
핵심은 피처 엔지니어링
원시값을 그대로 넣기보다 의미 있는 파생 변수를 만드는 것이 성능을 좌우합니다.
왜 원시값으로는 부족할까요. '준공연도 1998'이라는 숫자 자체는 모델에게 그저 큰 수일 뿐입니다. 하지만 '건물 나이 28년'으로 바꾸면 노후라는 의미가 담깁니다. 사람이 도메인 지식으로 '이 값이 왜 위험과 이어지는지'를 미리 계산해 떠먹여 주는 것 — 그게 피처 엔지니어링입니다. 재료를 잘 다듬어 내놓을수록 모델은 덜 헤맵니다.
| 원시값 | 그대로 넣으면 모델이 보는 것 | 파생 변수로 바꾸면 |
|---|---|---|
| 준공연도 2004 | 의미 없는 큰 숫자 | 건물 나이 = 노후 신호 |
| 기온·습도 각각 | 관계없는 두 열 | 건조지수 = 마름의 정도 |
| 월 = 12 | 달력 순번 | 난방 사용기(겨울) 여부 |
- 기상 파생: 건조지수, 최근 7일 누적 무강수일
- 시간 파생: 계절·요일·공휴일·난방 사용기(겨울)
- 건물 파생: 노후도(현재연도 − 준공연도), 용도별 위험 가중, 밀집도
- 이력 파생: 최근 N개월 출동 빈도, 직전 화재 이후 경과일
import pandas as pd df["건조지수"] = df["기온"] / (df["습도"] + 1) df["건물나이"] = 2026 - df["준공연도"] df["겨울"] = df["월"].isin([12, 1, 2]).astype(int) # 격자별 최근 3개월 출동 합계 — '직전까지'만 사용 df["최근출동"] = ( df.groupby("격자")["출동건수"] .transform(lambda s: s.rolling(3, min_periods=1).sum().shift(1)) )
shift(1) 이 핵심입니다. 예측 시점에는 알 수 없는 그 기간의 출동 건수를 피처에 넣으면, 검증 점수는 높지만 실제로는 미래를 훔쳐본 데이터 누수입니다. 위험도 예측에서 가장 흔하고 치명적인 실수입니다.시험공부를 하며 답지를 몰래 본 것과 같습니다. 모의고사(검증) 점수는 만점이지만, 답지가 없는 실전(운영)에서는 무너집니다. 데이터 누수가 무서운 이유는 틀린 티가 안 난다는 데 있습니다 — 오히려 점수가 좋아 보이니, 배포하고 한참 지나서야 문제를 눈치챕니다.
모델은 단순한 것부터
먼저 베이스라인을 세웁니다. "직전 기간 출동 빈도 순"으로 줄세우는 단순 규칙입니다. 머신러닝 모델이 이 베이스라인을 이기지 못하면 모델은 필요 없습니다.
베이스라인은 '이 정도는 공짜로 나온다'는 바닥선입니다. 새 저울을 사면 먼저 영점을 맞추듯, 모델을 만들면 먼저 이 바닥선과 견줍니다. 화려한 모델이 단순 규칙조차 못 이긴다면, 복잡함의 대가만 치르는 셈입니다.
다음으로 표 데이터·비선형·결측에 강한 트리 기반 모델(RandomForest, XGBoost)을 씁니다.
from sklearn.ensemble import RandomForestClassifier m = RandomForestClassifier( n_estimators=300, class_weight="balanced", # 화재는 드물다 — 불균형 보정 random_state=0, ) m.fit(X_train, y_train) risk = m.predict_proba(X_test)[:, 1] # 위험 점수
평가: 정확도의 함정
화재가 발생하는 단위가 전체의 1%라면, "전부 안전"이라고 찍는 모델도 정확도 99%입니다. 정확도(accuracy)는 여기서 무의미합니다.
울리지 않는 화재경보기를 떠올려 보세요. 대부분의 날에는 불이 안 나니, '절대 울리지 않는 경보기'도 판정만 놓고 보면 거의 매일 맞습니다. 하지만 정작 불이 났을 때 침묵하는 경보기는 아무 쓸모가 없습니다. 정확도 99%짜리 '전부 안전' 모델이 바로 이 경보기입니다. 대신 봐야 할 것:
- Recall(재현율): 실제 위험을 얼마나 놓치지 않았나 — 예방에서 가장 중요
- Precision(정밀도): 위험하다고 한 것 중 실제 비율
- PR-AUC: 불균형에서 ROC보다 정직한 종합 지표
- 상위 K% 포착률(운영 지표): 위험 상위 10% 구역이 실제 화재의 몇 %를 담는가
가상의 숫자로 감을 잡아 보겠습니다(실제 데이터가 아닌 예시입니다). 한 달 동안 격자 1,000곳 가운데 실제로 화재가 난 곳이 20곳이라고 합시다. 모델이 '위험'이라 지목한 100곳 안에 그 20곳 중 16곳이 들어 있었다면:
- 재현율 = 16 / 20 = 0.8 — 실제 위험의 80%를 그물에 담았습니다.
- 정밀도 = 16 / 100 = 0.16 — 위험이라 부른 곳의 16%만 실제 화재였습니다.
정밀도가 낮아 보이지만, 예방점검에서는 이 교환이 대개 합리적입니다. 놓친 위험은 불로 이어지지만, 헛다리는 점검을 한 번 더 나가는 비용에 그칩니다. 그래서 예방에서는 재현율을 먼저 보고, 인력이 감당할 만큼만 정밀도를 챙깁니다.
운영에 적용하기
- 모델 출력(점수)을 위험 지도와 순위표로 변환합니다.
- 임계값은 자원에 맞춰 정합니다. "상위 10% 구역 집중 예방점검"처럼 가용 인력에서 거꾸로 정하는 편이 현실적입니다.
- 재학습은 분기·반기 주기로. 계절·도시 변화로 분포가 바뀌므로(데이터 드리프트) 성능을 주기적으로 점검합니다.
이 '언제 다시 학습할까'는 생각보다 까다로운 문제라, 예측 모델 운영, 드리프트 감지와 재학습 시점에서 따로 다룹니다. 막연한 주기가 아니라 조건으로 재학습 시점을 잡는 법입니다.
한계와 책임 — 반드시 읽기
모델은 과거를 배웁니다. 그래서 과거의 편향까지 함께 배웁니다. 가장 조심할 세 가지입니다.
- 피드백 루프 편향: 과거 출동·점검이 많았던 곳일수록 "위험"으로 학습되고, 거기에 자원이 더 가고, 다시 데이터가 쌓입니다. 점검이 드물었던 사각지대는 영원히 낮게 나올 수 있습니다. 가로등 밑에서만 열쇠를 찾는 것과 같습니다 — 밝은 곳(자주 살피던 구역)만 자꾸 들여다보면, 어두운 곳(사각지대)의 위험은 데이터에 잡히지 않습니다. 신고·점검 분포를 함께 보며 보정하세요.
- 상관 ≠ 인과: 변수는 원인이 아니라 신호입니다. 모델은 "왜"를 설명하지 않습니다.
- 개인정보: 분석은 격자·행정동 단위 집계로. 개별 세대·개인을 식별하지 않습니다.
설계 판단
모델을 쓸지, 더 단순한 규칙을 쓸지는 알고리즘의 유명세가 아니라 데이터의 상태와 판단의 용도로 결정합니다. 아래 비교는 배치 명령이 아니라, 분석 방식을 고르기 전에 묻는 설계 질문입니다.
| 방식 | 맞는 상황 | 받아들이는 대가 | 사람이 확인할 것 |
|---|---|---|---|
| 최근 이력 순위 같은 단순 베이스라인 | 기록이 적거나 설명 가능성이 최우선인 때 | 복잡한 조건 결합을 놓칠 수 있음 | 예외 구역과 누락 기록 |
| 트리 기반 예측 모델 | 다양한 변수가 반복적으로 축적되고 시간 분할 검증이 가능할 때 | 변화 감시·재학습·편향 점검이 계속 필요함 | 베이스라인 대비 실질적 이점 |
| 모델 없이 담당자 검토 | 자료가 희소하거나 결측·편향을 설명하기 어려운 때 | 범위를 넓히기 어려움 | 자료를 더 모으는 동안의 명시적 판단 근거 |
운영 시나리오
아래는 실제 기관·사건과 무관한 가상 연습입니다. 점수가 이상할 때 바로 배치 판단을 바꾸지 말고, 탐지에서 재발 확인까지 가설을 줄여 가는 흐름에 주목합니다.
가상 시나리오 1: 예방 개입이 다음 학습 타깃을 바꾸는 경우
- 탐지: 높은 순위 구역을 집중 확인한 뒤 그 구역의 위반·점검 기록만 늘었지만, 정기 점검이 유지된 다른 구역의 기록 조건과는 차이를 보였습니다.
- 원인 가설: 실제 위험이 커졌기 때문이 아니라, 모델이 고른 구역을 더 자주 보면서 관측 기회 자체가 타깃을 바꾼 피드백 루프를 먼저 의심합니다.
- 대응: 점검 선택 여부·시점·근거를 타깃과 별도로 기록하고, 미점검을 '문제 없음'으로 채우지 않습니다. 정기 확인 구역과 선택 확인 구역을 나눠 비교하는 로깅·분석은 위의 최소 예제 코드가 구현하지 않으므로 별도로 설계해야 합니다.
- 재발 확인: 새 후보를 동일한 시간 분할에서 베이스라인과 다시 비교하되, 점검 기회가 달랐던 구역은 따로 보고 선택 이력 효과가 반복되지 않는지 사람이 확인합니다.
가상 시나리오 2: 자료가 희소한 구역의 순위가 계속 뒤집히는 경우
- 탐지: 최근 소수의 기록이 추가될 때마다 해당 구역의 순위가 크게 바뀌고, 시간 구간을 바꾸면 베이스라인 대비 이점도 일관되지 않습니다.
- 원인 가설: 안정된 신호가 아니라 희소한 타깃과 계절성이 겹쳐 우연한 흔들림을 학습했다고 봅니다.
- 대응: 복잡한 모델 결과를 낮은 확신도의 참고 후보로 표시하고, 단순 베이스라인과 정기 점검 범위를 유지한 채 추가 기록을 모으게 합니다.
- 재발 확인: 여러 시간 구간의 롤링 검증에서 동일한 경향과 베이스라인 대비 이점이 반복되기 전에는 활용 범위를 넓히지 않습니다.
한계와 리스크
| 리스크 | 발생 조건 | 영향 | 완화·사람 승인 |
|---|---|---|---|
| 점검 이력의 피드백 루프 | 과거에 자주 본 구역의 기록만 더 쌓임 | 사각지대가 계속 낮은 순위로 남음 | 정기 점검 범위를 보존하고 배치 변경은 담당자가 편향 지표와 함께 검토 |
| 결측·시차 불일치 | 데이터 출처별 갱신 시점과 코드가 다름 | 위험 변화처럼 보이는 인공 신호 발생 | 결합 커버리지와 기준일을 기록하고 공유 전 사람이 원천 표본 대조 |
| 점수의 확률·처분 근거 오인 | 상대 순위를 확정적 예측으로 설명함 | 특정 구역을 과도하게 겨냥하거나 점검에서 빼는 판단 | 출력을 참고 순위로만 표시하고 실제 배치·조치는 소속 기관 기준과 담당자 승인 필요 |
자주 묻는 질문
Q. 위험 점수를 화재 발생 확률로 읽어도 되나요?
아닙니다. 별도의 확률 보정과 검증을 하지 않았다면 점수는 비교를 위한 상대 순서입니다. 화재 시점이나 개별 건물의 발생 가능성을 단정하지 않습니다.
Q. 상위 순위는 자동으로 예방점검 대상이 되나요?
아닙니다. 점수는 담당자가 추가 확인할 후보를 줄일 뿐입니다. 정기 점검, 법정 요건, 현장 정보는 모델 순위와 별도로 유지합니다.
Q. 모델과 현장 판단이 다르면 어느 쪽을 남겨야 하나요?
둘 다 남겨야 합니다. 점수, 사용한 데이터 기준일, 담당자의 예외 판단과 근거를 같이 기록해야 다음 검증에서 모델이 무엇을 놓쳤는지 배울 수 있습니다.
Q. 데이터가 갱신될 때마다 모델을 다시 학습해야 하나요?
갱신 자체만으로 재학습을 결정하지 않습니다. 스키마·결측·분포 변화와 늦게 확인되는 실제 성능을 함께 보고, 기록된 조건에 따라 재학습 후보를 올립니다.
핵심 정리
- 위험도 예측은 화재를 단정하는 도구가 아니라 확인 후보를 정렬하는 도구입니다.
- 복잡한 모델은 단순 베이스라인을 반복적으로 이길 때만 운영 비용을 정당화합니다.
- 순위 이상은 현실 변화보다 결합·결측·시차 문제를 먼저 의심합니다.
- 정기 점검과 사람의 예외 판단을 보존해야 데이터의 사각지대가 자원의 사각지대로 굳지 않습니다.
다음 단계
- 예측 모델 운영, 드리프트 감지와 재학습 시점: 점수가 언제부터 낡았는지 탐지하고 교체 근거를 남깁니다.
- 데이터 정제: 결측·중복·표기 불일치가 위험 신호로 위장하지 않게 입력 경계를 다집니다.
- AI 출력 검토: 순위와 설명을 보고·공유하기 전에 근거와 단정 표현을 점검합니다.
사람 검토 체크리스트
- [ ] 타깃·공간 단위·시간 단위를 먼저 정의하고 그다음 모델을 만들었습니다. - [ ] 피처에 예측 시점에는 알 수 없는 정보(그 기간의 출동 등)가 새어들지 않았습니다. - [ ] 검증은 무작위가 아니라 시간 분할로, 과거 학습·미래 검증으로 했습니다. - [ ] 정확도 대신 재현율·정밀도·상위 K% 포착률로 평가했습니다. - [ ] 단순 베이스라인과 비교해 모델이 실제로 더 나은지 확인했습니다. - [ ] 결과는 개별 세대가 아니라 격자·행정동 집계로만 다뤘습니다. - [ ] 모델이 낮게 본 구역도 정기 점검에서 빼지 않았습니다.