feat(prediction): output 정상화 + dark 점수화 + transship 재설계 #23

병합
htlee fix/prediction-output-anomalies 에서 develop 로 5 commits 를 머지했습니다 2026-04-09 09:56:48 +09:00
소유자

변경 사항

prediction 출력 5종 이상 정상화 + dark 의심 점수화 재설계 + transship 베테랑 관점 재설계

커밋

  • 0a4d023 fix(prediction): output 5종 이상 정상화 (stats/event/lightweight)
  • 2e5d55a fix(prediction): dark 판정에 한국 AIS 수신 영역 필터 추가
  • e5d123e feat(prediction): dark 의심 점수화 + transship 베테랑 관점 재설계
  • dac4a3b fix(prediction): features JSONB 중첩 구조 sanitize
  • b15a940 docs: prediction 2차 개편 릴리즈 노트 + hourly snapshot 스크립트

핵심 변경

1. 5종 출력 이상 정상화

  • stats_aggregator hourly: UTC→KST boundary, by_category/by_zone 추가
  • event_generator: zone_code 실측 기반 룰 재편, break 제거, dedup prime 분산
  • lightweight path: 24h 궤적 기반 dark/spoof/speed_jump 산출

2. Dark Vessel 의심 점수화 (0~100점)

  • 8가지 패턴: 이동중OFF, 민감수역, 반복이력(7일), 거리비정상, 주간조업OFF, 직전이상행동, 무허가, 장기gap
  • 등급: CRITICAL(70+) / HIGH(50~69) / WATCH(30~49)
  • 어구(gear AIS), 한국선(440/441) dark 제외
  • features JSONB에 score/patterns/tier/history 저장

3. Transshipment 베테랑 관점 재설계

  • SOG 2→1kn, 근접 110→77m, 지속 60→45분 + gap tolerance 2사이클
  • 한국 EEZ 관할 필수, 어구/여객/군함 제외
  • 점수 기반 판정: base 40 + 야간/무허가/COG/지속/영해 가점
  • CRITICAL(90+) / HIGH(70~89) / WATCH(50~69)

테스트

  • redis-211 수동 배포 + 5분 사이클 정상 동작 확인
  • dark: CRITICAL 146→379, 반복 이력 기반 상위 정렬 확인
  • transship: 45분 후 pairs=343 (CRITICAL 24, HIGH 167) 정상 발동
  • events: 카테고리 6종+, 시간당 ~200건 안정
  • 사이클 시간 127s (5분 윈도우 내 안전)
## 변경 사항 prediction 출력 5종 이상 정상화 + dark 의심 점수화 재설계 + transship 베테랑 관점 재설계 ### 커밋 - `0a4d023` fix(prediction): output 5종 이상 정상화 (stats/event/lightweight) - `2e5d55a` fix(prediction): dark 판정에 한국 AIS 수신 영역 필터 추가 - `e5d123e` feat(prediction): dark 의심 점수화 + transship 베테랑 관점 재설계 - `dac4a3b` fix(prediction): features JSONB 중첩 구조 sanitize - `b15a940` docs: prediction 2차 개편 릴리즈 노트 + hourly snapshot 스크립트 ### 핵심 변경 **1. 5종 출력 이상 정상화** - stats_aggregator hourly: UTC→KST boundary, by_category/by_zone 추가 - event_generator: zone_code 실측 기반 룰 재편, break 제거, dedup prime 분산 - lightweight path: 24h 궤적 기반 dark/spoof/speed_jump 산출 **2. Dark Vessel 의심 점수화 (0~100점)** - 8가지 패턴: 이동중OFF, 민감수역, 반복이력(7일), 거리비정상, 주간조업OFF, 직전이상행동, 무허가, 장기gap - 등급: CRITICAL(70+) / HIGH(50~69) / WATCH(30~49) - 어구(gear AIS), 한국선(440/441) dark 제외 - features JSONB에 score/patterns/tier/history 저장 **3. Transshipment 베테랑 관점 재설계** - SOG 2→1kn, 근접 110→77m, 지속 60→45분 + gap tolerance 2사이클 - 한국 EEZ 관할 필수, 어구/여객/군함 제외 - 점수 기반 판정: base 40 + 야간/무허가/COG/지속/영해 가점 - CRITICAL(90+) / HIGH(70~89) / WATCH(50~69) ## 테스트 - [x] redis-211 수동 배포 + 5분 사이클 정상 동작 확인 - [x] dark: CRITICAL 146→379, 반복 이력 기반 상위 정렬 확인 - [x] transship: 45분 후 pairs=343 (CRITICAL 24, HIGH 167) 정상 발동 - [x] events: 카테고리 6종+, 시간당 ~200건 안정 - [x] 사이클 시간 127s (5분 윈도우 내 안전)
htlee added 5 commits 2026-04-09 09:56:39 +09:00
5가지 출력 이상 동시 해결:

