고위험 이슈는 ML에 맡기지 않았다: 키워드 룰 안전망 설계

2026.10.04
현장 데이터 서비스 구축기 5편

이슈 등급 분류에서 가장 중요한 고위험 이슈를 ML 대상에서 빼고 키워드 룰로 분리했다. 중대 여부가 접수 텍스트가 아니라 결과로 갈린다는 것을 데이터로 확인한 과정과, 오탐 키워드를 걸러 룰을 '검토 요망' 플래그로 만든 방법을 정리했다.

이 글의 순서

    처음 가정: 고위험 이슈는 키워드로 잡힌다

    등급 분류 모델을 만들면서 고위험 이슈는 처음부터 고민거리였다. 15년 치를 다 모아도 30여 건이다. 수천 건 중 30건으로는 ML이 패턴을 배우기 어렵다. 그래서 가정을 세웠다. 고위험 이슈는 추락, 화재, 끼임처럼 내용이 뚜렷하니 키워드 규칙으로 잡으면 되지 않을까.

    규칙을 만들기 전에 고위험 이슈 제목을 한 건씩 직접 읽었다. 가정은 절반만 맞았다.

    제목을 읽어 보니: 중대는 결과로 갈린다

    절반 정도는 예상대로였다. 깔림, 질식, 추락, 화재, 황산 접촉, 협착, 낙하물에 부딪힘처럼 제목만 봐도 위험한 이슈들이다.

    나머지 상당수는 의외였다. 사내 행사나 운동 중 다친 이슈가 많았다. 달리기, 축구, 족구를 하다가 골절되거나 인대가 끊어진 경우다. 이런 이슈가 중대로 분류된 이유는 등급 기준에 있었다. 등급이 '어떤 이슈였나'가 아니라 '얼마나 오래 치료했나' 같은 결과로 정해진다. 장기 요양이 필요하면 운동 중 부상도 중대가 된다.

    등급이 정해지는 시점

    이슈 발생
    접수제목·발생 내용 입력모델이 보는 시점
    치료·요양결과가 생기는 구간
    등급 확정요양 기간 등 반영

    접수 시점의 텍스트에는 등급을 가르는 결과 정보가 아직 없다.

    '축구 중 골절'이라는 텍스트로는 가벼운 골절인지 수술이 필요한 골절인지 알 수 없다. 규칙으로도 ML로도 접수 시점에 맞힐 수 없는 이슈다. 여기서 선을 긋지 않으면 헛수고를 하게 된다.

    역할을 나눴다: 룰은 안전망, ML은 나머지

    그래서 목표를 바꿨다. 고위험 이슈를 정확히 분류하는 것이 아니라, 명백히 위험한 이슈를 놓치지 않고 사람에게 올려주는 것이다.

    추론 구조

    이슈 텍스트제목 + 발생 내용
    키워드 룰고위험 키워드 검사걸리면 '중대 검토 요망'
    ML 3클래스등급·경미·아차 추천룰 결과와 함께 표시
    • ML은 중대를 뺀 3개 클래스(등급·경미·아차)만 학습한다. 30건짜리 클래스가 빠지니 나머지 학습이 안정된다.
    • 룰은 고위험 키워드가 있으면 '중대 검토 요망' 플래그를 단다. ML 추천은 그대로 같이 보여준다.
    • 운동 부상처럼 결과로만 중대가 되는 이슈는 사전 판별 대상이 아니라는 것을 명시하고 사람 검토에 맡긴다.

    키워드 정리: 오탐 주범을 뺐다

    처음 키워드 목록은 넓게 잡았다. 문제는 일상적인 단어였다. 전체 이슈 텍스트에 돌려 보니 '접촉'은 230건, '화학'은 134건, '누출'은 68건에 걸렸다. '벽에 손이 접촉', '화학물질 취급 중 베임' 같은 경미한 이슈까지 전부 중대 후보가 됐다.

    남긴 키워드 12개

    사망, 추락, 낙하, 깔림, 협착, 끼임, 감김, 질식, 화재, 폭발, 감전, 황산

    뺀 키워드

    접촉, 화학, 누출, 중독, 전복, 붕괴, 강타

    '누출'을 빼도 'CO2 누출로 질식'은 '질식'으로 잡힌다. '황산 접촉'은 '황산'으로 잡힌다. 위험의 본체를 가리키는 단어만 남기고, 상황을 설명하는 단어는 뺐다.

    아래에서 예시 이슈 제목을 눌러 보면 룰이 어떻게 반응하는지 볼 수 있다.

    룰 확인

    키워드 플래그
    HIGH_RISK_KW = ["사망", "추락", "낙하", "깔림", "협착", "끼임",
                    "감김", "질식", "화재", "폭발", "감전", "황산"]
    
    def flag_high_risk(title, occur):
        text = f"{title or ''} {occur or ''}"
        hits = [kw for kw in HIGH_RISK_KW if kw in text]
        return {"flag": bool(hits), "keywords": hits}

    허용한 오탐'끼임'은 손가락이 살짝 끼인 경미한 이슈에도 걸린다. 그래도 남겼다. 안전망은 놓치지 않는 쪽이 중요하고, 플래그는 자동 판정이 아니라 사람이 한 번 더 보라는 표시이기 때문이다.

    평가를 정확도로 하지 않았다

    룰에 '중대 분류 정확도 몇 %'를 붙이면 숫자는 그럴듯해 보이지만 거짓말이 된다. 운동 부상처럼 원래 맞힐 수 없는 이슈가 섞여 있어서 어떤 숫자를 내도 의미가 흐려진다.

    그래서 평가 대상을 좁혔다. 텍스트만 봐도 명백히 위험한 고위험 이슈 중에서 룰이 몇 건을 플래그했는지만 본다. 대표적인 파국형 이슈 제목 9건으로 확인했을 때 9건 모두 플래그됐다. 운동 부상류는 평가 대상에서 빼고, 빠졌다는 사실을 화면에 함께 적었다.

    구분평가 방법
    ML 3클래스교차검증 macro-F1
    키워드 룰명백한 고위험 이슈 중 플래그된 비율 (놓침 여부)
    결과로만 중대가 되는 이슈평가 제외, 사람 검토 대상으로 명시

    정리

    • 룰을 만들기 전에 데이터를 직접 읽는다. 가정이 절반만 맞는 경우가 많다.
    • 예측 대상이 결과로 정해지는지, 입력 시점에 알 수 있는지부터 확인한다.
    • 맞힐 수 없는 것은 모델 범위에서 빼고 그 사실을 드러낸다.
    • 안전망 룰은 정밀도보다 놓치지 않는 것을 우선하고, 상황 단어보다 위험 본체 단어로 만든다.
    이전 편4편. 3천 건 이슈 데이터로 등급 분류 모델 만들기: 10개 모델 비교와 재학습 가드
    다음 편6편. 비전문가에게 AI 성능을 보여주는 법: 점수 대신 '10건 중 약 6건'
    #머신러닝#룰기반#하이브리드모델#고위험 이슈#오탐#모델평가#PI

    댓글