- gear_correlation.py: 적응형 EMA + freeze + shadow + 배치 최적화 - 5개 글로벌 모델 병렬 추적 (default/aggressive/conservative/proximity-heavy/visit-pattern) - 어구 중심 점수 체계: 어구 비활성 시 FREEZE, 선박 shadow 추적 - 유형별 메트릭: 어구-선박(proximity+visit+activity), 선박-선박(DTW+SOG+COG) - DB: correlation_param_models + raw_metrics(일별 파티션) + scores + system_config - partition_manager: 일별 파티션 생성/정리 (system_config hot-reload) - track_similarity: SOG상관 + COG동조 + 근접비 3개 메트릭 추가 - scheduler Step 4.7 통합, fleet_tracker MMSI 점수 이전 - chat/tools: query_gear_correlation 도구 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
147 lines
5.5 KiB
PL/PgSQL
147 lines
5.5 KiB
PL/PgSQL
-- 010: 어구 연관성 추적 시스템
|
|
-- - correlation_param_models: 파라미터 모델 마스터
|
|
-- - gear_correlation_raw_metrics: raw 메트릭 (타임스탬프 파티셔닝, 7일 보존)
|
|
-- - gear_correlation_scores: 모델별 어피니티 점수 (상태 테이블)
|
|
-- - system_config: 런타임 설정 (파티션 보관기간 등)
|
|
|
|
SET search_path TO kcg, public;
|
|
|
|
-- ── 파라미터 모델 ──
|
|
CREATE TABLE IF NOT EXISTS kcg.correlation_param_models (
|
|
id SERIAL PRIMARY KEY,
|
|
name VARCHAR(100) NOT NULL UNIQUE,
|
|
is_default BOOLEAN DEFAULT FALSE,
|
|
is_active BOOLEAN DEFAULT TRUE,
|
|
params JSONB NOT NULL,
|
|
description TEXT,
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
-- default 모델 삽입
|
|
INSERT INTO kcg.correlation_param_models (name, is_default, is_active, params, description)
|
|
VALUES ('default', TRUE, TRUE,
|
|
'{"alpha_base":0.30,"alpha_min":0.08,"alpha_decay_per_streak":0.005,"track_threshold":0.50,"polygon_threshold":0.70,"w_proximity":0.45,"w_visit":0.35,"w_activity":0.20,"w_dtw":0.30,"w_sog_corr":0.20,"w_heading":0.25,"w_prox_vv":0.25,"w_prox_persist":0.50,"w_drift":0.30,"w_signal_sync":0.20,"group_quiet_ratio":0.30,"normal_gap_hours":1.0,"decay_slow":0.015,"decay_fast":0.08,"stale_hours":6.0,"shadow_stay_bonus":0.10,"shadow_return_bonus":0.15,"candidate_radius_factor":3.0,"proximity_threshold_nm":5.0,"visit_threshold_nm":5.0,"night_bonus":1.3,"long_decay_days":7.0}',
|
|
'기본 추적 모델')
|
|
ON CONFLICT (name) DO NOTHING;
|
|
|
|
-- ── Raw 메트릭 (모델 독립, 5분마다 기록, 타임스탬프 파티셔닝) ──
|
|
CREATE TABLE IF NOT EXISTS kcg.gear_correlation_raw_metrics (
|
|
id BIGSERIAL,
|
|
observed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
|
|
group_key VARCHAR(100) NOT NULL,
|
|
target_mmsi VARCHAR(20) NOT NULL,
|
|
target_type VARCHAR(10) NOT NULL,
|
|
target_name VARCHAR(200),
|
|
|
|
-- Raw 메트릭 (모든 모델이 공유)
|
|
proximity_ratio DOUBLE PRECISION,
|
|
visit_score DOUBLE PRECISION,
|
|
activity_sync DOUBLE PRECISION,
|
|
dtw_similarity DOUBLE PRECISION,
|
|
speed_correlation DOUBLE PRECISION,
|
|
heading_coherence DOUBLE PRECISION,
|
|
drift_similarity DOUBLE PRECISION,
|
|
|
|
-- Shadow
|
|
shadow_stay BOOLEAN DEFAULT FALSE,
|
|
shadow_return BOOLEAN DEFAULT FALSE,
|
|
|
|
-- 상태
|
|
gear_group_active_ratio DOUBLE PRECISION,
|
|
|
|
PRIMARY KEY (id, observed_at)
|
|
) PARTITION BY RANGE (observed_at);
|
|
|
|
-- 일별 파티션 생성 함수
|
|
CREATE OR REPLACE FUNCTION kcg.create_raw_metric_partitions(days_ahead INT DEFAULT 3)
|
|
RETURNS void AS $$
|
|
DECLARE
|
|
d DATE;
|
|
partition_name TEXT;
|
|
BEGIN
|
|
FOR i IN 0..days_ahead LOOP
|
|
d := CURRENT_DATE + i;
|
|
partition_name := 'gear_correlation_raw_metrics_' || TO_CHAR(d, 'YYYYMMDD');
|
|
IF NOT EXISTS (
|
|
SELECT 1 FROM pg_class c
|
|
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
WHERE c.relname = partition_name AND n.nspname = 'kcg'
|
|
) THEN
|
|
EXECUTE format(
|
|
'CREATE TABLE IF NOT EXISTS kcg.%I PARTITION OF kcg.gear_correlation_raw_metrics
|
|
FOR VALUES FROM (%L) TO (%L)',
|
|
partition_name, d, d + 1
|
|
);
|
|
END IF;
|
|
END LOOP;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
-- 초기 파티션 생성 (오늘 + 3일)
|
|
SELECT kcg.create_raw_metric_partitions(3);
|
|
|
|
-- raw_metrics 인덱스
|
|
CREATE INDEX IF NOT EXISTS idx_raw_metrics_group_time
|
|
ON kcg.gear_correlation_raw_metrics (group_key, observed_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_raw_metrics_target
|
|
ON kcg.gear_correlation_raw_metrics (target_mmsi, observed_at DESC);
|
|
|
|
-- ── 어피니티 점수 (모델별 독립, 상태 테이블) ──
|
|
CREATE TABLE IF NOT EXISTS kcg.gear_correlation_scores (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
model_id INT NOT NULL REFERENCES kcg.correlation_param_models(id) ON DELETE CASCADE,
|
|
|
|
group_key VARCHAR(100) NOT NULL,
|
|
target_mmsi VARCHAR(20) NOT NULL,
|
|
target_type VARCHAR(10) NOT NULL,
|
|
target_name VARCHAR(200),
|
|
|
|
-- 모델별 점수 (EMA 결과)
|
|
current_score DOUBLE PRECISION DEFAULT 0,
|
|
streak_count INT DEFAULT 0,
|
|
observation_count INT DEFAULT 0,
|
|
|
|
-- Shadow 축적
|
|
shadow_bonus_total DOUBLE PRECISION DEFAULT 0,
|
|
shadow_stay_count INT DEFAULT 0,
|
|
shadow_return_count INT DEFAULT 0,
|
|
|
|
-- 상태
|
|
freeze_state VARCHAR(20) DEFAULT 'ACTIVE',
|
|
|
|
-- 시간
|
|
first_observed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
last_observed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
|
|
UNIQUE (model_id, group_key, target_mmsi)
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_gc_model_group
|
|
ON kcg.gear_correlation_scores (model_id, group_key, current_score DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_gc_active
|
|
ON kcg.gear_correlation_scores (current_score DESC)
|
|
WHERE current_score >= 0.5;
|
|
|
|
-- ── 시스템 설정 (런타임 변경 가능, 재시작 불필요) ──
|
|
CREATE TABLE IF NOT EXISTS kcg.system_config (
|
|
key VARCHAR(100) PRIMARY KEY,
|
|
value JSONB NOT NULL,
|
|
description TEXT,
|
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_by VARCHAR(100) DEFAULT 'system'
|
|
);
|
|
|
|
INSERT INTO kcg.system_config (key, value, description) VALUES
|
|
('partition.raw_metrics.retention_days', '7',
|
|
'raw_metrics 파티션 보관 기간 (일). 초과 시 파티션 DROP.'),
|
|
('partition.raw_metrics.create_ahead_days', '3',
|
|
'미래 파티션 미리 생성 일수.'),
|
|
('partition.scores.cleanup_days', '30',
|
|
'미관측 점수 레코드 정리 기간 (일).'),
|
|
('correlation.max_active_models', '5',
|
|
'동시 활성 모델 최대 수.')
|
|
ON CONFLICT (key) DO NOTHING;
|