1. stats_aggregator (이상 1, 5)
   - aggregate_hourly에 by_category, by_zone JSON 집계 추가
   - hour_start를 KST 기준으로 변경 (대시보드 표기와 boundary 일치)

2. event_generator 룰 정리 (이상 2, 3, 4)
   - critical_risk 임계값 90→70 (risk.py CRITICAL 분류와 일치)
   - territorial_sea_violation, contiguous_zone_high_risk, special_zone_entry 신설
     (실측 zone_code: TERRITORIAL_SEA/CONTIGUOUS_ZONE/ZONE_*)
   - 잘못된 NLL/SPECIAL_FISHING_* 룰 제거
   - HIGH_RISK_VESSEL 신규 카테고리 (50~69 MEDIUM, 70+ CRITICAL)
   - break 제거: 한 분석결과가 여러 카테고리에 동시 매칭 가능

3. dedup window prime 분산 (이상 5)
   - 30/60/120/360분 → 33/67/127/367분
   - 5분 사이클 boundary와 LCM 회피하여 정시 일제 만료 패턴 완화

4. lightweight path 신호 보강 (이상 2, 3, 4 근본 해결)
   - vessel_store._tracks의 24h 누적 궤적으로 dark/spoof/speed_jump 산출
   - 6,500 vessels(전체 93%)의 is_dark, spoofing_score가 비로소 채워짐
   - compute_lightweight_risk_score에 dark gap, spoofing 가점 추가
     (max 60→100 가능, CRITICAL 도달 가능)

시간 처리 원칙 적용:
- DB 컬럼은 모두 timestamptz 확인 완료
- aggregate_hourly KST aware datetime 사용
- pandas Timestamp는 source-internal 비교만 (안전)
16:00 cron 1차 분석 결과, lightweight path가 6,500척 중 5,250척(80%)을
dark로 판정. 좌표 검증 결과 모두 30~37°N/122~125°E (동중국해/서해)로
한국 AIS 수신소 도달 한계 영역에 위치하여 정상 운항 중에도 20~24h 통째로
수신이 끊기는 자연 gap이 발생.

핫픽스: lightweight path에서 dark 판정 직후 마지막 위치가
북위 32~39.5, 동경 124~132 (한반도 + EEZ + 접속수역 여유 포함) 밖이면
dark를 False로 강제. 한국 측 관심 영역의 dark 탐지는 그대로 유지.

근본 개편(STATIONARY 정박 필터, 진입 후 단절 패턴, gap 임계값 재조정 등)은
12시간 추적 데이터 수집 후 내일 진행.
12h 누적 분석 결과 dark/transship이 운영 불가 수준으로 판정되어
탐지 철학을 근본부터 전환.

## dark 재설계: 넓은 탐지 + 의도적 OFF 의심 점수화

기존 "필터 제외" 방식에서 "넓게 기록 + 점수 산출 + 등급별 알람"으로 전환.
해경 베테랑 관점의 8가지 패턴을 가점 합산하여 0~100점 산출.

- P1 이동 중 OFF (gap 직전 SOG > 2kn)
- P2 민감 수역 경계 근처 OFF (영해/접속수역/특정조업수역)
- P3 반복 이력 (7일 내 재발) — 가장 강력
- P4 gap 후 이동거리 비정상 (은폐 이동)
- P5 주간 조업 시간 OFF
- P6 gap 직전 이상 행동 (teleport/급변)
- P7 무허가 선박 가점
- P8 장기 gap (3h/6h 구간별)
- 감점: gap 시작 위치가 한국 AIS 수신 커버리지 밖

완전 제외:
- 어구 AIS (GEAR_PATTERN 매칭, fleet_tracker SSOT)
- 한국 선박 (MMSI 440*, 441*) — 해경 관할 아님

