AI_Challenge2026 / scripts /precompute_artifacts.py
SeoYun
Claude Opus 5.5
feat: BAG lite ๋ชจ๋ธ(EHR 5๊ฐœ + ๊ณ ํ˜ˆ์•• PRS) ์ ์šฉ, PRS ์„ ํƒ ์ž…๋ ฅ์นธ ์ถ”๊ฐ€
f666f82
Raw History Blame Contribute Delete
5.42 kB
#!/usr/bin/env python3
"""WEB(HF Spaces) ๋ฐฐํฌ์šฉ ์š”์•ฝ ์‚ฐ์ถœ๋ฌผ์„ ๋ฏธ๋ฆฌ ๊ณ„์‚ฐํ•ด์„œ WEB/artifacts/์— ์ €์žฅํ•œ๋‹ค.
์ด ์Šคํฌ๋ฆฝํŠธ๋Š” ์›๋ณธ EHR ๋ฐ์ดํ„ฐ(data/processed/EHR_BASE_merged.csv ๋“ฑ)์—
์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ์ด ์ €์žฅ์†Œ ํ™˜๊ฒฝ(๋กœ์ปฌ)์—์„œ๋งŒ ์‹คํ–‰ํ•œ๋‹ค. **HF Spaces์—๋Š” ์ด
์Šคํฌ๋ฆฝํŠธ๋ฅผ ์˜ฌ๋ฆฌ์ง€ ์•Š๊ณ  ์ด ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ๋งŒ๋“  WEB/artifacts/ ๊ฒฐ๊ณผ๋ฌผ๋งŒ ์˜ฌ๋ฆฐ๋‹ค**
- ์›๋ณธ ๊ฐœ์ธ EHR ๋ ˆ์ฝ”๋“œ๋ฅผ ๊ณต๊ฐœ ์›น์— ๋ฐฐํฌํ•˜์ง€ ์•Š๊ธฐ ์œ„ํ•จ์ด๋‹ค. ์‚ฐ์ถœ๋ฌผ์€ ์ „๋ถ€
์•„๋ž˜ ์„ธ ์ข…๋ฅ˜๋ฟ์ด๊ณ , ์–ด๋А ๊ฒƒ๋„ ๊ฐœ์ธ ๋ ˆ์ฝ”๋“œ ์›๋ณธ์ด ์•„๋‹ˆ๋‹ค:
1. feature๋ณ„ ํ‰๊ท /์ค‘๊ฐ„๊ฐ’(์ง‘๊ณ„ ํ†ต๊ณ„ - ๋ช‡ ๋ช…์ด ์–ด๋–ค ๊ฐ’์ธ์ง€๋Š” ์•Œ ์ˆ˜ ์—†์Œ)
2. DM SHAP์šฉ ๋ฐฐ๊ฒฝํ‘œ๋ณธ - shap.kmeans๋กœ 10,000๋ช…์„ 100๊ฐœ์˜ "ํ•ฉ์„ฑ ๋Œ€ํ‘œ์ "
์œผ๋กœ ์••์ถ•ํ•œ ๊ฒƒ(๊ฐ ๋Œ€ํ‘œ์ ์€ ์‹ค์กดํ•˜๋Š” ํŠน์ • ๊ฐœ์ธ์ด ์•„๋‹ˆ๋ผ ๊ตฐ์ง‘ ์ค‘์‹ฌ ์ขŒํ‘œ)
3. Regression/main_regression.py๊ฐ€ ์ด๋ฏธ ์ ํ•ฉํ•ด๋‘” OLS ๊ณ„์ˆ˜,
ML/main_dm.py๊ฐ€ ์ด๋ฏธ ํ•™์Šตํ•ด๋‘” DM ์ฒดํฌํฌ์ธํŠธ(๋‘˜ ๋‹ค ๋ชจ๋ธ ํŒŒ๋ผ๋ฏธํ„ฐ์ผ
๋ฟ ์›๋ณธ ๋ฐ์ดํ„ฐ๊ฐ€ ์•„๋‹˜ - ์ฒดํฌํฌ์ธํŠธ 5๊ฐœ ํ•ฉ์ณ๋„ 580KB๋กœ ์•„์ฃผ ์ž‘์Œ)
BAG๋Š” lite ๋ฒ„์ „(ML/config/base_prs_htn.yaml)์„ ์“ด๋‹ค: ์•ž์„  EHR 15๊ฐœ ํšŒ๊ท€์—์„œ
FDR ์œ ์˜ํ–ˆ๋˜ 5๊ฐœ(SEX/HTN/HTNAG/DM/SBP)์— ๊ณ ํ˜ˆ์•• PRS(PRS_HTN)๋ฅผ covariate๋กœ
๋„ฃ์€ Regression/output_prs_htn/effect_sizes.csv(Rยฒ 0.1177 - EHR 15๊ฐœ 0.1186๊ณผ
๊ฑฐ์˜ ๊ฐ™์œผ๋ฉด์„œ ์ž…๋ ฅ์€ 6๊ฐœ๋ฟ). DM์€ PRS ๋ฒ„์ „(ML/config/dm_prs.yaml) ์ฒดํฌํฌ์ธํŠธ๋กœ PRS_DM์„ ๋„ฃ๊ณ 
--prs-dropout 0.3์œผ๋กœ ํ•™์Šตํ•œ ML/checkpoints/dm_prs/(PRS๋ฅผ ์•ˆ ๋„ฃ์œผ๋ฉด [MASK]๋กœ
์˜ˆ์ธก - ๊ทธ [MASK] ์ž„๋ฒ ๋”ฉ๋„ ํ•™์Šต๋ผ ์žˆ์–ด์„œ EHR๋งŒ ์ž…๋ ฅํ•ด๋„ ๋™์ž‘ํ•จ). feature
ํ†ต๊ณ„ํ‘œ์— PRS ํ–‰(ํ‰๊ท /์ค‘๊ฐ„๊ฐ’, z-ํ‘œ์ค€ํ™”๋ผ ~0)์„, DM SHAP ๋ฐฐ๊ฒฝ์ ์— PRS_DM
์ปฌ๋Ÿผ์„ ๊ฐ™์ด ๋„ฃ๋Š”๋‹ค(PRS๋ฅผ ์ž…๋ ฅํ•œ ์‚ฌ๋žŒ์€ PRS๋„ SHAP feature๋กœ ์„ค๋ช…).
WEB ์ชฝ ์ฝ”๋“œ(WEB/utils/patient_data.py ๋“ฑ)๋Š” ์ด ์‚ฐ์ถœ๋ฌผ๋งŒ ์ฝ๊ณ , MultimodalDataset
์ด๋‚˜ ์›๋ณธ CSV/ํ…์ŠคํŠธ ํŒŒ์ผ์„ ์ „ํ˜€ ์ฐธ์กฐํ•˜์ง€ ์•Š๋Š”๋‹ค - T_ID๋กœ ๊ธฐ์กด ์‚ฌ๋žŒ์„
์กฐํšŒํ•˜๋Š” ๊ธฐ๋Šฅ๋„ ๊ทธ๋ž˜์„œ WEB์—๋Š” ์—†๋‹ค(์ž„์˜ EHR ๊ฐ’ ์ž…๋ ฅ๋งŒ ์ง€์›).
์‚ฌ์šฉ๋ฒ• (torch/shap/pandas๊ฐ€ ์žˆ๋Š” hugging_face ํ™˜๊ฒฝ ํ•„์š”, ์ €์žฅ์†Œ ๋ฃจํŠธ์—์„œ ์‹คํ–‰):
/data1/software/anaconda3/envs/hugging_face/bin/python WEB/scripts/precompute_artifacts.py
"""
import shutil
import sys
from pathlib import Path
import pandas as pd
import shap
REPO_ROOT = Path(__file__).resolve().parent.parent.parent
sys.path.insert(0, str(REPO_ROOT))
from src.data_loaders import MultimodalDataset, load_config
ARTIFACTS_DIR = REPO_ROOT / "WEB" / "artifacts"
def save_feature_stats(config_path: str, out_name: str) -> tuple:
"""BASE ์ „์ฒด(fold ์—†์Œ)์—์„œ feature๋ณ„ ํ‰๊ท /์ค‘๊ฐ„๊ฐ’๋งŒ ๋ฝ‘์•„ ์ €์žฅํ•œ๋‹ค.
๋ฐ˜ํ™˜: (features, ๊ทธ df) - df๋Š” ์ด ํ•จ์ˆ˜๋ฅผ ๋ถ€๋ฅธ ๋’ค SHAP ๋ฐฐ๊ฒฝ ๊ณ„์‚ฐ์—๋„
์žฌ์‚ฌ์šฉํ•˜๊ณ  ํ•จ์ˆ˜ ๋ฐ–์œผ๋กœ๋Š” ์•ˆ ๋‚˜๊ฐ(์›๋ณธ์€ ์—ฌ๊ธฐ์„œ๋งŒ ์ž ๊น ๋ฉ”๋ชจ๋ฆฌ์— ์žˆ๋‹ค๊ฐ€ ๋ฒ„๋ ค์ง)."""
config = load_config(config_path)
df = MultimodalDataset(config, cohort="base", folds=None).df
# EHR feature ๋’ค์— PRS feature(PRS_HTN/PRS_DM)๋„ ๋ถ™์ธ๋‹ค - WEB/app.py๋Š”
# config.yaml์˜ prs ์„ค์ •์„ ๋ณด๊ณ  PRS ํ–‰์„ EHR ์ž…๋ ฅ ๋ชฉ๋ก์—์„œ ๋”ฐ๋กœ ๋บ€๋‹ค.
features = config["data"]["ehr"]["features"] + list(config["data"]["prs"]["features"])
stats = pd.DataFrame(
{
"feature": features,
"mean": df[features].mean().to_numpy(),
"median": df[features].median().to_numpy(),
}
)
out_path = ARTIFACTS_DIR / out_name
stats.to_csv(out_path, index=False)
print(f"[INFO] {out_name} ์ €์žฅ ({len(features)}๊ฐœ feature ํ‰๊ท /์ค‘๊ฐ„๊ฐ’): {out_path}")
return features, df
def main():
ARTIFACTS_DIR.mkdir(parents=True, exist_ok=True)
(ARTIFACTS_DIR / "checkpoints").mkdir(exist_ok=True)
print("[INFO] BAG feature ํ†ต๊ณ„ ๊ณ„์‚ฐ ์ค‘ (ML/config/base_prs_htn.yaml)...")
save_feature_stats("ML/config/base_prs_htn.yaml", "bag_feature_stats.csv")
print("[INFO] DM feature ํ†ต๊ณ„ ๊ณ„์‚ฐ ์ค‘ (ML/config/dm_prs.yaml)...")
dm_features, dm_df = save_feature_stats("ML/config/dm_prs.yaml", "dm_feature_stats.csv")
print("[INFO] DM local SHAP ๋ฐฐ๊ฒฝํ‘œ๋ณธ(kmeans 100๊ฐœ ํ•ฉ์„ฑ ๋Œ€ํ‘œ์ ) ๊ณ„์‚ฐ ์ค‘...")
background = shap.kmeans(dm_df[dm_features].to_numpy(dtype="float32"), 100)
background_df = pd.DataFrame(background.data, columns=dm_features)
background_path = ARTIFACTS_DIR / "dm_shap_background.csv"
background_df.to_csv(background_path, index=False)
print(f"[INFO] dm_shap_background.csv ์ €์žฅ ({len(background_df)}๊ฐœ ํ•ฉ์„ฑ ๋Œ€ํ‘œ์ , ์‹ค์ œ ๊ฐœ์ธ ์•„๋‹˜): {background_path}")
print("[INFO] effect_sizes.csv / DM 5-fold ์ฒดํฌํฌ์ธํŠธ ๋ณต์‚ฌ ์ค‘...")
shutil.copy(REPO_ROOT / "Regression/output_prs_htn/effect_sizes.csv", ARTIFACTS_DIR / "effect_sizes.csv")
for fold_id in range(5):
src = REPO_ROOT / "ML/checkpoints/dm_prs" / f"dm_fold{fold_id}.pt"
shutil.copy(src, ARTIFACTS_DIR / "checkpoints" / f"dm_fold{fold_id}.pt")
total_size = sum(f.stat().st_size for f in ARTIFACTS_DIR.rglob("*") if f.is_file())
print(f"\n[INFO] ์™„๋ฃŒ - {ARTIFACTS_DIR} ์ „์ฒด ํฌ๊ธฐ {total_size / 1024:.1f} KB")
print("[INFO] ์ด ํด๋” ์•ˆ์˜ ํŒŒ์ผ๋งŒ WEB ๋ฐฐํฌ์— ํฌํ•จํ•˜๋ฉด ๋จ (์›๋ณธ EHR ๋ฐ์ดํ„ฐ ์—†์Œ)")
if __name__ == "__main__":
main()