등급: CRITICAL(70+) / HIGH(50~69) / WATCH(30~49) / NONE
이벤트는 HIGH 이상만 생성 (WATCH는 DB 저장만).

신규 함수:
- algorithms/dark_vessel.py: analyze_dark_pattern, compute_dark_suspicion
- scheduler.py: _is_dark_excluded, _fetch_dark_history (사이클당 1회 7일 이력 일괄 조회)

pipeline path + lightweight path 모두 동일 로직 적용.
결과는 features JSONB에 {dark_suspicion_score, dark_patterns,
dark_tier, dark_history_7d, dark_history_24h, gap_start_*} 저장.

## transship 재설계: 베테랑 함정근무자 기준

한정된 함정 자원으로 단속 출동을 결정할 수 있는 신뢰도 확보.

상수 재조정:
- SOG_THRESHOLD_KN: 2.0 → 1.0 (완전 정박만)
- PROXIMITY_DEG: 0.001 → 0.0007 (~77m)
- SUSPECT_DURATION_MIN: 60 → 45 (gap tolerance 있음)
- PAIR_EXPIRY_MIN: 120 → 180
- GAP_TOLERANCE_CYCLES: 2 신규 (GPS 노이즈 완화)

필수 조건 (모두 충족):
- 한국 EEZ 관할 수역 이내
- 환적 불가 선종 제외 (passenger/military/tanker/pilot/tug/sar)
- 어구 AIS 양쪽 제외
- 45분 이상 지속 (miss_count 2 사이클까지 용인)

점수 체계 (base 40):
- 야간(KST 20~04): +15
- 무허가 가점: +20
- COG 편차 > 45°: +20 (나란히 가는 선단 배제)
- 지속 ≥ 90분: +20
- 영해/접속수역 위치: +15

등급: CRITICAL(90+) / HIGH(70~89) / WATCH(50~69)
WATCH는 저장 없이 로그만. HIGH/CRITICAL만 이벤트.

pair_history 구조 확장:
- 기존: {(a,b): datetime}
- 신규: {(a,b): {'first_seen', 'last_seen', 'miss_count', 'last_lat/lon/cog_a/cog_b'}}
- miss_count > GAP_TOLERANCE_CYCLES면 삭제 (즉시 리셋 아님)

## event_generator 룰 교체

- dark_vessel_long 룰 제거 → dark_critical, dark_high (features.dark_tier 기반)
- transship 룰 제거 → transship_critical, transship_high (features.transship_tier 기반)
- DEDUP: ILLEGAL_TRANSSHIP 67→181, DARK_VESSEL 127→131, ZONE_DEPARTURE 127→89

## 공통 정리

- scheduler.py의 _gear_re 삭제, fleet_tracker.GEAR_PATTERN 단일 SSOT로 통합
AnalysisResult.to_db_tuple이 기존에 features dict 값을 모두 float로
변환했는데, dark_suspicion 구조를 넣으면서 dark_patterns(list) 등
비스칼라 타입이 포함되어 upsert 실패 (float argument not a list).

_sanitize 재귀 함수로 JSON 호환 타입(str/int/float/bool/list/dict/None)을
그대로 보존하도록 변경.
- RELEASE-NOTES [Unreleased] 섹션에 dark 의심 점수화 + transship 재설계 변경사항 추가
- prediction/scripts/hourly-analysis-snapshot.sh: 시간별 상태 스냅샷 수집 (25개 섹션)
claude-bot 이 변경사항을 승인하였습니다. 2026-04-09 09:56:47 +09:00
claude-bot left a comment
멤버

MR 승인 (via /mr skill)

MR 승인 (via /mr skill)
htlee merged commit 0b2dbb523a into develop 2026-04-09 09:56:48 +09:00
htlee 삭제된 브랜치 fix/prediction-output-anomalies 2026-04-09 09:56:48 +09:00
"로그인하여 이 대화에 참여"
No reviewers
레이블 없음
마일스톤 없음
담당자 없음
참여자 2명
알림
마감일
기한이 올바르지 않거나 범위를 벗어났습니다. 'yyyy-mm-dd'형식을 사용해주십시오.

마감일이 설정되지 않았습니다.

의존성

No dependencies set.

Reference: gc/kcg-ai-monitoring#23
No description provided.